From 90985fd648a65d46b9a3c80ab2be4ec02710c3ec Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Thu, 1 Dec 2022 10:59:43 +0100 Subject: [PATCH 01/18] test(editor): Fix default-owner and credentials saving e2e specs --- cypress/e2e/3-default-owner.cy.ts | 4 ++-- cypress/pages/modals/credentials-modal.ts | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/cypress/e2e/3-default-owner.cy.ts b/cypress/e2e/3-default-owner.cy.ts index 59500954f15be..97de12f143874 100644 --- a/cypress/e2e/3-default-owner.cy.ts +++ b/cypress/e2e/3-default-owner.cy.ts @@ -106,9 +106,9 @@ describe('Default owner', () => { describe('can click back to main menu and have migrated credential after setup', () => { mainSidebar.actions.goToCredentials(); - cy.url().should('include', workflowsPage.url); + cy.url().should('include', credentialsPage.url); - workflowsPage.getters.workflowCards().should('have.length', 1); + credentialsPage.getters.credentialCards().should('have.length', 1); }); }); }); diff --git a/cypress/pages/modals/credentials-modal.ts b/cypress/pages/modals/credentials-modal.ts index 39fd613065e5e..db1979df9a387 100644 --- a/cypress/pages/modals/credentials-modal.ts +++ b/cypress/pages/modals/credentials-modal.ts @@ -13,7 +13,7 @@ export class CredentialsModal extends BasePage { .find('.n8n-input input'), name: () => cy.getByTestId('credential-name'), nameInput: () => cy.getByTestId('credential-name').find('input'), - saveButton: () => cy.getByTestId('credential-save-button'), + saveButton: () => cy.getByTestId('credential-save-button', { timeout: 5000 }), closeButton: () => this.getters.editCredentialModal().find('.el-dialog__close').first(), }; actions = { @@ -23,10 +23,9 @@ export class CredentialsModal extends BasePage { }, save: () => { cy.intercept('POST', '/rest/credentials').as('saveCredential'); - cy.intercept('POST', '/rest/credentials/test').as('testCredential'); this.getters.saveButton().click(); - cy.wait('@saveCredential').wait('@testCredential'); + cy.wait('@saveCredential'); this.getters.saveButton().should('contain.text', 'Saved'); }, close: () => { From ca9d1540e58c5d816da6ec23e7a88a51d94f5ba6 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Thu, 1 Dec 2022 12:05:20 +0100 Subject: [PATCH 02/18] test(editor): fix webhook node test --- cypress/e2e/5-ndv.cy.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index 69f25fd361c4e..208d894c73034 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -29,14 +29,6 @@ describe('NDV', () => { ndv.getters.nodeExecuteButton().first().click(); ndv.getters.copyInput().click(); - cy.wrap(Cypress.automation('remote:debugger:protocol', { - command: 'Browser.grantPermissions', - params: { - permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'], - origin: window.location.origin, - }, - })); - cy.window().its('navigator.permissions') .invoke('query', {name: 'clipboard-read'}) .its('state').should('equal', 'granted'); From 943f9f31964e694ab6cf43dbae3a6f54a79fabc0 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Thu, 1 Dec 2022 12:53:52 +0100 Subject: [PATCH 03/18] test(editor): add cy command for browser permissions --- cypress/e2e/5-ndv.cy.ts | 6 ++---- cypress/support/commands.ts | 12 ++++++++++++ cypress/support/index.ts | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index 208d894c73034..1c8015f91a798 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -22,16 +22,14 @@ describe('NDV', () => { ndv.getters.container().should('not.be.visible'); }); - it('should test webhook node', () => { + it.only('should test webhook node', () => { workflowPage.actions.addInitialNodeToCanvas('Webhook'); workflowPage.getters.canvasNodes().first().dblclick(); ndv.getters.nodeExecuteButton().first().click(); ndv.getters.copyInput().click(); - cy.window().its('navigator.permissions') - .invoke('query', {name: 'clipboard-read'}) - .its('state').should('equal', 'granted'); + cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite'); cy.window().its('navigator.clipboard').invoke('readText').then(url => { cy.request({ diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 7637577e0d925..fefd12b8a8895 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -128,3 +128,15 @@ Cypress.Commands.add('resetAll', () => { Cypress.Commands.add('setupOwner', (payload) => { cy.task('setup-owner', payload); }); + +Cypress.Commands.add('grantBrowserPermissions', (...permissions: string[]) => { + if(Cypress.isBrowser('chrome')) { + cy.wrap(Cypress.automation('remote:debugger:protocol', { + command: 'Browser.grantPermissions', + params: { + permissions, + origin: window.location.origin, + }, + })); + } +}); diff --git a/cypress/support/index.ts b/cypress/support/index.ts index afb2dee0c1272..b6d85d60d5838 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -24,6 +24,7 @@ declare global { setupOwner(payload: SetupPayload): void; skipSetup(): void; resetAll(): void; + grantBrowserPermissions(...permissions: string[]): void; } } } From 6c6d349a52bc8702a10947e7c9af93301f4b9a07 Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Thu, 1 Dec 2022 12:58:35 +0100 Subject: [PATCH 04/18] test(editor): add cy command for reading clipboard --- cypress/e2e/5-ndv.cy.ts | 4 ++-- cypress/support/commands.ts | 2 ++ cypress/support/index.ts | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index 1c8015f91a798..c9fee28e2b8f6 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -22,7 +22,7 @@ describe('NDV', () => { ndv.getters.container().should('not.be.visible'); }); - it.only('should test webhook node', () => { + it('should test webhook node', () => { workflowPage.actions.addInitialNodeToCanvas('Webhook'); workflowPage.getters.canvasNodes().first().dblclick(); @@ -31,7 +31,7 @@ describe('NDV', () => { cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite'); - cy.window().its('navigator.clipboard').invoke('readText').then(url => { + cy.readClipboard().then(url => { cy.request({ method: 'GET', url, diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index fefd12b8a8895..e8845e43fb3b6 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -140,3 +140,5 @@ Cypress.Commands.add('grantBrowserPermissions', (...permissions: string[]) => { })); } }); + +Cypress.Commands.add('readClipboard', () => cy.window().its('navigator.clipboard').invoke('readText')); diff --git a/cypress/support/index.ts b/cypress/support/index.ts index b6d85d60d5838..a5dbbf2a3110e 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -25,6 +25,7 @@ declare global { skipSetup(): void; resetAll(): void; grantBrowserPermissions(...permissions: string[]): void; + readClipboard(): Chainable; } } } From 49ba8a0d3c61af808500f77bebe40399462e004f Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Thu, 1 Dec 2022 13:34:41 +0100 Subject: [PATCH 05/18] Fix 3-default-owner spec --- cypress/e2e/3-default-owner.cy.ts | 112 +++++++++++++------------- cypress/pages/sidebar/main-sidebar.ts | 15 +++- 2 files changed, 69 insertions(+), 58 deletions(-) diff --git a/cypress/e2e/3-default-owner.cy.ts b/cypress/e2e/3-default-owner.cy.ts index 97de12f143874..4c654df61c847 100644 --- a/cypress/e2e/3-default-owner.cy.ts +++ b/cypress/e2e/3-default-owner.cy.ts @@ -26,90 +26,92 @@ const lastName = randLastName(); describe('Default owner', () => { // todo test should redirect to setup if have not skipped - beforeEach(() => { + before(() => { cy.resetAll(); }); + beforeEach(() => { + cy.skipSetup(); + }) - it('should be able to use n8n without user management and setup UM', () => { - describe('should skip owner setup', () => { - cy.skipSetup(); - cy.url().should('include', workflowsPage.url); - }); + it('should skip owner setup', () => { + cy.visit(workflowsPage.url); + cy.url().should('include', workflowsPage.url); + }); - describe('should be able to create workflows', () => { - workflowsPage.getters.newWorkflowButtonCard().should('be.visible'); - workflowsPage.getters.newWorkflowButtonCard().click(); + it('should be able to create workflows', () => { + workflowsPage.getters.newWorkflowButtonCard().should('be.visible'); + workflowsPage.getters.newWorkflowButtonCard().click(); - cy.createFixtureWorkflow('Test_workflow_1.json', `Test workflow`); + cy.createFixtureWorkflow('Test_workflow_1.json', `Test workflow`); - // reload page, ensure owner still has access - cy.reload(); + // reload page, ensure owner still has access + cy.reload(); - workflowPage.getters.workflowNameInput().should('contain.value', 'Test workflow'); - }); + workflowPage.getters.workflowNameInput().should('contain.value', 'Test workflow'); + }); - describe('should be able to add new credentials', () => { - cy.visit(credentialsPage.url); + it('should be able to add new credentials', () => { + cy.visit(credentialsPage.url); - credentialsPage.getters.emptyListCreateCredentialButton().click(); + credentialsPage.getters.emptyListCreateCredentialButton().click(); - credentialsModal.getters.newCredentialModal().should('be.visible'); - credentialsModal.getters.newCredentialTypeSelect().should('be.visible'); - credentialsModal.getters.newCredentialTypeOption('Notion API').click(); + credentialsModal.getters.newCredentialModal().should('be.visible'); + credentialsModal.getters.newCredentialTypeSelect().should('be.visible'); + credentialsModal.getters.newCredentialTypeOption('Notion API').click(); - credentialsModal.getters.newCredentialTypeButton().click(); + credentialsModal.getters.newCredentialTypeButton().click(); - credentialsModal.getters.connectionParameter('API Key').type('1234567890'); + credentialsModal.getters.connectionParameter('API Key').type('1234567890'); - credentialsModal.actions.setName('My awesome Notion account'); - credentialsModal.actions.save(); + credentialsModal.actions.setName('My awesome Notion account'); + credentialsModal.actions.save(); - credentialsModal.actions.close(); + credentialsModal.actions.close(); - credentialsModal.getters.newCredentialModal().should('not.exist'); - credentialsModal.getters.editCredentialModal().should('not.exist'); + credentialsModal.getters.newCredentialModal().should('not.exist'); + credentialsModal.getters.editCredentialModal().should('not.exist'); - credentialsPage.getters.credentialCards().should('have.length', 1); - }); + credentialsPage.getters.credentialCards().should('have.length', 1); + }); - describe('should be able to setup UM from settings', () => { - mainSidebar.getters.settings().should('be.visible'); - mainSidebar.actions.goToSettings(); - cy.url().should('include', settingsUsersPage.url); + it('should be able to setup UM from settings', () => { + mainSidebar.getters.settings().should('be.visible'); + mainSidebar.actions.goToSettings(); + cy.url().should('include', settingsUsersPage.url); - settingsUsersPage.actions.goToOwnerSetup(); + settingsUsersPage.actions.goToOwnerSetup(); - cy.url().should('include', signupPage.url); - }); + cy.url().should('include', signupPage.url); + }); - describe('should be able to setup instance and migrate workflows and credentials', () => { - cy.setup({ email, firstName, lastName, password }); + it('should be able to setup instance and migrate workflows and credentials', () => { + cy.setup({ email, firstName, lastName, password }); - messageBox.getters.content().should('contain.text', '1 existing workflow and 1 credential') + messageBox.getters.content().should('contain.text', '1 existing workflow and 1 credential') - messageBox.actions.confirm(); - }); + messageBox.actions.confirm(); + cy.url().should('include', settingsUsersPage.url); + }); - describe('should be redirected back to users page after setup', () => { - cy.url().should('include', settingsUsersPage.url); - // todo test users and that owner exist - }); + it('can click back to workflows and have migrated workflow after setup', () => { + cy.signin({ email, password }); + cy.visit(settingsUsersPage.url); + settingsSidebar.actions.back(); - describe('can click back to workflows and have migrated workflow after setup', () => { - settingsSidebar.actions.back(); + cy.url().should('include', workflowsPage.url); - cy.url().should('include', workflowsPage.url); + workflowsPage.getters.workflowCards().should('have.length', 1); + }); - workflowsPage.getters.workflowCards().should('have.length', 1); - }); + it('can click back to main menu and have migrated credential after setup', () => { + cy.signin({ email, password }); + cy.visit(workflowsPage.url); - describe('can click back to main menu and have migrated credential after setup', () => { - mainSidebar.actions.goToCredentials(); + mainSidebar.actions.goToCredentials(); - cy.url().should('include', credentialsPage.url); + cy.url().should('include', credentialsPage.url); - credentialsPage.getters.credentialCards().should('have.length', 1); - }); + credentialsPage.getters.credentialCards().should('have.length', 1); }); }); diff --git a/cypress/pages/sidebar/main-sidebar.ts b/cypress/pages/sidebar/main-sidebar.ts index 85e853627a5f0..c394eb4f08576 100644 --- a/cypress/pages/sidebar/main-sidebar.ts +++ b/cypress/pages/sidebar/main-sidebar.ts @@ -2,14 +2,23 @@ import { BasePage } from "../base"; export class MainSidebar extends BasePage { getters = { - settings: () => cy.getByTestId('menu-item-settings', { timeout: 5000 }), + settings: () => cy.getByTestId('menu-item-settings'), templates: () => cy.getByTestId('menu-item-templates'), workflows: () => cy.getByTestId('menu-item-workflows'), credentials: () => cy.getByTestId('menu-item-credentials'), executions: () => cy.getByTestId('menu-item-executions'), }; actions = { - goToSettings: () => this.getters.settings().click(), - goToCredentials: () => this.getters.credentials().click(), + goToSettings: () => { + // For some reason Cypress gets confused when clicking on the settings menu item + // and it pickups element that will get detached with the sidebar animation + // so we need to wait for it to happen first + cy.wait(500) + this.getters.settings().click(); + }, + goToCredentials: () => { + cy.wait(500) + this.getters.credentials().click() + }, }; } From 114c514b9e80c97781d2452a5411bdd9a0d409d2 Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 12:47:45 +0100 Subject: [PATCH 06/18] Resolve review comments --- cypress/e2e/3-default-owner.cy.ts | 7 ++----- cypress/pages/modals/credentials-modal.ts | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/cypress/e2e/3-default-owner.cy.ts b/cypress/e2e/3-default-owner.cy.ts index 4c654df61c847..8c5ab3b6021dc 100644 --- a/cypress/e2e/3-default-owner.cy.ts +++ b/cypress/e2e/3-default-owner.cy.ts @@ -24,18 +24,15 @@ const firstName = randFirstName(); const lastName = randLastName(); describe('Default owner', () => { - // todo test should redirect to setup if have not skipped - before(() => { cy.resetAll(); }); beforeEach(() => { - cy.skipSetup(); + cy.visit('/'); }) it('should skip owner setup', () => { - cy.visit(workflowsPage.url); - cy.url().should('include', workflowsPage.url); + cy.skipSetup(); }); it('should be able to create workflows', () => { diff --git a/cypress/pages/modals/credentials-modal.ts b/cypress/pages/modals/credentials-modal.ts index db1979df9a387..c249d528bde6c 100644 --- a/cypress/pages/modals/credentials-modal.ts +++ b/cypress/pages/modals/credentials-modal.ts @@ -13,7 +13,7 @@ export class CredentialsModal extends BasePage { .find('.n8n-input input'), name: () => cy.getByTestId('credential-name'), nameInput: () => cy.getByTestId('credential-name').find('input'), - saveButton: () => cy.getByTestId('credential-save-button', { timeout: 5000 }), + saveButton: () => cy.getByTestId('credential-save-button', { timeout: 7000 }), closeButton: () => this.getters.editCredentialModal().find('.el-dialog__close').first(), }; actions = { From e2e40cdb2afa9a05ec5c5ed5b98348d84ad2f01d Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 12:53:23 +0100 Subject: [PATCH 07/18] Merge spec --- cypress/e2e/3-default-owner.cy.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cypress/e2e/3-default-owner.cy.ts b/cypress/e2e/3-default-owner.cy.ts index 8c5ab3b6021dc..1fe111edc1d80 100644 --- a/cypress/e2e/3-default-owner.cy.ts +++ b/cypress/e2e/3-default-owner.cy.ts @@ -88,11 +88,6 @@ describe('Default owner', () => { messageBox.actions.confirm(); cy.url().should('include', settingsUsersPage.url); - }); - - it('can click back to workflows and have migrated workflow after setup', () => { - cy.signin({ email, password }); - cy.visit(settingsUsersPage.url); settingsSidebar.actions.back(); cy.url().should('include', workflowsPage.url); From e9565da58213319237c32c7d7b424da4f7627ebf Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 13:52:40 +0100 Subject: [PATCH 08/18] Fix http node and expression editor modal specs --- cypress/e2e/4-http-request-node.cy.ts | 4 ++-- cypress/e2e/9-expression-editor-modal.cy.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cypress/e2e/4-http-request-node.cy.ts b/cypress/e2e/4-http-request-node.cy.ts index e0803a93a6c64..e4a78205d4338 100644 --- a/cypress/e2e/4-http-request-node.cy.ts +++ b/cypress/e2e/4-http-request-node.cy.ts @@ -15,10 +15,10 @@ describe('HTTP Request node', () => { WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger'); WorkflowPage.actions.addNodeToCanvas('HTTP Request'); WorkflowPage.actions.openNodeNdv('HTTP Request'); - WorkflowPage.actions.typeIntoParameterInput('url', 'https://google.com'); + WorkflowPage.actions.typeIntoParameterInput('url', 'https://catfact.ninja/fact'); WorkflowPage.actions.executeNodeFromNdv(); - WorkflowPage.getters.ndvOutputPanel().contains(''); + WorkflowPage.getters.ndvOutputPanel().contains('fact'); }); }); diff --git a/cypress/e2e/9-expression-editor-modal.cy.ts b/cypress/e2e/9-expression-editor-modal.cy.ts index f6aabae4df70a..9e0db82665a72 100644 --- a/cypress/e2e/9-expression-editor-modal.cy.ts +++ b/cypress/e2e/9-expression-editor-modal.cy.ts @@ -4,7 +4,7 @@ const WorkflowPage = new WorkflowPageClass(); describe('Expression editor modal', () => { before(() => { - cy.task('db:reset'); + cy.task('reset'); cy.skipSetup(); }); From 88ab246e49a805b3eb05ad5a842e1374cd22f0ac Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 14:01:13 +0100 Subject: [PATCH 09/18] Add optional param to credentials modal saving action to wait for the test endpoint --- cypress/e2e/2-credentials.cy.ts | 2 +- cypress/e2e/3-default-owner.cy.ts | 2 +- cypress/pages/modals/credentials-modal.ts | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/cypress/e2e/2-credentials.cy.ts b/cypress/e2e/2-credentials.cy.ts index 8e6a030904bea..139dbd863b961 100644 --- a/cypress/e2e/2-credentials.cy.ts +++ b/cypress/e2e/2-credentials.cy.ts @@ -38,7 +38,7 @@ describe('Credentials', () => { credentialsModal.getters.connectionParameter('API Key').type('1234567890'); credentialsModal.actions.setName('My awesome Notion account'); - credentialsModal.actions.save(); + credentialsModal.actions.save(true); credentialsModal.actions.close(); credentialsPage.getters.credentialCards().should('have.length', 1); diff --git a/cypress/e2e/3-default-owner.cy.ts b/cypress/e2e/3-default-owner.cy.ts index 1fe111edc1d80..e6e0ea132440a 100644 --- a/cypress/e2e/3-default-owner.cy.ts +++ b/cypress/e2e/3-default-owner.cy.ts @@ -61,7 +61,7 @@ describe('Default owner', () => { credentialsModal.getters.connectionParameter('API Key').type('1234567890'); credentialsModal.actions.setName('My awesome Notion account'); - credentialsModal.actions.save(); + credentialsModal.actions.save(true); credentialsModal.actions.close(); diff --git a/cypress/pages/modals/credentials-modal.ts b/cypress/pages/modals/credentials-modal.ts index c249d528bde6c..0e62f8151e473 100644 --- a/cypress/pages/modals/credentials-modal.ts +++ b/cypress/pages/modals/credentials-modal.ts @@ -21,11 +21,16 @@ export class CredentialsModal extends BasePage { this.getters.name().click(); this.getters.nameInput().clear().type(name); }, - save: () => { + save: (test = false) => { cy.intercept('POST', '/rest/credentials').as('saveCredential'); + if(test) { + cy.intercept('POST', '/rest/credentials/test').as('testCredential'); + } this.getters.saveButton().click(); + cy.wait('@saveCredential'); + if(test) cy.wait('@testCredential') this.getters.saveButton().should('contain.text', 'Saved'); }, close: () => { From 1e9517e7d4cb1c28cc41ca495817ae3ed053c01d Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 14:32:13 +0100 Subject: [PATCH 10/18] Improve sidebar items clicking and increase credentials saving timeout --- cypress/pages/modals/credentials-modal.ts | 3 ++- cypress/pages/sidebar/main-sidebar.ts | 21 ++++++++++--------- cypress/pages/workflow.ts | 2 -- .../src/components/N8nMenuItem/MenuItem.vue | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cypress/pages/modals/credentials-modal.ts b/cypress/pages/modals/credentials-modal.ts index 0e62f8151e473..4779e3b775bd6 100644 --- a/cypress/pages/modals/credentials-modal.ts +++ b/cypress/pages/modals/credentials-modal.ts @@ -13,7 +13,8 @@ export class CredentialsModal extends BasePage { .find('.n8n-input input'), name: () => cy.getByTestId('credential-name'), nameInput: () => cy.getByTestId('credential-name').find('input'), - saveButton: () => cy.getByTestId('credential-save-button', { timeout: 7000 }), + // Saving of the credentials takes a while on the CI so we need to increase the timeout + saveButton: () => cy.getByTestId('credential-save-button', { timeout: 15000 }), closeButton: () => this.getters.editCredentialModal().find('.el-dialog__close').first(), }; actions = { diff --git a/cypress/pages/sidebar/main-sidebar.ts b/cypress/pages/sidebar/main-sidebar.ts index c394eb4f08576..d9398961e9ae8 100644 --- a/cypress/pages/sidebar/main-sidebar.ts +++ b/cypress/pages/sidebar/main-sidebar.ts @@ -2,22 +2,23 @@ import { BasePage } from "../base"; export class MainSidebar extends BasePage { getters = { - settings: () => cy.getByTestId('menu-item-settings'), - templates: () => cy.getByTestId('menu-item-templates'), - workflows: () => cy.getByTestId('menu-item-workflows'), - credentials: () => cy.getByTestId('menu-item-credentials'), - executions: () => cy.getByTestId('menu-item-executions'), + menuItem: (menuLabel: string) => cy.getByTestId('menu-item').filter(`:contains("${menuLabel}")`), + settings: () => this.getters.menuItem('Settings'), + templates: () => this.getters.menuItem('Templates'), + workflows: () => this.getters.menuItem('Workflows'), + credentials: () => this.getters.menuItem('Credentials'), + executions: () => this.getters.menuItem('Executions'), }; actions = { goToSettings: () => { - // For some reason Cypress gets confused when clicking on the settings menu item - // and it pickups element that will get detached with the sidebar animation - // so we need to wait for it to happen first - cy.wait(500) + this.getters.settings().should('be.visible'); + // We must wait before ElementUI menu is done with its animations + cy.get('[data-old-overflow]').should('not.exist'); this.getters.settings().click(); }, goToCredentials: () => { - cy.wait(500) + this.getters.credentials().should('be.visible'); + cy.get('[data-old-overflow]').should('not.exist'); this.getters.credentials().click() }, }; diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts index ed68f676856ce..8f82faf3d30fc 100644 --- a/cypress/pages/workflow.ts +++ b/cypress/pages/workflow.ts @@ -22,7 +22,6 @@ export class WorkflowPage extends BasePage { cy.getByTestId(`parameter-input-${parameterName}`), ndvOutputPanel: () => cy.getByTestId('output-panel'), ndvRunDataPaneHeader: () => cy.getByTestId('run-data-pane-header'), - successToast: () => cy.get('.el-notification__title'), activatorSwitch: () => cy.getByTestId('workflow-activate-switch'), workflowMenu: () => cy.getByTestId('workflow-menu'), @@ -34,7 +33,6 @@ export class WorkflowPage extends BasePage { nodeViewRoot: () => cy.getByTestId('node-view-root'), copyPasteInput: () => cy.getByTestId('hidden-copy-paste'), - canvasNodes: () => cy.getByTestId('canvas-node'), }; actions = { visit: () => { diff --git a/packages/design-system/src/components/N8nMenuItem/MenuItem.vue b/packages/design-system/src/components/N8nMenuItem/MenuItem.vue index c1c7f58527e5d..7bcc61341b7bd 100644 --- a/packages/design-system/src/components/N8nMenuItem/MenuItem.vue +++ b/packages/design-system/src/components/N8nMenuItem/MenuItem.vue @@ -30,7 +30,7 @@ [$style.disableActiveStyle]: !isItemActive(child), [$style.active]: isItemActive(child), }" - :data-test-id="`menu-item-${child.id}`" + data-test-id="menu-item" :index="child.id" @click="onItemClick(child)" > @@ -54,7 +54,7 @@ [$style.active]: isItemActive(item), [$style.compact]: compact, }" - :data-test-id="`menu-item-${item.id}`" + data-test-id="menu-item" :index="item.id" @click="onItemClick(item)" > From 88860b23d028c91c6220b20ad278db9ac1e5b37c Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 14:41:34 +0100 Subject: [PATCH 11/18] Rename http e2e spec to fix ordering --- .../e2e/{4-http-request-node.cy.ts => 8-http-request-node.cy.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cypress/e2e/{4-http-request-node.cy.ts => 8-http-request-node.cy.ts} (100%) diff --git a/cypress/e2e/4-http-request-node.cy.ts b/cypress/e2e/8-http-request-node.cy.ts similarity index 100% rename from cypress/e2e/4-http-request-node.cy.ts rename to cypress/e2e/8-http-request-node.cy.ts From 4c4cfe4c886a20f52687de8cc9303554748e9ddd Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 15:25:27 +0100 Subject: [PATCH 12/18] Fix pasting and copying of nodes e2e spec --- cypress/e2e/2-credentials.cy.ts | 2 +- cypress/e2e/3-default-owner.cy.ts | 2 +- cypress/e2e/7-workflow-actions.cy.ts | 8 ++++++-- cypress/pages/modals/credentials-modal.ts | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/cypress/e2e/2-credentials.cy.ts b/cypress/e2e/2-credentials.cy.ts index 139dbd863b961..8e6a030904bea 100644 --- a/cypress/e2e/2-credentials.cy.ts +++ b/cypress/e2e/2-credentials.cy.ts @@ -38,7 +38,7 @@ describe('Credentials', () => { credentialsModal.getters.connectionParameter('API Key').type('1234567890'); credentialsModal.actions.setName('My awesome Notion account'); - credentialsModal.actions.save(true); + credentialsModal.actions.save(); credentialsModal.actions.close(); credentialsPage.getters.credentialCards().should('have.length', 1); diff --git a/cypress/e2e/3-default-owner.cy.ts b/cypress/e2e/3-default-owner.cy.ts index e6e0ea132440a..1fe111edc1d80 100644 --- a/cypress/e2e/3-default-owner.cy.ts +++ b/cypress/e2e/3-default-owner.cy.ts @@ -61,7 +61,7 @@ describe('Default owner', () => { credentialsModal.getters.connectionParameter('API Key').type('1234567890'); credentialsModal.actions.setName('My awesome Notion account'); - credentialsModal.actions.save(true); + credentialsModal.actions.save(); credentialsModal.actions.close(); diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts index 9891d6a387de7..dcf7a92f8183d 100644 --- a/cypress/e2e/7-workflow-actions.cy.ts +++ b/cypress/e2e/7-workflow-actions.cy.ts @@ -92,8 +92,12 @@ describe('Workflow Actions', () => { it('should copy nodes', () => { WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE); - cy.get('body').type('{meta}', { release: false }).type('a'); - cy.get('body').type('{meta}', { release: false }).type('c'); + WorkflowPage.getters.canvasNodes().should('have.have.length', 2); + + cy.get("#node-creator").should('not.exist'); + cy.get('body').type('{meta}', { release: false }).wait(100).type('a') + cy.get('.jtk-drag-selected').should('have.length', 2); + cy.get('body').type('{meta}', { release: false }).wait(100).type('c'); WorkflowPage.getters.successToast().should('exist'); }); diff --git a/cypress/pages/modals/credentials-modal.ts b/cypress/pages/modals/credentials-modal.ts index 4779e3b775bd6..b4046382fcab3 100644 --- a/cypress/pages/modals/credentials-modal.ts +++ b/cypress/pages/modals/credentials-modal.ts @@ -14,7 +14,7 @@ export class CredentialsModal extends BasePage { name: () => cy.getByTestId('credential-name'), nameInput: () => cy.getByTestId('credential-name').find('input'), // Saving of the credentials takes a while on the CI so we need to increase the timeout - saveButton: () => cy.getByTestId('credential-save-button', { timeout: 15000 }), + saveButton: () => cy.getByTestId('credential-save-button', { timeout: 5000 }), closeButton: () => this.getters.editCredentialModal().find('.el-dialog__close').first(), }; actions = { From 9217e7eb3e291c24bd7faf7f4578787a5233f992 Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 16:06:18 +0100 Subject: [PATCH 13/18] Make sure to only access error.cause if it exists --- packages/cli/src/CredentialsHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/CredentialsHelper.ts b/packages/cli/src/CredentialsHelper.ts index cecb8ad5ceba4..1f3824610a39c 100644 --- a/packages/cli/src/CredentialsHelper.ts +++ b/packages/cli/src/CredentialsHelper.ts @@ -695,7 +695,7 @@ export class CredentialsHelper extends ICredentialsHelper { `Received HTTP status code: ${errorResponseData.statusCode}`, }; } - } else if (error.cause.code) { + } else if (error.cause?.code) { return { status: 'Error', message: error.cause.code, From 938d4ca403f8b74c6936ca5b80e2636d123b8bf9 Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 16:10:21 +0100 Subject: [PATCH 14/18] Wait longer for the keyboard press --- cypress/e2e/7-workflow-actions.cy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts index dcf7a92f8183d..40c36905f85a0 100644 --- a/cypress/e2e/7-workflow-actions.cy.ts +++ b/cypress/e2e/7-workflow-actions.cy.ts @@ -95,9 +95,9 @@ describe('Workflow Actions', () => { WorkflowPage.getters.canvasNodes().should('have.have.length', 2); cy.get("#node-creator").should('not.exist'); - cy.get('body').type('{meta}', { release: false }).wait(100).type('a') + cy.get('body').type('{meta}', { release: false }).wait(500).type('a') cy.get('.jtk-drag-selected').should('have.length', 2); - cy.get('body').type('{meta}', { release: false }).wait(100).type('c'); + cy.get('body').type('{meta}', { release: false }).wait(500).type('c'); WorkflowPage.getters.successToast().should('exist'); }); From 9a4734d8137f034f39f3416aac3e587635915d8e Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 16:20:50 +0100 Subject: [PATCH 15/18] Make sure to focus the body when typing --- cypress/e2e/7-workflow-actions.cy.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts index 40c36905f85a0..aed9da2f6b0d9 100644 --- a/cypress/e2e/7-workflow-actions.cy.ts +++ b/cypress/e2e/7-workflow-actions.cy.ts @@ -94,10 +94,9 @@ describe('Workflow Actions', () => { WorkflowPage.actions.addNodeToCanvas(CODE_NODE); WorkflowPage.getters.canvasNodes().should('have.have.length', 2); - cy.get("#node-creator").should('not.exist'); - cy.get('body').type('{meta}', { release: false }).wait(500).type('a') + cy.get('body').click().type('{meta} a', { release: false }); cy.get('.jtk-drag-selected').should('have.length', 2); - cy.get('body').type('{meta}', { release: false }).wait(500).type('c'); + cy.get('body').click().type('{meta} c', { release: false }); WorkflowPage.getters.successToast().should('exist'); }); From febff173cc55efac80e70b3a73fedb08688658eb Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 16:44:32 +0100 Subject: [PATCH 16/18] Try type delay --- cypress/e2e/7-workflow-actions.cy.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts index aed9da2f6b0d9..33b1d046a3b61 100644 --- a/cypress/e2e/7-workflow-actions.cy.ts +++ b/cypress/e2e/7-workflow-actions.cy.ts @@ -94,9 +94,10 @@ describe('Workflow Actions', () => { WorkflowPage.actions.addNodeToCanvas(CODE_NODE); WorkflowPage.getters.canvasNodes().should('have.have.length', 2); - cy.get('body').click().type('{meta} a', { release: false }); + cy.get("#node-creator").should('not.exist'); + cy.get('body').type('{meta}', { delay: 500, release: false }).type('a'); cy.get('.jtk-drag-selected').should('have.length', 2); - cy.get('body').click().type('{meta} c', { release: false }); + cy.get('body').type('{meta}', { delay: 500, release: false }).type('c'); WorkflowPage.getters.successToast().should('exist'); }); From 1873e74ccf92f3639c37c85f3e128c9cea86874e Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 17:36:59 +0100 Subject: [PATCH 17/18] Use meta key based on the running platform --- cypress/e2e/7-workflow-actions.cy.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cypress/e2e/7-workflow-actions.cy.ts b/cypress/e2e/7-workflow-actions.cy.ts index 33b1d046a3b61..495614d569a9a 100644 --- a/cypress/e2e/7-workflow-actions.cy.ts +++ b/cypress/e2e/7-workflow-actions.cy.ts @@ -90,14 +90,16 @@ describe('Workflow Actions', () => { }); it('should copy nodes', () => { + const metaKey = Cypress.platform === 'darwin' ? '{meta}' : '{ctrl}'; + WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE); WorkflowPage.getters.canvasNodes().should('have.have.length', 2); cy.get("#node-creator").should('not.exist'); - cy.get('body').type('{meta}', { delay: 500, release: false }).type('a'); + cy.get('body').type(metaKey, { delay: 500, release: false }).type('a'); cy.get('.jtk-drag-selected').should('have.length', 2); - cy.get('body').type('{meta}', { delay: 500, release: false }).type('c'); + cy.get('body').type(metaKey, { delay: 500, release: false }).type('c'); WorkflowPage.getters.successToast().should('exist'); }); From 8987e89e44fd61ec2fa8c65b935ccc819da59d9c Mon Sep 17 00:00:00 2001 From: Oleg Ivaniv Date: Wed, 7 Dec 2022 17:58:37 +0100 Subject: [PATCH 18/18] Fix flaky workflowTagElements getter --- cypress/pages/workflow.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts index 8f82faf3d30fc..77001f2fc0dd2 100644 --- a/cypress/pages/workflow.ts +++ b/cypress/pages/workflow.ts @@ -9,7 +9,7 @@ export class WorkflowPage extends BasePage { workflowTags: () => cy.getByTestId('workflow-tags'), workflowTagsContainer: () => cy.getByTestId('workflow-tags-container'), workflowTagsInput: () => this.getters.workflowTagsContainer().then(($el) => cy.wrap($el.find('input').first())), - workflowTagElements: () => this.getters.workflowTagsContainer().find('span.tags').children(), + workflowTagElements: () => cy.get('[data-test-id="workflow-tags-container"] span.tags > span'), workflowTagsDropdown: () => cy.getByTestId('workflow-tags-dropdown'), newTagLink: () => cy.getByTestId('new-tag-link'), saveButton: () => cy.getByTestId('workflow-save-button'),