test: Add tests for pinning data (#5157)

* test: add tests for pinning

* test: add test for value

* test: add pinned data tests

* test: refactor into ndv

* refactor: move to ndv

* refactor: rename node

* test: fix test

* test: fix refactor

* test: remove unused id

* test: update test

* test: chain rename input

* test: refactor invoking text

* test: fix ndv tests

* test: move test id

* test: update selectors
This commit is contained in:
Mutasem Aldmour 2023-01-18 15:48:36 +01:00 committed by GitHub
parent e36112a6d4
commit a89c9c68d1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 161 additions and 56 deletions

View file

@ -1,11 +1,13 @@
import { CODE_NODE_NAME, SET_NODE_NAME } from './../constants'; import { CODE_NODE_NAME, SET_NODE_NAME } from './../constants';
import { SCHEDULE_TRIGGER_NODE_NAME } from '../constants'; import { SCHEDULE_TRIGGER_NODE_NAME } from '../constants';
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
import { NDV } from '../pages/ndv';
// Suite-specific constants // Suite-specific constants
const CODE_NODE_NEW_NAME = 'Something else'; const CODE_NODE_NEW_NAME = 'Something else';
const WorkflowPage = new WorkflowPageClass(); const WorkflowPage = new WorkflowPageClass();
const ndv = new NDV();
describe('Undo/Redo', () => { describe('Undo/Redo', () => {
beforeEach(() => { beforeEach(() => {
@ -205,11 +207,7 @@ describe('Undo/Redo', () => {
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME); WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.getters.canvasNodes().last().click(); WorkflowPage.getters.canvasNodes().last().click();
cy.get('body').type('{enter}'); cy.get('body').type('{enter}');
WorkflowPage.getters.nodeNameContainerNDV().click(); ndv.actions.rename(CODE_NODE_NEW_NAME);
WorkflowPage.getters.nodeRenameInput().should('be.visible');
WorkflowPage.getters.nodeRenameInput().type('{selectall}');
WorkflowPage.getters.nodeRenameInput().type(CODE_NODE_NEW_NAME);
cy.get('body').type('{enter}');
cy.get('body').type('{esc}'); cy.get('body').type('{esc}');
WorkflowPage.actions.hitUndo(); WorkflowPage.actions.hitUndo();
cy.get('body').type('{esc}'); cy.get('body').type('{esc}');

View file

@ -12,7 +12,7 @@ describe('Inline expression editor', () => {
WorkflowPage.actions.visit(); WorkflowPage.actions.visit();
WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger'); WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
WorkflowPage.actions.addNodeToCanvas('Hacker News'); WorkflowPage.actions.addNodeToCanvas('Hacker News');
WorkflowPage.actions.openNodeNdv('Hacker News'); WorkflowPage.actions.openNode('Hacker News');
WorkflowPage.actions.openInlineExpressionEditor(); WorkflowPage.actions.openInlineExpressionEditor();
}); });

View file

@ -0,0 +1,65 @@
import { WorkflowPage, NDV } from '../pages';
const workflowPage = new WorkflowPage();
const ndv = new NDV();
describe('Data pinning', () => {
beforeEach(() => {
cy.resetAll();
cy.skipSetup();
workflowPage.actions.visit();
cy.waitForLoad();
});
it('Should be able to pin node output', () => {
workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger');
workflowPage.getters.canvasNodes().first().dblclick();
ndv.getters.container().should('be.visible');
ndv.getters.pinDataButton().should('not.exist');
ndv.getters.editPinnedDataButton().should('be.visible');
ndv.actions.execute();
ndv.getters.outputDataContainer().should('be.visible');
ndv.getters.outputDataContainer().get('table').should('be.visible');
ndv.getters.outputTableRows().should('have.length', 2);
ndv.getters.outputTableHeaders().should('have.length.at.least', 10);
ndv.getters.outputTableHeaders().first().should('include.text', 'timestamp');
ndv.getters.outputTableHeaders().eq(1).should('include.text', 'Readable date');
ndv.getters.outputTbodyCell(1, 0).invoke('text').then((prevValue) => {
ndv.actions.pinData();
ndv.actions.close();
workflowPage.actions.executeWorkflow();
workflowPage.actions.openNode('Schedule Trigger');
ndv.getters.outputTbodyCell(1, 0).invoke('text').should('eq', prevValue);
});
});
it('Should be be able to set pinned data', () => {
workflowPage.actions.addInitialNodeToCanvas('Schedule Trigger');
workflowPage.getters.canvasNodes().first().dblclick();
ndv.getters.container().should('be.visible');
ndv.getters.pinDataButton().should('not.exist');
ndv.getters.editPinnedDataButton().should('be.visible');
ndv.actions.setPinnedData([{ test: 1 }]);
ndv.getters.outputTableRows().should('have.length', 2);
ndv.getters.outputTableHeaders().should('have.length', 2);
ndv.getters.outputTableHeaders().first().should('include.text', 'test');
ndv.getters.outputTbodyCell(1, 0).should('include.text', 1);
ndv.actions.close();
workflowPage.actions.saveWorkflowOnButtonClick();
cy.reload();
workflowPage.actions.openNode('Schedule Trigger');
ndv.getters.outputTableHeaders().first().should('include.text', 'test');
ndv.getters.outputTbodyCell(1, 0).should('include.text', 1);
});
});

View file

@ -26,7 +26,7 @@ describe('NDV', () => {
workflowPage.actions.addInitialNodeToCanvas('Webhook'); workflowPage.actions.addInitialNodeToCanvas('Webhook');
workflowPage.getters.canvasNodes().first().dblclick(); workflowPage.getters.canvasNodes().first().dblclick();
ndv.getters.nodeExecuteButton().first().click(); ndv.actions.execute();
ndv.getters.copyInput().click(); ndv.getters.copyInput().click();
cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite'); cy.grantBrowserPermissions('clipboardReadWrite', 'clipboardSanitizedWrite');
@ -49,9 +49,7 @@ describe('NDV', () => {
workflowPage.getters.canvasNodes().last().dblclick(); workflowPage.getters.canvasNodes().last().dblclick();
ndv.getters.inputSelect().click(); ndv.getters.inputSelect().click();
ndv.getters.inputOption().last().click(); ndv.getters.inputOption().last().click();
ndv.getters.inputPanel().within(() => { ndv.getters.inputDataContainer().should('contain', 'start');
ndv.getters.dataContainer().should('contain', 'start');
});
}); });
it('should show correct validation state for resource locator params', () => { it('should show correct validation state for resource locator params', () => {
@ -61,7 +59,7 @@ describe('NDV', () => {
cy.get('[class*=hasIssues]').should('have.length', 0); cy.get('[class*=hasIssues]').should('have.length', 0);
ndv.getters.backToCanvas().click(); ndv.getters.backToCanvas().click();
// Both credentials and resource locator errors should be visible // Both credentials and resource locator errors should be visible
workflowPage.actions.openNodeNdv('Typeform'); workflowPage.actions.openNode('Typeform');
cy.get('.has-issues').should('have.length', 1); cy.get('.has-issues').should('have.length', 1);
cy.get('[class*=hasIssues]').should('have.length', 1); cy.get('[class*=hasIssues]').should('have.length', 1);
}); });
@ -71,11 +69,11 @@ describe('NDV', () => {
workflowPage.actions.addNodeToCanvas('Airtable', true); workflowPage.actions.addNodeToCanvas('Airtable', true);
ndv.getters.container().should('be.visible'); ndv.getters.container().should('be.visible');
cy.get('.has-issues').should('have.length', 0); cy.get('.has-issues').should('have.length', 0);
workflowPage.getters.ndvParameterInput('table').find('input').eq(1).focus().blur() ndv.getters.parameterInput('table').find('input').eq(1).focus().blur()
workflowPage.getters.ndvParameterInput('application').find('input').eq(1).focus().blur() ndv.getters.parameterInput('application').find('input').eq(1).focus().blur()
cy.get('.has-issues').should('have.length', 2); cy.get('.has-issues').should('have.length', 2);
ndv.getters.backToCanvas().click(); ndv.getters.backToCanvas().click();
workflowPage.actions.openNodeNdv('Airtable'); workflowPage.actions.openNode('Airtable');
cy.get('.has-issues').should('have.length', 3); cy.get('.has-issues').should('have.length', 3);
cy.get('[class*=hasIssues]').should('have.length', 1); cy.get('[class*=hasIssues]').should('have.length', 1);
}); });
@ -84,7 +82,7 @@ describe('NDV', () => {
cy.fixture('Test_workflow_ndv_errors.json').then((data) => { cy.fixture('Test_workflow_ndv_errors.json').then((data) => {
cy.get('body').paste(JSON.stringify(data)); cy.get('body').paste(JSON.stringify(data));
workflowPage.getters.canvasNodes().should('have.have.length', 1); workflowPage.getters.canvasNodes().should('have.have.length', 1);
workflowPage.actions.openNodeNdv('Airtable'); workflowPage.actions.openNode('Airtable');
cy.get('.has-issues').should('have.length', 3); cy.get('.has-issues').should('have.length', 3);
cy.get('[class*=hasIssues]').should('have.length', 1); cy.get('[class*=hasIssues]').should('have.length', 1);
}); });

View file

@ -1,6 +1,8 @@
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
import { NDV } from '../pages/ndv';
const WorkflowPage = new WorkflowPageClass(); const WorkflowPage = new WorkflowPageClass();
const ndv = new NDV();
describe('Code node', () => { describe('Code node', () => {
beforeEach(() => { beforeEach(() => {
@ -12,9 +14,9 @@ describe('Code node', () => {
WorkflowPage.actions.visit(); WorkflowPage.actions.visit();
WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger'); WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
WorkflowPage.actions.addNodeToCanvas('Code'); WorkflowPage.actions.addNodeToCanvas('Code');
WorkflowPage.actions.openNodeNdv('Code'); WorkflowPage.actions.openNode('Code');
WorkflowPage.actions.executeNodeFromNdv(); ndv.actions.execute();
WorkflowPage.getters.successToast().contains('Node executed successfully'); WorkflowPage.getters.successToast().contains('Node executed successfully');
}); });
@ -23,11 +25,11 @@ describe('Code node', () => {
WorkflowPage.actions.visit(); WorkflowPage.actions.visit();
WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger'); WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
WorkflowPage.actions.addNodeToCanvas('Code'); WorkflowPage.actions.addNodeToCanvas('Code');
WorkflowPage.actions.openNodeNdv('Code'); WorkflowPage.actions.openNode('Code');
WorkflowPage.getters.ndvParameterInput('mode').click(); ndv.getters.parameterInput('mode').click();
WorkflowPage.actions.selectOptionInParameterDropdown('mode', 'Run Once for Each Item'); ndv.actions.selectOptionInParameterDropdown('mode', 'Run Once for Each Item');
WorkflowPage.actions.executeNodeFromNdv(); ndv.actions.execute();
WorkflowPage.getters.successToast().contains('Node executed successfully'); WorkflowPage.getters.successToast().contains('Node executed successfully');
}); });

View file

@ -1,8 +1,8 @@
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; import { WorkflowPage, WorkflowsPage, NDV } from '../pages';
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
const WorkflowsPage = new WorkflowsPageClass(); const workflowsPage = new WorkflowsPage();
const WorkflowPage = new WorkflowPageClass(); const workflowPage = new WorkflowPage();
const ndv = new NDV()
describe('HTTP Request node', () => { describe('HTTP Request node', () => {
before(() => { before(() => {
@ -11,14 +11,14 @@ describe('HTTP Request node', () => {
}); });
it('should make a request with a URL and receive a response', () => { it('should make a request with a URL and receive a response', () => {
WorkflowsPage.actions.createWorkflowFromCard(); workflowsPage.actions.createWorkflowFromCard();
WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger'); workflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
WorkflowPage.actions.addNodeToCanvas('HTTP Request'); workflowPage.actions.addNodeToCanvas('HTTP Request');
WorkflowPage.actions.openNodeNdv('HTTP Request'); workflowPage.actions.openNode('HTTP Request');
WorkflowPage.actions.typeIntoParameterInput('url', 'https://catfact.ninja/fact'); ndv.actions.typeIntoParameterInput('url', 'https://catfact.ninja/fact');
WorkflowPage.actions.executeNodeFromNdv(); ndv.actions.execute();
WorkflowPage.getters.ndvOutputPanel().contains('fact'); ndv.getters.outputPanel().contains('fact');
}); });
}); });

View file

@ -12,7 +12,7 @@ describe('Expression editor modal', () => {
WorkflowPage.actions.visit(); WorkflowPage.actions.visit();
WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger'); WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
WorkflowPage.actions.addNodeToCanvas('Hacker News'); WorkflowPage.actions.addNodeToCanvas('Hacker News');
WorkflowPage.actions.openNodeNdv('Hacker News'); WorkflowPage.actions.openNode('Hacker News');
WorkflowPage.actions.openExpressionEditorModal(); WorkflowPage.actions.openExpressionEditorModal();
}); });

View file

@ -9,8 +9,65 @@ export class NDV extends BasePage {
inputSelect: () => cy.getByTestId('ndv-input-select'), inputSelect: () => cy.getByTestId('ndv-input-select'),
inputOption: () => cy.getByTestId('ndv-input-option'), inputOption: () => cy.getByTestId('ndv-input-option'),
inputPanel: () => cy.getByTestId('ndv-input-panel'), inputPanel: () => cy.getByTestId('ndv-input-panel'),
dataContainer: () => cy.getByTestId('ndv-data-container'), outputPanel: () => cy.getByTestId('output-panel'),
inputDataContainer: () => this.getters.inputPanel().findChildByTestId('ndv-data-container'),
outputDataContainer: () => this.getters.outputPanel().findChildByTestId('ndv-data-container'),
runDataDisplayMode: () => cy.getByTestId('ndv-run-data-display-mode'), runDataDisplayMode: () => cy.getByTestId('ndv-run-data-display-mode'),
digital: () => cy.getByTestId('ndv-run-data-display-mode'), digital: () => cy.getByTestId('ndv-run-data-display-mode'),
pinDataButton: () => cy.getByTestId('ndv-pin-data'),
editPinnedDataButton: () => cy.getByTestId('ndv-edit-pinned-data'),
pinnedDataEditor: () => this.getters.outputPanel().find('.monaco-editor'),
runDataPaneHeader: () => cy.getByTestId('run-data-pane-header'),
savePinnedDataButton: () => this.getters.runDataPaneHeader().find('button').contains('Save'),
outputTableRows: () => this.getters.outputDataContainer().find('table tr'),
outputTableHeaders: () => this.getters.outputDataContainer().find('table thead th'),
outputTableRow: (row: number) => this.getters.outputTableRows().eq(row),
outputTbodyCell: (row: number, cell: number) => this.getters.outputTableRow(row).find('td').eq(cell),
parameterInput: (parameterName: string) => cy.getByTestId(`parameter-input-${parameterName}`),
nodeNameContainer: () => cy.getByTestId('node-title-container'),
nodeRenameInput: () => cy.getByTestId('node-rename-input'),
};
actions = {
pinData: () => {
this.getters.pinDataButton().click();
},
editPinnedData: () => {
this.getters.editPinnedDataButton().click();
},
execute: () => {
this.getters.nodeExecuteButton().first().click();
},
close: () => {
this.getters.backToCanvas().click();
},
setPinnedData: (data: object) => {
this.getters.editPinnedDataButton().click();
const editor = this.getters.pinnedDataEditor()
editor.click();
editor.type(`{selectall}{backspace}`);
editor.type(JSON.stringify(data).replace(new RegExp('{', 'g'),'{{}'));
this.getters.savePinnedDataButton().click();
},
typeIntoParameterInput: (parameterName: string, content: string) => {
this.getters.parameterInput(parameterName).type(content);
},
selectOptionInParameterDropdown: (parameterName: string, content: string) => {
this.getters
.parameterInput(parameterName)
.find('.option-headline')
.contains(content)
.click();
},
rename: (newName: string) => {
this.getters.nodeNameContainer().click();
this.getters.nodeRenameInput()
.should('be.visible')
.type('{selectall}')
.type(newName);
cy.get('body').type('{enter}');
},
}; };
} }

View file

@ -24,10 +24,6 @@ export class WorkflowPage extends BasePage {
canvasNodes: () => cy.getByTestId('canvas-node'), canvasNodes: () => cy.getByTestId('canvas-node'),
canvasNodeByName: (nodeName: string) => canvasNodeByName: (nodeName: string) =>
this.getters.canvasNodes().filter(`:contains("${nodeName}")`), this.getters.canvasNodes().filter(`:contains("${nodeName}")`),
ndvParameterInput: (parameterName: string) =>
cy.getByTestId(`parameter-input-${parameterName}`),
ndvOutputPanel: () => cy.getByTestId('output-panel'),
ndvRunDataPaneHeader: () => cy.getByTestId('run-data-pane-header'),
successToast: () => cy.get('.el-notification .el-icon-success').parent(), successToast: () => cy.get('.el-notification .el-icon-success').parent(),
errorToast: () => cy.get('.el-notification .el-icon-error'), errorToast: () => cy.get('.el-notification .el-icon-error'),
activatorSwitch: () => cy.getByTestId('workflow-activate-switch'), activatorSwitch: () => cy.getByTestId('workflow-activate-switch'),
@ -45,8 +41,6 @@ export class WorkflowPage extends BasePage {
nodeEndpoints: () => cy.get('.jtk-endpoint-connected'), nodeEndpoints: () => cy.get('.jtk-endpoint-connected'),
disabledNodes: () => cy.get('.node-box.disabled'), disabledNodes: () => cy.get('.node-box.disabled'),
selectedNodes: () => this.getters.canvasNodes().filter('.jtk-drag-selected'), selectedNodes: () => this.getters.canvasNodes().filter('.jtk-drag-selected'),
nodeNameContainerNDV: () => cy.getByTestId('node-title-container'),
nodeRenameInput: () => cy.getByTestId('node-rename-input'),
// Workflow menu items // Workflow menu items
workflowMenuItemDuplicate: () => cy.getByTestId('workflow-menu-item-duplicate'), workflowMenuItemDuplicate: () => cy.getByTestId('workflow-menu-item-duplicate'),
workflowMenuItemDownload: () => cy.getByTestId('workflow-menu-item-download'), workflowMenuItemDownload: () => cy.getByTestId('workflow-menu-item-download'),
@ -100,7 +94,7 @@ export class WorkflowPage extends BasePage {
if (!preventNdvClose) cy.get('body').type('{esc}'); if (!preventNdvClose) cy.get('body').type('{esc}');
}, },
openNodeNdv: (nodeTypeName: string) => { openNode: (nodeTypeName: string) => {
this.getters.canvasNodeByName(nodeTypeName).dblclick(); this.getters.canvasNodeByName(nodeTypeName).dblclick();
}, },
openExpressionEditorModal: () => { openExpressionEditorModal: () => {
@ -111,19 +105,6 @@ export class WorkflowPage extends BasePage {
cy.contains('Expression').invoke('show').click(); cy.contains('Expression').invoke('show').click();
this.getters.inlineExpressionEditorInput().click(); this.getters.inlineExpressionEditorInput().click();
}, },
typeIntoParameterInput: (parameterName: string, content: string) => {
this.getters.ndvParameterInput(parameterName).type(content);
},
selectOptionInParameterDropdown: (parameterName: string, content: string) => {
this.getters
.ndvParameterInput(parameterName)
.find('.option-headline')
.contains(content)
.click();
},
executeNodeFromNdv: () => {
cy.contains('Execute node').click();
},
openWorkflowMenu: () => { openWorkflowMenu: () => {
this.getters.workflowMenu().click(); this.getters.workflowMenu().click();
}, },
@ -178,5 +159,8 @@ export class WorkflowPage extends BasePage {
hitPaste: () => { hitPaste: () => {
cy.get('body').type(META_KEY, { delay: 500, release: false }).type('P'); cy.get('body').type(META_KEY, { delay: 500, release: false }).type('P');
}, },
executeWorkflow: () => {
this.getters.executeWorkflowButton().click();
},
}; };
} }

View file

@ -5,7 +5,6 @@
</template> </template>
<div> <div>
<n8n-button <n8n-button
data-test-id="node-execute-button"
:loading="nodeRunning && !isListeningForEvents && !isListeningForWorkflowEvents" :loading="nodeRunning && !isListeningForEvents && !isListeningForWorkflowEvents"
:disabled="disabled || !!disabledHint" :disabled="disabled || !!disabledHint"
:label="buttonLabel" :label="buttonLabel"

View file

@ -18,6 +18,7 @@
<div v-if="isExecutable"> <div v-if="isExecutable">
<NodeExecuteButton <NodeExecuteButton
v-if="!blockUI" v-if="!blockUI"
data-test-id="node-execute-button"
:nodeName="node.name" :nodeName="node.name"
:disabled="outputPanelEditMode.enabled && !isTriggerNode" :disabled="outputPanelEditMode.enabled && !isTriggerNode"
size="small" size="small"

View file

@ -18,7 +18,6 @@
@tableMounted="$emit('tableMounted', $event)" @tableMounted="$emit('tableMounted', $event)"
@itemHover="$emit('itemHover', $event)" @itemHover="$emit('itemHover', $event)"
ref="runData" ref="runData"
data-test-id="ndv-output-panel"
> >
<template #header> <template #header>
<div :class="$style.titleSection"> <div :class="$style.titleSection">

View file

@ -67,6 +67,7 @@
class="ml-2xs" class="ml-2xs"
icon="pencil-alt" icon="pencil-alt"
type="tertiary" type="tertiary"
data-test-id="ndv-edit-pinned-data"
@click="enterEditMode({ origin: 'editIconButton' })" @click="enterEditMode({ origin: 'editIconButton' })"
/> />
<n8n-tooltip <n8n-tooltip
@ -100,6 +101,7 @@
icon="thumbtack" icon="thumbtack"
:disabled="editMode.enabled || (inputData.length === 0 && !hasPinData) || isReadOnly" :disabled="editMode.enabled || (inputData.length === 0 && !hasPinData) || isReadOnly"
@click="onTogglePinData({ source: 'pin-icon-click' })" @click="onTogglePinData({ source: 'pin-icon-click' })"
data-test-id="ndv-pin-data"
/> />
</n8n-tooltip> </n8n-tooltip>