From 140b2f64a2fcbe6d802bfe95f1309237ede7081b Mon Sep 17 00:00:00 2001 From: Manas Kenge Date: Tue, 16 Sep 2025 13:04:40 +0530 Subject: [PATCH 1/2] WIP --- .../volto-workflow-manager/cypress.config.js | 36 +++++++++ .../cypress/e2e/add-state.cy.js | 35 +++++++++ .../cypress/e2e/add-transition.cy.js | 43 ++++++++++ .../cypress/e2e/assign-workflow.cy.js | 29 +++++++ .../cypress/e2e/create-workflow.cy.js | 59 ++++++++++++++ .../cypress/e2e/delete-workflow.cy.js | 35 +++++++++ .../cypress/fixtures/test-workflow-id.json | 4 + .../cypress/support/commands.js | 32 ++++++++ .../cypress/support/e2e.js | 3 + .../cypress/support/workflow-setup.js | 78 +++++++++++++++++++ .../volto-workflow-manager/package.json | 8 +- frontend/pnpm-lock.yaml | 3 + 12 files changed, 364 insertions(+), 1 deletion(-) create mode 100644 frontend/packages/volto-workflow-manager/cypress.config.js create mode 100644 frontend/packages/volto-workflow-manager/cypress/e2e/add-state.cy.js create mode 100644 frontend/packages/volto-workflow-manager/cypress/e2e/add-transition.cy.js create mode 100644 frontend/packages/volto-workflow-manager/cypress/e2e/assign-workflow.cy.js create mode 100644 frontend/packages/volto-workflow-manager/cypress/e2e/create-workflow.cy.js create mode 100644 frontend/packages/volto-workflow-manager/cypress/e2e/delete-workflow.cy.js create mode 100644 frontend/packages/volto-workflow-manager/cypress/fixtures/test-workflow-id.json create mode 100644 frontend/packages/volto-workflow-manager/cypress/support/commands.js create mode 100644 frontend/packages/volto-workflow-manager/cypress/support/e2e.js create mode 100644 frontend/packages/volto-workflow-manager/cypress/support/workflow-setup.js diff --git a/frontend/packages/volto-workflow-manager/cypress.config.js b/frontend/packages/volto-workflow-manager/cypress.config.js new file mode 100644 index 0000000..0634b68 --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress.config.js @@ -0,0 +1,36 @@ +const { defineConfig } = require('cypress'); + +module.exports = defineConfig({ + e2e: { + baseUrl: 'http://localhost:3000', + specPattern: 'cypress/e2e/**/*.cy.{js,jsx,ts,tsx}', + supportFile: 'cypress/support/e2e.js', + viewportWidth: 1280, + viewportHeight: 720, + video: false, + screenshotOnRunFailure: true, + retries: { + runMode: 2, + openMode: 0, + }, + defaultCommandTimeout: 10000, + requestTimeout: 10000, + responseTimeout: 10000, + env: { + API_PATH: 'http://localhost:8080', + BACKEND_HOST: 'localhost', + BACKEND_PORT: 8080, + WORKFLOW_MANAGER_URL: '/controlpanel/workflowmanager', + }, + setupNodeEvents(on, config) { + // implement node event listeners here + on('task', { + log(message) { + // eslint-disable-next-line no-console + console.log(message); + return null; + }, + }); + }, + }, +}); diff --git a/frontend/packages/volto-workflow-manager/cypress/e2e/add-state.cy.js b/frontend/packages/volto-workflow-manager/cypress/e2e/add-state.cy.js new file mode 100644 index 0000000..70b59fd --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress/e2e/add-state.cy.js @@ -0,0 +1,35 @@ +function randomString(length = 5) { + const chars = 'abcdefghijklmnopqrstuvwxyz'; + return Array.from({ length }, () => + chars.charAt(Math.floor(Math.random() * chars.length)), + ).join(''); +} + +describe('Workflow Manager - Add State', () => { + before(() => { + cy.createTestWorkflow(); + }); + + beforeEach(() => { + cy.goToTestWorkflow(); + }); + + it('should add a new state to the workflow', () => { + cy.contains('button', 'Add State').click(); + + cy.get('[role="dialog"]').should('be.visible'); + + cy.get('[role="dialog"]').within(() => { + const stateName = `test-state-${randomString(5)}`; + cy.get('[aria-label="State name"]').type(stateName); + + cy.get('[aria-label="State description"]').type('Test state description'); + + cy.contains('button', 'Add State').click(); + }); + + cy.get('[role="dialog"]').should('not.exist'); + + cy.get('body').should('contain', 'test-state-'); + }); +}); diff --git a/frontend/packages/volto-workflow-manager/cypress/e2e/add-transition.cy.js b/frontend/packages/volto-workflow-manager/cypress/e2e/add-transition.cy.js new file mode 100644 index 0000000..7cd05b3 --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress/e2e/add-transition.cy.js @@ -0,0 +1,43 @@ +function randomString(length = 5) { + const chars = 'abcdefghijklmnopqrstuvwxyz'; + return Array.from({ length }, () => + chars.charAt(Math.floor(Math.random() * chars.length)), + ).join(''); +} + +describe('Workflow Manager - Add Transition', () => { + before(() => { + cy.createTestWorkflow(); + }); + + beforeEach(() => { + cy.goToTestWorkflow(); + }); + + it('should add a new transition to the workflow', () => { + cy.contains('button', 'Add Transition').click(); + + cy.get('[role="dialog"]').should('be.visible'); + + cy.get('[role="dialog"]').within(() => { + cy.get( + '[aria-label="Select the destination state for the transition"]', + ).click(); + }); + + cy.get('[role="option"]').first().click(); + + cy.get('[role="dialog"]').within(() => { + const transitionName = `test-transition-${randomString(5)}`; + cy.get('[aria-label="New transition name"]').type(transitionName); + + cy.get('[aria-label="Transition description"]').type( + 'Test transition description', + ); + + cy.contains('button', 'Add Transition').click(); + }); + + cy.wait(3000); + }); +}); diff --git a/frontend/packages/volto-workflow-manager/cypress/e2e/assign-workflow.cy.js b/frontend/packages/volto-workflow-manager/cypress/e2e/assign-workflow.cy.js new file mode 100644 index 0000000..432202b --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress/e2e/assign-workflow.cy.js @@ -0,0 +1,29 @@ +describe('Workflow Manager - Assign Workflow', () => { + before(() => { + cy.createTestWorkflow(); + }); + + beforeEach(() => { + cy.goToTestWorkflow(); + }); + + it('should assign workflow to content types', () => { + cy.wait(3000); + + cy.contains('button', 'Assign').click(); + + cy.get('[role="dialog"]').should('be.visible'); + + cy.get('[role="dialog"]').within(() => { + cy.contains('button', 'Select').click(); + }); + + cy.get('[role="option"]').first().click(); + + cy.get('[role="dialog"]').within(() => { + cy.contains('button', 'Assign').should('not.be.disabled').click(); + }); + + cy.get('[role="dialog"]').should('not.exist'); + }); +}); diff --git a/frontend/packages/volto-workflow-manager/cypress/e2e/create-workflow.cy.js b/frontend/packages/volto-workflow-manager/cypress/e2e/create-workflow.cy.js new file mode 100644 index 0000000..e15f6fb --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress/e2e/create-workflow.cy.js @@ -0,0 +1,59 @@ +function randomString(length = 5) { + const chars = 'abcdefghijklmnopqrstuvwxyz'; + return Array.from({ length }, () => + chars.charAt(Math.floor(Math.random() * chars.length)), + ).join(''); +} + +describe('Workflow Manager - Create Workflow', () => { + beforeEach(() => { + cy.loginAsAdmin(); + cy.goToWorkflowManager(); + }); + + it('should create a new workflow by cloning a base workflow', () => { + cy.wait(2000); + + cy.get('#toolbar-add-workflow').should('exist').scrollIntoView(); + + cy.get('#toolbar-add-workflow').click({ force: true }); + + cy.get('[role="dialog"]').should('be.visible'); + + cy.get('[role="dialog"]').within(() => { + const workflowName = `test-workflow-${randomString(5)}`; + + cy.get('[aria-label="New workflow name"]').type(workflowName); + + cy.get('[aria-label="Select a workflow to clone from"]').should( + 'be.visible', + ); + cy.get('[aria-label="Select a workflow to clone from"]').click(); + }); + + cy.get('[role="option"]').first().click(); + + cy.get('[role="dialog"]').within(() => { + cy.contains('button', 'Add').should('not.be.disabled').click(); + }); + + cy.url().should('include', 'workflow='); + + cy.get('#toolbar-saving-workflow').should('exist'); + }); + + it('should handle dialog cancellation', () => { + cy.wait(2000); + + cy.get('#toolbar-add-workflow').should('exist').scrollIntoView(); + cy.get('#toolbar-add-workflow').click({ force: true }); + + cy.get('[role="dialog"]').should('be.visible'); + + cy.get('[role="dialog"]').within(() => { + cy.contains('button', 'Cancel').click(); + }); + + cy.get('[role="dialog"]').should('not.exist'); + }); +}); diff --git a/frontend/packages/volto-workflow-manager/cypress/e2e/delete-workflow.cy.js b/frontend/packages/volto-workflow-manager/cypress/e2e/delete-workflow.cy.js new file mode 100644 index 0000000..23fed82 --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress/e2e/delete-workflow.cy.js @@ -0,0 +1,35 @@ +describe('Workflow Manager - Delete Workflow', () => { + before(() => { + cy.createTestWorkflow(); + }); + + beforeEach(() => { + cy.goToTestWorkflow(); + }); + + it('should delete a workflow from the workflow table', () => { + cy.visit('/controlpanel/workflowmanager'); + cy.wait(3000); + + cy.get('[aria-label="Workflow table"]').should('be.visible'); + + cy.readFile('cypress/fixtures/test-workflow-id.json').then((data) => { + const workflowName = data.workflowName || data.workflowId; + + cy.get('[aria-label="Workflow table"]').within(() => { + cy.contains('[role="row"]', workflowName).within(() => { + cy.get('[aria-label="Workflow actions"]').click(); + }); + }); + + cy.get('[role="menu"]').within(() => { + cy.contains('Delete').click(); + }); + + cy.get('[role="dialog"]').should('be.visible'); + cy.get('[role="dialog"]').within(() => { + cy.contains('button', 'Delete').click(); + }); + }); + }); +}); diff --git a/frontend/packages/volto-workflow-manager/cypress/fixtures/test-workflow-id.json b/frontend/packages/volto-workflow-manager/cypress/fixtures/test-workflow-id.json new file mode 100644 index 0000000..e4a981d --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress/fixtures/test-workflow-id.json @@ -0,0 +1,4 @@ +{ + "workflowId": "test-workflow-ddrdsaym", + "workflowName": "test-workflow-ddrdsaym" +} \ No newline at end of file diff --git a/frontend/packages/volto-workflow-manager/cypress/support/commands.js b/frontend/packages/volto-workflow-manager/cypress/support/commands.js new file mode 100644 index 0000000..07baa55 --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress/support/commands.js @@ -0,0 +1,32 @@ +// Custom commands for Workflow Manager e2e testing + +Cypress.Commands.add( + 'loginAsAdmin', + (username = 'admin', password = 'admin') => { + cy.visit('/login'); + + cy.get( + 'input[name="login"], input[name="__ac_name"], input[id="login-name"]', + ) + .first() + .type(username); + + cy.get( + 'input[name="password"], input[name="__ac_password"], input[type="password"]', + ) + .first() + .type(password); + + cy.get( + 'input[type="submit"], button[type="submit"], button:contains("Log in")', + ) + .first() + .click(); + + cy.url().should('not.include', '/login'); + }, +); + +Cypress.Commands.add('goToWorkflowManager', () => { + cy.visit('/controlpanel/workflowmanager'); +}); diff --git a/frontend/packages/volto-workflow-manager/cypress/support/e2e.js b/frontend/packages/volto-workflow-manager/cypress/support/e2e.js new file mode 100644 index 0000000..d3ddfec --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress/support/e2e.js @@ -0,0 +1,3 @@ +// Import commands.js using ES2015 syntax: +import './commands' +import './workflow-setup' diff --git a/frontend/packages/volto-workflow-manager/cypress/support/workflow-setup.js b/frontend/packages/volto-workflow-manager/cypress/support/workflow-setup.js new file mode 100644 index 0000000..45f94d9 --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress/support/workflow-setup.js @@ -0,0 +1,78 @@ +function randomString(length = 5) { + const chars = 'abcdefghijklmnopqrstuvwxyz'; + return Array.from({ length }, () => + chars.charAt(Math.floor(Math.random() * chars.length)), + ).join(''); +} + +Cypress.Commands.add('createTestWorkflow', () => { + cy.loginAsAdmin(); + cy.goToWorkflowManager(); + + cy.wait(2000); + + cy.get('#toolbar-add-workflow') + .should('exist') + .scrollIntoView() + .click({ force: true }); + + cy.get('[role="dialog"]').should('be.visible'); + cy.wait(1000); + + const workflowName = `test-workflow-${randomString(8)}`; + + cy.get('[role="dialog"]').within(() => { + cy.get('[aria-label="New workflow name"]') + .should('be.visible') + .clear() + .type(workflowName); + + cy.get('[aria-label="Select a workflow to clone from"]') + .should('be.visible') + .click(); + }); + + cy.get('[role="option"]').first().click(); + + cy.get('[role="dialog"]').within(() => { + cy.contains('button', 'Add') + .should('not.be.disabled') + .should('be.visible') + .click(); + }); + + cy.url().should('include', 'workflow='); + + cy.url().then((url) => { + const workflowParam = url.split('workflow=')[1]; + if (workflowParam) { + cy.writeFile('cypress/fixtures/test-workflow-id.json', { + workflowId: workflowParam, + workflowName: workflowName, + }); + } + }); + + cy.get('#toolbar-saving-workflow').should('exist'); + cy.log(`Created test workflow: ${workflowName}`); +}); + +Cypress.Commands.add('goToTestWorkflow', () => { + cy.readFile('cypress/fixtures/test-workflow-id.json').then((data) => { + if (!data || !data.workflowId) { + throw new Error( + 'No test workflow found. Make sure createTestWorkflow was called first.', + ); + } + + cy.log( + `Navigating to test workflow: ${data.workflowName || data.workflowId}`, + ); + cy.visit(`/controlpanel/workflowmanager?workflow=${data.workflowId}`); + }); + + cy.wait(1000); + + cy.url().should('include', 'workflowmanager'); + cy.url().should('include', 'workflow='); +}); diff --git a/frontend/packages/volto-workflow-manager/package.json b/frontend/packages/volto-workflow-manager/package.json index de35967..e591c60 100644 --- a/frontend/packages/volto-workflow-manager/package.json +++ b/frontend/packages/volto-workflow-manager/package.json @@ -24,7 +24,12 @@ "dry-release": "release-it --dry-run", "release": "release-it", "release-major-alpha": "release-it major --preRelease=alpha", - "release-alpha": "release-it --preRelease=alpha" + "release-alpha": "release-it --preRelease=alpha", + "cypress:open": "cypress open --config-file cypress.config.js", + "cypress:run": "cypress run --config-file cypress.config.js", + "cypress:run:headless": "cypress run --config-file cypress.config.js --headless", + "test:e2e": "cypress run --config-file cypress.config.js", + "test:e2e:open": "cypress open --config-file cypress.config.js" }, "addons": [], "dependencies": { @@ -53,6 +58,7 @@ "@plone/scripts": "^3.6.1", "@types/react": "^18", "@types/react-dom": "^18", + "cypress": "13.13.2", "release-it": "^17.1.1" } } diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index a0583ce..41a37c0 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -1614,6 +1614,9 @@ importers: '@types/react-dom': specifier: ^18 version: 18.3.7(@types/react@18.3.22) + cypress: + specifier: 13.13.2 + version: 13.13.2 release-it: specifier: ^17.1.1 version: 17.1.1(typescript@5.8.3) From 8b22f63c06c3c8fbb6350f59be7a171ef4ec9fbd Mon Sep 17 00:00:00 2001 From: Manas Kenge Date: Tue, 7 Oct 2025 19:50:11 +0530 Subject: [PATCH 2/2] add sanity check test --- .../cypress/e2e/sanity-check.cy.js | 31 +++++++++++++++++++ .../cypress/fixtures/test-workflow-id.json | 4 --- 2 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 frontend/packages/volto-workflow-manager/cypress/e2e/sanity-check.cy.js delete mode 100644 frontend/packages/volto-workflow-manager/cypress/fixtures/test-workflow-id.json diff --git a/frontend/packages/volto-workflow-manager/cypress/e2e/sanity-check.cy.js b/frontend/packages/volto-workflow-manager/cypress/e2e/sanity-check.cy.js new file mode 100644 index 0000000..cb3e82b --- /dev/null +++ b/frontend/packages/volto-workflow-manager/cypress/e2e/sanity-check.cy.js @@ -0,0 +1,31 @@ +describe('Workflow Manager - Sanity Check', () => { + before(() => { + cy.createTestWorkflow(); + }); + + beforeEach(() => { + cy.goToTestWorkflow(); + }); + + it('should run sanity check and view results', () => { + cy.contains('button', 'Sanity Check', { timeout: 3000 }) + .should('be.visible') + .click(); + + cy.contains('button', 'View Validation Results', { + timeout: 3000, + }) + .should('be.visible') + .click(); + + cy.get('[role="alertdialog"]', { timeout: 3000 }).should('be.visible'); + + cy.get('[role="alertdialog"]').within(() => { + cy.contains('Validation Results').should('be.visible'); + + cy.contains('button', 'Close').click(); + }); + + cy.get('[role="alertdialog"]').should('not.exist'); + }); +}); diff --git a/frontend/packages/volto-workflow-manager/cypress/fixtures/test-workflow-id.json b/frontend/packages/volto-workflow-manager/cypress/fixtures/test-workflow-id.json deleted file mode 100644 index e4a981d..0000000 --- a/frontend/packages/volto-workflow-manager/cypress/fixtures/test-workflow-id.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "workflowId": "test-workflow-ddrdsaym", - "workflowName": "test-workflow-ddrdsaym" -} \ No newline at end of file