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:
Mutasem Aldmour 2022-11-24 23:22:09 +01:00 committed by GitHub
parent e409813ea9
commit 95b97078e8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 173 additions and 14 deletions

View file

@ -1,9 +1,115 @@
describe('Authentication', () => {
it('should skip owner setup', () => {
cy.skipSetup();
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, 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
// todo test for setting up UM again through settings
// todo test that workflows migrated successfully
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);
});
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);
});
});
});

View file

@ -3,4 +3,6 @@ export * from './credentials';
export * from './signin';
export * from './signup';
export * from './workflows';
export * from './workflow';
export * from './modals';
export * from './settings-users';

View file

@ -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();
},
};
}

View file

@ -1 +1,2 @@
export * from './credentials-modal';
export * from './message-box';

View file

@ -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'),
};

View 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(),
}
}

View file

@ -0,0 +1,2 @@
export * from './main-sidebar';
export * from './settings-sidebar';

View 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(),
};
}

View 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(),
};
}

View file

@ -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'),

View file

@ -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'])

View file

@ -1,5 +1,5 @@
<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">
{{ emoji }}
</div>

View file

@ -1,7 +1,7 @@
// Vitest Snapshot v1
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=\\"_heading_pvdcm_20\\">
<n8n-heading-stub tag=\\"span\\" size=\\"xlarge\\" align=\\"center\\">Headline you need to know</n8n-heading-stub>

View file

@ -30,6 +30,7 @@
[$style.disableActiveStyle]: !isItemActive(child),
[$style.active]: isItemActive(child),
}"
:data-test-id="`menu-item-${child.id}`"
:index="child.id"
@click="onItemClick(child)"
>
@ -53,6 +54,7 @@
[$style.active]: isItemActive(item),
[$style.compact]: compact,
}"
:data-test-id="`menu-item-${item.id}`"
:index="item.id"
@click="onItemClick(item)"
>

View file

@ -2,7 +2,7 @@
<div :class="$style.container">
<n8n-menu :items="sidebarMenuItems" @select="handleSelect">
<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">
<font-awesome-icon icon="arrow-left" />
</i>