From 4000b656dfc7e4037c35d034abf164d9fe497722 Mon Sep 17 00:00:00 2001 From: Mutasem Date: Thu, 24 Nov 2022 15:24:59 +0100 Subject: [PATCH 1/7] add tests --- cypress/e2e/3-default-owner.cy.ts | 76 +++++++++++++++++-- cypress/pages/index.ts | 1 + cypress/pages/modals/credentials-modal.ts | 9 ++- cypress/pages/settings-users.ts | 7 ++ cypress/pages/sidebar/index.ts | 2 + cypress/pages/sidebar/main-sidebar.ts | 16 ++++ cypress/pages/sidebar/settings-sidebar.ts | 12 +++ cypress/pages/workflow.ts | 2 +- .../src/components/N8nMenuItem/MenuItem.vue | 2 + 9 files changed, 117 insertions(+), 10 deletions(-) create mode 100644 cypress/pages/settings-users.ts create mode 100644 cypress/pages/sidebar/index.ts create mode 100644 cypress/pages/sidebar/main-sidebar.ts create mode 100644 cypress/pages/sidebar/settings-sidebar.ts diff --git a/cypress/e2e/3-default-owner.cy.ts b/cypress/e2e/3-default-owner.cy.ts index 5f1682c35f5a6..8e4e86542ca6f 100644 --- a/cypress/e2e/3-default-owner.cy.ts +++ b/cypress/e2e/3-default-owner.cy.ts @@ -1,9 +1,71 @@ -describe('Authentication', () => { - it('should skip owner setup', () => { - cy.skipSetup(); - }); +import { SettingsUsersPage, WorkflowsPage } from '../pages'; + +import { MainSidebar } from "../pages/sidebar"; + +const mainSidebar = new MainSidebar(); + +const workflowsPage = new WorkflowsPage(); +// const workflowPage = new WorkflowPage(); + +// const credentialsPage = new CredentialsPage(); +// const credentialsModal = new CredentialsModal(); + +const settingsUsersPage = new SettingsUsersPage(); + +describe('Default owner', () => { + // todo test should redirect to setup if have not skipped + + it('should be able to use n8n without user management', () => { + describe('should skip owner setup', () => { + cy.skipSetup(); + cy.url().should('include', workflowsPage.url); + }); + + // todo blocked by db reseting + + // describe('should be able to create workflows', () => { + // WorkflowsPage.getters.newWorkflowButtonCard().should('be.visible'); + // WorkflowsPage.getters.newWorkflowButtonCard().click(); + + // cy.createFixtureWorkflow('Test_workflow_1.json', `Test workflow`); + + // // reload page, ensure owner still has access + // cy.reload(); + + // WorkflowPage.getters.workflowNameInput().should('contain.value', 'Test workflow'); + // }); - // todo test for adding workflow - // todo test for setting up UM again through settings - // todo test that workflows migrated successfully + // describe('should be able to add new credentials', () => { + // cy.visit(credentialsPage.url); + + // credentialsPage.getters.emptyListCreateCredentialButton().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.connectionParameter('API Key').type('1234567890'); + + // credentialsModal.actions.setName('My awesome Notion account'); + // credentialsModal.actions.save(); + + // credentialsModal.actions.close(); + + // credentialsModal.getters.newCredentialModal().should('not.exist'); + // credentialsModal.getters.editCredentialModal().should('not.exist'); + + // credentialsPage.getters.credentialCards().should('have.length', 1); + // }); + + // todo test for setting up UM again through settings + // todo test that workflows migrated successfully + + describe('should be able to setup UM from settings', () => { + mainSidebar.actions.goToSettings(); + cy.url().should('include', settingsUsersPage.url); + }); + }); }); + diff --git a/cypress/pages/index.ts b/cypress/pages/index.ts index 17a3e0efffb06..e978e4920bf4b 100644 --- a/cypress/pages/index.ts +++ b/cypress/pages/index.ts @@ -4,3 +4,4 @@ export * from './signin'; export * from './signup'; export * from './workflows'; export * from './modals'; +export * from './settings-users'; diff --git a/cypress/pages/modals/credentials-modal.ts b/cypress/pages/modals/credentials-modal.ts index d7d45f2262cb0..92d3aaa871d6c 100644 --- a/cypress/pages/modals/credentials-modal.ts +++ b/cypress/pages/modals/credentials-modal.ts @@ -6,13 +6,15 @@ export class CredentialsModal extends BasePage { newCredentialTypeSelect: () => cy.getByTestId('new-credential-type-select'), newCredentialTypeOption: (credentialType: string) => cy.getByTestId('new-credential-type-select-option').contains(credentialType), newCredentialTypeButton: () => cy.getByTestId('new-credential-type-button'), + editCredentialModal: () => cy.getByTestId('editCredential-modal', { timeout: 5000 }), connectionParameters: () => cy.getByTestId('credential-connection-parameter'), connectionParameter: (fieldName: string) => this.getters.connectionParameters().contains(fieldName) .parents('[data-test-id="credential-connection-parameter"]') .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'), + closeButton: () => this.getters.editCredentialModal().find('.el-dialog__close'), }; actions = { setName: (name: string) => { @@ -21,6 +23,9 @@ export class CredentialsModal extends BasePage { }, save: () => { this.getters.saveButton().click(); - } + }, + close: () => { + this.getters.closeButton().click(); + }, }; } diff --git a/cypress/pages/settings-users.ts b/cypress/pages/settings-users.ts new file mode 100644 index 0000000000000..388c97cc26397 --- /dev/null +++ b/cypress/pages/settings-users.ts @@ -0,0 +1,7 @@ +import { BasePage } from "./base"; + +export class SettingsUsersPage extends BasePage { + url = '/settings/users'; + getters = { + } +} diff --git a/cypress/pages/sidebar/index.ts b/cypress/pages/sidebar/index.ts new file mode 100644 index 0000000000000..3bfa6a680155d --- /dev/null +++ b/cypress/pages/sidebar/index.ts @@ -0,0 +1,2 @@ +export * from './main-sidebar'; +export * from './settings-sidebar'; diff --git a/cypress/pages/sidebar/main-sidebar.ts b/cypress/pages/sidebar/main-sidebar.ts new file mode 100644 index 0000000000000..554926a253cc1 --- /dev/null +++ b/cypress/pages/sidebar/main-sidebar.ts @@ -0,0 +1,16 @@ +import { BasePage } from "../base"; + +export class MainSidebar extends BasePage { + getters = { + settings: () => cy.getByTestId('menuitem-settings'), + templates: () => cy.getByTestId('menuitem-templates'), + workflows: () => cy.getByTestId('menuitem-workflows'), + credentials: () => cy.getByTestId('menuitem-credentials'), + executions: () => cy.getByTestId('menuitem-executions'), + }; + actions = { + goToSettings: () => { + this.getters.settings().click(); + }, + }; +} diff --git a/cypress/pages/sidebar/settings-sidebar.ts b/cypress/pages/sidebar/settings-sidebar.ts new file mode 100644 index 0000000000000..ae31687bf15bf --- /dev/null +++ b/cypress/pages/sidebar/settings-sidebar.ts @@ -0,0 +1,12 @@ +import { BasePage } from "../base"; + +export class SettingsSidebar extends BasePage { + getters = { + personal: () => cy.getByTestId('menuitem-settings-personal'), + users: () => cy.getByTestId('menuitem-settings-users'), + api: () => cy.getByTestId('menuitem-settings-api'), + communityNodes: () => cy.getByTestId('menuitem-settings-community-nodes'), + }; + actions = { + }; +} diff --git a/cypress/pages/workflow.ts b/cypress/pages/workflow.ts index 2a9615cdcae09..538a4086aacd8 100644 --- a/cypress/pages/workflow.ts +++ b/cypress/pages/workflow.ts @@ -3,7 +3,7 @@ import { BasePage } from "./base"; export class WorkflowPage extends BasePage { url = '/workflow/new'; getters = { - workflowNameInput: () => cy.getByTestId('workflow-name-input').then($el => cy.wrap($el.find('input'))), + workflowNameInput: () => cy.getByTestId('workflow-name-input', { timeout: 5000 }).then($el => cy.wrap($el.find('input'))), workflowImportInput: () => cy.getByTestId('workflow-import-input'), workflowTags: () => cy.getByTestId('workflow-tags'), saveButton: () => cy.getByTestId('save-button'), diff --git a/packages/design-system/src/components/N8nMenuItem/MenuItem.vue b/packages/design-system/src/components/N8nMenuItem/MenuItem.vue index 5367d560e3ff9..057a36c4178fa 100644 --- a/packages/design-system/src/components/N8nMenuItem/MenuItem.vue +++ b/packages/design-system/src/components/N8nMenuItem/MenuItem.vue @@ -30,6 +30,7 @@ [$style.disableActiveStyle]: !isItemActive(child), [$style.active]: isItemActive(child), }" + :data-test-id="`menuitem-${child.id}`" :index="child.id" @click="onItemClick(child)" > @@ -53,6 +54,7 @@ [$style.active]: isItemActive(item), [$style.compact]: compact, }" + :data-test-id="`menuitem-${item.id}`" :index="item.id" @click="onItemClick(item)" > From d08bf4b57de5ea35f823ce2350ca873b11b7f2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Thu, 24 Nov 2022 16:17:21 +0100 Subject: [PATCH 2/7] ci: Setup cypress tasks for resetting DB, and setting up an owner --- cypress.config.js | 17 +++- cypress/e2e/0-smoke.cy.ts | 17 ++-- package.json | 3 +- packages/cli/src/Db.ts | 2 +- packages/cli/src/Server.ts | 5 + packages/cli/src/api/e2e.api.ts | 107 ++++++++++++++++++++ packages/cli/src/config/index.ts | 1 + packages/cli/src/databases/entities/Role.ts | 4 +- pnpm-lock.yaml | 2 + 9 files changed, 147 insertions(+), 11 deletions(-) create mode 100644 packages/cli/src/api/e2e.api.ts diff --git a/cypress.config.js b/cypress.config.js index 43c7d2f2ab81a..19b1f86b2935e 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -1,11 +1,26 @@ +const fetch = require('node-fetch'); const { defineConfig } = require('cypress'); +const baseUrl = 'http://localhost:5678'; + module.exports = defineConfig({ e2e: { - baseUrl: 'http://localhost:5678', + baseUrl, video: false, screenshotOnRunFailure: true, experimentalSessionAndOrigin: true, experimentalInteractiveRunEvents: true, + + setupNodeEvents(on) { + on('task', { + 'db:reset': () => fetch(baseUrl + '/e2e/db/reset', { method: 'POST' }), + 'db:setup-owner': (payload) => + fetch(baseUrl + '/e2e/db/setup-owner', { + method: 'POST', + body: JSON.stringify(payload), + headers: { 'Content-Type': 'application/json' }, + }), + }); + }, }, }); diff --git a/cypress/e2e/0-smoke.cy.ts b/cypress/e2e/0-smoke.cy.ts index 3f394a418275f..f686075d47d5d 100644 --- a/cypress/e2e/0-smoke.cy.ts +++ b/cypress/e2e/0-smoke.cy.ts @@ -1,23 +1,28 @@ -import {DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD} from "../constants"; -import {randFirstName, randLastName} from "@ngneat/falso"; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; +import { randFirstName, randLastName } from '@ngneat/falso'; -const username = DEFAULT_USER_EMAIL; +const email = DEFAULT_USER_EMAIL; const password = DEFAULT_USER_PASSWORD; const firstName = randFirstName(); const lastName = randLastName(); describe('Authentication', () => { + beforeEach(() => { + cy.task('db:reset'); + }); + it('should setup owner', () => { - cy.signup(username, firstName, lastName, password); + cy.signup(email, firstName, lastName, password); }); it('should sign user in', () => { + cy.task('db:setup-owner', { email, password, firstName, lastName }); cy.on('uncaught:exception', (err, runnable) => { expect(err.message).to.include('Not logged in'); return false; - }) + }); - cy.signin(username, password); + cy.signin(email, password); }); }); diff --git a/package.json b/package.json index f51d4bcf85d4b..a4c4262f558db 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "webhook": "./packages/cli/bin/n8n webhook", "worker": "./packages/cli/bin/n8n worker", "cypress:install": "cypress install", - "test:e2e:ui": "cross-env E2E_TESTS=true CYPRESS_BASE_URL=http://localhost:5678 start-server-and-test start http://localhost:5678/favicon.ico 'cypress open'", + "test:e2e:ui": "cross-env E2E_TESTS=true start-server-and-test start http://localhost:5678/favicon.ico 'cypress open'", "test:e2e:dev": "cross-env E2E_TESTS=true CYPRESS_BASE_URL=http://localhost:8080 start-server-and-test dev http://localhost:8080/favicon.ico 'cypress open'", "test:e2e:smoke": "cross-env E2E_TESTS=true start-server-and-test start http://localhost:5678/favicon.ico 'cypress run --headless --spec \"cypress/e2e/0-smoke.cy.ts\"'", "test:e2e:all": "cross-env E2E_TESTS=true start-server-and-test start http://localhost:5678/favicon.ico 'cypress run'" @@ -42,6 +42,7 @@ "@types/node": "^16.11.22", "cross-env": "^7.0.3", "cypress": "^10.0.3", + "node-fetch": "^2.6.7", "jest": "^29.3.1", "jest-environment-jsdom": "^29.3.1", "jest-mock": "^29.3.1", diff --git a/packages/cli/src/Db.ts b/packages/cli/src/Db.ts index 58a0cb3b5ba4b..b18357381d4f6 100644 --- a/packages/cli/src/Db.ts +++ b/packages/cli/src/Db.ts @@ -32,7 +32,7 @@ import { export let isInitialized = false; export const collections = {} as IDatabaseCollections; -let connection: Connection; +export let connection: Connection; export async function transaction(fn: (entityManager: EntityManager) => Promise): Promise { return connection.transaction(fn); diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index b3346a8b053d3..42b77788d6b60 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -270,6 +270,10 @@ class App { setupErrorMiddleware(this.app); + if (process.env.E2E_TESTS === 'true') { + this.app.use('/e2e', require('./api/e2e.api').e2eController); + } + const urlBaseWebhook = WebhookHelpers.getWebhookBaseUrl(); const telemetrySettings: ITelemetrySettings = { enabled: config.getEnv('diagnostics.enabled'), @@ -431,6 +435,7 @@ class App { 'metrics', 'icons', 'types', + 'e2e', this.endpointWebhook, this.endpointWebhookTest, this.endpointPresetCredentials, diff --git a/packages/cli/src/api/e2e.api.ts b/packages/cli/src/api/e2e.api.ts new file mode 100644 index 0000000000000..2ee73318b787d --- /dev/null +++ b/packages/cli/src/api/e2e.api.ts @@ -0,0 +1,107 @@ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/naming-convention */ +import { Router } from 'express'; +import bodyParser from 'body-parser'; +import { v4 as uuid } from 'uuid'; +import config from '@/config'; +import * as Db from '@/Db'; +import { RoleNames, RoleScopes } from '@/databases/entities/Role'; +import { hashPassword } from '@/UserManagement/UserManagementHelper'; + +if (process.env.E2E_TESTS !== 'true') { + console.error('E2E endpoints only allowed during E2E tests'); + process.exit(1); +} + +const tablesToTruncate = [ + 'shared_workflow', + 'shared_credentials', + 'webhook_entity', + 'workflows_tags', + 'credentials_entity', + 'tag_entity', + 'workflow_entity', + 'execution_entity', + 'settings', + 'installed_packages', + 'installed_nodes', + 'user', + 'role', +]; + +const truncateAll = async () => { + const { connection } = Db; + for (const table of tablesToTruncate) { + await connection.query( + `DELETE FROM ${table}; DELETE FROM sqlite_sequence WHERE name=${table};`, + ); + } + config.set('userManagement.isInstanceOwnerSetUp', false); +}; + +const setupUserManagement = async () => { + const { connection } = Db; + await connection.query('INSERT INTO role (name, scope) VALUES ("owner", "global");'); + const instanceOwnerRole = (await connection.query( + 'SELECT last_insert_rowid() as insertId', + )) as Array<{ insertId: number }>; + + const roles: Array<[RoleNames, RoleScopes]> = [ + ['member', 'global'], + ['owner', 'workflow'], + ['owner', 'credential'], + ['user', 'credential'], + ['editor', 'workflow'], + ]; + + await Promise.all( + roles.map(async ([name, scope]) => + connection.query(`INSERT INTO role (name, scope) VALUES ("${name}", "${scope}");`), + ), + ); + await connection.query( + `INSERT INTO user (id, globalRoleId) values ("${uuid()}", ${instanceOwnerRole[0].insertId})`, + ); + await connection.query( + `INSERT INTO "settings" (key, value, loadOnStartup) values ('userManagement.isInstanceOwnerSetUp', 'false', true), ('userManagement.skipInstanceOwnerSetup', 'false', true)`, + ); +}; + +export const e2eController = Router(); + +e2eController.post('/db/reset', async (req, res) => { + await truncateAll(); + await setupUserManagement(); + + res.writeHead(204).end(); +}); + +e2eController.post('/db/setup-owner', bodyParser.json(), async (req, res) => { + if (config.get('userManagement.isInstanceOwnerSetUp')) { + res.writeHead(500).send({ error: 'Owner already setup' }); + return; + } + + const { Role, User, Settings } = Db.collections; + const globalRole = await Role.findOneOrFail({ + name: 'owner', + scope: 'global', + }); + + const owner = await User.findOneOrFail({ globalRole }); + + await User.update(owner.id, { + email: req.body.email, + password: await hashPassword(req.body.password), + firstName: req.body.firstName, + lastName: req.body.lastName, + }); + + await Settings.update({ key: 'userManagement.isInstanceOwnerSetUp' }, { value: 'true' }); + + config.set('userManagement.isInstanceOwnerSetUp', true); + + res.writeHead(204).end(); +}); diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index 80f632f0f8913..23d70b54cca59 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -13,6 +13,7 @@ const inE2ETests = process.env.E2E_TESTS === 'true'; if (inE2ETests) { // Skip loading config from env variables in end-to-end tests process.env = { + E2E_TESTS: 'true', N8N_USER_FOLDER: mkdtempSync(join(tmpdir(), 'n8n-e2e-')), N8N_DIAGNOSTICS_ENABLED: 'false', N8N_PUBLIC_API_DISABLED: 'true', diff --git a/packages/cli/src/databases/entities/Role.ts b/packages/cli/src/databases/entities/Role.ts index ec6831916b38c..49851d30b193a 100644 --- a/packages/cli/src/databases/entities/Role.ts +++ b/packages/cli/src/databases/entities/Role.ts @@ -6,8 +6,8 @@ import { SharedWorkflow } from './SharedWorkflow'; import { SharedCredentials } from './SharedCredentials'; import { AbstractEntity } from './AbstractEntity'; -type RoleNames = 'owner' | 'member' | 'user' | 'editor'; -type RoleScopes = 'global' | 'workflow' | 'credential'; +export type RoleNames = 'owner' | 'member' | 'user' | 'editor'; +export type RoleScopes = 'global' | 'workflow' | 'credential'; @Entity() @Unique(['scope', 'name']) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0bb816cebdc48..93f2b5e5519e9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,7 @@ importers: jest-environment-jsdom: ^29.3.1 jest-mock: ^29.3.1 n8n: '*' + node-fetch: ^2.6.7 prettier: ^2.3.2 rimraf: ^3.0.2 run-script-os: ^1.0.7 @@ -50,6 +51,7 @@ importers: jest: 29.3.1_@types+node@16.11.65 jest-environment-jsdom: 29.3.1 jest-mock: 29.3.1 + node-fetch: 2.6.7 prettier: 2.7.1 rimraf: 3.0.2 run-script-os: 1.1.6 From dc059f8a725c5765325f9ffcf0329724f828896d Mon Sep 17 00:00:00 2001 From: Mutasem Date: Thu, 24 Nov 2022 16:33:21 +0100 Subject: [PATCH 3/7] add test tests to check for settings --- cypress/e2e/3-default-owner.cy.ts | 78 ++++++++++++------- cypress/pages/index.ts | 1 + cypress/pages/modals/index.ts | 1 + cypress/pages/modals/message-box.ts | 1 + cypress/pages/settings-users.ts | 4 + cypress/pages/sidebar/main-sidebar.ts | 2 +- .../src/components/N8nActionBox/ActionBox.vue | 2 +- 7 files changed, 57 insertions(+), 32 deletions(-) diff --git a/cypress/e2e/3-default-owner.cy.ts b/cypress/e2e/3-default-owner.cy.ts index 8e4e86542ca6f..f35c8bd82c937 100644 --- a/cypress/e2e/3-default-owner.cy.ts +++ b/cypress/e2e/3-default-owner.cy.ts @@ -1,17 +1,27 @@ -import { SettingsUsersPage, WorkflowsPage } from '../pages'; +import { randFirstName, randLastName } from '@ngneat/falso'; +import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants'; +import { SettingsUsersPage, SignupPage, WorkflowsPage, WorkflowPage, CredentialsPage, CredentialsModal, MessageBox } from '../pages'; import { MainSidebar } from "../pages/sidebar"; const mainSidebar = new MainSidebar(); const workflowsPage = new WorkflowsPage(); -// const workflowPage = new WorkflowPage(); +const signupPage = new SignupPage(); +const workflowPage = new WorkflowPage(); -// const credentialsPage = new CredentialsPage(); -// const credentialsModal = new CredentialsModal(); +const credentialsPage = new CredentialsPage(); +const credentialsModal = new CredentialsModal(); const settingsUsersPage = new SettingsUsersPage(); +const messageBox = new MessageBox(); + +const username = DEFAULT_USER_EMAIL; +const password = DEFAULT_USER_PASSWORD; +const firstName = randFirstName(); +const lastName = randLastName(); + describe('Default owner', () => { // todo test should redirect to setup if have not skipped @@ -23,48 +33,56 @@ describe('Default owner', () => { // todo blocked by db reseting - // describe('should be able to create workflows', () => { - // WorkflowsPage.getters.newWorkflowButtonCard().should('be.visible'); - // WorkflowsPage.getters.newWorkflowButtonCard().click(); - - // cy.createFixtureWorkflow('Test_workflow_1.json', `Test workflow`); + describe('should be able to create workflows', () => { + workflowsPage.getters.newWorkflowButtonCard().should('be.visible'); + workflowsPage.getters.newWorkflowButtonCard().click(); - // // reload page, ensure owner still has access - // cy.reload(); + cy.createFixtureWorkflow('Test_workflow_1.json', `Test workflow`); - // WorkflowPage.getters.workflowNameInput().should('contain.value', 'Test workflow'); - // }); + // reload page, ensure owner still has access + cy.reload(); - // describe('should be able to add new credentials', () => { - // cy.visit(credentialsPage.url); + workflowPage.getters.workflowNameInput().should('contain.value', 'Test workflow'); + }); - // credentialsPage.getters.emptyListCreateCredentialButton().click(); + describe('should be able to add new credentials', () => { + cy.visit(credentialsPage.url); - // credentialsModal.getters.newCredentialModal().should('be.visible'); - // credentialsModal.getters.newCredentialTypeSelect().should('be.visible'); - // credentialsModal.getters.newCredentialTypeOption('Notion API').click(); + credentialsPage.getters.emptyListCreateCredentialButton().click(); - // credentialsModal.getters.newCredentialTypeButton().click(); + credentialsModal.getters.newCredentialModal().should('be.visible'); + credentialsModal.getters.newCredentialTypeSelect().should('be.visible'); + credentialsModal.getters.newCredentialTypeOption('Notion API').click(); - // credentialsModal.getters.connectionParameter('API Key').type('1234567890'); + credentialsModal.getters.newCredentialTypeButton().click(); - // credentialsModal.actions.setName('My awesome Notion account'); - // credentialsModal.actions.save(); + credentialsModal.getters.connectionParameter('API Key').type('1234567890'); - // credentialsModal.actions.close(); + credentialsModal.actions.setName('My awesome Notion account'); + credentialsModal.actions.save(); - // credentialsModal.getters.newCredentialModal().should('not.exist'); - // credentialsModal.getters.editCredentialModal().should('not.exist'); + credentialsModal.actions.close(); - // credentialsPage.getters.credentialCards().should('have.length', 1); - // }); + credentialsModal.getters.newCredentialModal().should('not.exist'); + credentialsModal.getters.editCredentialModal().should('not.exist'); - // todo test for setting up UM again through settings - // todo test that workflows migrated successfully + 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); + + settingsUsersPage.actions.goToOwnerSetup(); + + cy.url().should('include', signupPage.url); + }); + + describe('should be able to setup instance and migrate workflows and credentials', () => { + cy.signup(username, firstName, lastName, password); + + messageBox.getters.content().should('contain.text', '1 existing workflow and 1 credential') }); }); }); diff --git a/cypress/pages/index.ts b/cypress/pages/index.ts index e978e4920bf4b..0cea524db6343 100644 --- a/cypress/pages/index.ts +++ b/cypress/pages/index.ts @@ -3,5 +3,6 @@ export * from './credentials'; export * from './signin'; export * from './signup'; export * from './workflows'; +export * from './workflow'; export * from './modals'; export * from './settings-users'; diff --git a/cypress/pages/modals/index.ts b/cypress/pages/modals/index.ts index aaa7d6d707016..24f5101aed2df 100644 --- a/cypress/pages/modals/index.ts +++ b/cypress/pages/modals/index.ts @@ -1 +1,2 @@ export * from './credentials-modal'; +export * from './message-box'; diff --git a/cypress/pages/modals/message-box.ts b/cypress/pages/modals/message-box.ts index 55f76cd9fd026..388fc00cadbaf 100644 --- a/cypress/pages/modals/message-box.ts +++ b/cypress/pages/modals/message-box.ts @@ -4,6 +4,7 @@ export class MessageBox extends BasePage { getters = { modal: () => cy.get('.el-message-box', { withinSubject: null }), header: () => this.getters.modal().find('.el-message-box__title'), + content: () => this.getters.modal().find('.el-message-box__content'), confirm: () => this.getters.modal().find('.btn--confirm'), cancel: () => this.getters.modal().find('.btn--cancel'), }; diff --git a/cypress/pages/settings-users.ts b/cypress/pages/settings-users.ts index 388c97cc26397..e75282163ffcc 100644 --- a/cypress/pages/settings-users.ts +++ b/cypress/pages/settings-users.ts @@ -3,5 +3,9 @@ import { BasePage } from "./base"; export class SettingsUsersPage extends BasePage { url = '/settings/users'; getters = { + setUpOwnerButton: () => cy.getByTestId('action-box').find('button'), + } + actions = { + goToOwnerSetup: () => this.getters.setUpOwnerButton().click(), } } diff --git a/cypress/pages/sidebar/main-sidebar.ts b/cypress/pages/sidebar/main-sidebar.ts index 554926a253cc1..6fabcd9c0ed6a 100644 --- a/cypress/pages/sidebar/main-sidebar.ts +++ b/cypress/pages/sidebar/main-sidebar.ts @@ -2,7 +2,7 @@ import { BasePage } from "../base"; export class MainSidebar extends BasePage { getters = { - settings: () => cy.getByTestId('menuitem-settings'), + settings: () => cy.getByTestId('menuitem-settings', { timeout: 5000 }), templates: () => cy.getByTestId('menuitem-templates'), workflows: () => cy.getByTestId('menuitem-workflows'), credentials: () => cy.getByTestId('menuitem-credentials'), diff --git a/packages/design-system/src/components/N8nActionBox/ActionBox.vue b/packages/design-system/src/components/N8nActionBox/ActionBox.vue index a2233d83fd361..aa5004e13253e 100644 --- a/packages/design-system/src/components/N8nActionBox/ActionBox.vue +++ b/packages/design-system/src/components/N8nActionBox/ActionBox.vue @@ -1,5 +1,5 @@