mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 09:36:44 +00:00
test: Migrate expression editor modal tests from cypress -> playwright (#19535)
This commit is contained in:
@@ -1,146 +0,0 @@
|
||||
import { META_KEY } from '../constants';
|
||||
import { NDV } from '../pages/ndv';
|
||||
import { successToast } from '../pages/notifications';
|
||||
import { WorkflowPage as WorkflowPageClass } from '../pages/workflow';
|
||||
|
||||
const WorkflowPage = new WorkflowPageClass();
|
||||
const ndv = new NDV();
|
||||
|
||||
describe('Expression editor modal', () => {
|
||||
beforeEach(() => {
|
||||
WorkflowPage.actions.visit();
|
||||
WorkflowPage.actions.addInitialNodeToCanvas('Schedule');
|
||||
cy.on('uncaught:exception', (error) => error.name !== 'ExpressionError');
|
||||
});
|
||||
|
||||
describe('Keybinds', () => {
|
||||
beforeEach(() => {
|
||||
WorkflowPage.actions.addNodeToCanvas('Hacker News');
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
WorkflowPage.actions.openNode('Get many items');
|
||||
WorkflowPage.actions.openExpressionEditorModal();
|
||||
});
|
||||
|
||||
it('should save the workflow with save keybind', () => {
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ "hello"');
|
||||
WorkflowPage.getters.expressionModalOutput().contains('hello');
|
||||
WorkflowPage.getters.expressionModalInput().click().type(`{${META_KEY}+s}`);
|
||||
successToast().should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Static data', () => {
|
||||
beforeEach(() => {
|
||||
WorkflowPage.actions.addNodeToCanvas('Hacker News');
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
WorkflowPage.actions.openNode('Get many items');
|
||||
WorkflowPage.actions.openExpressionEditorModal();
|
||||
});
|
||||
|
||||
it('should resolve primitive resolvables', () => {
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ 1 + 2');
|
||||
WorkflowPage.getters.expressionModalOutput().contains(/^3$/);
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ "ab" + "cd"');
|
||||
WorkflowPage.getters.expressionModalOutput().contains(/^abcd$/);
|
||||
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ true && false');
|
||||
WorkflowPage.getters.expressionModalOutput().contains(/^false$/);
|
||||
});
|
||||
|
||||
it('should resolve object resolvables', () => {
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters
|
||||
.expressionModalInput()
|
||||
.click()
|
||||
.type('{{ { a : 1 }', { parseSpecialCharSequences: false });
|
||||
WorkflowPage.getters.expressionModalOutput().contains(/^\[Object: \{"a": 1\}\]$/);
|
||||
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
|
||||
WorkflowPage.getters
|
||||
.expressionModalInput()
|
||||
.click()
|
||||
.type('{{ { a : 1 }.a', { parseSpecialCharSequences: false });
|
||||
WorkflowPage.getters.expressionModalOutput().contains(/^1$/);
|
||||
});
|
||||
|
||||
it('should resolve array resolvables', () => {
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ [1, 2, 3]');
|
||||
WorkflowPage.getters.expressionModalOutput().contains(/^\[Array: \[1,2,3\]\]$/);
|
||||
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ [1, 2, 3][0]');
|
||||
WorkflowPage.getters.expressionModalOutput().contains(/^1$/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Dynamic data', () => {
|
||||
beforeEach(() => {
|
||||
WorkflowPage.actions.openNode('Schedule Trigger');
|
||||
ndv.actions.setPinnedData([{ myStr: 'Monday' }]);
|
||||
ndv.actions.close();
|
||||
WorkflowPage.actions.addNodeToCanvas('No Operation');
|
||||
WorkflowPage.actions.addNodeToCanvas('Hacker News');
|
||||
WorkflowPage.actions.zoomToFit();
|
||||
WorkflowPage.actions.openNode('Get many items');
|
||||
WorkflowPage.actions.openExpressionEditorModal();
|
||||
});
|
||||
|
||||
it('should resolve $parameter[]', () => {
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ $parameter["operation"]');
|
||||
WorkflowPage.getters.expressionModalOutput().should('have.text', 'getAll');
|
||||
});
|
||||
|
||||
it('should resolve input: $json,$input,$(nodeName)', () => {
|
||||
// Previous nodes have not run, input is empty
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ $json.myStr');
|
||||
WorkflowPage.getters
|
||||
.expressionModalOutput()
|
||||
.should('have.text', '[Execute previous nodes for preview]');
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ $input.item.json.myStr');
|
||||
WorkflowPage.getters
|
||||
.expressionModalOutput()
|
||||
.should('have.text', '[Execute previous nodes for preview]');
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters
|
||||
.expressionModalInput()
|
||||
.click()
|
||||
.type("{{ $('Schedule Trigger').item.json.myStr");
|
||||
WorkflowPage.getters
|
||||
.expressionModalOutput()
|
||||
.should('have.text', '[Execute previous nodes for preview]');
|
||||
|
||||
// Run workflow
|
||||
cy.get('body').type('{esc}');
|
||||
ndv.actions.close();
|
||||
WorkflowPage.actions.executeNode('No Operation, do nothing', { anchor: 'topLeft' });
|
||||
WorkflowPage.actions.openNode('Get many items');
|
||||
WorkflowPage.actions.openExpressionEditorModal();
|
||||
|
||||
// Previous nodes have run, input can be resolved
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ $json.myStr');
|
||||
WorkflowPage.getters.expressionModalOutput().should('have.text', 'Monday');
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters.expressionModalInput().click().type('{{ $input.item.json.myStr');
|
||||
WorkflowPage.getters.expressionModalOutput().should('have.text', 'Monday');
|
||||
WorkflowPage.getters.expressionModalInput().clear();
|
||||
WorkflowPage.getters
|
||||
.expressionModalInput()
|
||||
.click()
|
||||
.type("{{ $('Schedule Trigger').item.json.myStr");
|
||||
WorkflowPage.getters.expressionModalOutput().should('have.text', 'Monday');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -513,6 +513,31 @@ export class NodeDetailsViewPage extends BasePage {
|
||||
await this.getInlineExpressionEditorItemPrevButton().click();
|
||||
}
|
||||
|
||||
async openExpressionEditorModal(parameterName: string) {
|
||||
await this.activateParameterExpressionEditor(parameterName);
|
||||
const parameter = this.getParameterInput(parameterName);
|
||||
await parameter.click();
|
||||
const expander = parameter.getByTestId('expander');
|
||||
await expander.click();
|
||||
|
||||
await this.page.getByTestId('expression-modal-input').waitFor({ state: 'visible' });
|
||||
}
|
||||
|
||||
getExpressionEditorModalInput() {
|
||||
return this.page.getByTestId('expression-modal-input').getByRole('textbox');
|
||||
}
|
||||
|
||||
async fillExpressionEditorModalInput(text: string) {
|
||||
const input = this.getExpressionEditorModalInput();
|
||||
await input.clear();
|
||||
await input.click();
|
||||
await input.fill(text);
|
||||
}
|
||||
|
||||
getExpressionEditorModalOutput() {
|
||||
return this.page.getByTestId('expression-modal-output');
|
||||
}
|
||||
|
||||
async typeIntoParameterInput(parameterName: string, content: string): Promise<void> {
|
||||
const input = this.getParameterInput(parameterName);
|
||||
await input.type(content);
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
import { test, expect } from '../../fixtures/base';
|
||||
|
||||
test.describe('Expression editor modal', () => {
|
||||
test.beforeEach(async ({ n8n }) => {
|
||||
await n8n.start.fromBlankCanvas();
|
||||
await n8n.canvas.addInitialNodeToCanvas('Schedule Trigger');
|
||||
await n8n.ndv.close();
|
||||
});
|
||||
|
||||
test.describe('Keybinds', () => {
|
||||
test.beforeEach(async ({ n8n }) => {
|
||||
await n8n.canvas.addNode('Hacker News', { action: 'Get many items' });
|
||||
await n8n.ndv.openExpressionEditorModal('limit');
|
||||
});
|
||||
|
||||
test('should save the workflow with save keybind', async ({ n8n }) => {
|
||||
const input = n8n.ndv.getExpressionEditorModalInput();
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ "hello"');
|
||||
await expect(n8n.ndv.getExpressionEditorModalOutput()).toContainText('hello');
|
||||
|
||||
await input.press('ControlOrMeta+s');
|
||||
await n8n.notifications.waitForNotificationAndClose('Saved successfully');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Static data', () => {
|
||||
test.beforeEach(async ({ n8n }) => {
|
||||
await n8n.canvas.addNode('Hacker News', { action: 'Get many items' });
|
||||
await n8n.ndv.openExpressionEditorModal('limit');
|
||||
});
|
||||
|
||||
test('should resolve primitive resolvables', async ({ n8n }) => {
|
||||
const output = n8n.ndv.getExpressionEditorModalOutput();
|
||||
|
||||
// Test number addition
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ 1 + 2 }}');
|
||||
await expect(output).toContainText(/^3$/);
|
||||
|
||||
// Test string concatenation
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ "ab" + "cd" }}');
|
||||
await expect(output).toContainText(/^abcd$/);
|
||||
|
||||
// Test boolean logic
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ true && false }}');
|
||||
await expect(output).toContainText(/^false$/);
|
||||
});
|
||||
|
||||
test('should resolve object resolvables', async ({ n8n }) => {
|
||||
const output = n8n.ndv.getExpressionEditorModalOutput();
|
||||
|
||||
// Test object creation
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ { a : 1 } }}');
|
||||
await expect(output).toContainText(/^\[Object: \{"a": 1\}\]$/);
|
||||
|
||||
// Test object property access
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ { a : 1 }.a }}');
|
||||
await expect(output).toContainText(/^1$/);
|
||||
});
|
||||
|
||||
test('should resolve array resolvables', async ({ n8n }) => {
|
||||
const output = n8n.ndv.getExpressionEditorModalOutput();
|
||||
|
||||
// Test array creation
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ [1, 2, 3] }}');
|
||||
await expect(output).toContainText(/^\[Array: \[1,2,3\]\]$/);
|
||||
|
||||
// Test array element access
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ [1, 2, 3][0] }}');
|
||||
await expect(output).toContainText(/^1$/);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Dynamic data', () => {
|
||||
test.beforeEach(async ({ n8n }) => {
|
||||
await n8n.canvas.openNode('Schedule Trigger');
|
||||
await n8n.ndv.setPinnedData([{ myStr: 'Monday' }]);
|
||||
await n8n.ndv.clickBackToCanvasButton();
|
||||
await n8n.canvas.addNode('No Operation, do nothing', { closeNDV: true });
|
||||
await n8n.canvas.addNode('Hacker News', { action: 'Get many items' });
|
||||
await n8n.ndv.openExpressionEditorModal('limit');
|
||||
});
|
||||
|
||||
test('should resolve $parameter[]', async ({ n8n }) => {
|
||||
const output = n8n.ndv.getExpressionEditorModalOutput();
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ $parameter["operation"] }}');
|
||||
await expect(output).toHaveText('getAll');
|
||||
});
|
||||
|
||||
test('should resolve input: $json,$input,$(nodeName)', async ({ n8n }) => {
|
||||
const output = n8n.ndv.getExpressionEditorModalOutput();
|
||||
|
||||
// Previous nodes have not run, input is empty
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ $json.myStr }}');
|
||||
await expect(output).toHaveText('[Execute previous nodes for preview]');
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ $input.item.json.myStr }}');
|
||||
await expect(output).toHaveText('[Execute previous nodes for preview]');
|
||||
await n8n.ndv.fillExpressionEditorModalInput("{{ $('Schedule Trigger').item.json.myStr }}");
|
||||
await expect(output).toHaveText('[Execute previous nodes for preview]');
|
||||
|
||||
// Run workflow
|
||||
await output.click();
|
||||
await n8n.page.keyboard.press('Escape');
|
||||
await n8n.ndv.clickBackToCanvasButton();
|
||||
await n8n.canvas.executeNode('No Operation, do nothing');
|
||||
await n8n.canvas.openNode('Get many items');
|
||||
await n8n.ndv.openExpressionEditorModal('limit');
|
||||
|
||||
// Previous nodes have run, input can be resolved
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ $json.myStr }}');
|
||||
await expect(output).toHaveText('Monday');
|
||||
await n8n.ndv.fillExpressionEditorModalInput('{{ $input.item.json.myStr }}');
|
||||
await expect(output).toHaveText('Monday');
|
||||
await n8n.ndv.fillExpressionEditorModalInput("{{ $('Schedule Trigger').item.json.myStr }}");
|
||||
await expect(output).toHaveText('Monday');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user