test: Migrate cypress tests batch 2 to playwright (#19589)

This commit is contained in:
Artem Sorokin
2025-09-16 16:25:22 +02:00
committed by GitHub
parent a4fc24371d
commit a7f4e3e323
9 changed files with 1300 additions and 326 deletions

View File

@@ -0,0 +1,146 @@
import { test, expect } from '../../fixtures/base';
import type { n8nPage } from '../../pages/n8nPage';
test.describe('Data transformation expressions', () => {
test.beforeEach(async ({ n8n }) => {
await n8n.goHome();
});
async function addEditFields(n8n: n8nPage): Promise<void> {
await n8n.canvas.addNode('Edit Fields (Set)');
await n8n.ndv.getAssignmentCollectionAdd('assignments').click();
// Switch assignment value to Expression mode
const assignmentValue = n8n.ndv.getAssignmentValue('assignments');
await assignmentValue.locator('text=Expression').click();
}
test('$json + native string methods', async ({ n8n }) => {
await n8n.workflows.clickAddWorkflowButton();
await n8n.canvas.addNode('Schedule Trigger');
await n8n.ndv.setPinnedData([{ myStr: 'Monday' }]);
await n8n.ndv.close();
await addEditFields(n8n);
const input = '{{$json.myStr.toLowerCase() + " is " + "today".toUpperCase()}}';
const output = 'monday is TODAY';
await n8n.ndv.clearExpressionEditor();
await n8n.ndv.typeInExpressionEditor(input);
await expect(n8n.ndv.getInlineExpressionEditorOutput()).toContainText(output);
// Execute and verify output
await n8n.ndv.execute();
await expect(n8n.ndv.getOutputDataContainer()).toBeVisible();
await expect(n8n.ndv.getOutputDataContainer()).toContainText(output);
});
test('$json + n8n string methods', async ({ n8n }) => {
await n8n.workflows.clickAddWorkflowButton();
await n8n.canvas.addNode('Schedule Trigger');
await n8n.ndv.setPinnedData([{ myStr: 'hello@n8n.io is an email' }]);
await n8n.ndv.close();
await addEditFields(n8n);
const input = '{{$json.myStr.extractEmail() + " " + $json.myStr.isEmpty()}}';
const output = 'hello@n8n.io false';
await n8n.ndv.clearExpressionEditor();
await n8n.ndv.typeInExpressionEditor(input);
await expect(n8n.ndv.getInlineExpressionEditorOutput()).toContainText(output);
await n8n.ndv.execute();
await expect(n8n.ndv.getOutputDataContainer()).toBeVisible();
await expect(n8n.ndv.getOutputDataContainer()).toContainText(output);
});
test('$json + native numeric methods', async ({ n8n }) => {
await n8n.workflows.clickAddWorkflowButton();
await n8n.canvas.addNode('Schedule Trigger');
await n8n.ndv.setPinnedData([{ myNum: 9.123 }]);
await n8n.ndv.close();
await addEditFields(n8n);
const input = '{{$json.myNum.toPrecision(3)}}';
const output = '9.12';
await n8n.ndv.clearExpressionEditor();
await n8n.ndv.typeInExpressionEditor(input);
await expect(n8n.ndv.getInlineExpressionEditorOutput()).toContainText(output);
await n8n.ndv.execute();
await expect(n8n.ndv.getOutputDataContainer()).toBeVisible();
await expect(n8n.ndv.getOutputDataContainer()).toContainText(output);
});
test('$json + n8n numeric methods', async ({ n8n }) => {
await n8n.workflows.clickAddWorkflowButton();
await n8n.canvas.addNode('Schedule Trigger');
await n8n.ndv.setPinnedData([{ myStr: 'hello@n8n.io is an email' }]);
await n8n.ndv.close();
await addEditFields(n8n);
const input = '{{$json.myStr.extractEmail() + " " + $json.myStr.isEmpty()}}';
const output = 'hello@n8n.io false';
await n8n.ndv.clearExpressionEditor();
await n8n.ndv.typeInExpressionEditor(input);
await expect(n8n.ndv.getInlineExpressionEditorOutput()).toContainText(output);
await n8n.ndv.execute();
await expect(n8n.ndv.getOutputDataContainer()).toBeVisible();
await expect(n8n.ndv.getOutputDataContainer()).toContainText(output);
});
test('$json + native array access', async ({ n8n }) => {
await n8n.workflows.clickAddWorkflowButton();
await n8n.canvas.addNode('Schedule Trigger');
await n8n.ndv.setPinnedData([{ myArr: [1, 2, 3] }]);
await n8n.ndv.close();
await addEditFields(n8n);
const input = '{{$json.myArr.includes(1) + " " + $json.myArr[2]}}';
const output = 'true 3';
await n8n.ndv.clearExpressionEditor();
await n8n.ndv.typeInExpressionEditor(input);
await expect(n8n.ndv.getInlineExpressionEditorOutput()).toContainText(output);
await n8n.ndv.execute();
const valueElements = n8n.ndv.getOutputDataContainer().locator('[class*=value_]');
await expect(valueElements).toBeVisible();
await expect(valueElements).toContainText(output);
});
test('$json + n8n array methods', async ({ n8n }) => {
await n8n.workflows.clickAddWorkflowButton();
await n8n.canvas.addNode('Schedule Trigger');
await n8n.ndv.setPinnedData([{ myArr: [1, 2, 3] }]);
await n8n.ndv.close();
await addEditFields(n8n);
const input = '{{$json.myArr.first() + " " + $json.myArr.last()}}';
const output = '1 3';
await n8n.ndv.clearExpressionEditor();
await n8n.ndv.typeInExpressionEditor(input);
await expect(n8n.ndv.getInlineExpressionEditorOutput()).toContainText(output);
await n8n.ndv.execute();
const valueElements = n8n.ndv.getOutputDataContainer().locator('[class*=value_]');
await expect(valueElements).toBeVisible();
await expect(valueElements).toContainText(output);
});
});

View File

@@ -0,0 +1,115 @@
import { test, expect } from '../../fixtures/base';
test.describe('Node IO Filter', () => {
test.beforeEach(async ({ n8n }) => {
await n8n.start.fromImportedWorkflow('Node_IO_filter.json');
await n8n.canvas.clickExecuteWorkflowButton();
});
test('should filter pinned data', async ({ n8n }) => {
const canvasNodes = n8n.canvas.getCanvasNodes();
await canvasNodes.first().dblclick();
await n8n.ndv.close();
await canvasNodes.first().dblclick();
await expect(n8n.ndv.outputPanel.getDataContainer()).toBeVisible();
const searchInput = n8n.ndv.outputPanel.getSearchInput();
await expect(searchInput).toBeVisible();
await n8n.page.keyboard.press('/');
await expect(searchInput).toBeFocused();
const pagination = n8n.ndv.getOutputPagination();
await expect(pagination.locator('li')).toHaveCount(3);
await expect(n8n.ndv.outputPanel.getDataContainer().locator('mark')).toHaveCount(0);
await searchInput.fill('ar');
await expect(pagination.locator('li')).toHaveCount(2);
const markCount1 = await n8n.ndv.outputPanel.getDataContainer().locator('mark').count();
expect(markCount1).toBeGreaterThan(0);
await searchInput.fill('ari');
await expect(pagination).toBeHidden();
const markCount2 = await n8n.ndv.outputPanel.getDataContainer().locator('mark').count();
expect(markCount2).toBeGreaterThan(0);
});
test('should filter input/output data separately', async ({ n8n }) => {
const canvasNodes = n8n.canvas.getCanvasNodes();
await canvasNodes.nth(1).dblclick();
await expect(n8n.ndv.outputPanel.getDataContainer()).toBeVisible();
await expect(n8n.ndv.inputPanel.getDataContainer()).toBeVisible();
await n8n.ndv.inputPanel.switchDisplayMode('table');
await expect(n8n.ndv.outputPanel.getSearchInput()).toBeVisible();
await n8n.page.keyboard.press('/');
await expect(n8n.ndv.outputPanel.getSearchInput()).not.toBeFocused();
const inputSearchInput = n8n.ndv.inputPanel.getSearchInput();
await expect(inputSearchInput).toBeFocused();
const getInputPagination = () => n8n.ndv.inputPanel.get().getByTestId('ndv-data-pagination');
const getInputCounter = () => n8n.ndv.inputPanel.getItemsCount();
const getOutputPagination = () => n8n.ndv.outputPanel.get().getByTestId('ndv-data-pagination');
const getOutputCounter = () => n8n.ndv.outputPanel.getItemsCount();
await expect(getInputPagination().locator('li')).toHaveCount(3);
await expect(getInputCounter()).toContainText('21 items');
await expect(getOutputPagination().locator('li')).toHaveCount(3);
await expect(getOutputCounter()).toContainText('21 items');
await inputSearchInput.fill('ar');
await expect(getInputPagination().locator('li')).toHaveCount(2);
await expect(getInputCounter()).toContainText('14 of 21 items');
await expect(getOutputPagination().locator('li')).toHaveCount(3);
await expect(getOutputCounter()).toContainText('21 items');
await inputSearchInput.fill('ari');
await expect(getInputPagination()).toBeHidden();
await expect(getInputCounter()).toContainText('8 of 21 items');
await expect(getOutputPagination().locator('li')).toHaveCount(3);
await expect(getOutputCounter()).toContainText('21 items');
await inputSearchInput.clear();
await expect(getInputPagination().locator('li')).toHaveCount(3);
await expect(getInputCounter()).toContainText('21 items');
await expect(getOutputPagination().locator('li')).toHaveCount(3);
await expect(getOutputCounter()).toContainText('21 items');
await n8n.ndv.outputPanel.getDataContainer().click();
await n8n.page.keyboard.press('/');
await expect(n8n.ndv.inputPanel.getSearchInput()).not.toBeFocused();
const outputSearchInput = n8n.ndv.outputPanel.getSearchInput();
await expect(outputSearchInput).toBeFocused();
await expect(getInputPagination().locator('li')).toHaveCount(3);
await expect(getInputCounter()).toContainText('21 items');
await expect(getOutputPagination().locator('li')).toHaveCount(3);
await expect(getOutputCounter()).toContainText('21 items');
await outputSearchInput.fill('ar');
await expect(getInputPagination().locator('li')).toHaveCount(3);
await expect(getInputCounter()).toContainText('21 items');
await expect(getOutputPagination().locator('li')).toHaveCount(2);
await expect(getOutputCounter()).toContainText('14 of 21 items');
await outputSearchInput.fill('ari');
await expect(getInputPagination().locator('li')).toHaveCount(3);
await expect(getInputCounter()).toContainText('21 items');
await expect(getOutputPagination()).toBeHidden();
await expect(getOutputCounter()).toContainText('8 of 21 items');
await outputSearchInput.clear();
await expect(getInputPagination().locator('li')).toHaveCount(3);
await expect(getInputCounter()).toContainText('21 items');
await expect(getOutputPagination().locator('li')).toHaveCount(3);
await expect(getOutputCounter()).toContainText('21 items');
});
});

View File

@@ -0,0 +1,102 @@
import { test, expect } from '../../fixtures/base';
const WORKFLOW_FILE = 'Subworkflow-debugging-execute-workflow.json';
test.describe('Subworkflow debugging', () => {
test.beforeEach(async ({ n8n }) => {
await n8n.start.fromImportedWorkflow(WORKFLOW_FILE);
await expect(n8n.canvas.getCanvasNodes()).toHaveCount(11);
await n8n.canvas.clickZoomToFitButton();
await n8n.canvas.clickExecuteWorkflowButton();
});
test.describe('can inspect sub executed workflow', () => {
test('(Run once with all items/ Wait for Sub-workflow completion) (default behavior)', async ({
n8n,
}) => {
await n8n.canvas.openNode('Execute Workflow with param');
await expect(n8n.ndv.outputPanel.getItemsCount()).toContainText('2 items, 1 sub-execution');
await expect(n8n.ndv.outputPanel.getRelatedExecutionLink()).toContainText(
'View sub-execution',
);
await expect(n8n.ndv.outputPanel.getRelatedExecutionLink()).toHaveAttribute('href', /.+/);
await expect(n8n.ndv.outputPanel.getTableHeaders()).toHaveCount(2);
await expect(n8n.ndv.outputPanel.getTbodyCell(0, 0)).toHaveText('world Natalie Moore');
});
test('(Run once for each item/ Wait for Sub-workflow completion) param1', async ({ n8n }) => {
await n8n.canvas.openNode('Execute Workflow with param1');
await expect(n8n.ndv.outputPanel.getItemsCount()).toContainText('2 items, 2 sub-execution');
await expect(n8n.ndv.outputPanel.getRelatedExecutionLink()).not.toBeAttached();
await expect(n8n.ndv.outputPanel.getTableHeaders()).toHaveCount(3);
await expect(n8n.ndv.outputPanel.getTbodyCell(0, 0).locator('a')).toHaveAttribute(
'href',
/.+/,
);
await expect(n8n.ndv.outputPanel.getTbodyCell(0, 1)).toHaveText('world Natalie Moore');
});
test('(Run once with all items/ Wait for Sub-workflow completion) param2', async ({ n8n }) => {
await n8n.canvas.openNode('Execute Workflow with param2');
await expect(n8n.ndv.outputPanel.getItemsCount()).not.toBeAttached();
await expect(n8n.ndv.outputPanel.getRelatedExecutionLink()).toContainText(
'View sub-execution',
);
await expect(n8n.ndv.outputPanel.getRelatedExecutionLink()).toHaveAttribute('href', /.+/);
await expect(n8n.ndv.outputPanel.getRunSelectorInput()).toHaveValue(
'2 of 2 (3 items, 1 sub-execution)',
);
await expect(n8n.ndv.outputPanel.getTableHeaders()).toHaveCount(6);
await expect(n8n.ndv.outputPanel.getTableHeader(0)).toHaveText('uid');
await expect(n8n.ndv.outputPanel.getTableRows()).toHaveCount(4);
await expect(n8n.ndv.outputPanel.getTbodyCell(0, 1)).toContainText('Jon_Ebert@yahoo.com');
await n8n.ndv.changeOutputRunSelector('1 of 2 (2 items, 1 sub-execution)');
await expect(n8n.ndv.outputPanel.getRunSelectorInput()).toHaveValue(
'1 of 2 (2 items, 1 sub-execution)',
);
await expect(n8n.ndv.outputPanel.getTableHeaders()).toHaveCount(6);
await expect(n8n.ndv.outputPanel.getTableHeader(0)).toHaveText('uid');
await expect(n8n.ndv.outputPanel.getTableRows()).toHaveCount(3);
await expect(n8n.ndv.outputPanel.getTbodyCell(0, 1)).toContainText('Terry.Dach@hotmail.com');
});
test('(Run once for each item/ Wait for Sub-workflow completion) param3', async ({ n8n }) => {
await n8n.canvas.openNode('Execute Workflow with param3');
await expect(n8n.ndv.outputPanel.getRunSelectorInput()).toHaveValue(
'2 of 2 (3 items, 3 sub-executions)',
);
await expect(n8n.ndv.outputPanel.getTableHeaders()).toHaveCount(7);
await expect(n8n.ndv.outputPanel.getTableHeader(1)).toHaveText('uid');
await expect(n8n.ndv.outputPanel.getTableRows()).toHaveCount(4);
await expect(n8n.ndv.outputPanel.getTbodyCell(0, 0).locator('a')).toHaveAttribute(
'href',
/.+/,
);
await expect(n8n.ndv.outputPanel.getTbodyCell(0, 2)).toContainText('Jon_Ebert@yahoo.com');
await n8n.ndv.changeOutputRunSelector('1 of 2 (2 items, 2 sub-executions)');
await expect(n8n.ndv.outputPanel.getRunSelectorInput()).toHaveValue(
'1 of 2 (2 items, 2 sub-executions)',
);
await expect(n8n.ndv.outputPanel.getTableHeaders()).toHaveCount(7);
await expect(n8n.ndv.outputPanel.getTableHeader(1)).toHaveText('uid');
await expect(n8n.ndv.outputPanel.getTableRows()).toHaveCount(3);
await expect(n8n.ndv.outputPanel.getTbodyCell(0, 0).locator('a')).toHaveAttribute(
'href',
/.+/,
);
await expect(n8n.ndv.outputPanel.getTbodyCell(0, 2)).toContainText('Terry.Dach@hotmail.com');
});
});
});