feat(editor): Migrate Design System and Editor UI to Vue 3 (#6476)

* feat: remove vue-fragment (no-changelog)

* feat: partial design-system migration

* feat: migrate info-accordion and info-tip components

* feat: migrate several components to vue 3

* feat: migrated several components

* feat: migrate several components

* feat: migrate several components

* feat: migrate several components

* feat: re-exported all design system components

* fix: fix design for popper components

* fix: editor kind of working, lots of issues to fix

* fix: fix several vue 3 migration issues

* fix: replace @change with @update:modelValue in several places

* fix: fix translation linking

* fix: fix inline-edit input

* fix: fix ndv and dialog design

* fix: update parameter input event bindings

* fix: rename deprecated lifecycle methods

* fix: fix json view mapping

* build: update lock file

* fix(editor): revisit last conflict with master and fix issues

* fix(editor): revisit last conflict with master and fix issues

* fix: fix expression editor bug causing code mirror to no longer be reactive

* fix: fix resource locator bug

* fix: fix vue-agile integration

* fix: remove global import for vue-agile

* fix: replace element-plus buttons with n8n-buttons everywhere

* fix(editor): Fix various element-plus styles (#6571)

* fix(editor): Fix various element-plus styles

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

* Remove debugging code

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

* Address PR comments

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

---------

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

* fix(editor): Fix loading in production mode [Vue 3] (#6578)

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

* fix(editor): First round of e2e tests fixes with Vue 3 (#6579)

* fix(editor): Fix broken smoke and workflow list e2e tests
* ✔️ Fix failing canvas action tests. Updating some selectors used in credentials and workflow tests

* feat: add vue 3 eslint rules and fix issues

* fix: fix tags-dropdown

* fix: fix white-space issues caused by i18n-t

* fix: rename non-generic click events

* fix: fix search in resources list layout

* fix: fix datatable paginator

* fix: fix popper select caret and dropdown size

* fix: add width to action-dropdown

* fix: fix workflow settings icon not being hidden

* fix: refactor newly added code

* fix: fix merge issue

* fix: fix ndv credentials watcher

* fix: fix workflow saving and grabber notch

* fix: fix nodes list panel transition

* fix: fix node title visibility

* fix: fix data unpinning

* fix: fix value access

* fix: show  input panel only if trigger panel enabled or not trigger node

* fix: fix tags dropdown and executions status spcing

* fix(editor): Prevent execution list to load back when leaving the route (#6697)

fix(editor): prevent execution list to load back when leaving the route

* fix: fix drawer visibility

* fix: fix expression toggle padding

* fix: fix expressions editor styling

* chore: prepare for testing

* fix: fix styling for el-button without patching

* test: fix unit tests in design-system

* test: fix most unit tests

* fix: remove import cycle.

* fix: fix personalization modal tests

* fix further resource mapper test adjustments

* fix: fix multiple tests and n8n-route attr duplication

* fix: fix source control tets

* fix: fixed remaining unit tests

* fix: fix workflows and credentials e2e tests

* fix: fix localizeNodeNames

* fix: update ndv e2e tests

* fix: fix popper left placement arrow

* fix: fix 5-ndv e2e tests

* fix: fix 6-code-node e2e tests

* fix(editor): Drop click outside directive from NodeCreator (#6716)

* fix(editor): Drop click outside directive from NodeCreator

* fix(editor): make sure mouseup outside is unbound at least before the component is unmounted

* fix: fix 10-settings-log-streaming e2e tests

* fix: fix node redrawing

* fix: fix tooltip buttons styling

* fix: fix varous e2e suites

* fix: fix 15-scheduler-node e2e suite

* fix: fix route watcher

* fix: fixed param name update and credential edit

* feat: update event names

* refactor: Remove deprecated `$data` (#6576)

Co-authored-by: Alex Grozav <alex@grozav.com>

* fix: fix 17-sharing e2e suite

* fix: fix tags dropdown

* fix: fix tags manager

* fix(editor): move :deep selectors to a separate scoped style block

* fix: fix sticky component and inline text edit

* fix: update e2e tests

* fix: remove button override references

* fix(editor): Adjust spacing in templates for Vue 3 (#6744)

* fix(editor): Adjust spacing in templates

* fix: Undo unneeded change

* fix: Undo unneeded change

* fix(editor): Adjust NDV height for Vue 3 (#6742)

fix(editor): Adjust NDV height

* fix(editor): Restore collapsed sidebar items for Vue 3 (#6743)

fix(editor): Restore collapsed sidebar items

* fix: fix linting issues

* fix: fix design-system deps

* fix: post-merge fixes

* fix: update tests

* fix: increase timeout for executionslist tets

* chore: fix linting issue

* fix: fix 14-mapping e2e tests in ci

* fix: re-enable tests

* fix: fix workflow duplication e2e tests after tags update

* fix(editor): Change component prop to be typed

* fix: fix tags dropdown in duplicate wf modal

* fix: fix focus behaviour in tags selector

* fix: fix tag creation

* fix: fix log streaming e2e race condition

* fix(editor): Fix Vue 3 linting issues (#6748)

* fix(editor): Fix Vue 3 linting issues

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

* fix MainSidebar linter issues

* revert pnpm lock

* update pnpm lock file

---------

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Alex Grozav <alex@grozav.com>

* fix(editor): Some css fixes for vue3 branch (#6749)

*  Fixing filter button height

*  Update input modal button position

*  Updating tags styling

*  Fix event logging settings spacing

* 👕 Fixing lint errors

* fix: fix linting issues

* Revert to `// eslint-disable-next-line @typescript-eslint/no-misused-promises` disabling of mixins init

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

* fix: fix css issue

* fix(editor): Lint fix

* fix(editor): Fix settings initialisation (#6750)

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

* fix: fix initial settings loading

* fix: replace realClick with click force

* fix: fix randomly failing mapping e2e tests

* fix(editor): Fix menu item event handling

* fix: fix resource filters dropdown events (#6752)

* fix: fix resource filters dropdown events

* fix: remove teleported:false

* fix: fix event selection event naming (#6753)

* fix: removed console.log (#6754)

* fix: rever await nextTick changes

* fix: redo linting changes

* fix(editor): Redraw node connections if adding more than one node to canvas (#6755)

* fix(editor): Redraw node connections if adding more than one node to canvas

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

* Update position before connection two nodes

* Lint fix

---------

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Alex Grozav <alex@grozav.com>

* fix(editor): Fix `ResourceMapper` unit tests (#6758)

* ✔️ Fix matching columns test

* ✔️ Fix multiple matching columns test

* ✔️ Removing `skip` from the last test

* fix: Allow pasting a big workflow (#6760)

* fix: pasting a big workflow

* chore: update comment

* refactor: move try/catch to function

* refactor: move try/catch to function

* fix(editor): Fix modal layer width

* fix: fix position changes

* fix: undo it.only

* fix: make undo/redo multiple steps more verbose

* fix: Fix value survey styles (#6764)

* fix: fix value survey styles

* fix: lint

* Revert "fix: lint"

72869c431f1448861df021be041b61c62f1e3118

* fix: lint

* fix(editor): Fix collapsed sub menu

* fix: Fix drawer animation (#6767)

fix: drawer animation

* fix(editor): Fix source control buttons (#6769)

* fix(editor): Fix App loading & auth  (#6768)

* fix(editor): Fix App loading & auth

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

* Await promises

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

* Fix eslint error

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

---------

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>

---------

Signed-off-by: Oleg Ivaniv <me@olegivaniv.com>
Co-authored-by: Csaba Tuncsik <csaba@n8n.io>
Co-authored-by: OlegIvaniv <me@olegivaniv.com>
Co-authored-by: Milorad FIlipović <milorad@n8n.io>
Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
Co-authored-by: Mutasem Aldmour <4711238+mutdmour@users.noreply.github.com>
This commit is contained in:
Alex Grozav
2023-07-28 10:51:07 +03:00
committed by GitHub
parent d050b99fb2
commit dd6a4c956a
459 changed files with 8815 additions and 9913 deletions

View File

@@ -1,4 +1,6 @@
import { SettingsLogStreamingPage } from '../pages';
import { getVisibleModalOverlay } from '../utils/modal';
import { getVisibleDropdown } from '../utils';
const settingsLogStreamingPage = new SettingsLogStreamingPage();
@@ -19,6 +21,7 @@ describe('Log Streaming Settings', () => {
});
it('should show the add destination modal', () => {
cy.enableFeature('logStreaming');
cy.visit('/settings/log-streaming');
settingsLogStreamingPage.actions.clickAddFirstDestination();
cy.wait(100);
@@ -27,7 +30,7 @@ describe('Log Streaming Settings', () => {
settingsLogStreamingPage.getters.getSelectDestinationButton().should('be.visible');
settingsLogStreamingPage.getters.getSelectDestinationButton().should('have.attr', 'disabled');
settingsLogStreamingPage.getters
.getDestinationModalDialog()
.getDestinationModal()
.invoke('css', 'width')
.then((widthStr) => parseInt((widthStr as unknown as string).replace('px', '')))
.should('be.lessThan', 500);
@@ -36,65 +39,67 @@ describe('Log Streaming Settings', () => {
settingsLogStreamingPage.getters
.getSelectDestinationButton()
.should('not.have.attr', 'disabled');
settingsLogStreamingPage.getters.getDestinationModal().click(1, 1);
getVisibleModalOverlay().click(1, 1);
settingsLogStreamingPage.getters.getDestinationModal().should('not.exist');
});
it('should create a destination and delete it', () => {
cy.enableFeature('logStreaming');
cy.visit('/settings/log-streaming');
cy.wait(1000); // Race condition with getDestinationDataFromBackend()
settingsLogStreamingPage.actions.clickAddFirstDestination();
cy.wait(100);
settingsLogStreamingPage.getters.getDestinationModal().should('be.visible');
settingsLogStreamingPage.getters.getSelectDestinationType().click();
settingsLogStreamingPage.getters.getSelectDestinationTypeItems().eq(0).click();
settingsLogStreamingPage.getters.getSelectDestinationButton().click();
settingsLogStreamingPage.getters.getDestinationNameInput().click()
settingsLogStreamingPage.getters.getDestinationNameInput().click();
settingsLogStreamingPage.getters.getDestinationNameInput().find('input').clear().type('Destination 0');
settingsLogStreamingPage.getters
.getDestinationNameInput()
.find('input')
.clear()
.type('Destination 0');
settingsLogStreamingPage.getters.getDestinationSaveButton().click();
cy.wait(100);
settingsLogStreamingPage.getters.getDestinationModal().click(1, 1);
getVisibleModalOverlay().click(1, 1);
cy.reload();
settingsLogStreamingPage.getters.getDestinationCards().eq(0).click();
settingsLogStreamingPage.getters.getDestinationDeleteButton().should('be.visible').click();
cy.get('.el-message-box').should('be.visible').find('.btn--cancel').click();
settingsLogStreamingPage.getters.getDestinationDeleteButton().click();
cy.get('.el-message-box').should('be.visible').find('.btn--confirm').click();
cy.reload();
});
it('should create a destination and delete it via card actions', () => {
cy.enableFeature('logStreaming');
cy.visit('/settings/log-streaming');
cy.wait(1000); // Race condition with getDestinationDataFromBackend()
settingsLogStreamingPage.actions.clickAddFirstDestination();
cy.wait(100);
settingsLogStreamingPage.getters.getDestinationModal().should('be.visible');
settingsLogStreamingPage.getters.getSelectDestinationType().click();
settingsLogStreamingPage.getters.getSelectDestinationTypeItems().eq(0).click();
settingsLogStreamingPage.getters.getSelectDestinationButton().click();
settingsLogStreamingPage.getters.getDestinationNameInput().click()
settingsLogStreamingPage.getters.getDestinationNameInput().find('input').clear().type('Destination 1');
settingsLogStreamingPage.getters.getDestinationNameInput().click();
settingsLogStreamingPage.getters
.getDestinationNameInput()
.find('input')
.clear()
.type('Destination 1');
settingsLogStreamingPage.getters.getDestinationSaveButton().should('not.have.attr', 'disabled');
settingsLogStreamingPage.getters.getDestinationSaveButton().click();
cy.wait(100);
settingsLogStreamingPage.getters.getDestinationModal().click(1, 1);
getVisibleModalOverlay().click(1, 1);
cy.reload();
settingsLogStreamingPage.getters
.getDestinationCards()
.eq(0)
.find('.el-dropdown-selfdefine')
.click();
cy.get('.el-dropdown-menu').find('.el-dropdown-menu__item').eq(0).click();
settingsLogStreamingPage.getters.getDestinationCards().eq(0).find('.el-dropdown').click();
getVisibleDropdown().find('.el-dropdown-menu__item').eq(0).click();
settingsLogStreamingPage.getters.getDestinationSaveButton().should('not.exist');
settingsLogStreamingPage.getters.getDestinationModal().click(1, 1);
getVisibleModalOverlay().click(1, 1);
settingsLogStreamingPage.getters
.getDestinationCards()
.eq(0)
.find('.el-dropdown-selfdefine')
.click();
cy.get('.el-dropdown-menu').find('.el-dropdown-menu__item').eq(1).click();
settingsLogStreamingPage.getters.getDestinationCards().eq(0).find('.el-dropdown').click();
getVisibleDropdown().find('.el-dropdown-menu__item').eq(1).click();
cy.get('.el-message-box').should('be.visible').find('.btn--confirm').click();
cy.reload();
});
});

View File

@@ -119,18 +119,15 @@ describe('Undo/Redo', () => {
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150]);
WorkflowPage.getters
.canvasNodes()
.last()
.canvasNodeByName('Code')
.should('have.attr', 'style', 'left: 740px; top: 320px;');
WorkflowPage.actions.hitUndo();
WorkflowPage.getters
.canvasNodes()
.last()
.canvasNodeByName('Code')
.should('have.attr', 'style', 'left: 640px; top: 220px;');
WorkflowPage.actions.hitRedo();
WorkflowPage.getters
.canvasNodes()
.last()
.canvasNodeByName('Code')
.should('have.attr', 'style', 'left: 740px; top: 320px;');
});
@@ -138,7 +135,10 @@ describe('Undo/Redo', () => {
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.getters.nodeConnections().realHover();
cy.get('.connection-actions .delete').filter(':visible').should('be.visible').click();
cy.get('.connection-actions .delete')
.filter(':visible')
.should('be.visible')
.click({ force: true });
WorkflowPage.getters.nodeConnections().should('have.length', 0);
WorkflowPage.actions.hitUndo();
WorkflowPage.getters.nodeConnections().should('have.length', 1);
@@ -256,6 +256,9 @@ describe('Undo/Redo', () => {
});
it('should undo/redo multiple steps', () => {
const initialPosition = 'left: 420px; top: 220px;';
const movedPosition = 'left: 540px; top: 360px;';
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(SET_NODE_NAME);
@@ -266,8 +269,10 @@ describe('Undo/Redo', () => {
WorkflowPage.getters.canvasNodes().last().click();
WorkflowPage.actions.hitDisableNodeShortcut();
// Move first one
WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', initialPosition);
WorkflowPage.getters.canvasNodes().first().click();
cy.drag('[data-test-id="canvas-node"].jtk-drag-selected', [50, 150]);
WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', movedPosition);
// Delete the set node
WorkflowPage.getters.canvasNodeByName(SET_NODE_NAME).click().click();
cy.get('body').type('{backspace}');
@@ -278,10 +283,7 @@ describe('Undo/Redo', () => {
WorkflowPage.getters.nodeConnections().should('have.length', 3);
// Second undo: Should move first node to it's original position
WorkflowPage.actions.hitUndo();
WorkflowPage.getters
.canvasNodes()
.first()
.should('have.attr', 'style', 'left: 420px; top: 220px;');
WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', initialPosition);
// Third undo: Should enable last node
WorkflowPage.actions.hitUndo();
WorkflowPage.getters.disabledNodes().should('have.length', 0);
@@ -291,10 +293,7 @@ describe('Undo/Redo', () => {
WorkflowPage.getters.disabledNodes().should('have.length', 1);
// Second redo: Should move the first node
WorkflowPage.actions.hitRedo();
WorkflowPage.getters
.canvasNodes()
.first()
.should('have.attr', 'style', 'left: 540px; top: 360px;');
WorkflowPage.getters.canvasNodes().first().should('have.attr', 'style', movedPosition);
// Third redo: Should delete the Set node
WorkflowPage.actions.hitRedo();
WorkflowPage.getters.canvasNodes().should('have.length', 3);

View File

@@ -66,7 +66,6 @@ describe('Canvas Actions', () => {
WorkflowPage.getters.nodeViewBackground().click({ force: true });
});
it('should add a connected node using plus endpoint', () => {
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
cy.get('.plus-endpoint').should('be.visible').click();

View File

@@ -107,7 +107,7 @@ describe('Canvas Node Manipulation and Navigation', () => {
WorkflowPage.actions.zoomToFit();
cy.get('.plus-draggable-endpoint').filter(':visible').should('not.have.class', 'ep-success');
cy.get('.jtk-connector.success').should('have.length', 4);
cy.get('.jtk-connector.success').should('have.length', 3);
cy.get('.jtk-connector').should('have.length', 4);
});

View File

@@ -7,12 +7,10 @@ describe('Data transformation expressions', () => {
beforeEach(() => {
wf.actions.visit();
cy.window().then(
(win) => {
// @ts-ignore
win.preventNodeViewBeforeUnload = true;
},
);
cy.window().then((win) => {
// @ts-ignore
win.preventNodeViewBeforeUnload = true;
});
});
it('$json + native string methods', () => {
@@ -85,7 +83,7 @@ describe('Data transformation expressions', () => {
ndv.getters.inlineExpressionEditorInput().clear().type(input);
ndv.actions.execute();
ndv.getters.outputDataContainer().find('[class*=value_]').should('exist')
ndv.getters.outputDataContainer().find('[class*=value_]').should('exist');
ndv.getters.outputDataContainer().find('[class*=value_]').should('contain', output);
});
@@ -100,7 +98,7 @@ describe('Data transformation expressions', () => {
ndv.getters.inlineExpressionEditorInput().clear().type(input);
ndv.actions.execute();
ndv.getters.outputDataContainer().find('[class*=value_]').should('exist')
ndv.getters.outputDataContainer().find('[class*=value_]').should('exist');
ndv.getters.outputDataContainer().find('[class*=value_]').should('contain', output);
});
});
@@ -111,7 +109,7 @@ describe('Data transformation expressions', () => {
const addSet = () => {
wf.actions.addNodeToCanvas('Set', true, true);
ndv.getters.parameterInput('keepOnlySet').find('div[role=switch]').click(); // shorten output
ndv.getters.parameterInput('keepOnlySet').find('.el-switch').click(); // shorten output
cy.get('input[placeholder="Add Value"]').click();
cy.get('span').contains('String').click();
ndv.getters.nthParam(3).contains('Expression').invoke('show').click(); // Values to Set > String > Value

View File

@@ -4,6 +4,7 @@ import {
SCHEDULE_TRIGGER_NODE_NAME,
} from './../constants';
import { WorkflowPage, NDV } from '../pages';
import { getVisibleSelect } from '../utils';
const workflowPage = new WorkflowPage();
const ndv = new NDV();
@@ -28,11 +29,7 @@ describe('Data mapping', () => {
ndv.getters.inputDataContainer().get('table', { timeout: 10000 }).should('exist');
ndv.getters.nodeParameters().find('input[placeholder*="Add Value"]').click();
ndv.getters
.nodeParameters()
.find('.el-select-dropdown__list li:nth-child(3)')
.should('have.text', 'String')
.click();
getVisibleSelect().find('li:nth-child(3)').should('have.text', 'String').click();
ndv.getters
.parameterInput('name')
.should('have.length', 1)
@@ -128,7 +125,7 @@ describe('Data mapping', () => {
.find('.json-data')
.should(
'have.text',
'[{"input":[{"count":0,"with space":"!!","with.dot":"!!","with"quotes":"!!"}]},{"input":[{"count":1}]}]',
'[{"input": [{"count": 0,"with space": "!!","with.dot": "!!","with"quotes": "!!"}]},{"input": [{"count": 1}]}]',
)
.find('span')
.contains('"count"')
@@ -178,6 +175,7 @@ describe('Data mapping', () => {
it('maps expressions from previous nodes', () => {
cy.createFixtureWorkflow('Test_workflow_3.json', `My test workflow`);
workflowPage.actions.zoomToFit();
workflowPage.actions.openNode('Set1');
ndv.actions.selectInputNode(SCHEDULE_TRIGGER_NODE_NAME);
@@ -245,7 +243,8 @@ describe('Data mapping', () => {
workflowPage.actions.addNodeToCanvas('Item Lists');
workflowPage.actions.openNode('Item Lists');
ndv.getters.parameterInput('operation').click().find('li').contains('Sort').click();
ndv.getters.parameterInput('operation').click();
getVisibleSelect().find('li').contains('Sort').click();
ndv.getters.nodeParameters().find('button').contains('Add Field To Sort By').click();
@@ -274,6 +273,8 @@ describe('Data mapping', () => {
ndv.actions.typeIntoParameterInput('value', 'fun');
ndv.actions.clearParameterInput('value'); // keep focus on param
ndv.actions.dismissMappingTooltip();
cy.wait(300);
ndv.getters.inputDataContainer().should('exist').find('span').contains('count').realMouseDown();

View File

@@ -1,5 +1,6 @@
import { WorkflowPage, WorkflowsPage, NDV } from '../pages';
import { BACKEND_BASE_URL } from '../constants';
import { getVisibleSelect } from '../utils';
const workflowsPage = new WorkflowsPage();
const workflowPage = new WorkflowPage();
@@ -24,11 +25,7 @@ describe('Schedule Trigger node', async () => {
workflowPage.actions.openNode('Schedule Trigger');
cy.getByTestId('parameter-input-field').click();
cy.getByTestId('parameter-input-field')
.find('.el-select-dropdown')
.find('.option-headline')
.contains('Seconds')
.click();
getVisibleSelect().find('.option-headline').contains('Seconds').click();
cy.getByTestId('parameter-input-secondsInterval').clear().type('1');
ndv.getters.backToCanvas().click();

View File

@@ -2,6 +2,7 @@ import { WorkflowPage, NDV, CredentialsModal } from '../pages';
import { v4 as uuid } from 'uuid';
import { cowBase64 } from '../support/binaryTestFiles';
import { BACKEND_BASE_URL } from '../constants';
import { getVisibleSelect } from '../utils';
const workflowPage = new WorkflowPage();
const ndv = new NDV();
@@ -34,11 +35,7 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => {
workflowPage.actions.openNode('Webhook');
cy.getByTestId('parameter-input-httpMethod').click();
cy.getByTestId('parameter-input-httpMethod')
.find('.el-select-dropdown')
.find('.option-headline')
.contains(method)
.click();
getVisibleSelect().find('.option-headline').contains(method).click();
cy.getByTestId('parameter-input-path')
.find('.parameter-input')
.find('input')
@@ -47,11 +44,7 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => {
if (authentication) {
cy.getByTestId('parameter-input-authentication').click();
cy.getByTestId('parameter-input-authentication')
.find('.el-select-dropdown')
.find('.option-headline')
.contains(authentication)
.click();
getVisibleSelect().find('.option-headline').contains(authentication).click();
}
if (responseCode) {
@@ -64,20 +57,12 @@ const simpleWebhookCall = (options: SimpleWebhookCallOptions) => {
if (respondWith) {
cy.getByTestId('parameter-input-responseMode').click();
cy.getByTestId('parameter-input-responseMode')
.find('.el-select-dropdown')
.find('.option-headline')
.contains(respondWith)
.click();
getVisibleSelect().find('.option-headline').contains(respondWith).click();
}
if (responseData) {
cy.getByTestId('parameter-input-responseData').click();
cy.getByTestId('parameter-input-responseData')
.find('.el-select-dropdown')
.find('.option-headline')
.contains(responseData)
.click();
getVisibleSelect().find('.option-headline').contains(responseData).click();
}
if (executeNow) {
@@ -136,13 +121,13 @@ describe('Webhook Trigger node', async () => {
workflowPage.actions.addNodeToCanvas('Set');
workflowPage.actions.openNode('Set');
cy.get('.add-option').click();
cy.get('.add-option').find('.el-select-dropdown__item').contains('Number').click();
getVisibleSelect().find('.el-select-dropdown__item').contains('Number').click();
cy.get('.fixed-collection-parameter')
.getByTestId('parameter-input-name')
.clear()
.type('MyValue');
cy.get('.fixed-collection-parameter').getByTestId('parameter-input-value').clear().type('1234');
ndv.getters.backToCanvas().click();
ndv.getters.backToCanvas().click({ force: true });
workflowPage.actions.addNodeToCanvas('Respond to Webhook');
@@ -185,13 +170,18 @@ describe('Webhook Trigger node', async () => {
workflowPage.actions.addNodeToCanvas('Set');
workflowPage.actions.openNode('Set');
cy.get('.add-option').click();
cy.get('.add-option').find('.el-select-dropdown__item').contains('Number').click();
getVisibleSelect().find('.el-select-dropdown__item').contains('Number').click();
cy.get('.fixed-collection-parameter')
.getByTestId('parameter-input-name')
.find('input')
.clear()
.type('MyValue');
cy.get('.fixed-collection-parameter').getByTestId('parameter-input-value').clear().type('1234');
ndv.getters.backToCanvas().click();
cy.get('.fixed-collection-parameter')
.getByTestId('parameter-input-value')
.find('input')
.clear()
.type('1234');
ndv.getters.backToCanvas().click({ force: true });
workflowPage.actions.executeWorkflow();
cy.wait(waitForWebhook);
@@ -216,7 +206,7 @@ describe('Webhook Trigger node', async () => {
workflowPage.actions.addNodeToCanvas('Set');
workflowPage.actions.openNode('Set');
cy.get('.add-option').click();
cy.get('.add-option').find('.el-select-dropdown__item').contains('String').click();
getVisibleSelect().find('.el-select-dropdown__item').contains('String').click();
cy.get('.fixed-collection-parameter').getByTestId('parameter-input-name').clear().type('data');
cy.get('.fixed-collection-parameter')
.getByTestId('parameter-input-value')
@@ -231,11 +221,7 @@ describe('Webhook Trigger node', async () => {
workflowPage.actions.openNode('Move Binary Data');
cy.getByTestId('parameter-input-mode').click();
cy.getByTestId('parameter-input-mode')
.find('.el-select-dropdown')
.find('.option-headline')
.contains('JSON to Binary')
.click();
getVisibleSelect().find('.option-headline').contains('JSON to Binary').click();
ndv.getters.backToCanvas().click();
workflowPage.actions.executeWorkflow();
@@ -274,7 +260,7 @@ describe('Webhook Trigger node', async () => {
});
// add credentials
workflowPage.getters.nodeCredentialsSelect().click();
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
getVisibleSelect().find('li').last().click();
credentialsModal.getters.credentialsEditModal().should('be.visible');
credentialsModal.actions.fillCredentialsForm();
@@ -317,7 +303,7 @@ describe('Webhook Trigger node', async () => {
});
// add credentials
workflowPage.getters.nodeCredentialsSelect().click();
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
getVisibleSelect().find('li').last().click();
credentialsModal.getters.credentialsEditModal().should('be.visible');
credentialsModal.actions.fillCredentialsForm();

View File

@@ -48,7 +48,7 @@ describe('Sharing', { disableAutoLogin: true }, () => {
workflowPage.actions.setWorkflowName('Workflow W1');
workflowPage.actions.addInitialNodeToCanvas('Manual Trigger');
workflowPage.actions.addNodeToCanvas('Notion', true, true);
ndv.getters.credentialInput().should('contain', 'Credential C1');
ndv.getters.credentialInput().find('input').should('have.value', 'Credential C1');
ndv.actions.close();
workflowPage.actions.openShareModal();
@@ -87,16 +87,12 @@ describe('Sharing', { disableAutoLogin: true }, () => {
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.getters.credentialInput().find('input').should('have.value', '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.getters.credentialInput().should('have.value', 'Credential C1').should('be.disabled');
ndv.actions.close();
});
@@ -116,11 +112,7 @@ describe('Sharing', { disableAutoLogin: true }, () => {
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.getters.credentialInput().should('have.value', 'Credential C1').should('be.disabled');
ndv.actions.close();
cy.waitForLoad();

View File

@@ -30,7 +30,7 @@ describe('Workflow tags', () => {
}
cy.contains('Done').click();
wf.getters.createTagButton().click();
wf.getters.tagsDropdown().click();
wf.getters.tagsInDropdown().should('have.length', 5);
wf.getters.tagPills().should('have.length', 0); // none attached
});
@@ -45,7 +45,7 @@ describe('Workflow tags', () => {
});
cy.contains('Done').click();
wf.getters.createTagButton().click();
wf.getters.tagsDropdown().click();
wf.getters.tagsInDropdown().should('have.length', 0); // none stored
wf.getters.tagPills().should('have.length', 0); // none attached
});
@@ -57,7 +57,8 @@ describe('Workflow tags', () => {
cy.contains('Create a tag').click();
cy.getByTestId('tags-table').find('input').type(first).type('{enter}');
cy.getByTestId('edit-tag-button').click({ force: true });
cy.getByTestId('tags-table').should('contain.text', first);
cy.getByTestId('edit-tag-button').eq(-1).click({ force: true });
cy.wait(300);
cy.getByTestId('tags-table')
.find('.el-input--large')
@@ -65,7 +66,7 @@ describe('Workflow tags', () => {
.type(' Updated')
.type('{enter}');
cy.contains('Done').click();
wf.getters.createTagButton().click();
wf.getters.tagsDropdown().click();
wf.getters.tagsInDropdown().should('have.length', 1); // one stored
wf.getters.tagsInDropdown().contains('Updated').should('exist');
wf.getters.tagPills().should('have.length', 0); // none attached
@@ -76,7 +77,7 @@ describe('Workflow tags', () => {
wf.actions.addTags(TEST_TAGS);
wf.getters.nthTagPill(1).click();
wf.getters.tagsDropdown().find('.el-tag__close').first().click();
cy.get('body').type('{enter}');
cy.get('body').click(0, 0);
wf.getters.tagPills().should('have.length', TEST_TAGS.length - 1);
});
@@ -84,8 +85,8 @@ describe('Workflow tags', () => {
wf.getters.createTagButton().click();
wf.actions.addTags(TEST_TAGS);
wf.getters.nthTagPill(1).click();
wf.getters.tagsDropdown().find('li.selected').first().click();
cy.get('body').type('{enter}');
wf.getters.tagsInDropdown().filter('.selected').first().click();
cy.get('body').click(0, 0);
wf.getters.tagPills().should('have.length', TEST_TAGS.length - 1);
});
});

View File

@@ -58,19 +58,19 @@ describe('User Management', { disableAutoLogin: true }, () => {
it('should delete user and their data', () => {
usersSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password, true);
usersSettingsPage.actions.opedDeleteDialog(INSTANCE_MEMBERS[0].email);
usersSettingsPage.getters.deleteDataRadioButton().realClick();
usersSettingsPage.getters.deleteDataRadioButton().click();
usersSettingsPage.getters.deleteDataInput().type('delete all data');
usersSettingsPage.getters.deleteUserButton().realClick();
usersSettingsPage.getters.deleteUserButton().click();
workflowPage.getters.successToast().should('contain', 'User deleted');
});
it('should delete user and transfer their data', () => {
usersSettingsPage.actions.loginAndVisit(INSTANCE_OWNER.email, INSTANCE_OWNER.password, true);
usersSettingsPage.actions.opedDeleteDialog(INSTANCE_MEMBERS[1].email);
usersSettingsPage.getters.transferDataRadioButton().realClick();
usersSettingsPage.getters.userSelectDropDown().realClick();
usersSettingsPage.getters.userSelectOptions().first().realClick();
usersSettingsPage.getters.deleteUserButton().realClick();
usersSettingsPage.getters.transferDataRadioButton().click();
usersSettingsPage.getters.userSelectDropDown().click();
usersSettingsPage.getters.userSelectOptions().first().click();
usersSettingsPage.getters.deleteUserButton().click();
workflowPage.getters.successToast().should('contain', 'User deleted');
});

View File

@@ -4,8 +4,6 @@ import {
PIPEDRIVE_NODE_NAME,
HTTP_REQUEST_NODE_NAME,
NEW_QUERY_AUTH_ACCOUNT_NAME,
} from './../constants';
import {
GMAIL_NODE_NAME,
NEW_GOOGLE_ACCOUNT_NAME,
NEW_TRELLO_ACCOUNT_NAME,
@@ -13,6 +11,7 @@ import {
TRELLO_NODE_NAME,
} from '../constants';
import { CredentialsPage, CredentialsModal, WorkflowPage, NDV } from '../pages';
import { getVisibleSelect } from '../utils';
const credentialsPage = new CredentialsPage();
const credentialsModal = new CredentialsModal();
@@ -90,13 +89,16 @@ describe('Credentials', () => {
workflowPage.getters.canvasNodes().last().click();
cy.get('body').type('{enter}');
workflowPage.getters.nodeCredentialsSelect().click();
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
getVisibleSelect().find('li').last().click();
credentialsModal.getters.credentialsEditModal().should('be.visible');
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
credentialsModal.getters.credentialAuthTypeRadioButtons().first().click();
credentialsModal.actions.fillCredentialsForm();
cy.get('.el-message-box').find('button').contains('Close').click();
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_GOOGLE_ACCOUNT_NAME);
workflowPage.getters
.nodeCredentialsSelect()
.find('input')
.should('have.value', NEW_GOOGLE_ACCOUNT_NAME);
});
it('should show multiple credential types in the same dropdown', () => {
@@ -107,7 +109,7 @@ describe('Credentials', () => {
cy.get('body').type('{enter}');
workflowPage.getters.nodeCredentialsSelect().click();
// Add oAuth credentials
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
getVisibleSelect().find('li').last().click();
credentialsModal.getters.credentialsEditModal().should('be.visible');
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
credentialsModal.getters.credentialAuthTypeRadioButtons().first().click();
@@ -115,13 +117,14 @@ describe('Credentials', () => {
cy.get('.el-message-box').find('button').contains('Close').click();
workflowPage.getters.nodeCredentialsSelect().click();
// Add Service account credentials
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
getVisibleSelect().find('li').last().click();
credentialsModal.getters.credentialsEditModal().should('be.visible');
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
credentialsModal.getters.credentialAuthTypeRadioButtons().last().click();
credentialsModal.actions.fillCredentialsForm();
// Both (+ the 'Create new' option) should be in the dropdown
workflowPage.getters.nodeCredentialsSelect().find('li').should('have.length.greaterThan', 3);
workflowPage.getters.nodeCredentialsSelect().click();
getVisibleSelect().find('li').should('have.length.greaterThan', 2);
});
it('should correctly render required and optional credentials', () => {
@@ -132,18 +135,18 @@ describe('Credentials', () => {
// Select incoming authentication
nodeDetailsView.getters.parameterInput('incomingAuthentication').should('exist');
nodeDetailsView.getters.parameterInput('incomingAuthentication').click();
nodeDetailsView.getters.parameterInput('incomingAuthentication').find('li').first().click();
getVisibleSelect().find('li').first().click();
// There should be two credential fields
workflowPage.getters.nodeCredentialsSelect().should('have.length', 2);
workflowPage.getters.nodeCredentialsSelect().first().click();
workflowPage.getters.nodeCredentialsSelect().first().find('li').last().click();
getVisibleSelect().find('li').last().click();
// This one should show auth type selector
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
cy.get('body').type('{esc}');
workflowPage.getters.nodeCredentialsSelect().last().click();
workflowPage.getters.nodeCredentialsSelect().last().find('li').last().click();
getVisibleSelect().find('li').last().click();
// This one should not show auth type selector
credentialsModal.getters.credentialsAuthTypeSelector().should('not.exist');
});
@@ -155,10 +158,13 @@ describe('Credentials', () => {
workflowPage.getters.canvasNodes().last().click();
cy.get('body').type('{enter}');
workflowPage.getters.nodeCredentialsSelect().click();
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
getVisibleSelect().find('li').last().click();
credentialsModal.getters.credentialsAuthTypeSelector().should('not.exist');
credentialsModal.actions.fillCredentialsForm();
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_TRELLO_ACCOUNT_NAME);
workflowPage.getters
.nodeCredentialsSelect()
.find('input')
.should('have.value', NEW_TRELLO_ACCOUNT_NAME);
});
it('should delete credentials from NDV', () => {
@@ -168,16 +174,22 @@ describe('Credentials', () => {
workflowPage.getters.canvasNodes().last().click();
cy.get('body').type('{enter}');
workflowPage.getters.nodeCredentialsSelect().click();
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
getVisibleSelect().find('li').last().click();
credentialsModal.actions.fillCredentialsForm();
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_NOTION_ACCOUNT_NAME);
workflowPage.getters
.nodeCredentialsSelect()
.find('input')
.should('have.value', NEW_NOTION_ACCOUNT_NAME);
workflowPage.getters.nodeCredentialsEditButton().click();
credentialsModal.getters.credentialsEditModal().should('be.visible');
credentialsModal.getters.deleteButton().click();
cy.get('.el-message-box').find('button').contains('Yes').click();
workflowPage.getters.successToast().contains('Credential deleted');
workflowPage.getters.nodeCredentialsSelect().should('not.contain', NEW_TRELLO_ACCOUNT_NAME);
workflowPage.getters
.nodeCredentialsSelect()
.find('input')
.should('not.have.value', NEW_TRELLO_ACCOUNT_NAME);
});
it('should rename credentials from NDV', () => {
@@ -187,17 +199,18 @@ describe('Credentials', () => {
workflowPage.getters.canvasNodes().last().click();
cy.get('body').type('{enter}');
workflowPage.getters.nodeCredentialsSelect().click();
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
getVisibleSelect().find('li').last().click();
credentialsModal.actions.fillCredentialsForm();
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_TRELLO_ACCOUNT_NAME);
workflowPage.getters.nodeCredentialsEditButton().click();
credentialsModal.getters.credentialsEditModal().should('be.visible');
credentialsModal.getters.name().click();
credentialsModal.actions.renameCredential(NEW_CREDENTIAL_NAME);
credentialsModal.getters.saveButton().click();
credentialsModal.getters.closeButton().click();
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_CREDENTIAL_NAME);
workflowPage.getters
.nodeCredentialsSelect()
.find('input')
.should('have.value', NEW_CREDENTIAL_NAME);
});
it('should setup generic authentication for HTTP node', () => {
@@ -207,20 +220,20 @@ describe('Credentials', () => {
workflowPage.getters.canvasNodes().last().click();
cy.get('body').type('{enter}');
nodeDetailsView.getters.parameterInput('authentication').click();
nodeDetailsView.getters.parameterInput('authentication').find('li').should('have.length', 3);
nodeDetailsView.getters.parameterInput('authentication').find('li').last().click();
getVisibleSelect().find('li').should('have.length', 3);
getVisibleSelect().find('li').last().click();
nodeDetailsView.getters.parameterInput('genericAuthType').should('exist');
nodeDetailsView.getters.parameterInput('genericAuthType').click();
nodeDetailsView.getters
.parameterInput('genericAuthType')
.find('li')
.should('have.length.greaterThan', 0);
nodeDetailsView.getters.parameterInput('genericAuthType').find('li').last().click();
getVisibleSelect().find('li').should('have.length.greaterThan', 0);
getVisibleSelect().find('li').last().click();
workflowPage.getters.nodeCredentialsSelect().should('exist');
workflowPage.getters.nodeCredentialsSelect().click();
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
getVisibleSelect().find('li').last().click();
credentialsModal.actions.fillCredentialsForm();
workflowPage.getters.nodeCredentialsSelect().should('contain', NEW_QUERY_AUTH_ACCOUNT_NAME);
workflowPage.getters
.nodeCredentialsSelect()
.find('input')
.should('have.value', NEW_QUERY_AUTH_ACCOUNT_NAME);
});
});

View File

@@ -4,6 +4,7 @@ import { CredentialsModal, WorkflowPage } from '../pages';
import CustomNodeWithN8nCredentialFixture from '../fixtures/Custom_node_n8n_credential.json';
import CustomNodeWithCustomCredentialFixture from '../fixtures/Custom_node_custom_credential.json';
import CustomCredential from '../fixtures/Custom_credential.json';
import { getVisibleSelect } from '../utils';
const credentialsModal = new CredentialsModal();
const nodeCreatorFeature = new NodeCreator();
@@ -20,9 +21,13 @@ describe('Community Nodes', () => {
req.on('response', (res) => {
const nodes = res.body || [];
nodes.push(CustomNodeFixture, CustomNodeWithN8nCredentialFixture, CustomNodeWithCustomCredentialFixture);
nodes.push(
CustomNodeFixture,
CustomNodeWithN8nCredentialFixture,
CustomNodeWithCustomCredentialFixture,
);
});
})
});
cy.intercept('/types/credentials.json', { middleware: true }, (req) => {
req.headers['cache-control'] = 'no-cache, no-store';
@@ -31,8 +36,8 @@ describe('Community Nodes', () => {
const credentials = res.body || [];
credentials.push(CustomCredential);
})
})
});
});
workflowPage.actions.visit();
});
@@ -45,7 +50,7 @@ describe('Community Nodes', () => {
nodeCreatorFeature.getters
.getCreatorItem(customNode)
.findChildByTestId('node-creator-item-tooltip')
.find('.el-tooltip__trigger')
.should('exist');
nodeCreatorFeature.actions.selectNode(customNode);
@@ -65,16 +70,9 @@ describe('Community Nodes', () => {
secondParameter().find('label').contains('Resource').should('exist');
secondParameter().find('input.el-input__inner').should('have.value', 'option2');
secondParameter().find('.el-select').click();
secondParameter().find('.el-select-dropdown__list').should('exist');
// Check if all options are rendered and select the fourth one
secondParameter().find('.el-select-dropdown__list').children().should('have.length', 4);
secondParameter()
.find('.el-select-dropdown__list')
.children()
.eq(3)
.contains('option4')
.should('exist')
.click();
getVisibleSelect().find('li').should('have.length', 4);
getVisibleSelect().find('li').eq(3).contains('option4').should('exist').click();
secondParameter().find('input.el-input__inner').should('have.value', 'option4');
});

View File

@@ -28,59 +28,36 @@ describe('NDV', () => {
ndv.actions.switchOutputMode('Table');
// input to output
ndv.getters.inputTableRow(1)
ndv.getters
.inputTableRow(1)
.should('exist')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.inputTableRow(1)
.realHover();
ndv.getters.outputTableRow(4)
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.inputTableRow(1).realHover();
ndv.getters.outputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
ndv.getters.inputTableRow(2)
.realHover();
ndv.getters.outputTableRow(2)
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.inputTableRow(3)
.realHover();
ndv.getters.outputTableRow(6)
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.inputTableRow(2).realHover();
ndv.getters.outputTableRow(2).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
ndv.getters.inputTableRow(3).realHover();
ndv.getters.outputTableRow(6).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
// output to input
ndv.getters.outputTableRow(1)
.realHover();
ndv.getters.inputTableRow(4)
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.outputTableRow(1).realHover();
ndv.getters.inputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
ndv.getters.outputTableRow(4)
.realHover();
ndv.getters.inputTableRow(1)
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.outputTableRow(4).realHover();
ndv.getters.inputTableRow(1).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
ndv.getters.outputTableRow(2)
.realHover();
ndv.getters.inputTableRow(2)
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.outputTableRow(6)
.realHover();
ndv.getters.inputTableRow(3)
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.outputTableRow(2).realHover();
ndv.getters.inputTableRow(2).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
ndv.getters.outputTableRow(1)
.realHover();
ndv.getters.inputTableRow(4)
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.outputTableRow(6).realHover();
ndv.getters.inputTableRow(3).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
ndv.getters.outputTableRow(1).realHover();
ndv.getters.inputTableRow(4).invoke('attr', 'data-test-id').should('equal', 'hovering-item');
});
it('maps paired input and output items based on selected input node', () => {
@@ -92,9 +69,11 @@ describe('NDV', () => {
workflowPage.actions.openNode('Set2');
ndv.getters.inputPanel().contains('6 items').should('exist');
ndv.getters.outputRunSelector()
ndv.getters
.outputRunSelector()
.find('input')
.should('exist')
.should('include.text', '2 of 2 (6 items)');
.should('have.value', '2 of 2 (6 items)');
ndv.actions.switchInputMode('Table');
ndv.actions.switchOutputMode('Table');
@@ -106,7 +85,8 @@ describe('NDV', () => {
ndv.actions.selectInputNode('Set1');
ndv.getters.backToCanvas().realHover(); // reset to default hover
ndv.getters.inputTableRow(1)
ndv.getters
.inputTableRow(1)
.should('have.text', '1000')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
@@ -119,7 +99,8 @@ describe('NDV', () => {
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
ndv.getters.backToCanvas().realHover(); // reset to default hover
ndv.getters.inputTableRow(1)
ndv.getters
.inputTableRow(1)
.should('have.text', '1111')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
@@ -137,11 +118,13 @@ describe('NDV', () => {
workflowPage.actions.executeWorkflow();
workflowPage.actions.openNode('Set3');
ndv.getters.inputRunSelector()
ndv.getters
.inputRunSelector()
.should('exist')
.find('input')
.should('include.value', '2 of 2 (6 items)');
ndv.getters.outputRunSelector()
ndv.getters
.outputRunSelector()
.should('exist')
.find('input')
.should('include.value', '2 of 2 (6 items)');
@@ -150,23 +133,19 @@ describe('NDV', () => {
ndv.actions.switchOutputMode('Table');
ndv.actions.changeOutputRunSelector('1 of 2 (6 items)');
ndv.getters.inputRunSelector().find('input')
.should('include.value', '1 of 2 (6 items)');
ndv.getters.outputRunSelector().find('input')
.should('include.value', '1 of 2 (6 items)');
ndv.getters.inputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
ndv.getters.outputRunSelector().find('input').should('include.value', '1 of 2 (6 items)');
ndv.getters.inputTableRow(1)
ndv.getters
.inputTableRow(1)
.should('have.text', '1111')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.outputTableRow(1)
.should('have.text', '1111')
.realHover();
ndv.getters.outputTableRow(1).should('have.text', '1111').realHover();
ndv.getters.outputTableRow(3)
.should('have.text', '4444')
.realHover();
ndv.getters.inputTableRow(3)
ndv.getters.outputTableRow(3).should('have.text', '4444').realHover();
ndv.getters
.inputTableRow(3)
.should('have.text', '4444')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
@@ -174,18 +153,16 @@ describe('NDV', () => {
ndv.actions.changeOutputRunSelector('2 of 2 (6 items)');
cy.wait(50);
ndv.getters.inputTableRow(1)
.should('have.text', '1000')
.realHover();
ndv.getters.outputTableRow(1)
ndv.getters.inputTableRow(1).should('have.text', '1000').realHover();
ndv.getters
.outputTableRow(1)
.should('have.text', '1000')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.outputTableRow(3)
.should('have.text', '2000')
.realHover();
ndv.getters.inputTableRow(3)
ndv.getters.outputTableRow(3).should('have.text', '2000').realHover();
ndv.getters
.inputTableRow(3)
.should('have.text', '2000')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
@@ -200,15 +177,18 @@ describe('NDV', () => {
workflowPage.actions.openNode('Set2');
ndv.getters.inputPanel().contains('6 items').should('exist');
ndv.getters.outputRunSelector()
ndv.getters
.outputRunSelector()
.find('input')
.should('exist')
.should('include.text', '2 of 2 (6 items)');
.should('have.value', '2 of 2 (6 items)');
ndv.actions.switchInputMode('Table');
ndv.actions.switchOutputMode('Table');
ndv.getters.backToCanvas().realHover(); // reset to default hover
ndv.getters.inputTableRow(1)
ndv.getters
.inputTableRow(1)
.should('have.text', '1111')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
@@ -218,28 +198,32 @@ describe('NDV', () => {
ndv.actions.selectInputNode('Code1');
ndv.getters.inputTableRow(1).realHover();
ndv.getters.inputTableRow(1)
ndv.getters
.inputTableRow(1)
.should('have.text', '1000')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.outputTableRow(1)
.should('have.text', '1000');
ndv.getters.outputTableRow(1).should('have.text', '1000');
ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');
ndv.actions.selectInputNode('Code');
ndv.getters.inputTableRow(1).realHover();
ndv.getters.inputTableRow(1)
.should('have.text', '6666')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters
.inputTableRow(1)
.should('have.text', '6666')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.outputHoveringItem().should('not.exist');
ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');
ndv.actions.selectInputNode('When clicking');
ndv.getters.inputTableRow(1).realHover();
ndv.getters.inputTableRow(1).should('have.text', "This is an item, but it's empty.").realHover();
ndv.getters
.inputTableRow(1)
.should('have.text', "This is an item, but it's empty.")
.realHover();
ndv.getters.outputHoveringItem().should('have.length', 6);
ndv.getters.parameterExpressionPreview('value').should('include.text', '1000');
});
@@ -256,18 +240,16 @@ describe('NDV', () => {
ndv.actions.switchOutputMode('Table');
ndv.actions.switchOutputBranch('False Branch (2 items)');
ndv.getters.outputTableRow(1)
.should('have.text', '8888')
.realHover();
ndv.getters.inputTableRow(5)
ndv.getters.outputTableRow(1).should('have.text', '8888').realHover();
ndv.getters
.inputTableRow(5)
.should('have.text', '8888')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
ndv.getters.outputTableRow(2)
.should('have.text', '9999')
.realHover();
ndv.getters.inputTableRow(6)
ndv.getters.outputTableRow(2).should('have.text', '9999').realHover();
ndv.getters
.inputTableRow(6)
.should('have.text', '9999')
.invoke('attr', 'data-test-id')
.should('equal', 'hovering-item');
@@ -277,31 +259,21 @@ describe('NDV', () => {
workflowPage.actions.openNode('Set5');
ndv.actions.switchInputBranch('True Branch');
ndv.actions.changeOutputRunSelector('1 of 2 (2 items)')
ndv.getters.outputTableRow(1)
.should('have.text', '8888')
.realHover();
ndv.actions.changeOutputRunSelector('1 of 2 (2 items)');
ndv.getters.outputTableRow(1).should('have.text', '8888').realHover();
ndv.getters.inputHoveringItem().should('not.exist');
ndv.getters.inputTableRow(1)
.should('have.text', '1111')
.realHover();
ndv.getters.inputTableRow(1).should('have.text', '1111').realHover();
ndv.getters.outputHoveringItem().should('not.exist');
ndv.actions.switchInputBranch('False Branch');
ndv.getters.inputTableRow(1)
.should('have.text', '8888')
.realHover();
ndv.getters.inputTableRow(1).should('have.text', '8888').realHover();
ndv.actions.changeOutputRunSelector('2 of 2 (4 items)')
ndv.getters.outputTableRow(1)
.should('have.text', '1111')
.realHover();
ndv.actions.changeOutputRunSelector('2 of 2 (4 items)');
ndv.getters.outputTableRow(1).should('have.text', '1111').realHover();
ndv.actions.changeOutputRunSelector('1 of 2 (2 items)')
ndv.getters.inputTableRow(1)
.should('have.text', '8888')
.realHover();
ndv.actions.changeOutputRunSelector('1 of 2 (2 items)');
ndv.getters.inputTableRow(1).should('have.text', '8888').realHover();
ndv.getters.outputHoveringItem().should('have.text', '8888');
// todo there's a bug here need to fix ADO-534
// ndv.getters.outputHoveringItem().should('not.exist');

View File

@@ -2,7 +2,13 @@ import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
const workflowPage = new WorkflowPageClass();
function checkStickiesStyle( top: number, left: number, height: number, width: number, zIndex?: number) {
function checkStickiesStyle(
top: number,
left: number,
height: number,
width: number,
zIndex?: number,
) {
workflowPage.getters.stickies().should(($el) => {
expect($el).to.have.css('top', `${top}px`);
expect($el).to.have.css('left', `${left}px`);
@@ -18,22 +24,23 @@ describe('Canvas Actions', () => {
beforeEach(() => {
workflowPage.actions.visit();
cy.window().then(
(win) => {
// @ts-ignore
win.preventNodeViewBeforeUnload = true;
},
);
cy.window().then((win) => {
// @ts-ignore
win.preventNodeViewBeforeUnload = true;
});
});
it('adds sticky to canvas with default text and position', () => {
workflowPage.getters.addStickyButton().should('not.be.visible');
addDefaultSticky()
workflowPage.getters.stickies().eq(0)
addDefaultSticky();
workflowPage.getters
.stickies()
.eq(0)
.should('have.text', 'Im a note\nDouble click to edit me. Guide\n')
.find('a').contains('Guide').should('have.attr', 'href');
.find('a')
.contains('Guide')
.should('have.attr', 'href');
});
it('drags sticky around to top left corner', () => {
@@ -57,18 +64,19 @@ describe('Canvas Actions', () => {
it('deletes sticky', () => {
workflowPage.actions.addSticky();
workflowPage.getters.stickies().should('have.length', 1)
workflowPage.getters.stickies().should('have.length', 1);
workflowPage.actions.deleteSticky();
workflowPage.getters.stickies().should('have.length', 0)
workflowPage.getters.stickies().should('have.length', 0);
});
it('edits sticky and updates content as markdown', () => {
workflowPage.actions.addSticky();
workflowPage.getters.stickies()
.should('have.text', 'Im a note\nDouble click to edit me. Guide\n')
workflowPage.getters
.stickies()
.should('have.text', 'Im a note\nDouble click to edit me. Guide\n');
workflowPage.getters.stickies().dblclick();
workflowPage.actions.editSticky('# hello world \n ## text text');
@@ -159,32 +167,41 @@ describe('Canvas Actions', () => {
cy.drag('[data-test-id="sticky"] [data-dir="topLeft"]', [-150, -150]);
checkStickiesStyle(124, 256, 316, 384, -121);
workflowPage.getters.canvasNodes().eq(0)
workflowPage.getters
.canvasNodes()
.eq(0)
.should(($el) => {
expect($el).to.have.css('z-index', 'auto');
});
workflowPage.actions.addSticky();
workflowPage.getters.stickies().eq(0)
workflowPage.getters
.stickies()
.eq(0)
.should(($el) => {
expect($el).to.have.css('z-index', '-121');
});
workflowPage.getters.stickies().eq(1)
workflowPage.getters
.stickies()
.eq(1)
.should(($el) => {
expect($el).to.have.css('z-index', '-38');
});
cy.drag('[data-test-id="sticky"] [data-dir="topLeft"]', [-200, -200], { index: 1 });
workflowPage.getters.stickies().eq(0)
workflowPage.getters
.stickies()
.eq(0)
.should(($el) => {
expect($el).to.have.css('z-index', '-121');
});
workflowPage.getters.stickies().eq(1)
workflowPage.getters
.stickies()
.eq(1)
.should(($el) => {
expect($el).to.have.css('z-index', '-158');
});
});
});
@@ -198,15 +215,20 @@ type BoundingBox = {
width: number;
top: number;
left: number;
}
};
function dragRightEdge(curr: BoundingBox, move: number) {
workflowPage.getters.stickies().first().then(($el) => {
const { left, top, height, width } = curr;
cy.drag(`[data-test-id="sticky"] [data-dir="right"]`, [left + width + move, 0], { abs: true });
stickyShouldBePositionedCorrectly({ top, left });
stickyShouldHaveCorrectSize([height, width * 1.5 + move]);
});
workflowPage.getters
.stickies()
.first()
.then(($el) => {
const { left, top, height, width } = curr;
cy.drag(`[data-test-id="sticky"] [data-dir="right"]`, [left + width + move, 0], {
abs: true,
});
stickyShouldBePositionedCorrectly({ top, left });
stickyShouldHaveCorrectSize([height, width * 1.5 + move]);
});
}
function shouldHaveOneSticky() {
@@ -214,17 +236,20 @@ function shouldHaveOneSticky() {
}
function shouldBeInDefaultLocation() {
workflowPage.getters.stickies().eq(0).should(($el) => {
expect($el).to.have.css('height', '160px');
expect($el).to.have.css('width', '240px');
})
workflowPage.getters
.stickies()
.eq(0)
.should(($el) => {
expect($el).to.have.css('height', '160px');
expect($el).to.have.css('width', '240px');
});
}
function shouldHaveDefaultSize() {
workflowPage.getters.stickies().should(($el) => {
expect($el).to.have.css('height', '160px');
expect($el).to.have.css('width', '240px');
})
});
}
function addDefaultSticky() {
@@ -237,21 +262,19 @@ function addDefaultSticky() {
function stickyShouldBePositionedCorrectly(position: Position) {
const yOffset = -100;
const xOffset = -180;
workflowPage.getters.stickies()
.should(($el) => {
expect($el).to.have.css('top', `${yOffset + position.top}px`);
expect($el).to.have.css('left', `${xOffset + position.left}px`);
});
workflowPage.getters.stickies().should(($el) => {
expect($el).to.have.css('top', `${yOffset + position.top}px`);
expect($el).to.have.css('left', `${xOffset + position.left}px`);
});
}
function stickyShouldHaveCorrectSize(size: [number, number]) {
const yOffset = 0;
const xOffset = 0;
workflowPage.getters.stickies()
.should(($el) => {
expect($el).to.have.css('height', `${yOffset + size[0]}px`);
expect($el).to.have.css('width', `${xOffset + size[1]}px`);
});
workflowPage.getters.stickies().should(($el) => {
expect($el).to.have.css('height', `${yOffset + size[0]}px`);
expect($el).to.have.css('width', `${xOffset + size[1]}px`);
});
}
function moveSticky(target: Position) {

View File

@@ -1,4 +1,5 @@
import { WorkflowPage, NDV, CredentialsModal } from '../pages';
import { getVisibleSelect } from '../utils';
const workflowPage = new WorkflowPage();
const ndv = new NDV();
@@ -32,7 +33,7 @@ describe('Resource Locator', () => {
workflowPage.actions.addNodeToCanvas('Google Sheets', true, true);
workflowPage.getters.nodeCredentialsSelect().click();
// Add oAuth credentials
workflowPage.getters.nodeCredentialsSelect().find('li').last().click();
getVisibleSelect().find('li').last().click();
credentialsModal.getters.credentialsEditModal().should('be.visible');
credentialsModal.getters.credentialAuthTypeRadioButtons().should('have.length', 2);
credentialsModal.getters.credentialAuthTypeRadioButtons().first().click();

View File

@@ -1,6 +1,7 @@
import { NodeCreator } from '../pages/features/node-creator';
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
import { NDV } from '../pages/ndv';
import { getVisibleSelect } from '../utils';
const nodeCreatorFeature = new NodeCreator();
const WorkflowPage = new WorkflowPageClass();
@@ -85,7 +86,7 @@ describe('Node Creator', () => {
nodeCreatorFeature.getters.getCreatorItem(editImageNode).click();
nodeCreatorFeature.getters.activeSubcategory().should('have.text', editImageNode);
nodeCreatorFeature.getters.getCreatorItem('Crop Image').click();
NDVModal.getters.parameterInput('operation').should('contain.text', 'Crop');
NDVModal.getters.parameterInput('operation').find('input').should('have.value', 'Crop');
});
it('should search through actions and confirm added action', () => {
@@ -95,9 +96,9 @@ describe('Node Creator', () => {
nodeCreatorFeature.getters.activeSubcategory().should('have.text', 'FTP');
nodeCreatorFeature.getters.searchBar().find('input').clear().type('file');
// Navigate to rename action which should be the 4th item
nodeCreatorFeature.getters.searchBar().find('input').type('{uparrow}{uparrow}{rightarrow}');
NDVModal.getters.parameterInput('operation').should('contain.text', 'Rename');
})
nodeCreatorFeature.getters.searchBar().find('input').type('{uparrow}{rightarrow}');
NDVModal.getters.parameterInput('operation').find('input').should('have.value', 'Rename');
});
it('should not show actions for single action nodes', () => {
const singleActionNodes = [
@@ -110,19 +111,22 @@ describe('Node Creator', () => {
'Spontit',
'Vonage',
'Send Email',
'Toggl Trigger'
]
const doubleActionNode = 'OpenWeatherMap'
'Toggl Trigger',
];
const doubleActionNode = 'OpenWeatherMap';
nodeCreatorFeature.actions.openNodeCreator();
singleActionNodes.forEach((node) => {
nodeCreatorFeature.getters.searchBar().find('input').clear().type(node);
nodeCreatorFeature.getters.getCreatorItem(node).find('button[class*="panelIcon"]').should('not.exist');
})
nodeCreatorFeature.getters
.getCreatorItem(node)
.find('button[class*="panelIcon"]')
.should('not.exist');
});
nodeCreatorFeature.getters.searchBar().find('input').clear().type(doubleActionNode);
nodeCreatorFeature.getters.getCreatorItem(doubleActionNode).click();
nodeCreatorFeature.getters.creatorItem().should('have.length', 4);
})
});
it('should have "Actions" section collapsed when opening actions view from Trigger root view', () => {
nodeCreatorFeature.actions.openNodeCreator();
@@ -131,10 +135,19 @@ describe('Node Creator', () => {
nodeCreatorFeature.getters.getCategoryItem('Actions').should('exist');
nodeCreatorFeature.getters.getCategoryItem('Triggers').should('exist');
nodeCreatorFeature.getters.getCategoryItem('Triggers').parent().should('not.have.attr', 'data-category-collapsed');
nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('have.attr', 'data-category-collapsed', 'true');
nodeCreatorFeature.getters.getCategoryItem('Actions').click()
nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('not.have.attr', 'data-category-collapsed');
nodeCreatorFeature.getters
.getCategoryItem('Triggers')
.parent()
.should('have.attr', 'data-category-collapsed', 'false');
nodeCreatorFeature.getters
.getCategoryItem('Actions')
.parent()
.should('have.attr', 'data-category-collapsed', 'true');
nodeCreatorFeature.getters.getCategoryItem('Actions').click();
nodeCreatorFeature.getters
.getCategoryItem('Actions')
.parent()
.should('have.attr', 'data-category-collapsed', 'false');
});
it('should have "Triggers" section collapsed when opening actions view from Regular root view', () => {
@@ -145,17 +158,33 @@ describe('Node Creator', () => {
nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n');
nodeCreatorFeature.getters.getCreatorItem('n8n').click();
nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('not.have.attr', 'data-category-collapsed');
nodeCreatorFeature.getters.getCategoryItem('Actions').click()
nodeCreatorFeature.getters.getCategoryItem('Actions').parent().should('have.attr', 'data-category-collapsed');
nodeCreatorFeature.getters.getCategoryItem('Triggers').parent().should('have.attr', 'data-category-collapsed');
nodeCreatorFeature.getters.getCategoryItem('Triggers').click()
nodeCreatorFeature.getters.getCategoryItem('Triggers').parent().should('not.have.attr', 'data-category-collapsed');
nodeCreatorFeature.getters
.getCategoryItem('Actions')
.parent()
.should('have.attr', 'data-category-collapsed', 'false');
nodeCreatorFeature.getters.getCategoryItem('Actions').click();
nodeCreatorFeature.getters
.getCategoryItem('Actions')
.parent()
.should('have.attr', 'data-category-collapsed', 'true');
nodeCreatorFeature.getters
.getCategoryItem('Triggers')
.parent()
.should('have.attr', 'data-category-collapsed', 'true');
nodeCreatorFeature.getters.getCategoryItem('Triggers').click();
nodeCreatorFeature.getters
.getCategoryItem('Triggers')
.parent()
.should('have.attr', 'data-category-collapsed', 'false');
});
it('should show callout and two suggested nodes if node has no trigger actions', () => {
nodeCreatorFeature.actions.openNodeCreator();
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)');
nodeCreatorFeature.getters
.searchBar()
.find('input')
.clear()
.type('Customer Datastore (n8n training)');
nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click();
cy.getByTestId('actions-panel-no-triggers-callout').should('be.visible');
@@ -165,28 +194,32 @@ describe('Node Creator', () => {
it('should show intro callout if user has not made a production execution', () => {
nodeCreatorFeature.actions.openNodeCreator();
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)');
nodeCreatorFeature.getters
.searchBar()
.find('input')
.clear()
.type('Customer Datastore (n8n training)');
nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click();
cy.getByTestId('actions-panel-activation-callout').should('be.visible');
nodeCreatorFeature.getters.activeSubcategory().find('button').click();
nodeCreatorFeature.getters.searchBar().find('input').clear()
nodeCreatorFeature.getters.searchBar().find('input').clear();
nodeCreatorFeature.getters.getCreatorItem('On a schedule').click();
// Setup 1s interval execution
cy.getByTestId('parameter-input-field').click();
cy.getByTestId('parameter-input-field')
.find('.el-select-dropdown')
.find('.option-headline')
.contains('Seconds')
.click();
getVisibleSelect().find('.option-headline').contains('Seconds').click();
cy.getByTestId('parameter-input-secondsInterval').clear().type('1');
NDVModal.actions.close();
nodeCreatorFeature.actions.openNodeCreator();
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)');
nodeCreatorFeature.getters
.searchBar()
.find('input')
.clear()
.type('Customer Datastore (n8n training)');
nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click();
nodeCreatorFeature.getters.getCreatorItem('Get All People').click();
NDVModal.actions.close();
@@ -197,11 +230,15 @@ describe('Node Creator', () => {
// Wait for schedule 1s execution to mark user as having made a production execution
cy.wait(1500);
cy.reload()
cy.reload();
// Action callout should not be visible after user has made a production execution
nodeCreatorFeature.actions.openNodeCreator();
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)');
nodeCreatorFeature.getters
.searchBar()
.find('input')
.clear()
.type('Customer Datastore (n8n training)');
nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click();
cy.getByTestId('actions-panel-activation-callout').should('not.exist');
@@ -210,7 +247,11 @@ describe('Node Creator', () => {
it('should show Trigger and Actions sections during search', () => {
nodeCreatorFeature.actions.openNodeCreator();
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Customer Datastore (n8n training)');
nodeCreatorFeature.getters
.searchBar()
.find('input')
.clear()
.type('Customer Datastore (n8n training)');
nodeCreatorFeature.getters.getCreatorItem('Customer Datastore (n8n training)').click();
nodeCreatorFeature.getters.searchBar().find('input').clear().type('Non existent action name');
@@ -228,7 +269,8 @@ describe('Node Creator', () => {
{
name: 'canvas add button',
handler: () => nodeCreatorFeature.getters.canvasAddButton().click(),
}, {
},
{
name: 'plus button',
handler: () => nodeCreatorFeature.getters.plusButton().click(),
},
@@ -238,10 +280,10 @@ describe('Node Creator', () => {
// name: 'tab key',
// handler: () => cy.realPress('Tab'),
// },
]
];
sourcesWithAppend.forEach((source) => {
it(`should append manual trigger when source is ${source.name}`, () => {
source.handler()
source.handler();
nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n');
nodeCreatorFeature.getters.getCreatorItem('n8n').click();
nodeCreatorFeature.getters.getCategoryItem('Actions').click();
@@ -251,6 +293,7 @@ describe('Node Creator', () => {
});
});
// @TODO FIX ADDING 2 NODES IN ONE GO
it('should not append manual trigger when source is canvas related', () => {
nodeCreatorFeature.getters.canvasAddButton().click();
nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n');
@@ -258,8 +301,8 @@ describe('Node Creator', () => {
nodeCreatorFeature.getters.getCategoryItem('Actions').click();
nodeCreatorFeature.getters.getCreatorItem('Create a credential').click();
NDVModal.actions.close();
WorkflowPage.actions.deleteNode('When clicking "Execute Workflow"')
WorkflowPage.getters.canvasNodePlusEndpointByName('n8n').click()
WorkflowPage.actions.deleteNode('When clicking "Execute Workflow"');
WorkflowPage.getters.canvasNodePlusEndpointByName('n8n').click();
nodeCreatorFeature.getters.searchBar().find('input').clear().type('n8n');
nodeCreatorFeature.getters.getCreatorItem('n8n').click();
nodeCreatorFeature.getters.getCategoryItem('Actions').click();
@@ -267,8 +310,8 @@ describe('Node Creator', () => {
NDVModal.actions.close();
WorkflowPage.getters.canvasNodes().should('have.length', 2);
WorkflowPage.actions.zoomToFit();
WorkflowPage.actions.addNodeBetweenNodes('n8n', 'n8n1', 'Item Lists', 'Summarize')
WorkflowPage.actions.addNodeBetweenNodes('n8n', 'n8n1', 'Item Lists', 'Summarize');
WorkflowPage.getters.canvasNodes().should('have.length', 3);
})
});
});
});

View File

@@ -117,7 +117,7 @@ describe('NDV', () => {
setupSchemaWorkflow();
ndv.getters.outputDisplayMode().children().should('have.length', 3);
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Table');
ndv.getters.outputDisplayMode().contains('Schema').click();
ndv.actions.switchOutputMode('Schema');
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema');
schemaKeys.forEach((key) => {
@@ -130,7 +130,7 @@ describe('NDV', () => {
});
it('should preserve schema view after execution', () => {
setupSchemaWorkflow();
ndv.getters.outputDisplayMode().contains('Schema').click();
ndv.actions.switchOutputMode('Schema');
ndv.actions.execute();
ndv.getters.outputDisplayMode().find('[class*=active]').should('contain', 'Schema');
});
@@ -142,7 +142,7 @@ describe('NDV', () => {
.outputPanel()
.find('[data-test-id=run-data-schema-item]')
.filter(':contains("objectValue")');
ndv.getters.outputDisplayMode().contains('Schema').click();
ndv.actions.switchOutputMode('Schema');
expandedObjectProps.forEach((key) => {
ndv.getters
@@ -173,9 +173,9 @@ describe('NDV', () => {
ndv.actions.execute();
ndv.getters.outputPanel().contains('25 items').should('exist');
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
ndv.getters.outputDisplayMode().contains('Schema').click();
ndv.actions.switchOutputMode('Schema');
ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist');
ndv.getters.outputDisplayMode().contains('JSON').click();
ndv.actions.switchOutputMode('JSON');
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
});
it('should display large schema', () => {
@@ -188,7 +188,7 @@ describe('NDV', () => {
ndv.getters.outputPanel().contains('20 items').should('exist');
ndv.getters.outputPanel().find('[class*=_pagination]').should('exist');
ndv.getters.outputDisplayMode().contains('Schema').click();
ndv.actions.switchOutputMode('Schema');
ndv.getters.outputPanel().find('[class*=_pagination]').should('not.exist');
ndv.getters
.outputPanel()

View File

@@ -6,9 +6,11 @@ import {
} from '../constants';
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows';
import { getVisibleDropdown, getVisibleSelect } from '../utils';
const NEW_WORKFLOW_NAME = 'Something else';
const IMPORT_WORKFLOW_URL = 'https://gist.githubusercontent.com/OlegIvaniv/010bd3f45c8a94f8eb7012e663a8b671/raw/3afea1aec15573cc168d9af7e79395bd76082906/test-workflow.json';
const IMPORT_WORKFLOW_URL =
'https://gist.githubusercontent.com/OlegIvaniv/010bd3f45c8a94f8eb7012e663a8b671/raw/3afea1aec15573cc168d9af7e79395bd76082906/test-workflow.json';
const DUPLICATE_WORKFLOW_NAME = 'Duplicated workflow';
const DUPLICATE_WORKFLOW_TAG = 'Duplicate';
@@ -67,11 +69,11 @@ describe('Workflow Actions', () => {
it('should not save workflow if canvas is loading', () => {
let interceptCalledCount = 0;
// There's no way in Cypress to check if intercept was not called
// There's no way in Cypress to check if intercept was not called
// so we'll count the number of times it was called
cy.intercept('PATCH', '/rest/workflows/*', () => {
interceptCalledCount++;
}).as('saveWorkflow');
cy.intercept('PATCH', '/rest/workflows/*', () => {
interceptCalledCount++;
}).as('saveWorkflow');
WorkflowPage.actions.addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME);
WorkflowPage.actions.saveWorkflowOnButtonClick();
@@ -84,11 +86,11 @@ describe('Workflow Actions', () => {
(req) => {
// Delay the response to give time for the save to be triggered
req.on('response', async (res) => {
await new Promise((resolve) => setTimeout(resolve, 2000))
await new Promise((resolve) => setTimeout(resolve, 2000));
res.send();
})
}
)
});
},
);
cy.reload();
cy.get('.el-loading-mask').should('exist');
cy.get('body').type(META_KEY, { release: false }).type('s');
@@ -99,7 +101,7 @@ describe('Workflow Actions', () => {
cy.get('body').type(META_KEY, { release: false }).type('s');
cy.wait('@saveWorkflow');
cy.wrap(null).then(() => expect(interceptCalledCount).to.eq(1));
})
});
it('should copy nodes', () => {
WorkflowPage.actions.addNodeToCanvas(SCHEDULE_TRIGGER_NODE_NAME);
WorkflowPage.actions.addNodeToCanvas(CODE_NODE_NAME);
@@ -127,7 +129,7 @@ describe('Workflow Actions', () => {
cy.get('.el-message-box').should('be.visible');
cy.get('.el-message-box').find('input').type(IMPORT_WORKFLOW_URL);
cy.get('body').type('{enter}');
cy.waitForLoad(false)
cy.waitForLoad(false);
WorkflowPage.actions.zoomToFit();
WorkflowPage.getters.canvasNodes().should('have.length', 2);
WorkflowPage.getters.nodeConnections().should('have.length', 1);
@@ -137,7 +139,7 @@ describe('Workflow Actions', () => {
WorkflowPage.getters
.workflowImportInput()
.selectFile('cypress/fixtures/Test_workflow-actions_paste-data.json', { force: true });
cy.waitForLoad(false)
cy.waitForLoad(false);
WorkflowPage.actions.zoomToFit();
WorkflowPage.getters.canvasNodes().should('have.length', 2);
WorkflowPage.getters.nodeConnections().should('have.length', 1);
@@ -157,57 +159,33 @@ describe('Workflow Actions', () => {
WorkflowPage.getters.workflowMenuItemSettings().click();
// Change all settings
// totalWorkflows + 1 (current workflow) + 1 (no workflow option)
WorkflowPage.getters.workflowSettingsErrorWorkflowSelect().find('li').should('have.length', totalWorkflows + 2);
WorkflowPage.getters
.workflowSettingsErrorWorkflowSelect()
WorkflowPage.getters.workflowSettingsErrorWorkflowSelect().click();
getVisibleSelect()
.find('li')
.last()
.click({ force: true });
WorkflowPage.getters.workflowSettingsTimezoneSelect().find('li').should('exist');
WorkflowPage.getters.workflowSettingsTimezoneSelect().find('li').eq(1).click({ force: true });
WorkflowPage.getters
.workflowSettingsSaveFiledExecutionsSelect()
.find('li')
.should('have.length', 3);
WorkflowPage.getters
.workflowSettingsSaveFiledExecutionsSelect()
.find('li')
.last()
.click({ force: true });
WorkflowPage.getters
.workflowSettingsSaveSuccessExecutionsSelect()
.find('li')
.should('have.length', 3);
WorkflowPage.getters
.workflowSettingsSaveSuccessExecutionsSelect()
.find('li')
.last()
.click({ force: true });
WorkflowPage.getters
.workflowSettingsSaveManualExecutionsSelect()
.find('li')
.should('have.length', 3);
WorkflowPage.getters
.workflowSettingsSaveManualExecutionsSelect()
.find('li')
.last()
.click({ force: true });
WorkflowPage.getters
.workflowSettingsSaveExecutionProgressSelect()
.find('li')
.should('have.length', 3);
WorkflowPage.getters
.workflowSettingsSaveExecutionProgressSelect()
.find('li')
.last()
.click({ force: true });
.should('have.length', totalWorkflows + 2);
getVisibleSelect().find('li').last().click({ force: true });
WorkflowPage.getters.workflowSettingsTimezoneSelect().click();
getVisibleSelect().find('li').should('exist');
getVisibleSelect().find('li').eq(1).click({ force: true });
WorkflowPage.getters.workflowSettingsSaveFiledExecutionsSelect().click();
getVisibleSelect().find('li').should('have.length', 3);
getVisibleSelect().find('li').last().click({ force: true });
WorkflowPage.getters.workflowSettingsSaveSuccessExecutionsSelect().click();
getVisibleSelect().find('li').should('have.length', 3);
getVisibleSelect().find('li').last().click({ force: true });
WorkflowPage.getters.workflowSettingsSaveManualExecutionsSelect().click();
getVisibleSelect().find('li').should('have.length', 3);
getVisibleSelect().find('li').last().click({ force: true });
WorkflowPage.getters.workflowSettingsSaveExecutionProgressSelect().click();
getVisibleSelect().find('li').should('have.length', 3);
getVisibleSelect().find('li').last().click({ force: true });
WorkflowPage.getters.workflowSettingsTimeoutWorkflowSwitch().click();
WorkflowPage.getters.workflowSettingsTimeoutForm().find('input').first().type('1');
// Save settings
WorkflowPage.getters.workflowSettingsSaveButton().click();
WorkflowPage.getters.workflowSettingsModal().should('not.exist');
WorkflowPage.getters.successToast().should('exist');
})
});
});
it('should not be able to delete unsaved workflow', () => {
@@ -245,7 +223,7 @@ describe('Workflow Actions', () => {
.find('.el-select__tags input')
.type(DUPLICATE_WORKFLOW_TAG);
WorkflowPage.getters.duplicateWorkflowModal().find('.el-select__tags input').type('{enter}');
WorkflowPage.getters.duplicateWorkflowModal().find('.el-select__tags input').type('{enter}');
WorkflowPage.getters.duplicateWorkflowModal().find('.el-select__tags input').type('{esc}');
WorkflowPage.getters
.duplicateWorkflowModal()
.find('button')

View File

@@ -5,7 +5,7 @@ export class CredentialsPage extends BasePage {
getters = {
emptyListCreateCredentialButton: () => cy.getByTestId('empty-resources-list').find('button'),
createCredentialButton: () => cy.getByTestId('resources-list-add'),
searchInput: () => cy.getByTestId('resources-list-search').find('input'),
searchInput: () => cy.getByTestId('resources-list-search'),
emptyList: () => cy.getByTestId('resources-list-empty'),
credentialCards: () => cy.getByTestId('resources-list-item'),
credentialCard: (credentialName: string) =>
@@ -17,8 +17,8 @@ export class CredentialsPage extends BasePage {
this.getters.credentialCard(credentialName).findChildByTestId('credential-card-actions'),
credentialDeleteButton: () =>
cy.getByTestId('action-toggle-dropdown').filter(':visible').contains('Delete'),
sort: () => cy.getByTestId('resources-list-sort'),
sortOption: (label: string) => this.getters.sort().contains(label).first(),
sort: () => cy.getByTestId('resources-list-sort').first(),
sortOption: (label: string) => cy.getByTestId('resources-list-sort-item').contains(label).first(),
filtersTrigger: () => cy.getByTestId('resources-list-filters-trigger'),
filtersDropdown: () => cy.getByTestId('resources-list-filters-dropdown'),
};

View File

@@ -20,7 +20,7 @@ export class CredentialsModal extends BasePage {
credentialsEditModal: () => cy.getByTestId('credential-edit-dialog'),
credentialsAuthTypeSelector: () => cy.getByTestId('node-auth-type-selector'),
credentialAuthTypeRadioButtons: () =>
this.getters.credentialsAuthTypeSelector().find('label[role=radio]'),
this.getters.credentialsAuthTypeSelector().find('label.el-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),
@@ -42,7 +42,7 @@ export class CredentialsModal extends BasePage {
},
save: (test = false) => {
cy.intercept('POST', '/rest/credentials').as('saveCredential');
this.getters.saveButton().click();
this.getters.saveButton().click({ force: true });
cy.wait('@saveCredential');
if (test) cy.wait('@testCredential');

View File

@@ -5,15 +5,15 @@ export class MessageBox extends BasePage {
modal: () => cy.get('.el-message-box', { withinSubject: null }),
header: () => this.getters.modal().find('.el-message-box__title'),
content: () => this.getters.modal().find('.el-message-box__content'),
confirm: () => this.getters.modal().find('.btn--confirm'),
cancel: () => this.getters.modal().find('.btn--cancel'),
confirm: () => this.getters.modal().find('.btn--confirm').first(),
cancel: () => this.getters.modal().find('.btn--cancel').first(),
};
actions = {
confirm: () => {
this.getters.confirm().click();
this.getters.confirm().click({ force: true});
},
cancel: () => {
this.getters.cancel().click();
this.getters.cancel().click({ force: true});
},
};
}

View File

@@ -1,4 +1,5 @@
import { BasePage } from './base';
import { getVisibleSelect } from '../utils';
export class NDV extends BasePage {
getters = {
@@ -101,10 +102,11 @@ export class NDV extends BasePage {
this.getters.parameterInput(parameterName).type(content);
},
selectOptionInParameterDropdown: (parameterName: string, content: string) => {
this.getters.parameterInput(parameterName).find('.option-headline').contains(content).click();
getVisibleSelect().find('.option-headline').contains(content).click();
},
dismissMappingTooltip: () => {
cy.getByTestId('dismiss-mapping-tooltip').click();
cy.getByTestId('dismiss-mapping-tooltip').should('not.be.visible');
},
rename: (newName: string) => {
this.getters.nodeNameContainer().click();
@@ -139,11 +141,11 @@ export class NDV extends BasePage {
},
changeInputRunSelector: (runName: string) => {
this.getters.inputRunSelector().click();
cy.get('.el-select-dropdown:visible .el-select-dropdown__item').contains(runName).click();
getVisibleSelect().find('.el-select-dropdown__item').contains(runName).click();
},
changeOutputRunSelector: (runName: string) => {
this.getters.outputRunSelector().click();
cy.get('.el-select-dropdown:visible .el-select-dropdown__item').contains(runName).click();
getVisibleSelect().find('.el-select-dropdown__item').contains(runName).click();
},
toggleOutputRunLinking: () => {
this.getters.outputRunSelector().find('button').click();
@@ -159,7 +161,7 @@ export class NDV extends BasePage {
},
setRLCValue: (paramName: string, value: string) => {
this.getters.resourceLocatorModeSelector(paramName).click();
this.getters.resourceLocatorModeSelector(paramName).find('li').last().click();
getVisibleSelect().find('li').last().click();
this.getters.resourceLocatorInput(paramName).type(value);
},
validateExpressionPreview: (paramName: string, value: string) => {

View File

@@ -1,4 +1,5 @@
import { BasePage } from './base';
import { getVisibleSelect } from '../utils';
export class SettingsLogStreamingPage extends BasePage {
url = '/settings/log-streaming';
@@ -6,11 +7,9 @@ export class SettingsLogStreamingPage extends BasePage {
getActionBoxUnlicensed: () => cy.getByTestId('action-box-unlicensed'),
getActionBoxLicensed: () => cy.getByTestId('action-box-licensed'),
getDestinationModal: () => cy.getByTestId('destination-modal'),
getDestinationModalDialog: () => this.getters.getDestinationModal().find('.el-dialog'),
getSelectDestinationType: () => cy.getByTestId('select-destination-type'),
getDestinationNameInput: () => cy.getByTestId('subtitle-showing-type'),
getSelectDestinationTypeItems: () =>
this.getters.getSelectDestinationType().find('.el-select-dropdown__item'),
getSelectDestinationTypeItems: () => getVisibleSelect().find('.el-select-dropdown__item'),
getSelectDestinationButton: () => cy.getByTestId('select-destination-button'),
getContactUsButton: () => this.getters.getActionBoxUnlicensed().find('button'),
getAddFirstDestinationButton: () => this.getters.getActionBoxLicensed().find('button'),

View File

@@ -11,7 +11,7 @@ export class PersonalSettingsPage extends BasePage {
lastNameInput: () => cy.getByTestId('lastName').find('input').first(),
emailInputContainer: () => cy.getByTestId('email'),
emailInput: () => cy.getByTestId('email').find('input').first(),
changePasswordLink: () => cy.getByTestId('change-password-link').find('a').first(),
changePasswordLink: () => cy.getByTestId('change-password-link').first(),
saveSettingsButton: () => cy.getByTestId('save-settings-button'),
};
actions = {
@@ -34,7 +34,10 @@ export class PersonalSettingsPage extends BasePage {
},
tryToSetWeakPassword: (oldPassword: string, newPassword: string) => {
this.actions.updatePassword(oldPassword, newPassword);
changePasswordModal.getters.newPasswordInputContainer().find('div[class^="_errorInput"]').should('exist');
changePasswordModal.getters
.newPasswordInputContainer()
.find('div[class^="_errorInput"]')
.should('exist');
},
updateEmail: (newEmail: string) => {
this.getters.emailInput().type('{selectall}').type(newEmail).type('{enter}');

View File

@@ -4,8 +4,8 @@ import { WorkflowPage } from './workflow';
import { WorkflowsPage } from './workflows';
import { BasePage } from './base';
const workflowPage = new WorkflowPage();
const workflowsPage = new WorkflowsPage();
const workflowPage = new WorkflowPage();
const workflowsPage = new WorkflowsPage();
const mainSidebar = new MainSidebar();
const settingsSidebar = new SettingsSidebar();
@@ -18,11 +18,15 @@ export class SettingsUsersPage extends BasePage {
inviteUsersModalEmailsInput: () => cy.getByTestId('emails').find('input').first(),
userListItems: () => cy.get('[data-test-id^="user-list-item"]'),
userItem: (email: string) => cy.getByTestId(`user-list-item-${email.toLowerCase()}`),
userActionsToggle: (email: string) => this.getters.userItem(email).find('[data-test-id="action-toggle"]'),
deleteUserAction: () => cy.getByTestId('action-toggle-dropdown').find('li:contains("Delete"):visible'),
userActionsToggle: (email: string) =>
this.getters.userItem(email).find('[data-test-id="action-toggle"]'),
deleteUserAction: () =>
cy.getByTestId('action-toggle-dropdown').find('li:contains("Delete"):visible'),
confirmDeleteModal: () => cy.getByTestId('deleteUser-modal').last(),
transferDataRadioButton: () => this.getters.confirmDeleteModal().find('[role="radio"]').first(),
deleteDataRadioButton: () => this.getters.confirmDeleteModal().find('[role="radio"]').last(),
transferDataRadioButton: () =>
this.getters.confirmDeleteModal().find('.el-radio .el-radio__input').first(),
deleteDataRadioButton: () =>
this.getters.confirmDeleteModal().find('.el-radio .el-radio__input').last(),
userSelectDropDown: () => this.getters.confirmDeleteModal().find('.n8n-select'),
userSelectOptions: () => cy.get('.el-select-dropdown:visible .el-select-dropdown__item'),
deleteUserButton: () => this.getters.confirmDeleteModal().find('button:contains("Delete")'),

View File

@@ -10,7 +10,7 @@ export class VariablesPage extends BasePage {
goToUpgrade: () => cy.getByTestId('go-to-upgrade'),
actionBox: () => cy.getByTestId('action-box'),
emptyResourcesListNewVariableButton: () => this.getters.emptyResourcesList().find('button'),
searchBar: () => cy.getByTestId('resources-list-search').find('input'),
searchBar: () => cy.getByTestId('resources-list-search'),
createVariableButton: () => cy.getByTestId('resources-list-add'),
variablesRows: () => cy.getByTestId('variables-row'),
variablesEditableRows: () =>

View File

@@ -1,5 +1,6 @@
import { META_KEY } from '../constants';
import { BasePage } from './base';
import { getVisibleSelect } from '../utils';
export class WorkflowPage extends BasePage {
url = '/workflow/new';
@@ -16,7 +17,7 @@ export class WorkflowPage extends BasePage {
nthTagPill: (n: number) =>
cy.get(`[data-test-id="workflow-tags-container"] span.tags > span:nth-child(${n})`),
tagsDropdown: () => cy.getByTestId('workflow-tags-dropdown'),
tagsInDropdown: () => cy.getByTestId('workflow-tags-dropdown').find('li').filter('.tag'),
tagsInDropdown: () => getVisibleSelect().find('li').filter('.tag'),
createTagButton: () => cy.getByTestId('new-tag-link'),
saveButton: () => cy.getByTestId('workflow-save-button'),
nodeCreatorSearchBar: () => cy.getByTestId('node-creator-search-bar'),
@@ -37,8 +38,8 @@ export class WorkflowPage extends BasePage {
canvasNodePlusEndpointByName: (nodeName: string, index = 0) => {
return cy.get(this.getters.getEndpointSelector('plus', nodeName, index));
},
successToast: () => cy.get('.el-notification .el-icon-success').parent(),
errorToast: () => cy.get('.el-notification .el-icon-error'),
successToast: () => cy.get('.el-notification .el-notification--success').parent(),
errorToast: () => cy.get('.el-notification .el-notification--error'),
activatorSwitch: () => cy.getByTestId('workflow-activate-switch'),
workflowMenu: () => cy.getByTestId('workflow-menu'),
firstStepButton: () => cy.getByTestId('canvas-add-button'),
@@ -84,7 +85,8 @@ export class WorkflowPage extends BasePage {
duplicateWorkflowModal: () => cy.getByTestId('duplicate-modal'),
nodeViewBackground: () => cy.getByTestId('node-view-background'),
nodeView: () => cy.getByTestId('node-view'),
inlineExpressionEditorInput: () => cy.getByTestId('inline-expression-editor-input').find('[role=textbox]'),
inlineExpressionEditorInput: () =>
cy.getByTestId('inline-expression-editor-input').find('[role=textbox]'),
inlineExpressionEditorOutput: () => cy.getByTestId('inline-expression-editor-output'),
zoomInButton: () => cy.getByTestId('zoom-in-button'),
zoomOutButton: () => cy.getByTestId('zoom-out-button'),
@@ -92,8 +94,10 @@ export class WorkflowPage extends BasePage {
executeWorkflowButton: () => cy.getByTestId('execute-workflow-button'),
clearExecutionDataButton: () => cy.getByTestId('clear-execution-data-button'),
stopExecutionButton: () => cy.getByTestId('stop-execution-button'),
stopExecutionWaitingForWebhookButton: () => cy.getByTestId('stop-execution-waiting-for-webhook-button'),
stopExecutionWaitingForWebhookButton: () =>
cy.getByTestId('stop-execution-waiting-for-webhook-button'),
nodeCredentialsSelect: () => cy.getByTestId('node-credentials-select'),
nodeCredentialsCreateOption: () => cy.getByTestId('node-credentials-select-item-new'),
nodeCredentialsEditButton: () => cy.getByTestId('credential-edit-button'),
nodeCreatorItems: () => cy.getByTestId('item-iterator-item'),
ndvParameters: () => cy.getByTestId('parameter-item'),
@@ -134,17 +138,17 @@ export class WorkflowPage extends BasePage {
this.getters.nodeCreatorSearchBar().type(nodeDisplayName);
this.getters.nodeCreatorSearchBar().type('{enter}');
cy.wait(500)
cy.wait(500);
cy.get('body').then((body) => {
if(body.find('[data-test-id=node-creator]').length > 0) {
if(action) {
cy.contains(action).click()
if (body.find('[data-test-id=node-creator]').length > 0) {
if (action) {
cy.contains(action).click();
} else {
// Select the first action
cy.get('[data-keyboard-nav-type="action"]').eq(0).click()
cy.get('[data-keyboard-nav-type="action"]').eq(0).click();
}
}
})
});
if (!preventNdvClose) cy.get('body').type('{esc}');
},
@@ -157,7 +161,8 @@ export class WorkflowPage extends BasePage {
},
openTagManagerModal: () => {
this.getters.createTagButton().click();
this.getters.tagsDropdown().find('li.manage-tags').first().click();
this.getters.tagsDropdown().click();
getVisibleSelect().find('li.manage-tags').first().click();
},
openInlineExpressionEditor: () => {
cy.contains('Expression').invoke('show').click();
@@ -209,7 +214,7 @@ export class WorkflowPage extends BasePage {
this.getters.workflowTagsInput().type(tag);
this.getters.workflowTagsInput().type('{enter}');
});
cy.get('body').type('{enter}');
cy.get('body').click(0, 0);
// For a brief moment the Element UI tag component shows the tags as(+X) string
// so we need to wait for it to disappear
this.getters.workflowTagsContainer().should('not.contain', `+${tags.length}`);
@@ -241,7 +246,12 @@ export class WorkflowPage extends BasePage {
executeWorkflow: () => {
this.getters.executeWorkflowButton().click();
},
addNodeBetweenNodes: (sourceNodeName: string, targetNodeName: string, newNodeName: string, action?: string) => {
addNodeBetweenNodes: (
sourceNodeName: string,
targetNodeName: string,
newNodeName: string,
action?: string,
) => {
this.getters.getConnectionBetweenNodes(sourceNodeName, targetNodeName).first().realHover();
this.getters
.getConnectionActionsBetweenNodes(sourceNodeName, targetNodeName)
@@ -268,18 +278,10 @@ export class WorkflowPage extends BasePage {
this.getters.addStickyButton().click();
},
deleteSticky: () => {
this.getters.stickies().eq(0)
.realHover()
.find('[data-test-id="delete-sticky"]')
.click();
this.getters.stickies().eq(0).realHover().find('[data-test-id="delete-sticky"]').click();
},
editSticky: (content: string) => {
this.getters.stickies()
.dblclick()
.find('textarea')
.clear()
.type(content)
.type('{esc}');
this.getters.stickies().dblclick().find('textarea').clear().type(content).type('{esc}');
},
};
}

View File

@@ -5,7 +5,7 @@ export class WorkflowsPage extends BasePage {
getters = {
newWorkflowButtonCard: () => cy.getByTestId('new-workflow-card'),
newWorkflowTemplateCard: () => cy.getByTestId('new-workflow-template-card'),
searchBar: () => cy.getByTestId('resources-list-search').find('input'),
searchBar: () => cy.getByTestId('resources-list-search'),
createWorkflowButton: () => cy.getByTestId('resources-list-add'),
workflowCards: () => cy.getByTestId('resources-list-item'),
workflowCard: (workflowName: string) =>

View File

@@ -53,12 +53,12 @@ Cypress.Commands.add('signin', ({ email, password }) => {
});
Cypress.Commands.add('signout', () => {
cy.request('POST', '/rest/logout');
cy.request('POST', `${BACKEND_BASE_URL}/rest/logout`);
cy.getCookie(N8N_AUTH_COOKIE).should('not.exist');
});
Cypress.Commands.add('interceptREST', (method, url) => {
cy.intercept(method, `http://localhost:5678/rest${url}`);
cy.intercept(method, `${BACKEND_BASE_URL}/rest${url}`);
});
const setFeature = (feature: string, enabled: boolean) =>

View File

@@ -6,6 +6,10 @@ before(() => {
owner: INSTANCE_OWNER,
members: INSTANCE_MEMBERS,
});
Cypress.on('uncaught:exception', (err) => {
return !err.message.includes('ResizeObserver');
});
});
beforeEach(() => {

1
cypress/utils/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from './popper';

3
cypress/utils/modal.ts Normal file
View File

@@ -0,0 +1,3 @@
export function getVisibleModalOverlay() {
return cy.get('.el-overlay .el-overlay-dialog').filter(':visible');
}

11
cypress/utils/popper.ts Normal file
View File

@@ -0,0 +1,11 @@
export function getVisiblePopper() {
return cy.get('.el-popper').filter(':visible');
}
export function getVisibleSelect() {
return getVisiblePopper().filter('.el-select__popper');
}
export function getVisibleDropdown() {
return getVisiblePopper().filter('.el-dropdown__popper');
}