test: Migrate expression editor modal tests from cypress -> playwright (#19535)

This commit is contained in:
Elias Meire
2025-09-15 11:43:09 +02:00
committed by GitHub
parent b5adcc8f9f
commit a5c18c3c2d
3 changed files with 142 additions and 146 deletions

View File

@@ -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);

View File

@@ -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');
});
});
});