mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
test: Migrate 3 specs from Cypress - Playwright (#19269)
This commit is contained in:
@@ -1,216 +0,0 @@
|
||||
import type { ICredentialType } from 'n8n-workflow';
|
||||
|
||||
import CustomCredential from '../fixtures/Custom_credential.json';
|
||||
import CustomNodeFixture from '../fixtures/Custom_node.json';
|
||||
import CustomNodeWithCustomCredentialFixture from '../fixtures/Custom_node_custom_credential.json';
|
||||
import CustomNodeWithN8nCredentialFixture from '../fixtures/Custom_node_n8n_credential.json';
|
||||
import { CredentialsModal, WorkflowPage } from '../pages';
|
||||
import { NodeCreator } from '../pages/features/node-creator';
|
||||
import {
|
||||
confirmCommunityNodeUninstall,
|
||||
confirmCommunityNodeUpdate,
|
||||
getCommunityCards,
|
||||
installFirstCommunityNode,
|
||||
visitCommunityNodesSettings,
|
||||
} from '../pages/settings-community-nodes';
|
||||
import { getVisibleSelect } from '../utils';
|
||||
|
||||
const credentialsModal = new CredentialsModal();
|
||||
const nodeCreatorFeature = new NodeCreator();
|
||||
const workflowPage = new WorkflowPage();
|
||||
const ADD_TO_WORKFLOW_BUTTON = 'Add to workflow';
|
||||
|
||||
const addCommunityNodeToCanvas = (name: string) => {
|
||||
nodeCreatorFeature.actions.openNodeCreator();
|
||||
nodeCreatorFeature.getters.searchBar().find('input').clear().type(name);
|
||||
|
||||
nodeCreatorFeature.getters.getCreatorItem(name).find('.el-tooltip__trigger').should('exist');
|
||||
nodeCreatorFeature.actions.selectNode(name);
|
||||
|
||||
cy.contains('span', name).should('be.visible');
|
||||
cy.contains(ADD_TO_WORKFLOW_BUTTON).should('be.visible').click();
|
||||
};
|
||||
|
||||
// We separate-out the custom nodes because they require injecting nodes and credentials
|
||||
// so the /nodes and /credentials endpoints are intercepted and non-cached.
|
||||
// We want to keep the other tests as fast as possible so we don't want to break the cache in those.
|
||||
describe('Community and custom nodes in canvas', () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('/types/nodes.json', { middleware: true }, (req) => {
|
||||
req.headers['cache-control'] = 'no-cache, no-store';
|
||||
|
||||
req.on('response', (res) => {
|
||||
const nodes = res.body || [];
|
||||
|
||||
nodes.push(
|
||||
CustomNodeFixture,
|
||||
CustomNodeWithN8nCredentialFixture,
|
||||
CustomNodeWithCustomCredentialFixture,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
cy.intercept('/types/credentials.json', { middleware: true }, (req) => {
|
||||
req.headers['cache-control'] = 'no-cache, no-store';
|
||||
|
||||
req.on('response', (res) => {
|
||||
const credentials: ICredentialType[] = res.body || [];
|
||||
|
||||
credentials.push(CustomCredential as ICredentialType);
|
||||
});
|
||||
});
|
||||
|
||||
// next intercepts are not strictly needed, but they make the tests faster
|
||||
// - intercept request to vetted community types, returning empty list
|
||||
// - intercept request to vetted community type details, return null
|
||||
// - intercept request npm registry, return 404
|
||||
// --------------------------------------------------------------------------
|
||||
cy.intercept('/community-node-types', (req) => {
|
||||
req.reply({
|
||||
statusCode: 200,
|
||||
body: {
|
||||
data: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
cy.intercept('GET', '/community-node-types/*', {
|
||||
statusCode: 200,
|
||||
body: null,
|
||||
});
|
||||
|
||||
cy.intercept('GET', 'https://registry.npmjs.org/*', {
|
||||
statusCode: 404,
|
||||
body: {},
|
||||
});
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
workflowPage.actions.visit();
|
||||
});
|
||||
|
||||
it('should render and select community node', () => {
|
||||
addCommunityNodeToCanvas('E2E Node');
|
||||
|
||||
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();
|
||||
// Check if all options are rendered and select the fourth one
|
||||
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');
|
||||
});
|
||||
|
||||
it('should render custom node with n8n credential', () => {
|
||||
workflowPage.actions.addNodeToCanvas('Manual');
|
||||
addCommunityNodeToCanvas('E2E Node with native n8n credential');
|
||||
workflowPage.getters.nodeCredentialsLabel().click();
|
||||
workflowPage.getters.nodeCredentialsCreateOption().click();
|
||||
credentialsModal.getters.editCredentialModal().should('be.visible');
|
||||
credentialsModal.getters.editCredentialModal().should('contain.text', 'Notion API');
|
||||
});
|
||||
|
||||
it('should render custom node with custom credential', () => {
|
||||
workflowPage.actions.addNodeToCanvas('Manual');
|
||||
addCommunityNodeToCanvas('E2E Node with custom credential');
|
||||
workflowPage.getters.nodeCredentialsLabel().click();
|
||||
workflowPage.getters.nodeCredentialsCreateOption().click();
|
||||
credentialsModal.getters.editCredentialModal().should('be.visible');
|
||||
credentialsModal.getters.editCredentialModal().should('contain.text', 'Custom E2E Credential');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Community nodes', () => {
|
||||
const mockPackage = {
|
||||
createdAt: '2024-07-22T19:08:06.505Z',
|
||||
updatedAt: '2024-07-22T19:08:06.505Z',
|
||||
packageName: 'n8n-nodes-chatwork',
|
||||
installedVersion: '1.0.0',
|
||||
authorName: null,
|
||||
authorEmail: null,
|
||||
installedNodes: [
|
||||
{
|
||||
name: 'Chatwork',
|
||||
type: 'n8n-nodes-chatwork.chatwork',
|
||||
latestVersion: 1,
|
||||
},
|
||||
],
|
||||
updateAvailable: '1.1.2',
|
||||
};
|
||||
|
||||
it('can install, update and uninstall community nodes', () => {
|
||||
cy.intercept(
|
||||
{
|
||||
hostname: 'api.npms.io',
|
||||
pathname: '/v2/search',
|
||||
query: { q: 'keywords:n8n-community-node-package' },
|
||||
},
|
||||
{ body: {} },
|
||||
);
|
||||
cy.intercept(
|
||||
{ method: 'GET', pathname: '/rest/community-packages', times: 1 },
|
||||
{
|
||||
body: { data: [] },
|
||||
},
|
||||
).as('getEmptyPackages');
|
||||
visitCommunityNodesSettings();
|
||||
cy.wait('@getEmptyPackages');
|
||||
|
||||
// install a package
|
||||
cy.intercept(
|
||||
{ method: 'POST', pathname: '/rest/community-packages', times: 1 },
|
||||
{
|
||||
body: { data: mockPackage },
|
||||
},
|
||||
).as('installPackage');
|
||||
cy.intercept(
|
||||
{ method: 'GET', pathname: '/rest/community-packages', times: 1 },
|
||||
{
|
||||
body: { data: [mockPackage] },
|
||||
},
|
||||
).as('getPackages');
|
||||
installFirstCommunityNode('n8n-nodes-chatwork@1.0.0');
|
||||
cy.wait('@installPackage');
|
||||
cy.wait('@getPackages');
|
||||
getCommunityCards().should('have.length', 1);
|
||||
getCommunityCards().eq(0).should('include.text', 'v1.0.0');
|
||||
|
||||
// update the package
|
||||
cy.intercept(
|
||||
{ method: 'PATCH', pathname: '/rest/community-packages' },
|
||||
{
|
||||
body: { data: { ...mockPackage, installedVersion: '1.2.0', updateAvailable: undefined } },
|
||||
},
|
||||
).as('updatePackage');
|
||||
getCommunityCards().eq(0).find('button').click();
|
||||
confirmCommunityNodeUpdate();
|
||||
cy.wait('@updatePackage');
|
||||
getCommunityCards().should('have.length', 1);
|
||||
getCommunityCards().eq(0).should('not.include.text', 'v1.0.0');
|
||||
|
||||
// uninstall the package
|
||||
cy.intercept(
|
||||
{
|
||||
method: 'DELETE',
|
||||
pathname: '/rest/community-packages',
|
||||
query: { name: 'n8n-nodes-chatwork' },
|
||||
},
|
||||
{ statusCode: 204 },
|
||||
).as('uninstallPackage');
|
||||
getCommunityCards().getByTestId('action-toggle').click();
|
||||
cy.getByTestId('action-uninstall').click();
|
||||
confirmCommunityNodeUninstall();
|
||||
cy.wait('@uninstallPackage');
|
||||
|
||||
cy.getByTestId('action-box').should('exist');
|
||||
});
|
||||
});
|
||||
@@ -1,124 +0,0 @@
|
||||
import { VariablesPage } from '../pages/variables';
|
||||
|
||||
const variablesPage = new VariablesPage();
|
||||
|
||||
describe('Variables', () => {
|
||||
it('should show the unlicensed action box when the feature is disabled', () => {
|
||||
cy.disableFeature('variables');
|
||||
cy.visit(variablesPage.url);
|
||||
|
||||
variablesPage.getters.unavailableResourcesList().should('be.visible');
|
||||
variablesPage.getters.resourcesList().should('not.exist');
|
||||
});
|
||||
|
||||
describe('licensed', () => {
|
||||
before(() => {
|
||||
cy.enableFeature('variables');
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', '/rest/variables').as('loadVariables');
|
||||
cy.intercept('GET', '/rest/login').as('login');
|
||||
|
||||
cy.visit(variablesPage.url);
|
||||
cy.wait(['@loadVariables', '@loadSettings', '@login']);
|
||||
});
|
||||
|
||||
it('should show the licensed action box when the feature is enabled', () => {
|
||||
variablesPage.getters.emptyResourcesList().should('be.visible');
|
||||
variablesPage.getters.emptyResourcesListNewVariableButton().should('be.visible');
|
||||
});
|
||||
|
||||
it('should create a new variable using empty state row', () => {
|
||||
const key = 'ENV_VAR';
|
||||
const value = 'value';
|
||||
|
||||
variablesPage.actions.createVariableFromEmptyState(key, value);
|
||||
variablesPage.getters.variableRow(key).should('contain', value).should('be.visible');
|
||||
variablesPage.getters.variablesRows().should('have.length', 1);
|
||||
});
|
||||
|
||||
it('should create a new variable using pre-existing state', () => {
|
||||
const key = 'ENV_VAR_NEW';
|
||||
const value = 'value2';
|
||||
|
||||
variablesPage.actions.createVariable(key, value);
|
||||
variablesPage.getters.variableRow(key).should('contain', value).should('be.visible');
|
||||
variablesPage.getters.variablesRows().should('have.length', 2);
|
||||
|
||||
const otherKey = 'ENV_EXAMPLE';
|
||||
const otherValue = 'value3';
|
||||
|
||||
variablesPage.actions.createVariable(otherKey, otherValue);
|
||||
variablesPage.getters
|
||||
.variableRow(otherKey)
|
||||
.should('contain', otherValue)
|
||||
.should('be.visible');
|
||||
variablesPage.getters.variablesRows().should('have.length', 3);
|
||||
});
|
||||
|
||||
it('should get validation errors and cancel variable creation', () => {
|
||||
const key = 'ENV_VAR_NEW$';
|
||||
const value = 'value3';
|
||||
|
||||
variablesPage.getters.createVariableButton().click();
|
||||
const editingRow = variablesPage.getters.variablesEditableRows().eq(0);
|
||||
variablesPage.actions.setRowValue(editingRow, 'key', key);
|
||||
variablesPage.actions.setRowValue(editingRow, 'value', value);
|
||||
variablesPage.actions.saveRowEditing(editingRow);
|
||||
variablesPage.getters
|
||||
.variablesEditableRows()
|
||||
.eq(0)
|
||||
.should('contain', 'This field may contain only letters');
|
||||
variablesPage.actions.cancelRowEditing(editingRow);
|
||||
|
||||
variablesPage.getters.variablesRows().should('have.length', 3);
|
||||
});
|
||||
|
||||
it('should edit a variable', () => {
|
||||
const key = 'ENV_VAR_NEW';
|
||||
const newValue = 'value4';
|
||||
|
||||
variablesPage.actions.editRow(key);
|
||||
const editingRow = variablesPage.getters.variablesEditableRows().eq(0);
|
||||
variablesPage.actions.setRowValue(editingRow, 'value', newValue);
|
||||
variablesPage.actions.saveRowEditing(editingRow);
|
||||
|
||||
variablesPage.getters.variableRow(key).should('contain', newValue).should('be.visible');
|
||||
variablesPage.getters.variablesRows().should('have.length', 3);
|
||||
});
|
||||
|
||||
it('should delete a variable', () => {
|
||||
const key = 'TO_DELETE';
|
||||
const value = 'xxx';
|
||||
|
||||
variablesPage.actions.createVariable(key, value);
|
||||
variablesPage.actions.deleteVariable(key);
|
||||
});
|
||||
|
||||
it('should search for a variable', () => {
|
||||
// One Result
|
||||
variablesPage.getters.searchBar().type('NEW');
|
||||
variablesPage.getters.variablesRows().should('have.length', 1);
|
||||
variablesPage.getters.variableRow('NEW').should('contain.text', 'ENV_VAR_NEW');
|
||||
cy.url().should('include', 'search=NEW');
|
||||
|
||||
// Multiple Results
|
||||
variablesPage.getters.searchBar().clear().type('ENV_VAR');
|
||||
variablesPage.getters.variablesRows().should('have.length', 2);
|
||||
cy.url().should('include', 'search=ENV_VAR');
|
||||
|
||||
// All Results
|
||||
variablesPage.getters.searchBar().clear().type('ENV');
|
||||
variablesPage.getters.variablesRows().should('have.length', 3);
|
||||
cy.url().should('include', 'search=ENV');
|
||||
|
||||
// No Results
|
||||
variablesPage.getters.searchBar().clear().type('Some non-existent variable');
|
||||
variablesPage.getters.variablesRows().should('not.exist');
|
||||
cy.url().should('include', 'search=Some+non-existent+variable');
|
||||
|
||||
cy.contains('No variables found').should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,64 +0,0 @@
|
||||
import { META_KEY } from '../constants';
|
||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
|
||||
const workflowPage = new WorkflowPageClass();
|
||||
|
||||
describe('Canvas Actions', () => {
|
||||
beforeEach(() => {
|
||||
workflowPage.actions.visit();
|
||||
cy.get('#collapse-change-button').should('be.visible').click();
|
||||
cy.get('#side-menu[class*=collapsed i]').should('be.visible');
|
||||
workflowPage.actions.zoomToFit();
|
||||
});
|
||||
|
||||
it('adds sticky to canvas with default text and position', () => {
|
||||
workflowPage.getters.addStickyButton().should('be.visible');
|
||||
|
||||
addDefaultSticky();
|
||||
workflowPage.actions.deselectAll();
|
||||
workflowPage.actions.addStickyFromContextMenu();
|
||||
workflowPage.actions.hitAddSticky();
|
||||
|
||||
workflowPage.getters.stickies().should('have.length', 3);
|
||||
|
||||
// Should not add a sticky for ctrl+shift+s
|
||||
cy.get('body').type(`{${META_KEY}+shift+s}`);
|
||||
|
||||
workflowPage.getters.stickies().should('have.length', 3);
|
||||
workflowPage.getters
|
||||
.stickies()
|
||||
.eq(0)
|
||||
.should('have.text', 'I’m a note\nDouble click to edit me. Guide\n')
|
||||
.find('a')
|
||||
.contains('Guide')
|
||||
.should('have.attr', 'href');
|
||||
});
|
||||
});
|
||||
|
||||
function shouldHaveOneSticky() {
|
||||
workflowPage.getters.stickies().should('have.length', 1);
|
||||
}
|
||||
|
||||
function shouldBeInDefaultLocation() {
|
||||
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() {
|
||||
workflowPage.actions.addSticky();
|
||||
shouldHaveOneSticky();
|
||||
shouldHaveDefaultSize();
|
||||
shouldBeInDefaultLocation();
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import { WorkflowPage, NDV } from '../pages';
|
||||
import { NodeCreator } from '../pages/features/node-creator';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
const nodeCreatorFeature = new NodeCreator();
|
||||
const ndv = new NDV();
|
||||
|
||||
describe('HTTP Request node', () => {
|
||||
beforeEach(() => {
|
||||
workflowPage.actions.visit();
|
||||
});
|
||||
|
||||
it('should make a request with a URL and receive a response', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Manual');
|
||||
workflowPage.actions.addNodeToCanvas('HTTP Request');
|
||||
workflowPage.actions.openNode('HTTP Request');
|
||||
ndv.actions.typeIntoParameterInput('url', 'https://catfact.ninja/fact');
|
||||
|
||||
ndv.actions.execute();
|
||||
|
||||
ndv.getters.outputPanel().contains('fact');
|
||||
});
|
||||
|
||||
describe('Credential-only HTTP Request Node variants', () => {
|
||||
it('should render a modified HTTP Request Node', () => {
|
||||
workflowPage.actions.addInitialNodeToCanvas('Manual');
|
||||
|
||||
workflowPage.getters.nodeCreatorPlusButton().click();
|
||||
workflowPage.getters.nodeCreatorSearchBar().type('VirusTotal');
|
||||
|
||||
expect(nodeCreatorFeature.getters.nodeItemName().first().should('have.text', 'VirusTotal'));
|
||||
expect(
|
||||
nodeCreatorFeature.getters
|
||||
.nodeItemDescription()
|
||||
.first()
|
||||
.should('have.text', 'HTTP request'),
|
||||
);
|
||||
|
||||
nodeCreatorFeature.actions.selectNode('VirusTotal');
|
||||
expect(ndv.getters.nodeNameContainer().should('contain.text', 'VirusTotal HTTP Request'));
|
||||
expect(
|
||||
ndv.getters
|
||||
.parameterInput('url')
|
||||
.find('input')
|
||||
.should('contain.value', 'https://www.virustotal.com/api/v3/'),
|
||||
);
|
||||
|
||||
// These parameters exist for normal HTTP Request Node, but are hidden for credential-only variants
|
||||
expect(ndv.getters.parameterInput('authentication').should('not.exist'));
|
||||
expect(ndv.getters.parameterInput('nodeCredentialType').should('not.exist'));
|
||||
|
||||
expect(
|
||||
workflowPage.getters
|
||||
.nodeCredentialsLabel()
|
||||
.should('contain.text', 'Credential for VirusTotal'),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user