mirror of
https://github.com/n8n-io/n8n.git
synced 2025-02-21 02:56:40 -08:00
test: Add e2e tests for workflow/credential migrations when enabling UM (#4719)
* add tests * ci: Setup cypress tasks for resetting DB, and setting up an owner * add test tests to check for settings * add more tests * clean up * rename tag * update test id Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ <aditya@netroy.in>
This commit is contained in:
parent
e409813ea9
commit
95b97078e8
|
@ -1,9 +1,115 @@
|
||||||
describe('Authentication', () => {
|
import { randFirstName, randLastName } from '@ngneat/falso';
|
||||||
it('should skip owner setup', () => {
|
import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants';
|
||||||
cy.skipSetup();
|
import { SettingsUsersPage, SignupPage, WorkflowsPage, WorkflowPage, CredentialsPage, CredentialsModal, MessageBox } from '../pages';
|
||||||
|
|
||||||
|
import { MainSidebar, SettingsSidebar } from "../pages/sidebar";
|
||||||
|
|
||||||
|
const mainSidebar = new MainSidebar();
|
||||||
|
const settingsSidebar = new SettingsSidebar();
|
||||||
|
|
||||||
|
const workflowsPage = new WorkflowsPage();
|
||||||
|
const signupPage = new SignupPage();
|
||||||
|
const workflowPage = new WorkflowPage();
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.task('db:reset');
|
||||||
});
|
});
|
||||||
|
|
||||||
// todo test for adding workflow
|
it('should be able to use n8n without user management and setup UM', () => {
|
||||||
// todo test for setting up UM again through settings
|
describe('should skip owner setup', () => {
|
||||||
// todo test that workflows migrated successfully
|
cy.skipSetup();
|
||||||
|
cy.url().should('include', workflowsPage.url);
|
||||||
|
});
|
||||||
|
|
||||||
|
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');
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
|
||||||
|
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')
|
||||||
|
|
||||||
|
messageBox.actions.confirm();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('should be redirected back to users page after setup', () => {
|
||||||
|
cy.url().should('include', settingsUsersPage.url);
|
||||||
|
// todo test users and that owner exist
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('can click back to workflows and have migrated workflow after setup', () => {
|
||||||
|
settingsSidebar.actions.back();
|
||||||
|
|
||||||
|
cy.url().should('include', workflowsPage.url);
|
||||||
|
|
||||||
|
workflowsPage.getters.workflowCards().should('have.length', 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('can click back to main menu and have migrated credential after setup', () => {
|
||||||
|
mainSidebar.actions.goToCredentials();
|
||||||
|
|
||||||
|
cy.url().should('include', workflowsPage.url);
|
||||||
|
|
||||||
|
workflowsPage.getters.workflowCards().should('have.length', 1);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,6 @@ export * from './credentials';
|
||||||
export * from './signin';
|
export * from './signin';
|
||||||
export * from './signup';
|
export * from './signup';
|
||||||
export * from './workflows';
|
export * from './workflows';
|
||||||
|
export * from './workflow';
|
||||||
export * from './modals';
|
export * from './modals';
|
||||||
|
export * from './settings-users';
|
||||||
|
|
|
@ -6,13 +6,15 @@ export class CredentialsModal extends BasePage {
|
||||||
newCredentialTypeSelect: () => cy.getByTestId('new-credential-type-select'),
|
newCredentialTypeSelect: () => cy.getByTestId('new-credential-type-select'),
|
||||||
newCredentialTypeOption: (credentialType: string) => cy.getByTestId('new-credential-type-select-option').contains(credentialType),
|
newCredentialTypeOption: (credentialType: string) => cy.getByTestId('new-credential-type-select-option').contains(credentialType),
|
||||||
newCredentialTypeButton: () => cy.getByTestId('new-credential-type-button'),
|
newCredentialTypeButton: () => cy.getByTestId('new-credential-type-button'),
|
||||||
|
editCredentialModal: () => cy.getByTestId('editCredential-modal', { timeout: 5000 }),
|
||||||
connectionParameters: () => cy.getByTestId('credential-connection-parameter'),
|
connectionParameters: () => cy.getByTestId('credential-connection-parameter'),
|
||||||
connectionParameter: (fieldName: string) => this.getters.connectionParameters().contains(fieldName)
|
connectionParameter: (fieldName: string) => this.getters.connectionParameters().contains(fieldName)
|
||||||
.parents('[data-test-id="credential-connection-parameter"]')
|
.parents('[data-test-id="credential-connection-parameter"]')
|
||||||
.find('.n8n-input input'),
|
.find('.n8n-input input'),
|
||||||
name: () => cy.getByTestId('credential-name'),
|
name: () => cy.getByTestId('credential-name'),
|
||||||
nameInput: () => cy.getByTestId('credential-name').find('input'),
|
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 = {
|
actions = {
|
||||||
setName: (name: string) => {
|
setName: (name: string) => {
|
||||||
|
@ -21,6 +23,9 @@ export class CredentialsModal extends BasePage {
|
||||||
},
|
},
|
||||||
save: () => {
|
save: () => {
|
||||||
this.getters.saveButton().click();
|
this.getters.saveButton().click();
|
||||||
}
|
},
|
||||||
|
close: () => {
|
||||||
|
this.getters.closeButton().click();
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
export * from './credentials-modal';
|
export * from './credentials-modal';
|
||||||
|
export * from './message-box';
|
||||||
|
|
|
@ -4,6 +4,7 @@ export class MessageBox extends BasePage {
|
||||||
getters = {
|
getters = {
|
||||||
modal: () => cy.get('.el-message-box', { withinSubject: null }),
|
modal: () => cy.get('.el-message-box', { withinSubject: null }),
|
||||||
header: () => this.getters.modal().find('.el-message-box__title'),
|
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'),
|
confirm: () => this.getters.modal().find('.btn--confirm'),
|
||||||
cancel: () => this.getters.modal().find('.btn--cancel'),
|
cancel: () => this.getters.modal().find('.btn--cancel'),
|
||||||
};
|
};
|
||||||
|
|
11
cypress/pages/settings-users.ts
Normal file
11
cypress/pages/settings-users.ts
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
2
cypress/pages/sidebar/index.ts
Normal file
2
cypress/pages/sidebar/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * from './main-sidebar';
|
||||||
|
export * from './settings-sidebar';
|
15
cypress/pages/sidebar/main-sidebar.ts
Normal file
15
cypress/pages/sidebar/main-sidebar.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import { BasePage } from "../base";
|
||||||
|
|
||||||
|
export class MainSidebar extends BasePage {
|
||||||
|
getters = {
|
||||||
|
settings: () => cy.getByTestId('menu-item-settings', { timeout: 5000 }),
|
||||||
|
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(),
|
||||||
|
};
|
||||||
|
}
|
14
cypress/pages/sidebar/settings-sidebar.ts
Normal file
14
cypress/pages/sidebar/settings-sidebar.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { BasePage } from "../base";
|
||||||
|
|
||||||
|
export class SettingsSidebar extends BasePage {
|
||||||
|
getters = {
|
||||||
|
personal: () => cy.getByTestId('menu-item-settings-personal'),
|
||||||
|
users: () => cy.getByTestId('menu-item-settings-users'),
|
||||||
|
api: () => cy.getByTestId('menu-item-settings-api'),
|
||||||
|
communityNodes: () => cy.getByTestId('menu-item-settings-community-nodes'),
|
||||||
|
back: () => cy.getByTestId('settings-back'),
|
||||||
|
};
|
||||||
|
actions = {
|
||||||
|
back: () => this.getters.back().click(),
|
||||||
|
};
|
||||||
|
}
|
|
@ -3,7 +3,7 @@ import { BasePage } from "./base";
|
||||||
export class WorkflowPage extends BasePage {
|
export class WorkflowPage extends BasePage {
|
||||||
url = '/workflow/new';
|
url = '/workflow/new';
|
||||||
getters = {
|
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'),
|
workflowImportInput: () => cy.getByTestId('workflow-import-input'),
|
||||||
workflowTags: () => cy.getByTestId('workflow-tags'),
|
workflowTags: () => cy.getByTestId('workflow-tags'),
|
||||||
saveButton: () => cy.getByTestId('save-button'),
|
saveButton: () => cy.getByTestId('save-button'),
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { SharedWorkflow } from './SharedWorkflow';
|
||||||
import { SharedCredentials } from './SharedCredentials';
|
import { SharedCredentials } from './SharedCredentials';
|
||||||
import { AbstractEntity } from './AbstractEntity';
|
import { AbstractEntity } from './AbstractEntity';
|
||||||
|
|
||||||
type RoleNames = 'owner' | 'member' | 'user' | 'editor';
|
export type RoleNames = 'owner' | 'member' | 'user' | 'editor';
|
||||||
type RoleScopes = 'global' | 'workflow' | 'credential';
|
export type RoleScopes = 'global' | 'workflow' | 'credential';
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Unique(['scope', 'name'])
|
@Unique(['scope', 'name'])
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="['n8n-action-box', $style.container]">
|
<div :class="['n8n-action-box', $style.container]" data-test-id="action-box">
|
||||||
<div :class="$style.emoji" v-if="emoji">
|
<div :class="$style.emoji" v-if="emoji">
|
||||||
{{ emoji }}
|
{{ emoji }}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Vitest Snapshot v1
|
// Vitest Snapshot v1
|
||||||
|
|
||||||
exports[`N8NActionBox > should render correctly 1`] = `
|
exports[`N8NActionBox > should render correctly 1`] = `
|
||||||
"<div class=\\"n8n-action-box _container_pvdcm_1\\">
|
"<div data-test-id=\\"action-box\\" class=\\"n8n-action-box _container_pvdcm_1\\">
|
||||||
<div class=\\"_emoji_pvdcm_16\\"> 😿 </div>
|
<div class=\\"_emoji_pvdcm_16\\"> 😿 </div>
|
||||||
<div class=\\"_heading_pvdcm_20\\">
|
<div class=\\"_heading_pvdcm_20\\">
|
||||||
<n8n-heading-stub tag=\\"span\\" size=\\"xlarge\\" align=\\"center\\">Headline you need to know</n8n-heading-stub>
|
<n8n-heading-stub tag=\\"span\\" size=\\"xlarge\\" align=\\"center\\">Headline you need to know</n8n-heading-stub>
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
[$style.disableActiveStyle]: !isItemActive(child),
|
[$style.disableActiveStyle]: !isItemActive(child),
|
||||||
[$style.active]: isItemActive(child),
|
[$style.active]: isItemActive(child),
|
||||||
}"
|
}"
|
||||||
|
:data-test-id="`menu-item-${child.id}`"
|
||||||
:index="child.id"
|
:index="child.id"
|
||||||
@click="onItemClick(child)"
|
@click="onItemClick(child)"
|
||||||
>
|
>
|
||||||
|
@ -53,6 +54,7 @@
|
||||||
[$style.active]: isItemActive(item),
|
[$style.active]: isItemActive(item),
|
||||||
[$style.compact]: compact,
|
[$style.compact]: compact,
|
||||||
}"
|
}"
|
||||||
|
:data-test-id="`menu-item-${item.id}`"
|
||||||
:index="item.id"
|
:index="item.id"
|
||||||
@click="onItemClick(item)"
|
@click="onItemClick(item)"
|
||||||
>
|
>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div :class="$style.container">
|
<div :class="$style.container">
|
||||||
<n8n-menu :items="sidebarMenuItems" @select="handleSelect">
|
<n8n-menu :items="sidebarMenuItems" @select="handleSelect">
|
||||||
<template #header>
|
<template #header>
|
||||||
<div :class="$style.returnButton" @click="$emit('return')">
|
<div :class="$style.returnButton" @click="$emit('return')" data-test-id="settings-back">
|
||||||
<i class="mr-xs">
|
<i class="mr-xs">
|
||||||
<font-awesome-icon icon="arrow-left" />
|
<font-awesome-icon icon="arrow-left" />
|
||||||
</i>
|
</i>
|
||||||
|
|
Loading…
Reference in a new issue