Skip to content

Commit

Permalink
Merge branch 'master' into ado-251-data-transformation-fixes
Browse files Browse the repository at this point in the history
* master:
  ci(core): Add unit tests for "me" controller (no-changelog) (#5479)
  fix(core): Use stricter typing on queries in cli commands (no-changelog) (#5476)
  fix(editor): Fix Vite dev mode (no-changelog) (#5475)
  feat(editor): Add correct credential owner contact details for readonly credentials (#5208)
  feat: Add workflow and credential sharing access e2e tests (#5463)
  ci: Update the "Check Documentation URLs" workflow (no-changelog) (#5473)
  feat(editor): Upgrade to Storybook v7 (no-changelog) (#5454)
  docs: Update release notes link (#5472)
  test(editor): Add e2e tests for executions preview (#5458)
  📚 Update CHANGELOG.md and main package.json to 0.215.2
  🔖 Release n8n@0.215.2
  fix(core): Fix the issue with test webhooks getting removed incorrectly (no-changelog) (#5466)

# Conflicts:
#	pnpm-lock.yaml
  • Loading branch information
MiloradFilipovic committed Feb 15, 2023
2 parents f87cc6d + 83505cb commit b9ba7ae
Show file tree
Hide file tree
Showing 38 changed files with 2,238 additions and 3,319 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/check-documentation-urls.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ on:
push:
tags:
- n8n@*
schedule:
- cron: '0 0 * * *'
workflow_dispatch:

jobs:
build:
check-docs-urls:
runs-on: ubuntu-latest

timeout-minutes: 5
Expand All @@ -23,7 +25,7 @@ jobs:
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile
run: pnpm install

- name: Build nodes-base
run: pnpm --filter n8n-workflow --filter=n8n-core --filter=n8n-nodes-base build
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## [0.215.2](https://github.com/n8n-io/n8n/compare/n8n@0.215.1...n8n@0.215.2) (2023-02-14)


### Bug Fixes

* **core:** Fix the issue with test webhooks getting removed incorrectly ([#5466](https://github.com/n8n-io/n8n/issues/5466)) ([4dc458e](https://github.com/n8n-io/n8n/commit/4dc458eca5587cf7765bed6fd384d47a31e66e2c)), closes [/github.com/n8n-io/n8n/pull/5443/files#diff-b386248ff00977749c873ed85821c241b773e9740d7e7adf94e05b73b350ed74L152](https://github.com//github.com/n8n-io/n8n/pull/5443/files/issues/diff-b386248ff00977749c873ed85821c241b773e9740d7e7adf94e05b73b350ed74L152)



## [0.215.1](https://github.com/n8n-io/n8n/compare/n8n@0.215.0...n8n@0.215.1) (2023-02-11)


Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ The official n8n documentation can be found on our [documentation website](https

Additional information and example workflows on the [n8n.io website](https://n8n.io)

The release notes can be found [here](https://docs.n8n.io/reference/release-notes/) and the list of breaking
The release notes can be found [here](https://docs.n8n.io/release-notes/) and the list of breaking
changes [here](https://github.com/n8n-io/n8n/blob/master/packages/cli/BREAKING-CHANGES.md).

## Usage
Expand Down
168 changes: 168 additions & 0 deletions cypress/e2e/17-sharing.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from '../constants';
import {
CredentialsModal,
CredentialsPage,
NDV,
WorkflowPage,
WorkflowSharingModal,
WorkflowsPage,
} from '../pages';

/**
* User U1 - Instance owner
* User U2 - User, owns C1, W1, W2
* User U3 - User, owns C2
*
* W1 - Workflow owned by User U2, shared with User U3
* W2 - Workflow owned by User U2
*
* C1 - Credential owned by User U2
* C2 - Credential owned by User U3, shared with User U1 and User U2
*/

const credentialsPage = new CredentialsPage();
const credentialsModal = new CredentialsModal();

const workflowsPage = new WorkflowsPage();
const workflowPage = new WorkflowPage();
const workflowSharingModal = new WorkflowSharingModal();
const ndv = new NDV();

const instanceOwner = {
email: `${DEFAULT_USER_EMAIL}one`,
password: DEFAULT_USER_PASSWORD,
firstName: 'User',
lastName: 'U1',
};

const users = [
{
email: `${DEFAULT_USER_EMAIL}two`,
password: DEFAULT_USER_PASSWORD,
firstName: 'User',
lastName: 'U2',
},
{
email: `${DEFAULT_USER_EMAIL}three`,
password: DEFAULT_USER_PASSWORD,
firstName: 'User',
lastName: 'U3',
},
];

describe('Sharing', () => {
before(() => {
cy.resetAll();
cy.setupOwner(instanceOwner);
});

beforeEach(() => {
cy.on('uncaught:exception', (err, runnable) => {
expect(err.message).to.include('Not logged in');
return false;
});
});

it('should invite User U2 and User U3 to instance', () => {
cy.inviteUsers({ instanceOwner, users });
});

let workflowW2Url = '';
it('should create C1, W1, W2, share W1 with U3, as U2', () => {
cy.signin(users[0]);

cy.visit(credentialsPage.url);
credentialsPage.getters.emptyListCreateCredentialButton().click();
credentialsModal.getters.newCredentialTypeOption('Notion API').click();
credentialsModal.getters.newCredentialTypeButton().click();
credentialsModal.getters.connectionParameter('API Key').type('1234567890');
credentialsModal.actions.setName('Credential C1');
credentialsModal.actions.save();
credentialsModal.actions.close();

cy.visit(workflowsPage.url);
workflowsPage.getters.newWorkflowButtonCard().click();
workflowPage.actions.setWorkflowName('Workflow W1');
workflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
workflowPage.actions.addNodeToCanvas('Notion', true, true);
ndv.getters.credentialInput().should('contain', 'Credential C1');
ndv.actions.close();

workflowPage.actions.openShareModal();
workflowSharingModal.actions.addUser(users[1].email);
workflowSharingModal.actions.save();
workflowPage.actions.saveWorkflowOnButtonClick();

cy.visit(workflowsPage.url);
workflowsPage.getters.createWorkflowButton().click();
cy.createFixtureWorkflow('Test_workflow_1.json', 'Workflow W2');
cy.url().then((url) => {
workflowW2Url = url;
});
});

it('should create C2, share C2 with U1 and U2, as U3', () => {
cy.signin(users[1]);

cy.visit(credentialsPage.url);
credentialsPage.getters.emptyListCreateCredentialButton().click();
credentialsModal.getters.newCredentialTypeOption('Airtable API').click();
credentialsModal.getters.newCredentialTypeButton().click();
credentialsModal.getters.connectionParameter('API Key').type('1234567890');
credentialsModal.actions.setName('Credential C2');
credentialsModal.actions.changeTab('Sharing');
credentialsModal.actions.addUser(instanceOwner.email);
credentialsModal.actions.addUser(users[0].email);
credentialsModal.actions.save();
credentialsModal.actions.close();
});

it('should open W1, add node using C2 as U3', () => {
cy.signin(users[1]);

cy.visit(workflowsPage.url);
workflowsPage.getters.workflowCards().should('have.length', 1);
workflowsPage.getters.workflowCard('Workflow W1').click();
workflowPage.actions.addNodeToCanvas('Airtable', true, true);
ndv.getters.credentialInput().should('contain', 'Credential C2');
ndv.actions.close();
workflowPage.actions.saveWorkflowOnButtonClick();

workflowPage.actions.openNode('Notion');
ndv.getters
.credentialInput()
.find('input')
.should('have.value', 'Credential C1')
.should('be.disabled');
ndv.actions.close();
});

it('should not have access to W2, as U3', () => {
cy.signin(users[1]);

cy.visit(workflowW2Url);
cy.waitForLoad();
cy.wait(1000);
cy.get('.el-notification').contains('Could not find workflow').should('be.visible');
});

it('should have access to W1, W2, as U1', () => {
cy.signin(instanceOwner);

cy.visit(workflowsPage.url);
workflowsPage.getters.workflowCards().should('have.length', 2);
workflowsPage.getters.workflowCard('Workflow W1').click();
workflowPage.actions.openNode('Notion');
ndv.getters
.credentialInput()
.find('input')
.should('have.value', 'Credential C1')
.should('be.disabled');
ndv.actions.close();

cy.waitForLoad();
cy.visit(workflowsPage.url);
workflowsPage.getters.workflowCard('Workflow W2').click();
workflowPage.actions.executeWorkflow();
});
});
40 changes: 40 additions & 0 deletions cypress/e2e/20-workflow-executions.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { WorkflowPage } from "../pages";
import { WorkflowExecutionsTab } from "../pages/workflow-executions-tab";

const workflowPage = new WorkflowPage();
const executionsTab = new WorkflowExecutionsTab();

// Test suite for executions tab
describe('Current Workflow Executions', () => {
before(() => {
cy.resetAll();
cy.skipSetup();
workflowPage.actions.visit();
cy.waitForLoad();
cy.createFixtureWorkflow('Test_workflow_4_executions_view.json', `My test workflow`);
createMockExecutions();
});

it('should render executions tab correctly', () => {
cy.waitForLoad();
executionsTab.getters.executionListItems().should('have.length', 11);
executionsTab.getters.successfulExecutionListItems().should('have.length', 9);
executionsTab.getters.failedExecutionListItems().should('have.length', 2);
executionsTab.getters.executionListItems().first().invoke('attr','class').should('match', /_active_/);
});

});


const createMockExecutions = () => {
workflowPage.actions.turnOnManualExecutionSaving();
executionsTab.actions.createManualExecutions(5);
// Make some failed executions by enabling Code node with syntax error
executionsTab.actions.toggleNodeEnabled('Error');
executionsTab.actions.createManualExecutions(2);
// Then add some more successful ones
executionsTab.actions.toggleNodeEnabled('Error');
executionsTab.actions.createManualExecutions(4);
executionsTab.actions.switchToExecutionsTab();
cy.waitForLoad();
}
69 changes: 69 additions & 0 deletions cypress/fixtures/Test_workflow_4_executions_view.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"meta": {
"instanceId": "6b85439d79c07750ea49eced4bc2a12b283cfcba0ab2917cd4f3fee36080e869"
},
"nodes": [
{
"parameters": {
"jsCode": "// Loop over input items and add a new field\n// called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n error\n}\n\nreturn $input.all();"
},
"id": "d0ab7e12-0e1b-4c08-8081-83107794f37d",
"name": "Error",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
680,
460
],
"disabled": true
},
{
"parameters": {},
"id": "f5026145-66c1-463c-8ac8-46a1309a6632",
"name": "On clicking 'execute'",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
460,
460
]
},
{
"parameters": {
"jsCode": "// Loop over input items and add a new field\n// called 'myNewField' to the JSON of each one\nfor (const item of $input.all()) {\n item.json.myNewField = 1;\n}\n\nreturn $input.all();"
},
"id": "9926f884-348a-4af0-872e-dd7c8b3da811",
"name": "Code",
"type": "n8n-nodes-base.code",
"typeVersion": 1,
"position": [
900,
460
]
}
],
"connections": {
"Error": {
"main": [
[
{
"node": "Code",
"type": "main",
"index": 0
}
]
]
},
"On clicking 'execute'": {
"main": [
[
{
"node": "Error",
"type": "main",
"index": 0
}
]
]
}
}
}
14 changes: 14 additions & 0 deletions cypress/pages/modals/credentials-modal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,19 @@ export class CredentialsModal extends BasePage {
credentialAuthTypeRadioButtons: () =>
this.getters.credentialsAuthTypeSelector().find('label[role=radio]'),
credentialInputs: () => cy.getByTestId('credential-connection-parameter'),
menu: () => this.getters.editCredentialModal().get('.menu-container'),
menuItem: (name: string) => this.getters.menu().get('.n8n-menu-item').contains(name),
usersSelect: () => cy.getByTestId('credential-sharing-modal-users-select'),
};
actions = {
addUser: (email: string) => {
this.getters.usersSelect().click();
this.getters
.usersSelect()
.get('.el-select-dropdown__item')
.contains(email.toLowerCase())
.click();
},
setName: (name: string) => {
this.getters.name().click();
this.getters.nameInput().clear().type(name);
Expand Down Expand Up @@ -64,5 +75,8 @@ export class CredentialsModal extends BasePage {
this.getters.nameInput().type(newName);
this.getters.nameInput().type('{enter}');
},
changeTab: (tabName: string) => {
this.getters.menuItem(tabName).click();
},
};
}
1 change: 1 addition & 0 deletions cypress/pages/modals/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './credentials-modal';
export * from './message-box';
export * from './workflow-sharing-modal';
26 changes: 26 additions & 0 deletions cypress/pages/modals/workflow-sharing-modal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { BasePage } from '../base';

export class WorkflowSharingModal extends BasePage {
getters = {
modal: () => cy.getByTestId('workflowShare-modal', { timeout: 5000 }),
usersSelect: () => cy.getByTestId('workflow-sharing-modal-users-select'),
saveButton: () => cy.getByTestId('workflow-sharing-modal-save-button'),
closeButton: () => this.getters.modal().find('.el-dialog__close').first(),
};
actions = {
addUser: (email: string) => {
this.getters.usersSelect().click();
this.getters
.usersSelect()
.get('.el-select-dropdown__item')
.contains(email.toLowerCase())
.click();
},
save: () => {
this.getters.saveButton().click();
},
closeModal: () => {
this.getters.closeButton().click();
},
};
}
Loading

0 comments on commit b9ba7ae

Please sign in to comment.