test: E2E NDV (#4712)

* test: start testing node details view

* test: deleting workflow after test

* test: rename deleteWorkflow command

* test: webhook node

* test: roll back deleting workflow in workflow test

* test: check output display modes after webhook node invoked

* test: simplifying test

* test: simplifying test

* test: adding more tests related to NDV

* test: adding more tests related to NDV

* test: update after merge

* Merge remote-tracking branch 'origin/master' into n8n-5494-e2e-ndv

# Conflicts:
#	cypress/pages/workflow.ts

* test: improving test blocks

* test: NDV change node input

* test: NDV change node input

* test: NDV fix missing command

* test(editor): Add NDV page object
This commit is contained in:
Csaba Tuncsik 2022-12-01 09:26:38 +01:00 committed by GitHub
parent 00bed61b39
commit 430cc65a4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 195 additions and 18 deletions

66
cypress/e2e/5-ndv.cy.ts Normal file
View file

@ -0,0 +1,66 @@
import { WorkflowsPage, WorkflowPage, NDV } from '../pages';
import { v4 as uuid } from 'uuid';
const workflowsPage = new WorkflowsPage();
const workflowPage = new WorkflowPage();
const ndv = new NDV();
describe('NDV', () => {
beforeEach(() => {
cy.resetAll();
cy.skipSetup();
workflowsPage.actions.createWorkflowFromCard();
workflowPage.actions.renameWorkflow(uuid());
workflowPage.actions.saveWorkflowOnButtonClick();
});
it('should show up when double clicked on a node and close when Back to canvas clicked', () => {
workflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
workflowPage.getters.canvasNodes().first().dblclick();
ndv.getters.container().should('be.visible');
ndv.getters.backToCanvas().click()
ndv.getters.container().should('not.be.visible');
});
it('should test webhook node', () => {
workflowPage.actions.addInitialNodeToCanvas('Webhook');
workflowPage.getters.canvasNodes().first().dblclick();
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.window().its('navigator.permissions')
.invoke('query', {name: 'clipboard-read'})
.its('state').should('equal', 'granted');
cy.window().its('navigator.clipboard').invoke('readText').then(url => {
cy.request({
method: 'GET',
url,
}).then((resp) => {
expect(resp.status).to.eq(200)
})
});
ndv.getters.runDataDisplayMode().should('have.length.at.least', 1).and('be.visible');
});
it('should change input', () => {
cy.createFixtureWorkflow('NDV-test-select-input.json', `NDV test select input ${uuid()}`);
workflowPage.actions.zoomToFit();
workflowPage.getters.canvasNodes().last().dblclick();
ndv.getters.inputSelect().click();
ndv.getters.inputOption().last().click();
ndv.getters.inputPanel().within(() => {
ndv.getters.dataContainer().should('contain', 'start');
});
});
});

View file

@ -0,0 +1,83 @@
{
"name": "ff739753-9d6e-46a7-94d3-25bd03dd4973",
"nodes": [
{
"parameters": {},
"id": "c30d1114-d7f7-44dc-b55a-15312ef2d76d",
"name": "On clicking 'execute'",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
600,
580
]
},
{
"parameters": {
"options": {}
},
"id": "5bf514bd-65ae-4a1c-8b69-a01bfeaad411",
"name": "Set",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [
820,
580
]
},
{
"parameters": {
"options": {}
},
"id": "02bf49e9-44b2-4f4e-8cb2-8c02399208af",
"name": "Set1",
"type": "n8n-nodes-base.set",
"typeVersion": 1,
"position": [
1040,
580
]
}
],
"pinData": {
"On clicking 'execute'": [
{
"json": {
"start": true
}
}
]
},
"connections": {
"On clicking 'execute'": {
"main": [
[
{
"node": "Set",
"type": "main",
"index": 0
}
]
]
},
"Set": {
"main": [
[
{
"node": "Set1",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {},
"hash": "abd7b28aa2605c96ba24474d72cbe610",
"id": 3,
"meta": {
"instanceId": "08a83d394781701f5c18052cde68e8d92c88b26f5cc8809eb10ad545f14015cb"
},
"tags": []
}

View file

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

16
cypress/pages/ndv.ts Normal file
View file

@ -0,0 +1,16 @@
import { BasePage } from "./base";
export class NDV extends BasePage {
getters = {
container: () => cy.getByTestId('ndv'),
backToCanvas: () => cy.getByTestId('back-to-canvas'),
copyInput: () => cy.getByTestId('copy-input'),
nodeExecuteButton: () => cy.getByTestId('node-execute-button'),
inputSelect: () => cy.getByTestId('ndv-input-select'),
inputOption: () => cy.getByTestId('ndv-input-option'),
inputPanel: () => cy.getByTestId('ndv-input-panel'),
dataContainer: () => cy.getByTestId('ndv-data-container'),
runDataDisplayMode: () => cy.getByTestId('ndv-run-data-display-mode'),
digital: () => cy.getByTestId('ndv-run-data-display-mode'),
}
}

View file

@ -11,17 +11,11 @@ export class WorkflowPage extends BasePage {
workflowTagsContainer: () => cy.getByTestId('workflow-tags-container'),
newTagLink: () => cy.getByTestId('new-tag-link'),
saveButton: () => cy.getByTestId('workflow-save-button'),
nodeCreatorSearchBar: () => cy.getByTestId('node-creator-search-bar'),
nodeCreatorPlusButton: () => cy.getByTestId('node-creator-plus-button'),
canvasPlusButton: () => cy.getByTestId('canvas-plus-button'),
canvasNodeBox: (nodeDisplayName: string) => {
return cy
.getByTestId('canvas-node-box-title')
.contains(nodeDisplayName)
.parents('[data-test-id="canvas-node-box"]');
},
canvasNodes: () => cy.getByTestId('canvas-node'),
canvasNodeByName: (nodeName: string) => this.getters.canvasNodes().filter(`:contains("${nodeName}")`),
ndvParameterInput: (parameterName: string) =>
cy.getByTestId(`parameter-input-${parameterName}`),
ndvOutputPanel: () => cy.getByTestId('output-panel'),
@ -51,7 +45,7 @@ export class WorkflowPage extends BasePage {
this.getters.nodeCreatorSearchBar().type('{enter}{esc}');
},
openNodeNdv: (nodeTypeName: string) => {
this.getters.canvasNodeBox(nodeTypeName).dblclick();
this.getters.canvasNodeByName(nodeTypeName).dblclick();
},
typeIntoParameterInput: (parameterName: string, content: string) => {
this.getters.ndvParameterInput(parameterName).type(content);
@ -94,5 +88,8 @@ export class WorkflowPage extends BasePage {
});
cy.get('body').type('{enter}');
},
zoomToFit: () => {
cy.getByTestId('zoom-to-fit').click();
},
};
}

View file

@ -29,5 +29,12 @@ export class WorkflowsPage extends BasePage {
createWorkflowFromCard: () => {
this.getters.newWorkflowButtonCard().click();
},
deleteWorkFlow: (name: string) => {
cy.visit(this.url);
this.getters.workflowCardActions(name).click();
this.getters.workflowDeleteButton().click();
cy.get('button').contains('delete').click();
}
}
}

View file

@ -1,8 +1,9 @@
<template>
<div
:class="{ [$style.zoomMenu]: true, [$style.regularZoomMenu]: !isDemo, [$style.demoZoomMenu]: isDemo }">
<div :class="{ [$style.zoomMenu]: true, [$style.regularZoomMenu]: !isDemo, [$style.demoZoomMenu]: isDemo }">
<n8n-icon-button @click="zoomToFit" type="tertiary" size="large" :title="$locale.baseText('nodeView.zoomToFit')"
icon="expand" />
icon="expand"
data-test-id="zoom-to-fit" />
/>
<n8n-icon-button @click="zoomIn" type="tertiary" size="large" :title="$locale.baseText('nodeView.zoomIn')"
icon="search-plus" />
<n8n-icon-button @click="zoomOut" type="tertiary" size="large" :title="$locale.baseText('nodeView.zoomOut')"

View file

@ -1,7 +1,7 @@
<template>
<div>
<n8n-input-label :label="label">
<div :class="{[$style.copyText]: true, [$style[size]]: true, [$style.collapsed]: collapse}" @click="copy">
<div :class="{[$style.copyText]: true, [$style[size]]: true, [$style.collapsed]: collapse}" @click="copy" data-test-id="copy-input">
<span ref="copyInputValue">{{ value }}</span>
<div :class="$style.copyButton"><span>{{ copyButtonText }}</span></div>
</div>

View file

@ -20,14 +20,15 @@
@unlinkRun="onUnlinkRun"
@runChange="onRunIndexChange"
@tableMounted="$emit('tableMounted', $event)"
data-test-id="ndv-input-panel"
>
<template #header>
<div :class="$style.titleSection">
<n8n-select v-if="parentNodes.length" :popper-append-to-body="true" size="small" :value="currentNodeName" @input="onSelect" :no-data-text="$locale.baseText('ndv.input.noNodesFound')" :placeholder="$locale.baseText('ndv.input.parentNodes')" filterable>
<n8n-select v-if="parentNodes.length" :popper-append-to-body="true" size="small" :value="currentNodeName" @input="onSelect" :no-data-text="$locale.baseText('ndv.input.noNodesFound')" :placeholder="$locale.baseText('ndv.input.parentNodes')" filterable data-test-id="ndv-input-select">
<template #prepend>
<span :class="$style.title">{{ $locale.baseText('ndv.input') }}</span>
</template>
<n8n-option v-for="node of parentNodes" :value="node.name" :key="node.name" class="node-option" :label="`${truncate(node.name)} ${getMultipleNodesText(node.name)}`">
<n8n-option v-for="node of parentNodes" :value="node.name" :key="node.name" class="node-option" :label="`${truncate(node.name)} ${getMultipleNodesText(node.name)}`" data-test-id="ndv-input-option">
{{ truncate(node.name) }}&nbsp;
<span v-if="getMultipleNodesText(node.name)">{{ getMultipleNodesText(node.name) }}</span>
<span v-else>{{ $locale.baseText('ndv.input.nodeDistance', {adjustToNumber: node.depth}) }}</span>

View file

@ -1,5 +1,5 @@
<template>
<div class="node-wrapper" :style="nodePosition" :id="nodeId" data-test-id="canvas-node-box">
<div class="node-wrapper" :style="nodePosition" :id="nodeId" data-test-id="canvas-node">
<div class="select-background" v-show="isSelected"></div>
<div :class="{'node-default': true, 'touch-active': isTouchActive, 'is-touch-device': isTouchDevice}" :data-name="data.name" :ref="data.name">
<div :class="nodeClass" :style="nodeStyle" @click.left="onClick" v-touch:start="touchStart" v-touch:end="touchEnd">

View file

@ -7,6 +7,7 @@
class="ndv-wrapper"
width="auto"
append-to-body
data-test-id="ndv"
>
<n8n-tooltip
placement="bottom-start"

View file

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

View file

@ -19,6 +19,7 @@
@tableMounted="$emit('tableMounted', $event)"
@itemHover="$emit('itemHover', $event)"
ref="runData"
data-test-id="ndv-output-panel"
>
<template #header>
<div :class="$style.titleSection">
@ -47,8 +48,8 @@
</template>
<template #node-not-run>
<n8n-text v-if="workflowRunning && !isTriggerNode">{{ $locale.baseText('ndv.output.waitingToRun') }}</n8n-text>
<n8n-text v-if="!workflowRunning">
<n8n-text v-if="workflowRunning && !isTriggerNode" data-test-id="ndv-output-waiting">{{ $locale.baseText('ndv.output.waitingToRun') }}</n8n-text>
<n8n-text v-if="!workflowRunning" data-test-id="ndv-output-run-node-hint">
{{ $locale.baseText('ndv.output.runNodeHint') }}
<span @click="insertTestData" v-if="canPinData">
<br>

View file

@ -43,6 +43,7 @@
:value="displayMode"
:options="buttons"
@input="onDisplayModeChange"
data-test-id="ndv-run-data-display-mode"
/>
<n8n-icon-button
v-if="canPinData && !isReadOnly"
@ -133,6 +134,7 @@
<div
:class="$style['data-container']"
ref="dataContainer"
data-test-id="ndv-data-container"
>
<div v-if="isExecuting" :class="$style.center">
<div :class="$style.spinner"><n8n-spinner type="ring" /></div>