Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test(editor): Fix default-owner and credentials saving e2e specs #4779

Merged
merged 19 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 52 additions & 58 deletions cypress/e2e/3-default-owner.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,92 +24,86 @@ const firstName = randFirstName();
const lastName = randLastName();

describe('Default owner', () => {
// todo test should redirect to setup if have not skipped

beforeEach(() => {
before(() => {
cy.resetAll();
});
beforeEach(() => {
cy.visit('/');
})

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();
it('should skip owner setup', () => {
cy.skipSetup();
mutdmour marked this conversation as resolved.
Show resolved Hide resolved
});

cy.createFixtureWorkflow('Test_workflow_1.json', `Test workflow`);
it('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();
it('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');

describe('should be able to setup UM from settings', () => {
mainSidebar.getters.settings().should('be.visible');
mainSidebar.actions.goToSettings();
cy.url().should('include', settingsUsersPage.url);
credentialsPage.getters.credentialCards().should('have.length', 1);
});

settingsUsersPage.actions.goToOwnerSetup();
it('should be able to setup UM from settings', () => {
mainSidebar.getters.settings().should('be.visible');
mainSidebar.actions.goToSettings();
cy.url().should('include', settingsUsersPage.url);

cy.url().should('include', signupPage.url);
});
settingsUsersPage.actions.goToOwnerSetup();

describe('should be able to setup instance and migrate workflows and credentials', () => {
cy.setup({ email, firstName, lastName, password });
cy.url().should('include', signupPage.url);
});

messageBox.getters.content().should('contain.text', '1 existing workflow and 1 credential')
it('should be able to setup instance and migrate workflows and credentials', () => {
cy.setup({ email, firstName, lastName, password });

messageBox.actions.confirm();
});
messageBox.getters.content().should('contain.text', '1 existing workflow and 1 credential')

describe('should be redirected back to users page after setup', () => {
cy.url().should('include', settingsUsersPage.url);
// todo test users and that owner exist
});
messageBox.actions.confirm();
cy.url().should('include', settingsUsersPage.url);
settingsSidebar.actions.back();

describe('can click back to workflows and have migrated workflow after setup', () => {
settingsSidebar.actions.back();
cy.url().should('include', workflowsPage.url);

cy.url().should('include', workflowsPage.url);
workflowsPage.getters.workflowCards().should('have.length', 1);
});

workflowsPage.getters.workflowCards().should('have.length', 1);
});
it('can click back to main menu and have migrated credential after setup', () => {
cy.signin({ email, password });
cy.visit(workflowsPage.url);

describe('can click back to main menu and have migrated credential after setup', () => {
mainSidebar.actions.goToCredentials();
mainSidebar.actions.goToCredentials();

cy.url().should('include', workflowsPage.url);
cy.url().should('include', credentialsPage.url);

workflowsPage.getters.workflowCards().should('have.length', 1);
});
credentialsPage.getters.credentialCards().should('have.length', 1);
});
});

14 changes: 2 additions & 12 deletions cypress/e2e/5-ndv.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,9 @@ describe('NDV', () => {
ndv.getters.nodeExecuteButton().first().click();
ndv.getters.copyInput().click();

cy.wrap(Cypress.automation('remote:debugger:protocol', {
command: 'Browser.grantPermissions',
params: {
permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'],
origin: window.location.origin,
},
}));
cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite');

cy.window().its('navigator.permissions')
.invoke('query', {name: 'clipboard-read'})
.its('state').should('equal', 'granted');

cy.window().its('navigator.clipboard').invoke('readText').then(url => {
cy.readClipboard().then(url => {
cy.request({
method: 'GET',
url,
Expand Down
10 changes: 8 additions & 2 deletions cypress/e2e/7-workflow-actions.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,16 @@ describe('Workflow Actions', () => {
});

it('should copy nodes', () => {
const metaKey = Cypress.platform === 'darwin' ? '{meta}' : '{ctrl}';

WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE);
cy.get('body').type('{meta}', { release: false }).type('a');
cy.get('body').type('{meta}', { release: false }).type('c');
WorkflowPage.getters.canvasNodes().should('have.have.length', 2);

cy.get("#node-creator").should('not.exist');
cy.get('body').type(metaKey, { delay: 500, release: false }).type('a');
cy.get('.jtk-drag-selected').should('have.length', 2);
cy.get('body').type(metaKey, { delay: 500, release: false }).type('c');
WorkflowPage.getters.successToast().should('exist');
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ describe('HTTP Request node', () => {
WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
WorkflowPage.actions.addNodeToCanvas('HTTP Request');
WorkflowPage.actions.openNodeNdv('HTTP Request');
WorkflowPage.actions.typeIntoParameterInput('url', 'https://google.com');
WorkflowPage.actions.typeIntoParameterInput('url', 'https://catfact.ninja/fact');

WorkflowPage.actions.executeNodeFromNdv();

WorkflowPage.getters.ndvOutputPanel().contains('<!doctype html>');
WorkflowPage.getters.ndvOutputPanel().contains('fact');
});
});
2 changes: 1 addition & 1 deletion cypress/e2e/9-expression-editor-modal.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const WorkflowPage = new WorkflowPageClass();

describe('Expression editor modal', () => {
before(() => {
cy.task('db:reset');
cy.task('reset');
cy.skipSetup();
});

Expand Down
13 changes: 9 additions & 4 deletions cypress/pages/modals/credentials-modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,25 @@ export class CredentialsModal extends BasePage {
.find('.n8n-input input'),
name: () => cy.getByTestId('credential-name'),
nameInput: () => cy.getByTestId('credential-name').find('input'),
saveButton: () => cy.getByTestId('credential-save-button'),
// Saving of the credentials takes a while on the CI so we need to increase the timeout
saveButton: () => cy.getByTestId('credential-save-button', { timeout: 5000 }),
closeButton: () => this.getters.editCredentialModal().find('.el-dialog__close').first(),
};
actions = {
setName: (name: string) => {
this.getters.name().click();
this.getters.nameInput().clear().type(name);
},
save: () => {
save: (test = false) => {
cy.intercept('POST', '/rest/credentials').as('saveCredential');
cy.intercept('POST', '/rest/credentials/test').as('testCredential');
OlegIvaniv marked this conversation as resolved.
Show resolved Hide resolved
if(test) {
cy.intercept('POST', '/rest/credentials/test').as('testCredential');
}

this.getters.saveButton().click();
cy.wait('@saveCredential').wait('@testCredential');

cy.wait('@saveCredential');
if(test) cy.wait('@testCredential')
this.getters.saveButton().should('contain.text', 'Saved');
},
close: () => {
Expand Down
24 changes: 17 additions & 7 deletions cypress/pages/sidebar/main-sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,24 @@ 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'),
menuItem: (menuLabel: string) => cy.getByTestId('menu-item').filter(`:contains("${menuLabel}")`),
settings: () => this.getters.menuItem('Settings'),
templates: () => this.getters.menuItem('Templates'),
workflows: () => this.getters.menuItem('Workflows'),
credentials: () => this.getters.menuItem('Credentials'),
executions: () => this.getters.menuItem('Executions'),
};
actions = {
goToSettings: () => this.getters.settings().click(),
goToCredentials: () => this.getters.credentials().click(),
goToSettings: () => {
this.getters.settings().should('be.visible');
// We must wait before ElementUI menu is done with its animations
cy.get('[data-old-overflow]').should('not.exist');
this.getters.settings().click();
},
goToCredentials: () => {
this.getters.credentials().should('be.visible');
cy.get('[data-old-overflow]').should('not.exist');
this.getters.credentials().click()
},
};
}
4 changes: 1 addition & 3 deletions cypress/pages/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export class WorkflowPage extends BasePage {
workflowTags: () => cy.getByTestId('workflow-tags'),
workflowTagsContainer: () => cy.getByTestId('workflow-tags-container'),
workflowTagsInput: () => this.getters.workflowTagsContainer().then(($el) => cy.wrap($el.find('input').first())),
workflowTagElements: () => this.getters.workflowTagsContainer().find('span.tags').children(),
workflowTagElements: () => cy.get('[data-test-id="workflow-tags-container"] span.tags > span'),
workflowTagsDropdown: () => cy.getByTestId('workflow-tags-dropdown'),
newTagLink: () => cy.getByTestId('new-tag-link'),
saveButton: () => cy.getByTestId('workflow-save-button'),
Expand All @@ -22,7 +22,6 @@ export class WorkflowPage extends BasePage {
cy.getByTestId(`parameter-input-${parameterName}`),
ndvOutputPanel: () => cy.getByTestId('output-panel'),
ndvRunDataPaneHeader: () => cy.getByTestId('run-data-pane-header'),

successToast: () => cy.get('.el-notification__title'),
activatorSwitch: () => cy.getByTestId('workflow-activate-switch'),
workflowMenu: () => cy.getByTestId('workflow-menu'),
Expand All @@ -34,7 +33,6 @@ export class WorkflowPage extends BasePage {

nodeViewRoot: () => cy.getByTestId('node-view-root'),
copyPasteInput: () => cy.getByTestId('hidden-copy-paste'),
canvasNodes: () => cy.getByTestId('canvas-node'),
};
actions = {
visit: () => {
Expand Down
12 changes: 12 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,18 @@ Cypress.Commands.add('setupOwner', (payload) => {
cy.task('setup-owner', payload);
});

Cypress.Commands.add('grantBrowserPermissions', (...permissions: string[]) => {
if(Cypress.isBrowser('chrome')) {
cy.wrap(Cypress.automation('remote:debugger:protocol', {
command: 'Browser.grantPermissions',
params: {
permissions,
origin: window.location.origin,
},
}));
}
});
Cypress.Commands.add('readClipboard', () => cy.window().its('navigator.clipboard').invoke('readText'));
Cypress.Commands.add('paste', { prevSubject: true }, (selector, pastePayload) => {
// https://developer.mozilla.org/en-US/docs/Web/API/Element/paste_event
cy.wrap(selector).then($destination => {
Expand Down
2 changes: 2 additions & 0 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ declare global {
setupOwner(payload: SetupPayload): void;
skipSetup(): void;
resetAll(): void;
grantBrowserPermissions(...permissions: string[]): void;
readClipboard(): Chainable<string>;
paste(pastePayload: string): void,
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/CredentialsHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ export class CredentialsHelper extends ICredentialsHelper {
`Received HTTP status code: ${errorResponseData.statusCode}`,
};
}
} else if (error.cause.code) {
} else if (error.cause?.code) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error.cause was added in Node 16 and it throws 500 in Node 14.

return {
status: 'Error',
message: error.cause.code,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
[$style.disableActiveStyle]: !isItemActive(child),
[$style.active]: isItemActive(child),
}"
:data-test-id="`menu-item-${child.id}`"
data-test-id="menu-item"
:index="child.id"
@click="onItemClick(child)"
>
Expand All @@ -54,7 +54,7 @@
[$style.active]: isItemActive(item),
[$style.compact]: compact,
}"
:data-test-id="`menu-item-${item.id}`"
data-test-id="menu-item"
:index="item.id"
@click="onItemClick(item)"
>
Expand Down