-
Notifications
You must be signed in to change notification settings - Fork 8.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into N8N-5498-workflow-action-tests
* master: refactor: Trim down the response on the Workflows listing endpoint (#4726) fix: Fix HTTP Request node e2e test in full run (no-changelog) (#4728) test: Add e2e tests for node creator (#4714) test: Set up e2e tests for HTTP Request node (#4718) fix: Lint typescript stories (no-changelog) (#4723) fix(editor): Fix slots rendering of NodeCreator's NoResults component (#4721) # Conflicts: # cypress/pages/workflow.ts # packages/editor-ui/src/components/Node/NodeCreator/ItemIterator.vue
- Loading branch information
Showing
50 changed files
with
479 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { WorkflowsPage as WorkflowsPageClass } from '../pages/workflows'; | ||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow'; | ||
|
||
const WorkflowsPage = new WorkflowsPageClass(); | ||
const WorkflowPage = new WorkflowPageClass(); | ||
|
||
describe('HTTP Request node', () => { | ||
beforeEach(() => { | ||
cy.task('db:reset'); | ||
cy.skipSetup(); | ||
}); | ||
|
||
it('should make a request with a URL and receive a response', () => { | ||
WorkflowsPage.actions.createWorkflowFromCard(); | ||
WorkflowPage.actions.addInitialNodeToCanvas('Manual Trigger'); | ||
WorkflowPage.actions.addNodeToCanvas('HTTP Request'); | ||
WorkflowPage.actions.openNodeNdv('HTTP Request'); | ||
WorkflowPage.actions.typeIntoParameterInput('url', 'https://google.com'); | ||
|
||
WorkflowPage.actions.executeNodeFromNdv(); | ||
|
||
WorkflowPage.getters.ndvOutputPanel().contains('<!doctype html>'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
import { DEFAULT_USER_EMAIL, DEFAULT_USER_PASSWORD } from "../constants"; | ||
import { randFirstName, randLastName } from "@ngneat/falso"; | ||
import { NodeCreator } from '../pages/features/node-creator'; | ||
import { INodeTypeDescription } from '../../packages/workflow'; | ||
import CustomNodeFixture from '../fixtures/Custom_node.json'; | ||
|
||
const username = DEFAULT_USER_EMAIL; | ||
const password = DEFAULT_USER_PASSWORD; | ||
const firstName = randFirstName(); | ||
const lastName = randLastName(); | ||
const nodeCreatorFeature = new NodeCreator(); | ||
|
||
describe('Node Creator', () => { | ||
beforeEach(() => { | ||
cy.intercept('GET', '/types/nodes.json', (req) => { | ||
// Delete caching headers so that we can intercept the request | ||
['etag', 'if-none-match', 'if-modified-since'].forEach(header => {delete req.headers[header]}); | ||
|
||
|
||
req.continue((res) => { | ||
const nodes = res.body as INodeTypeDescription[]; | ||
|
||
nodes.push(CustomNodeFixture as INodeTypeDescription); | ||
res.send(nodes) | ||
}) | ||
}).as('nodesIntercept') | ||
|
||
cy.signup(username, firstName, lastName, password); | ||
|
||
cy.on('uncaught:exception', (err, runnable) => { | ||
expect(err.message).to.include('Not logged in'); | ||
|
||
return false; | ||
}) | ||
|
||
cy.signin(username, password); | ||
cy.visit(nodeCreatorFeature.url); | ||
}); | ||
|
||
it('should open node creator on trigger tab if no trigger is on canvas', () => { | ||
nodeCreatorFeature.getters.canvasAddButton().click(); | ||
|
||
nodeCreatorFeature.getters.nodeCreator().contains('When should this workflow run?').should('be.visible'); | ||
|
||
nodeCreatorFeature.getters.nodeCreatorTabs().should('not.exist'); | ||
}) | ||
|
||
it('should see all tabs when opening via plus button', () => { | ||
nodeCreatorFeature.actions.openNodeCreator(); | ||
nodeCreatorFeature.getters.nodeCreatorTabs().should('exist'); | ||
nodeCreatorFeature.getters.selectedTab().should('have.text', 'Trigger'); | ||
}); | ||
|
||
it('should navigate subcategory', () => { | ||
nodeCreatorFeature.actions.openNodeCreator(); | ||
nodeCreatorFeature.getters.getCreatorItem('On App Event').click(); | ||
nodeCreatorFeature.getters.activeSubcategory().should('have.text', 'On App Event'); | ||
// Go back | ||
nodeCreatorFeature.getters.activeSubcategory().find('button').click() | ||
nodeCreatorFeature.getters.activeSubcategory().should('not.exist'); | ||
}) | ||
|
||
it('should search for nodes', () => { | ||
nodeCreatorFeature.actions.openNodeCreator(); | ||
nodeCreatorFeature.getters.selectedTab().should('have.text', 'Trigger'); | ||
|
||
nodeCreatorFeature.getters.searchBar().find('input').type('manual'); | ||
nodeCreatorFeature.getters.creatorItem().should('have.length', 1); | ||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('manual123'); | ||
nodeCreatorFeature.getters.creatorItem().should('have.length', 0); | ||
nodeCreatorFeature.getters.noResults() | ||
.should('exist') | ||
.should('contain.text', 'We didn\'t make that... yet'); | ||
|
||
nodeCreatorFeature.getters.searchBar().find('input').clear().type('edit image'); | ||
nodeCreatorFeature.getters.creatorItem().should('have.length', 0); | ||
nodeCreatorFeature.getters.noResults() | ||
.should('exist') | ||
.should('contain.text', 'To see results, click here'); | ||
|
||
nodeCreatorFeature.getters.noResults().contains('click here').click(); | ||
nodeCreatorFeature.getters.nodeCreatorTabs().should('exist'); | ||
nodeCreatorFeature.getters.getCreatorItem('Edit Image').should('exist'); | ||
nodeCreatorFeature.getters.selectedTab().should('have.text', 'All'); | ||
nodeCreatorFeature.getters.searchBar().find('button').click(); | ||
nodeCreatorFeature.getters.searchBar().find('input').should('be.empty') | ||
}) | ||
|
||
it('should add manual trigger node', () => { | ||
nodeCreatorFeature.getters.canvasAddButton().click(); | ||
nodeCreatorFeature.getters.getCreatorItem('Manually').click(); | ||
|
||
// TODO: Replace once we have canvas feature utils | ||
cy.get('span').contains('Back to canvas').click(); | ||
|
||
nodeCreatorFeature.getters.canvasAddButton().should('not.be.visible'); | ||
nodeCreatorFeature.getters.nodeCreator().should('not.exist'); | ||
|
||
// TODO: Replace once we have canvas feature utils | ||
cy.get('div').contains("On clicking 'execute'").should('exist'); | ||
}) | ||
|
||
it('check if non-core nodes are rendered all nodes', () => { | ||
cy.wait('@nodesIntercept').then((interception) => { | ||
const nodes = interception.response?.body as INodeTypeDescription[]; | ||
|
||
const categorizedNodes = nodeCreatorFeature.actions.categorizeNodes(nodes); | ||
nodeCreatorFeature.actions.openNodeCreator(); | ||
nodeCreatorFeature.actions.selectTab('All'); | ||
|
||
const categories = Object.keys(categorizedNodes); | ||
categories.forEach((category: string) => { | ||
// Core Nodes contains subcategories which we'll test separately | ||
if(category === 'Core Nodes') return; | ||
|
||
nodeCreatorFeature.actions.toggleCategory(category) | ||
|
||
// Check if all nodes are present | ||
categorizedNodes[category].forEach((node: INodeTypeDescription) => { | ||
if(node.hidden) return; | ||
nodeCreatorFeature.getters.categorizedItems().contains(node.displayName).should('exist'); | ||
}) | ||
|
||
nodeCreatorFeature.actions.toggleCategory(category) | ||
}) | ||
}) | ||
}) | ||
|
||
it.only('should render and select community node', () => { | ||
cy.wait('@nodesIntercept').then(() => { | ||
const customCategory = 'customCategory'; | ||
const customNode = 'E2E Node'; | ||
const customNodeDescription = 'Demonstrate rendering of node'; | ||
|
||
nodeCreatorFeature.actions.openNodeCreator(); | ||
nodeCreatorFeature.actions.selectTab('All'); | ||
|
||
nodeCreatorFeature.getters.getCreatorItem(customCategory).should('exist'); | ||
|
||
nodeCreatorFeature.actions.toggleCategory(customCategory); | ||
nodeCreatorFeature.getters.getCreatorItem(customNode).findChildByTestId('node-item-community-tooltip').should('exist'); | ||
nodeCreatorFeature.getters.getCreatorItem(customNode).contains(customNodeDescription).should('exist'); | ||
nodeCreatorFeature.actions.selectNode(customNode); | ||
|
||
// TODO: Replace once we have canvas feature utils | ||
cy.get('.data-display .node-name').contains(customNode).should('exist'); | ||
|
||
const nodeParameters = () => cy.getByTestId('node-parameters') | ||
const firstParameter = () => nodeParameters().find('.parameter-item').eq(0); | ||
const secondParameter = () => nodeParameters().find('.parameter-item').eq(1); | ||
|
||
// Check correct fields are rendered | ||
nodeParameters().should('exist') | ||
// Test property text input | ||
firstParameter().contains('Test property').should('exist'); | ||
firstParameter().find('input.el-input__inner').should('have.value', 'Some default'); | ||
// Resource select input | ||
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(); | ||
secondParameter().find('input.el-input__inner').should('have.value', 'option4'); | ||
}) | ||
}) | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
{ | ||
"properties": [{ | ||
"displayName": "Test property", | ||
"name": "testProp", | ||
"type": "string", | ||
"required": true, | ||
"noDataExpression": false, | ||
"default": "Some default" | ||
}, | ||
{ | ||
"displayName": "Resource", | ||
"name": "resource", | ||
"type": "options", | ||
"noDataExpression": true, | ||
"options": [{ | ||
"name": "option1", | ||
"value": "option1" | ||
}, | ||
{ | ||
"name": "option2", | ||
"value": "option2" | ||
}, | ||
{ | ||
"name": "option3", | ||
"value": "option3" | ||
}, | ||
{ | ||
"name": "option4", | ||
"value": "option4" | ||
} | ||
], | ||
"default": "option2" | ||
} | ||
], | ||
"displayName": "E2E Node", | ||
"name": "@e2e/n8n-nodes-e2e", | ||
"group": [ | ||
"transform" | ||
], | ||
"codex": { | ||
"categories": ["CustomCategory"] | ||
}, | ||
"version": 1, | ||
"description": "Demonstrate rendering of node", | ||
"defaults": { | ||
"name": "E2E Node " | ||
}, | ||
"inputs": [ | ||
"main" | ||
], | ||
"outputs": [ | ||
"main" | ||
], | ||
"icon": "fa:network-wired" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import { BasePage } from "../base"; | ||
import { INodeTypeDescription } from '../../packages/workflow'; | ||
|
||
export class NodeCreator extends BasePage { | ||
url = '/workflow/new'; | ||
getters = { | ||
plusButton: () => cy.getByTestId('node-creator-plus-button'), | ||
canvasAddButton: () => cy.getByTestId('canvas-add-button'), | ||
searchBar: () => cy.getByTestId('search-bar'), | ||
getCreatorItem: (label: string) => this.getters.creatorItem().contains(label).parents('[data-test-id="item-iterator-item"]'), | ||
getNthCreatorItem: (n: number) => this.getters.creatorItem().eq(n), | ||
nodeCreator: () => cy.getByTestId('node-creator'), | ||
nodeCreatorTabs: () => cy.getByTestId('node-creator-type-selector'), | ||
selectedTab: () => this.getters.nodeCreatorTabs().find('.is-active'), | ||
categorizedItems: () => cy.getByTestId('categorized-items'), | ||
creatorItem: () => cy.getByTestId('item-iterator-item'), | ||
communityNodeTooltip: () => cy.getByTestId('node-item-community-tooltip'), | ||
noResults: () => cy.getByTestId('categorized-no-results'), | ||
activeSubcategory: () => cy.getByTestId('categorized-items-subcategory'), | ||
expandedCategories: () => this.getters.creatorItem().find('>div').filter('.active').invoke('text'), | ||
}; | ||
actions = { | ||
openNodeCreator: () => { | ||
this.getters.plusButton().click(); | ||
this.getters.nodeCreator().should('be.visible') | ||
}, | ||
selectNode: (displayName: string) => { | ||
this.getters.getCreatorItem(displayName).click(); | ||
}, | ||
selectTab: (tab: string) => { | ||
this.getters.nodeCreatorTabs().contains(tab).click(); | ||
}, | ||
toggleCategory: (category: string) => { | ||
this.getters.getCreatorItem(category).click() | ||
}, | ||
categorizeNodes: (nodes: INodeTypeDescription[]) => { | ||
const categorizedNodes = nodes.reduce((acc, node) => { | ||
const categories = (node?.codex?.categories || []).map((category: string) => category.trim()); | ||
|
||
categories.forEach((category: {[key: string]: INodeTypeDescription[]}) => { | ||
// Node creator should show only the latest version of a node | ||
const newerVersion = nodes.find((n: INodeTypeDescription) => n.name === node.name && (n.version > node.version || Array.isArray(n.version))); | ||
|
||
if (acc[category] === undefined) { | ||
acc[category] = []; | ||
} | ||
acc[category].push(newerVersion ?? node); | ||
}); | ||
return acc; | ||
}, {}) | ||
|
||
return categorizedNodes; | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.