Skip to content

Commit

Permalink
Merge branch 'master' into feature/undo-redo
Browse files Browse the repository at this point in the history
* master:
  fix(core): OAuth2 scopes does not save
  fix(Gmail Trigger Node): Trigger node missing some emails
  test(editor): Add more workflow actions tests (#4799)
  feat: Fix checkbox line height and make checkbox label clickable (#4818)
  feat: Add prompt to overwrite changes when concurrent editing occurs (#4817)
  • Loading branch information
MiloradFilipovic committed Dec 5, 2022
2 parents 253bb3b + 7aefed4 commit cb7bda2
Show file tree
Hide file tree
Showing 17 changed files with 274 additions and 106 deletions.
74 changes: 0 additions & 74 deletions cypress/e2e/5-workflow-actions.cy.ts

This file was deleted.

107 changes: 107 additions & 0 deletions cypress/e2e/7-workflow-actions.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';

const NEW_WORKFLOW_NAME = 'Something else';
const MANUAL_TRIGGER_NODE_NAME = 'Manual Trigger';
const SCHEDULE_TRIGGER_NODE_NAME = 'Schedule Trigger';
const CODE_NODE = 'Code'
const TEST_WF_TAGS = ['Tag 1', 'Tag 2', 'Tag 3'];

const WorkflowPage = new WorkflowPageClass();

describe('Workflow Actions', () => {
beforeEach(() => {
cy.resetAll();
cy.skipSetup();
WorkflowPage.actions.visit();
});

it('should be able to save on button click', () => {
WorkflowPage.actions.saveWorkflowOnButtonClick();
WorkflowPage.getters.isWorkflowSaved();
});

it('should save workflow on keyboard shortcut', () => {
WorkflowPage.actions.saveWorkflowUsingKeyboardShortcut();
WorkflowPage.getters.isWorkflowSaved();
});

it('should not be able to activate unsaved workflow', () => {
WorkflowPage.getters.activatorSwitch().find('input').first().should('be.disabled');
});

it('should not be able to activate workflow without trigger node', () => {
// Manual trigger is not enough to activate the workflow
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
WorkflowPage.actions.saveWorkflowOnButtonClick();
WorkflowPage.getters.activatorSwitch().find('input').first().should('be.disabled');
});

it('should be able to activate workflow', () => {
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.saveWorkflowOnButtonClick();
WorkflowPage.actions.activateWorkflow();
WorkflowPage.getters.isWorkflowActivated();
});

it('should save new workflow after renaming', () => {
WorkflowPage.actions.renameWorkflow(NEW_WORKFLOW_NAME);
WorkflowPage.getters.isWorkflowSaved();
});

it('should rename workflow', () => {
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.saveWorkflowOnButtonClick();
WorkflowPage.actions.renameWorkflow(NEW_WORKFLOW_NAME);
WorkflowPage.getters.isWorkflowSaved();
WorkflowPage.getters.workflowNameInputContainer().invoke('attr', 'title').should('eq', NEW_WORKFLOW_NAME);
});

it('should add tags', () => {
WorkflowPage.getters.newTagLink().click();
WorkflowPage.actions.addTags(TEST_WF_TAGS);
WorkflowPage.getters.isWorkflowSaved();
WorkflowPage.getters.workflowTagElements().should('have.length', TEST_WF_TAGS.length);
});

it('should add more tags', () => {
WorkflowPage.getters.newTagLink().click();
WorkflowPage.actions.addTags(TEST_WF_TAGS);
WorkflowPage.getters.workflowTagElements().first().click();
WorkflowPage.actions.addTags(['Another one']);
WorkflowPage.getters.workflowTagElements().should('have.length', TEST_WF_TAGS.length + 1);
});

it('should remove tags by clicking X in tag', () => {
WorkflowPage.getters.newTagLink().click();
WorkflowPage.actions.addTags(TEST_WF_TAGS);
WorkflowPage.getters.workflowTagElements().first().click();
WorkflowPage.getters.workflowTagsContainer().find('.el-tag__close').first().click();
cy.get('body').type('{enter}');
WorkflowPage.getters.workflowTagElements().should('have.length', TEST_WF_TAGS.length - 1);
});

it('should remove tags from dropdown', () => {
WorkflowPage.getters.newTagLink().click();
WorkflowPage.actions.addTags(TEST_WF_TAGS);
WorkflowPage.getters.workflowTagElements().first().click();
WorkflowPage.getters.workflowTagsDropdown().find('li').first().click();
cy.get('body').type('{enter}');
WorkflowPage.getters.workflowTagElements().should('have.length', TEST_WF_TAGS.length - 1);
});

it('should copy nodes', () => {
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.successToast().should('exist');
});

it('should paste nodes', () => {
cy.fixture('Test_workflow-actions_paste-data.json').then(data => {
cy.get('body').paste(JSON.stringify(data));
WorkflowPage.getters.canvasNodes().should('have.have.length', 2);
});
});

});
37 changes: 37 additions & 0 deletions cypress/fixtures/Test_workflow-actions_paste-data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"meta": {
"instanceId": "1a30c82b98a30444ad25bce513655a5e02be772d361403542c23172be6062f04"
},
"nodes": [{
"parameters": {
"rule": {
"interval": [{}]
}
},
"id": "a898563b-d2a4-4b15-a979-366872e801b0",
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [420, 260]
}, {
"parameters": {
"options": {}
},
"id": "b9a13e3d-bfa5-4873-959f-fd3d67e380d9",
"name": "Set",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [640, 260]
}],
"connections": {
"Schedule Trigger": {
"main": [
[{
"node": "Set",
"type": "main",
"index": 0
}]
]
}
}
}
15 changes: 10 additions & 5 deletions cypress/pages/workflow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import { BasePage } from './base';
export class WorkflowPage extends BasePage {
url = '/workflow/new';
getters = {
workflowNameInputContainer: () => cy
.getByTestId('workflow-name-input', { timeout: 5000 }),
workflowNameInputContainer: () => cy.getByTestId('workflow-name-input', { timeout: 5000 }),
workflowNameInput: () => this.getters.workflowNameInputContainer().then(($el) => cy.wrap($el.find('input'))),
workflowImportInput: () => cy.getByTestId('workflow-import-input'),
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(),
workflowTagsDropdown: () => cy.getByTestId('workflow-tags-dropdown'),
newTagLink: () => cy.getByTestId('new-tag-link'),
saveButton: () => cy.getByTestId('workflow-save-button'),
nodeCreatorSearchBar: () => cy.getByTestId('node-creator-search-bar'),
Expand All @@ -29,6 +31,10 @@ export class WorkflowPage extends BasePage {
isWorkflowActivated: () => this.getters.activatorSwitch().should('have.class', 'is-checked'),
expressionModalInput: () => cy.getByTestId('expression-modal-input'),
expressionModalOutput: () => cy.getByTestId('expression-modal-output'),

nodeViewRoot: () => cy.getByTestId('node-view-root'),
copyPasteInput: () => cy.getByTestId('hidden-copy-paste'),
canvasNodes: () => cy.getByTestId('canvas-node'),
};
actions = {
visit: () => {
Expand Down Expand Up @@ -86,10 +92,9 @@ export class WorkflowPage extends BasePage {
cy.get('body').type('{enter}');
},
addTags: (tags: string[]) => {
this.getters.newTagLink().click();
tags.forEach(tag => {
cy.get('body').type(tag);
cy.get('body').type('{enter}');
this.getters.workflowTagsInput().type(tag);
this.getters.workflowTagsInput().type('{enter}');
});
cy.get('body').type('{enter}');
},
Expand Down
12 changes: 12 additions & 0 deletions cypress/support/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,15 @@ Cypress.Commands.add('resetAll', () => {
Cypress.Commands.add('setupOwner', (payload) => {
cy.task('setup-owner', payload);
});

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 => {
const pasteEvent = Object.assign(new Event('paste', { bubbles: true, cancelable: true }), {
clipboardData: {
getData: () => pastePayload
}
});
$destination[0].dispatchEvent(pasteEvent);
});
});
1 change: 1 addition & 0 deletions cypress/support/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ declare global {
setupOwner(payload: SetupPayload): void;
skipSetup(): void;
resetAll(): void;
paste(pastePayload: string): void,
}
}
}
Expand Down
14 changes: 10 additions & 4 deletions packages/cli/src/credentials/oauth2Credential.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,26 +78,32 @@ oauth2CredentialController.get(
throw new ResponseHelper.InternalServerError((error as Error).message);
}

const credentialType = (credential as unknown as ICredentialsEncrypted).type;

const mode: WorkflowExecuteMode = 'internal';
const timezone = config.getEnv('generic.timezone');
const credentialsHelper = new CredentialsHelper(encryptionKey);
const decryptedDataOriginal = await credentialsHelper.getDecrypted(
credential as INodeCredentialsDetails,
(credential as unknown as ICredentialsEncrypted).type,
credentialType,
mode,
timezone,
true,
);

// At some point in the past we saved hidden scopes to credentials (but shouldn't)
// Delete scope before applying defaults to make sure new scopes are present on reconnect
if (decryptedDataOriginal?.scope) {
if (
decryptedDataOriginal?.scope &&
credentialType.includes('OAuth2') &&
!['oAuth2Api'].includes(credentialType)
) {
delete decryptedDataOriginal.scope;
}

const oauthCredentials = credentialsHelper.applyDefaultsAndOverwrites(
decryptedDataOriginal,
(credential as unknown as ICredentialsEncrypted).type,
credentialType,
mode,
timezone,
);
Expand Down Expand Up @@ -128,7 +134,7 @@ oauth2CredentialController.get(
// Encrypt the data
const credentials = new Credentials(
credential as INodeCredentialsDetails,
(credential as unknown as ICredentialsEncrypted).type,
credentialType,
(credential as unknown as ICredentialsEncrypted).nodesAccess,
);
decryptedDataOriginal.csrfSecret = csrfSecret;
Expand Down
16 changes: 15 additions & 1 deletion packages/design-system/src/components/N8nCheckbox/Checkbox.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<template>
<el-checkbox
v-bind="$props"
ref="checkbox"
:class="['n8n-checkbox', $style.n8nCheckbox]"
:disabled="disabled"
:indeterminate="indeterminate"
Expand All @@ -12,7 +13,8 @@
:tooltipText="tooltipText"
:bold="false"
:size="labelSize"
></n8n-input-label>
@click.prevent="onLabelClick"
/>
</el-checkbox>
</template>

Expand Down Expand Up @@ -58,6 +60,14 @@ export default Vue.extend({
onChange(event: Event) {
this.$emit('input', event);
},
onLabelClick() {
const checkboxComponent = this.$refs.checkbox as ElCheckbox;
if (!checkboxComponent) {
return;
}
(checkboxComponent.$el as HTMLElement).click();
},
},
});
</script>
Expand All @@ -70,5 +80,9 @@ export default Vue.extend({
span {
white-space: normal;
}
label {
cursor: pointer;
}
}
</style>
Loading

0 comments on commit cb7bda2

Please sign in to comment.