diff --git a/cypress/README.md b/cypress/README.md index e24a5bb4030..ea071e8fcb0 100644 --- a/cypress/README.md +++ b/cypress/README.md @@ -29,5 +29,8 @@ cy.getContent() | `getContent` | Get editor content | | | `clearContent` | Clear the editor content | | | `getMenu` | Get editor menu bar | | +| `getMenuEntry` | Same as `getActionEntry` but also searches the overflow menu | `name` | +| `getSubmenuEntry` | Open parent menu and then return sub menu entry | `parent`, `name` | | `getActionEntry` | Get menu entry | `name` | +| `getActionSubEntry` | Get submenu entry (after menu clicked) | `name` | | `openWorkspace` | Open workspace and return Editor content | | diff --git a/cypress/e2e/links.spec.js b/cypress/e2e/links.spec.js index a7d5694d510..2469b59c314 100644 --- a/cypress/e2e/links.spec.js +++ b/cypress/e2e/links.spec.js @@ -52,10 +52,9 @@ describe('test link marks', function() { .then($el => { const id = $el.data('id') - const link = `${Cypress.env('baseUrl')}/file-name?fileId=${id} ` - cy.getContent() - .type('{enter}') - .type(link) + const link = `${Cypress.env('baseUrl')}/file-name?fileId=${id}` + cy.clearContent() + .type(`${link}{enter}`) cy.getContent() .find(`a[href*="${Cypress.env('baseUrl')}"]`) @@ -67,16 +66,16 @@ describe('test link marks', function() { }) }) - it('whithout protocol', () => { - cy.getContent() + it('without protocol', () => { + cy.clearContent() .type('google.com{enter}') - - cy.getContent() - .find('a[href*="google.com"]') - .should('not.exist') + .then(() => cy.getContent() + .find('a[href*="google.com"]') + .should('not.exist') + ) }) - it('whithout space', () => { + it('with protocol but without space', () => { cy.getContent() .type('https://nextcloud.com') @@ -85,4 +84,83 @@ describe('test link marks', function() { .should('not.exist') }) }) + + describe('link menu', function() { + beforeEach(() => cy.clearContent()) + const text = 'some text' + + describe('link to website', function() { + const url = 'https://nextcloud.com/' + // Helper to reduce duplicated code, checking inserting with and without selected text + const checkLinkWebsite = (url, text) => { + cy.getSubmenuEntry('insert-link', 'insert-link-website').click() + cy.getActionSubEntry('insert-link-input').find('input[type="text"]').type(`${url}{enter}`) + cy.getContent() + .get(`a[href*="${url}"]`) + .should('have.text', text) // ensure correct text used + .click({ force: true }) + + cy.get('@winOpen') + .should('have.been.calledOnce') + .should('have.been.calledWith', url) + } + + beforeEach(cy.clearContent) + it('Link website without selection', () => { + cy.getFile(fileName) + .then($el => { + checkLinkWebsite(url, url) + }) + }) + + it('Link website with selection', () => { + cy.getFile(fileName) + .then($el => { + cy.getContent().type(`${text}{selectAll}`) + checkLinkWebsite(url, text) + }) + }) + }) + + describe('link to local file', function() { + // Helper to reduce duplicated code, checking inserting with and without selected text + const checkLinkFile = (filename, text) => { + cy.getSubmenuEntry('insert-link', 'insert-link-file').click() + cy.get('.oc-dialog').find(`tr[data-entryname="${filename}"]`).click() + cy.get('.oc-dialog').find('.oc-dialog-buttonrow > button').click() + + return cy.getContent() + .find(`a[href*="${encodeURIComponent(filename)}"]`) + .should('have.text', text === undefined ? filename : text) + .click({ force: true }) + } + + beforeEach(() => cy.clearContent()) + + it('without text', () => { + cy.getFile(fileName) + .then($el => { + checkLinkFile(fileName) + cy.get('.modal-title').should('include.text', fileName) + }) + }) + it('with selected text', () => { + cy.getFile(fileName) + .then($el => { + cy.getContent().type(`${text}{selectAll}`) + checkLinkFile(fileName, text) + cy.get('.modal-title').should('include.text', fileName) + }) + }) + it('link to directory', () => { + cy.createFolder(`${window.__currentDirectory}/dummy folder`) + cy.getFile(fileName).then($el => { + cy.getContent().type(`${text}{selectAll}`) + checkLinkFile('dummy folder', text) + cy.get('@winOpen') + .should('have.been.calledOnce') + }) + }) + }) + }) }) diff --git a/cypress/e2e/sections.spec.js b/cypress/e2e/sections.spec.js index 21bd7d4cd37..1fd595f910c 100644 --- a/cypress/e2e/sections.spec.js +++ b/cypress/e2e/sections.spec.js @@ -89,12 +89,14 @@ describe('Content Sections', () => { // Create link to top heading cy.clearContent() .type('{selectAll}{backspace}move top\n{selectAll}') - .get('.menububble button[data-text-bubble-action="add-link"]') - .click({ force: true }) .then(() => { - cy.get('.menububble .menububble__input') - .type('{shift}') - .type('#top{enter}', { force: true }) + cy.getSubmenuEntry('insert-link', 'insert-link-website') + .click() + .then(() => { + cy.getActionSubEntry('insert-link-input') + .find('input[type="text"]') + .type('#top{enter}') + }) }) // Insert content above link cy.getContent() diff --git a/cypress/e2e/workspace.spec.js b/cypress/e2e/workspace.spec.js index 57547f1b455..e82bc1a0294 100644 --- a/cypress/e2e/workspace.spec.js +++ b/cypress/e2e/workspace.spec.js @@ -63,45 +63,18 @@ describe('Workspace', function() { ['underline', 'u'], ['strikethrough', 's'], ].forEach(([button, tag]) => { - menuButton(button) + cy.getMenuEntry(button) .click({ force: true }) .should('have.class', 'is-active') cy.getContent() .find(`${tag}`) .should('contain', 'Format me') - menuButton(button) + cy.getMenuEntry(button) .click({ force: true }) .should('not.have.class', 'is-active') }) }) - it('links via menububble', function() { - cy.openWorkspace() - .type('Nextcloud') - .type('{selectall}') - menuBubbleButton('add-link').click() - cy.get('.menububble input').type('https://nextcloud.com{enter}') - cy.getContent() - .find('a') - .should('contain', 'Nextcloud') - .should('be.visible') - cy.getContent() - .find('a').invoke('attr', 'href') - .should('include', 'https://nextcloud.com') - cy.window().then((win) => { - cy.stub(win, 'open').as('windowOpen') - }) - cy.getContent() - .find('a').click() - cy.get('@windowOpen').should('be.calledWith', 'https://nextcloud.com/') - cy.getContent().type('{selectall}') - menuBubbleButton('add-link').click() - cy.get('.menububble input').type('/team{enter}') - cy.getContent() - .find('a').click() - cy.get('@windowOpen').should('be.calledWith', 'https://nextcloud.com/team') - }) - it('creates headings via submenu', function() { cy.openWorkspace() .type('Heading') @@ -109,17 +82,17 @@ describe('Workspace', function() { ;['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].forEach((heading) => { const actionName = `headings-${heading}` - getSubmenuItem('headings', actionName).click() + cy.getSubmenuEntry('headings', actionName).click() cy.getContent() .find(`${heading}`) .should('contain', 'Heading') - getSubmenuItem('headings', actionName) + cy.getSubmenuEntry('headings', actionName) .should('have.class', 'is-active') .click() - menuButton('headings').should('not.have.class', 'is-active') + cy.getMenuEntry('headings').should('not.have.class', 'is-active') }) }) @@ -132,14 +105,14 @@ describe('Workspace', function() { ['ordered-list', 'ol'], ['task-list', 'ul[data-type="taskList"]'], ].forEach(([button, tag]) => { - menuButton(button) + cy.getMenuEntry(button) .click({ force: true }) .should('have.class', 'is-active') cy.getContent() .find(`${tag}`).should('contain', 'List me') - menuButton(button) + cy.getMenuEntry(button) .click({ force: true }) .should('not.have.class', 'is-active') }) @@ -157,7 +130,7 @@ describe('Workspace', function() { cy.openWorkspace() .type('# Let\'s smile together{enter}## ') - menuButton('emoji-picker') + cy.getMenuEntry('emoji-picker') .click() cy.get('#emoji-mart-list button[aria-label="😀, grinning"]') @@ -187,7 +160,7 @@ describe('Workspace', function() { const actionName = `callout-${type}` // enable callout - getSubmenuItem('callouts', actionName) + cy.getSubmenuEntry('callouts', actionName) .click() .then(() => { // check content @@ -196,7 +169,7 @@ describe('Workspace', function() { .should('contain', 'Callout') // disable - return getSubmenuItem('callouts', actionName) + return cy.getSubmenuEntry('callouts', actionName) .should('have.class', 'is-active') .click() }) @@ -209,13 +182,13 @@ describe('Workspace', function() { let last = first // enable callout - getSubmenuItem('callouts', `callout-${first}`) + cy.getSubmenuEntry('callouts', `callout-${first}`) .click() cy.wrap(rest) .each(type => { const actionName = `callout-${type}` - return getSubmenuItem('callouts', actionName) + return cy.getSubmenuEntry('callouts', actionName) .click() .then(() => cy.getContent().find(`.callout.callout--${type}`)) .should('contain', 'Callout') @@ -224,10 +197,10 @@ describe('Workspace', function() { }) }) .then(() => { - getSubmenuItem('callouts', `callout-${last}`) + cy.getSubmenuEntry('callouts', `callout-${last}`) .click() - menuButton('callouts') + cy.getMenuEntry('callouts') .should('not.have.class', 'is-active') }) }) @@ -287,23 +260,6 @@ describe('Workspace', function() { }) }) -const menuButton = (name) => { - return cy.getActionEntry(name) -} - -const submenuButton = (name) => { - return cy.get('.v-popper__wrapper .open').getActionEntry(name) -} - -const menuBubbleButton = (name) => { - return cy.get('[data-text-el="menu-bubble"]').find(`[data-text-bubble-action="${name}"]`) -} - -const getSubmenuItem = (parent, item) => { - menuButton(parent).click() - return submenuButton(item) -} - const openSidebar = filename => { cy.get(`.files-fileList tr[data-file="${filename}"]`) .should('contain', filename) diff --git a/cypress/support/commands.js b/cypress/support/commands.js index 2df78beb3c7..d9632335d37 100644 --- a/cypress/support/commands.js +++ b/cypress/support/commands.js @@ -173,6 +173,7 @@ Cypress.Commands.add('isolateTest', ({ sourceFile = 'text.md', targetFile = null cy.createFolder(folderName) cy.uploadFile(sourceFile, 'text/markdown', `${encodeURIComponent(folderName)}/${targetFile}`) + window.__currentDirectory = folderName return cy.visit(`apps/files?dir=/${encodeURIComponent(folderName)}`, { onBeforeLoad }) .then(() => ({ folderName, fileName: targetFile })) }) @@ -294,11 +295,32 @@ Cypress.Commands.add('getMenu', { prevSubject: 'optional' }, (subject) => { .find('[data-text-el="menubar"]') }) +// Get menu entry even if moved into overflow menu +Cypress.Commands.add('getMenuEntry', (name) => { + cy.getMenu().then(($body) => { + if ($body.find(`[data-text-action-entry="${name}"]`).length) { + return cy.getActionEntry(name) + } + return cy.getSubmenuEntry('remain', name) + }) +}) + +Cypress.Commands.add('getSubmenuEntry', { prevSubject: 'optional' }, (subject, parent, name) => { + return (subject ? cy.wrap(subject) : cy.getMenu()) + .getActionEntry(parent) + .click() + .then(() => cy.getActionSubEntry(name)) +}) + Cypress.Commands.add('getActionEntry', { prevSubject: 'optional' }, (subject, name) => { return (subject ? cy.wrap(subject) : cy.getMenu()) .find(`[data-text-action-entry="${name}"]`) }) +Cypress.Commands.add('getActionSubEntry', (name) => { + return cy.get('.action-item__popper .open').getActionEntry(name) +}) + Cypress.Commands.add('getContent', { prevSubject: 'optional' }, (subject) => { return (subject ? cy.wrap(subject) : cy.getEditor()) .find('.ProseMirror') @@ -315,7 +337,10 @@ Cypress.Commands.add('getTOC', () => { Cypress.Commands.add('clearContent', () => { return cy.getContent() .scrollIntoView() - .type('{selectAll}{backspace}', { force: true }) + .then(() => cy.getContent() + .type('{selectAll}{backspace}') + ) + .then(() => cy.getContent()) }) Cypress.Commands.add('openWorkspace', () => { diff --git a/src/components/Editor.vue b/src/components/Editor.vue index f849ee69290..2829045d95a 100644 --- a/src/components/Editor.vue +++ b/src/components/Editor.vue @@ -55,11 +55,7 @@