diff --git a/cypress/e2e/1-workflows.cy.ts b/cypress/e2e/1-workflows.cy.ts index d14506d17e..d01f046d75 100644 --- a/cypress/e2e/1-workflows.cy.ts +++ b/cypress/e2e/1-workflows.cy.ts @@ -1,6 +1,6 @@ -import { v4 as uuid } from 'uuid'; import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; +import { getUniqueWorkflowName } from '../utils/workflowUtils'; const WorkflowsPage = new WorkflowsPageClass(); const WorkflowPage = new WorkflowPageClass(); @@ -16,7 +16,7 @@ describe('Workflows', () => { WorkflowsPage.getters.newWorkflowButtonCard().should('be.visible'); WorkflowsPage.getters.newWorkflowButtonCard().click(); - cy.createFixtureWorkflow('Test_workflow_1.json', `Empty State Card Workflow ${uuid()}`); + cy.createFixtureWorkflow('Test_workflow_1.json', 'Empty State Card Workflow'); WorkflowPage.getters.workflowTags().should('contain.text', 'some-tag-1'); WorkflowPage.getters.workflowTags().should('contain.text', 'some-tag-2'); @@ -27,7 +27,7 @@ describe('Workflows', () => { cy.visit(WorkflowsPage.url); WorkflowsPage.getters.createWorkflowButton().click(); - cy.createFixtureWorkflow('Test_workflow_2.json', `My New Workflow ${uuid()}`); + cy.createFixtureWorkflow('Test_workflow_2.json', getUniqueWorkflowName('My New Workflow')); WorkflowPage.getters.workflowTags().should('contain.text', 'other-tag-1'); WorkflowPage.getters.workflowTags().should('contain.text', 'other-tag-2'); diff --git a/cypress/e2e/1338-ADO-ndv-missing-input-panel.cy.ts b/cypress/e2e/1338-ADO-ndv-missing-input-panel.cy.ts index baead21d67..dfc635404d 100644 --- a/cypress/e2e/1338-ADO-ndv-missing-input-panel.cy.ts +++ b/cypress/e2e/1338-ADO-ndv-missing-input-panel.cy.ts @@ -1,4 +1,3 @@ -import { v4 as uuid } from 'uuid'; import { NDV, WorkflowPage as WorkflowPageClass } from '../pages'; import { successToast } from '../pages/notifications'; @@ -11,7 +10,7 @@ describe('ADO-1338-ndv-missing-input-panel', () => { }); it('should show the input and output panels when node is missing input and output data', () => { - cy.createFixtureWorkflow('Test_ado_1338.json', uuid()); + cy.createFixtureWorkflow('Test_ado_1338.json'); // Execute the workflow workflowPage.getters.zoomToFitButton().click(); diff --git a/cypress/e2e/16-webhook-node.cy.ts b/cypress/e2e/16-webhook-node.cy.ts index c28046dc17..791a704174 100644 --- a/cypress/e2e/16-webhook-node.cy.ts +++ b/cypress/e2e/16-webhook-node.cy.ts @@ -1,4 +1,4 @@ -import { v4 as uuid } from 'uuid'; +import { nanoid } from 'nanoid'; import { WorkflowPage, NDV, CredentialsModal } from '../pages'; import { cowBase64 } from '../support/binaryTestFiles'; import { BACKEND_BASE_URL, EDIT_FIELDS_SET_NODE_NAME } from '../constants'; @@ -81,28 +81,28 @@ describe('Webhook Trigger node', () => { }); it('should listen for a GET request', () => { - simpleWebhookCall({ method: 'GET', webhookPath: uuid(), executeNow: true }); + simpleWebhookCall({ method: 'GET', webhookPath: nanoid(), executeNow: true }); }); it('should listen for a POST request', () => { - simpleWebhookCall({ method: 'POST', webhookPath: uuid(), executeNow: true }); + simpleWebhookCall({ method: 'POST', webhookPath: nanoid(), executeNow: true }); }); it('should listen for a DELETE request', () => { - simpleWebhookCall({ method: 'DELETE', webhookPath: uuid(), executeNow: true }); + simpleWebhookCall({ method: 'DELETE', webhookPath: nanoid(), executeNow: true }); }); it('should listen for a HEAD request', () => { - simpleWebhookCall({ method: 'HEAD', webhookPath: uuid(), executeNow: true }); + simpleWebhookCall({ method: 'HEAD', webhookPath: nanoid(), executeNow: true }); }); it('should listen for a PATCH request', () => { - simpleWebhookCall({ method: 'PATCH', webhookPath: uuid(), executeNow: true }); + simpleWebhookCall({ method: 'PATCH', webhookPath: nanoid(), executeNow: true }); }); it('should listen for a PUT request', () => { - simpleWebhookCall({ method: 'PUT', webhookPath: uuid(), executeNow: true }); + simpleWebhookCall({ method: 'PUT', webhookPath: nanoid(), executeNow: true }); }); it('should listen for a GET request and respond with Respond to Webhook node', () => { - const webhookPath = uuid(); + const webhookPath = nanoid(); simpleWebhookCall({ method: 'GET', webhookPath, @@ -130,7 +130,7 @@ describe('Webhook Trigger node', () => { }); it('should listen for a GET request and respond custom status code 201', () => { - const webhookPath = uuid(); + const webhookPath = nanoid(); simpleWebhookCall({ method: 'GET', webhookPath, @@ -147,7 +147,7 @@ describe('Webhook Trigger node', () => { }); it('should listen for a GET request and respond with last node', () => { - const webhookPath = uuid(); + const webhookPath = nanoid(); simpleWebhookCall({ method: 'GET', webhookPath, @@ -172,7 +172,7 @@ describe('Webhook Trigger node', () => { }); it('should listen for a GET request and respond with last node binary data', () => { - const webhookPath = uuid(); + const webhookPath = nanoid(); simpleWebhookCall({ method: 'GET', webhookPath, @@ -213,7 +213,7 @@ describe('Webhook Trigger node', () => { }); it('should listen for a GET request and respond with an empty body', () => { - const webhookPath = uuid(); + const webhookPath = nanoid(); simpleWebhookCall({ method: 'GET', webhookPath, @@ -232,7 +232,7 @@ describe('Webhook Trigger node', () => { }); it('should listen for a GET request with Basic Authentication', () => { - const webhookPath = uuid(); + const webhookPath = nanoid(); simpleWebhookCall({ method: 'GET', webhookPath, @@ -275,7 +275,7 @@ describe('Webhook Trigger node', () => { }); it('should listen for a GET request with Header Authentication', () => { - const webhookPath = uuid(); + const webhookPath = nanoid(); simpleWebhookCall({ method: 'GET', webhookPath, diff --git a/cypress/e2e/19-execution.cy.ts b/cypress/e2e/19-execution.cy.ts index 90d1e92576..a09529968e 100644 --- a/cypress/e2e/19-execution.cy.ts +++ b/cypress/e2e/19-execution.cy.ts @@ -1,4 +1,3 @@ -import { v4 as uuid } from 'uuid'; import { NDV, WorkflowExecutionsTab, WorkflowPage as WorkflowPageClass } from '../pages'; import { SCHEDULE_TRIGGER_NODE_NAME, EDIT_FIELDS_SET_NODE_NAME } from '../constants'; import { errorToast, successToast } from '../pages/notifications'; @@ -13,7 +12,7 @@ describe('Execution', () => { }); it('should test manual workflow', () => { - cy.createFixtureWorkflow('Manual_wait_set.json', `Manual wait set ${uuid()}`); + cy.createFixtureWorkflow('Manual_wait_set.json'); // Check workflow buttons workflowPage.getters.executeWorkflowButton().should('be.visible'); @@ -73,7 +72,7 @@ describe('Execution', () => { }); it('should test manual workflow stop', () => { - cy.createFixtureWorkflow('Manual_wait_set.json', `Manual wait set ${uuid()}`); + cy.createFixtureWorkflow('Manual_wait_set.json'); // Check workflow buttons workflowPage.getters.executeWorkflowButton().should('be.visible'); @@ -132,7 +131,7 @@ describe('Execution', () => { }); it('should test webhook workflow', () => { - cy.createFixtureWorkflow('Webhook_wait_set.json', `Webhook wait set ${uuid()}`); + cy.createFixtureWorkflow('Webhook_wait_set.json'); // Check workflow buttons workflowPage.getters.executeWorkflowButton().should('be.visible'); @@ -205,7 +204,7 @@ describe('Execution', () => { }); it('should test webhook workflow stop', () => { - cy.createFixtureWorkflow('Webhook_wait_set.json', `Webhook wait set ${uuid()}`); + cy.createFixtureWorkflow('Webhook_wait_set.json'); // Check workflow buttons workflowPage.getters.executeWorkflowButton().should('be.visible'); @@ -293,7 +292,7 @@ describe('Execution', () => { describe('connections should be colored differently for pinned data', () => { beforeEach(() => { - cy.createFixtureWorkflow('Schedule_pinned.json', `Schedule pinned ${uuid()}`); + cy.createFixtureWorkflow('Schedule_pinned.json'); workflowPage.actions.deselectAll(); workflowPage.getters.zoomToFitButton().click(); @@ -492,10 +491,7 @@ describe('Execution', () => { }); it('should send proper payload for node rerun', () => { - cy.createFixtureWorkflow( - 'Multiple_trigger_node_rerun.json', - `Multiple trigger node rerun ${uuid()}`, - ); + cy.createFixtureWorkflow('Multiple_trigger_node_rerun.json', 'Multiple trigger node rerun'); workflowPage.getters.zoomToFitButton().click(); workflowPage.getters.executeWorkflowButton().click(); @@ -520,10 +516,7 @@ describe('Execution', () => { }); it('should send proper payload for manual node run', () => { - cy.createFixtureWorkflow( - 'Check_manual_node_run_for_pinned_and_rundata.json', - `Check manual node run for pinned and rundata ${uuid()}`, - ); + cy.createFixtureWorkflow('Check_manual_node_run_for_pinned_and_rundata.json'); workflowPage.getters.zoomToFitButton().click(); @@ -576,10 +569,7 @@ describe('Execution', () => { }); it('should successfully execute partial executions with nodes attached to the second output', () => { - cy.createFixtureWorkflow( - 'Test_Workflow_pairedItem_incomplete_manual_bug.json', - 'My test workflow', - ); + cy.createFixtureWorkflow('Test_Workflow_pairedItem_incomplete_manual_bug.json'); cy.intercept('POST', '/rest/workflows/**/run').as('workflowRun'); @@ -599,10 +589,7 @@ describe('Execution', () => { }); it('should execute workflow partially up to the node that has issues', () => { - cy.createFixtureWorkflow( - 'Test_workflow_partial_execution_with_missing_credentials.json', - 'My test workflow', - ); + cy.createFixtureWorkflow('Test_workflow_partial_execution_with_missing_credentials.json'); cy.intercept('POST', '/rest/workflows/**/run').as('workflowRun'); diff --git a/cypress/e2e/2106-ADO-pinned-data-execution-preview.cy.ts b/cypress/e2e/2106-ADO-pinned-data-execution-preview.cy.ts index 6c69f4f79d..23aa216a0b 100644 --- a/cypress/e2e/2106-ADO-pinned-data-execution-preview.cy.ts +++ b/cypress/e2e/2106-ADO-pinned-data-execution-preview.cy.ts @@ -1,4 +1,3 @@ -import { v4 as uuid } from 'uuid'; import { WorkflowExecutionsTab, WorkflowPage as WorkflowPageClass } from '../pages'; import { BACKEND_BASE_URL } from '../constants'; @@ -11,7 +10,7 @@ describe('ADO-2106 connections should be colored correctly for pinned data in ex }); beforeEach(() => { - cy.createFixtureWorkflow('Webhook_set_pinned.json', `Webhook set pinned ${uuid()}`); + cy.createFixtureWorkflow('Webhook_set_pinned.json'); workflowPage.actions.deselectAll(); workflowPage.getters.zoomToFitButton().click(); diff --git a/cypress/e2e/24-ndv-paired-item.cy.ts b/cypress/e2e/24-ndv-paired-item.cy.ts index 3d57a33a3b..2def4173d3 100644 --- a/cypress/e2e/24-ndv-paired-item.cy.ts +++ b/cypress/e2e/24-ndv-paired-item.cy.ts @@ -1,4 +1,3 @@ -import { v4 as uuid } from 'uuid'; import { WorkflowPage, NDV } from '../pages'; const workflowPage = new WorkflowPage(); @@ -7,7 +6,7 @@ const ndv = new NDV(); describe('NDV', () => { beforeEach(() => { workflowPage.actions.visit(); - workflowPage.actions.renameWorkflow(uuid()); + workflowPage.actions.renameWithUniqueName(); workflowPage.actions.saveWorkflowOnButtonClick(); }); diff --git a/cypress/e2e/5-ndv.cy.ts b/cypress/e2e/5-ndv.cy.ts index 35021e7333..b606769ec7 100644 --- a/cypress/e2e/5-ndv.cy.ts +++ b/cypress/e2e/5-ndv.cy.ts @@ -1,4 +1,3 @@ -import { v4 as uuid } from 'uuid'; import { getVisibleSelect } from '../utils'; import { MANUAL_TRIGGER_NODE_DISPLAY_NAME } from '../constants'; import { NDV, WorkflowPage } from '../pages'; @@ -13,7 +12,7 @@ const ndv = new NDV(); describe('NDV', () => { beforeEach(() => { workflowPage.actions.visit(); - workflowPage.actions.renameWorkflow(uuid()); + workflowPage.actions.renameWithUniqueName(); workflowPage.actions.saveWorkflowOnButtonClick(); }); @@ -55,7 +54,7 @@ describe('NDV', () => { }); it('should change input and go back to canvas', () => { - cy.createFixtureWorkflow('NDV-test-select-input.json', `NDV test select input ${uuid()}`); + cy.createFixtureWorkflow('NDV-test-select-input.json', 'NDV test select input'); workflowPage.actions.zoomToFit(); workflowPage.getters.canvasNodes().last().dblclick(); ndv.getters.inputSelect().click(); @@ -157,7 +156,7 @@ describe('NDV', () => { 'prop2', ]; function setupSchemaWorkflow() { - cy.createFixtureWorkflow('Test_workflow_schema_test.json', `NDV test schema view ${uuid()}`); + cy.createFixtureWorkflow('Test_workflow_schema_test.json'); workflowPage.actions.zoomToFit(); workflowPage.actions.openNode('Set'); ndv.actions.execute(); @@ -231,7 +230,7 @@ describe('NDV', () => { it('should display large schema', () => { cy.createFixtureWorkflow( 'Test_workflow_schema_test_pinned_data.json', - `NDV test schema view ${uuid()}`, + 'NDV test schema view 2', ); workflowPage.actions.zoomToFit(); workflowPage.actions.openNode('Set'); @@ -306,7 +305,7 @@ describe('NDV', () => { it('should display parameter hints correctly', () => { workflowPage.actions.visit(); - cy.createFixtureWorkflow('Test_workflow_3.json', 'My test workflow'); + cy.createFixtureWorkflow('Test_workflow_3.json', 'My test workflow 1'); workflowPage.actions.openNode('Set1'); ndv.actions.typeIntoParameterInput('value', '='); // switch to expressions @@ -574,7 +573,7 @@ describe('NDV', () => { }); it('should show node name and version in settings', () => { - cy.createFixtureWorkflow('Test_workflow_ndv_version.json', `NDV test version ${uuid()}`); + cy.createFixtureWorkflow('Test_workflow_ndv_version.json', 'NDV test version'); workflowPage.actions.openNode('Edit Fields (old)'); ndv.actions.openSettings(); @@ -711,7 +710,7 @@ describe('NDV', () => { }; cy.createFixtureWorkflow( 'open_node_creator_for_connection.json', - `open_node_creator_for_connection ${uuid()}`, + 'open_node_creator_for_connection', ); Object.entries(hintMapper).forEach(([node, group]) => { @@ -742,7 +741,7 @@ describe('NDV', () => { it('should allow selecting item for expressions', () => { workflowPage.actions.visit(); - cy.createFixtureWorkflow('Test_workflow_3.json', 'My test workflow'); + cy.createFixtureWorkflow('Test_workflow_3.json', 'My test workflow 2'); workflowPage.actions.openNode('Set'); ndv.actions.typeIntoParameterInput('value', '='); // switch to expressions diff --git a/cypress/package.json b/cypress/package.json index 7052418fb0..7740b5483f 100644 --- a/cypress/package.json +++ b/cypress/package.json @@ -15,7 +15,6 @@ }, "devDependencies": { "@types/lodash": "^4.14.195", - "@types/uuid": "^8.3.2", "eslint-plugin-cypress": "^3.3.0", "n8n-workflow": "workspace:*" }, @@ -26,7 +25,7 @@ "cypress-otp": "^1.0.3", "cypress-real-events": "^1.12.0", "lodash": "4.17.21", - "start-server-and-test": "^2.0.3", - "uuid": "8.3.2" + "nanoid": "3.3.6", + "start-server-and-test": "^2.0.3" } } diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts index e3a75c508d..0c2a269607 100644 --- a/cypress/pages/workflow.ts +++ b/cypress/pages/workflow.ts @@ -1,5 +1,6 @@ import { META_KEY } from '../constants'; import { getVisibleSelect } from '../utils'; +import { getUniqueWorkflowName } from '../utils/workflowUtils'; import { BasePage } from './base'; import { NodeCreator } from './features/node-creator'; @@ -311,6 +312,9 @@ export class WorkflowPage extends BasePage { cy.get('body').type(newName); cy.get('body').type('{enter}'); }, + renameWithUniqueName: () => { + this.actions.renameWorkflow(getUniqueWorkflowName()); + }, addTags: (tags: string | string[]) => { if (!Array.isArray(tags)) tags = [tags]; diff --git a/cypress/scripts/run-e2e.js b/cypress/scripts/run-e2e.js index 8da6b5a857..05ddae1a9f 100755 --- a/cypress/scripts/run-e2e.js +++ b/cypress/scripts/run-e2e.js @@ -59,10 +59,13 @@ switch (scenario) { }); break; case 'all': + const specSuiteFilter = process.argv[3]; + const specParam = specSuiteFilter ? ` --spec **/*${specSuiteFilter}*` : ''; + runTests({ startCommand: 'start', url: 'http://localhost:5678/favicon.ico', - testCommand: 'cypress run --headless', + testCommand: `cypress run --headless ${specParam}`, }); break; default: diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index da875e3cae..e58f92c308 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -9,6 +9,7 @@ import { INSTANCE_OWNER, N8N_AUTH_COOKIE, } from '../constants'; +import { getUniqueWorkflowName } from '../utils/workflowUtils'; Cypress.Commands.add('setAppDate', (targetDate: number | Date) => { cy.window().then((win) => { @@ -24,17 +25,22 @@ Cypress.Commands.add('getByTestId', (selector, ...args) => { return cy.get(`[data-test-id="${selector}"]`, ...args); }); -Cypress.Commands.add('createFixtureWorkflow', (fixtureKey, workflowName) => { - const workflowPage = new WorkflowPage(); +Cypress.Commands.add( + 'createFixtureWorkflow', + (fixtureKey: string, workflowName = getUniqueWorkflowName()) => { + const workflowPage = new WorkflowPage(); - // We need to force the click because the input is hidden - workflowPage.getters.workflowImportInput().selectFile(`fixtures/${fixtureKey}`, { force: true }); + // We need to force the click because the input is hidden + workflowPage.getters + .workflowImportInput() + .selectFile(`fixtures/${fixtureKey}`, { force: true }); - cy.waitForLoad(false); - workflowPage.actions.setWorkflowName(workflowName); - workflowPage.getters.saveButton().should('contain', 'Saved'); - workflowPage.actions.zoomToFit(); -}); + cy.waitForLoad(false); + workflowPage.actions.setWorkflowName(workflowName); + workflowPage.getters.saveButton().should('contain', 'Saved'); + workflowPage.actions.zoomToFit(); + }, +); Cypress.Commands.add( 'findChildByTestId', diff --git a/cypress/support/index.ts b/cypress/support/index.ts index a8865fa1ea..6e16f6c2b9 100644 --- a/cypress/support/index.ts +++ b/cypress/support/index.ts @@ -25,7 +25,13 @@ declare global { ...args: Array | undefined> ): Chainable>; findChildByTestId(childTestId: string): Chainable>; - createFixtureWorkflow(fixtureKey: string, workflowName: string): void; + /** + * Creates a workflow from the given fixture and optionally renames it. + * + * @param fixtureKey + * @param [workflowName] Optional name for the workflow. A random nanoid is used if not given + */ + createFixtureWorkflow(fixtureKey: string, workflowName?: string): void; /** @deprecated */ signin(payload: SigninPayload): void; signinAsOwner(): void; diff --git a/cypress/utils/workflowUtils.ts b/cypress/utils/workflowUtils.ts new file mode 100644 index 0000000000..5001dbe1b6 --- /dev/null +++ b/cypress/utils/workflowUtils.ts @@ -0,0 +1,5 @@ +import { nanoid } from 'nanoid'; + +export function getUniqueWorkflowName(workflowNamePrefix?: string) { + return workflowNamePrefix ? `${workflowNamePrefix} ${nanoid(12)}` : nanoid(12); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f89725ecb6..a170404683 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,19 +140,16 @@ importers: lodash: specifier: 4.17.21 version: 4.17.21 + nanoid: + specifier: 3.3.6 + version: 3.3.6 start-server-and-test: specifier: ^2.0.3 version: 2.0.3 - uuid: - specifier: 8.3.2 - version: 8.3.2 devDependencies: '@types/lodash': specifier: ^4.14.195 version: 4.14.195 - '@types/uuid': - specifier: ^8.3.2 - version: 8.3.4 eslint-plugin-cypress: specifier: ^3.3.0 version: 3.3.0(eslint@8.57.0)