diff --git a/packages/testing/playwright/pages/NodeDetailsViewPage.ts b/packages/testing/playwright/pages/NodeDetailsViewPage.ts index 69585a1624..267ce73398 100644 --- a/packages/testing/playwright/pages/NodeDetailsViewPage.ts +++ b/packages/testing/playwright/pages/NodeDetailsViewPage.ts @@ -3,13 +3,16 @@ import { expect } from '@playwright/test'; import { BasePage } from './BasePage'; import { NodeParameterHelper } from '../helpers/NodeParameterHelper'; +import { EditFieldsNode } from './nodes/EditFieldsNode'; export class NodeDetailsViewPage extends BasePage { readonly setupHelper: NodeParameterHelper; + readonly editFields: EditFieldsNode; constructor(page: Page) { super(page); this.setupHelper = new NodeParameterHelper(this); + this.editFields = new EditFieldsNode(page); } async clickBackToCanvasButton() { @@ -443,4 +446,15 @@ export class NodeDetailsViewPage extends BasePage { ); } } + + getAssignmentCollectionContainer(paramName: string) { + return this.page.getByTestId(`assignment-collection-${paramName}`); + } + + getAssignmentName(paramName: string, index = 0) { + return this.getAssignmentCollectionContainer(paramName) + .getByTestId('assignment') + .nth(index) + .getByTestId('assignment-name'); + } } diff --git a/packages/testing/playwright/pages/nodes/EditFieldsNode.ts b/packages/testing/playwright/pages/nodes/EditFieldsNode.ts new file mode 100644 index 0000000000..fd9de5973d --- /dev/null +++ b/packages/testing/playwright/pages/nodes/EditFieldsNode.ts @@ -0,0 +1,111 @@ +import type { Locator, Page } from '@playwright/test'; + +import { BasePage } from '../BasePage'; + +export class EditFieldsNode extends BasePage { + constructor(page: Page) { + super(page); + } + + async setFieldsValues( + fields: Array<{ + name: string; + type: 'string' | 'number' | 'boolean' | 'array' | 'object'; + value: string | number | boolean; + }>, + paramName = 'assignments', + ): Promise { + const container = this.page.getByTestId(`assignment-collection-${paramName}`); + + for (let i = 0; i < fields.length; i++) { + await this.ensureFieldExists(container, i); + const assignment = container.getByTestId('assignment').nth(i); + + await this.setFieldName(assignment, fields[i].name); + await this.setFieldType(assignment, fields[i].type); + await this.setFieldValue(assignment, fields[i].type, fields[i].value); + } + } + + async setSingleFieldValue( + name: string, + type: 'string' | 'number' | 'boolean' | 'array' | 'object', + value: string | number | boolean, + paramName = 'assignments', + ): Promise { + await this.setFieldsValues([{ name, type, value }], paramName); + } + + private async ensureFieldExists(container: Locator, index: number): Promise { + if (index > 0) { + await container.getByTestId('assignment-collection-drop-area').click(); + await container.getByTestId('assignment').nth(index).waitFor({ state: 'visible' }); + } else { + const existingFields = await container.getByTestId('assignment').count(); + if (existingFields === 0) { + await container.getByTestId('assignment-collection-drop-area').click(); + await container.getByTestId('assignment').first().waitFor({ state: 'visible' }); + } + } + } + + private async setFieldName(assignment: Locator, name: string): Promise { + const nameInput = assignment.getByTestId('assignment-name').getByRole('textbox'); + await nameInput.waitFor({ state: 'visible' }); + await nameInput.fill(name); + await nameInput.blur(); + } + + private async setFieldType(assignment: Locator, type: string): Promise { + const typeSelect = assignment.getByTestId('assignment-type-select'); + await typeSelect.waitFor({ state: 'visible' }); + await typeSelect.click(); + + const typeOptionText = this.getTypeOptionText(type); + const option = this.page.getByRole('option', { name: typeOptionText }); + await option.waitFor({ state: 'visible' }); + await option.click(); + } + + private async setFieldValue( + assignment: Locator, + type: string, + value: string | number | boolean, + ): Promise { + const valueContainer = assignment.getByTestId('assignment-value'); + await valueContainer.waitFor({ state: 'visible' }); + + if (type === 'boolean') { + await this.setBooleanValue(valueContainer, value as boolean); + } else { + await this.setTextValue(valueContainer, String(value)); + } + } + + private getTypeOptionText(type: string): string { + const typeMap = new Map([ + ['string', 'String'], + ['number', 'Number'], + ['boolean', 'Boolean'], + ['array', 'Array'], + ['object', 'Object'], + ]); + return typeMap.get(type) ?? 'String'; + } + + private async setTextValue(valueContainer: Locator, value: string): Promise { + const input = valueContainer + .getByRole('textbox') + .or(valueContainer.locator('input, textarea, [contenteditable]').first()); + await input.waitFor({ state: 'visible' }); + await input.fill(value); + } + + private async setBooleanValue(valueContainer: Locator, value: boolean): Promise { + await valueContainer.click(); + const booleanValue = value ? 'True' : 'False'; + const option = this.page.getByRole('option', { name: booleanValue }); + await option.waitFor({ state: 'visible' }); + await option.click(); + } +} diff --git a/packages/testing/playwright/tests/ui/building-blocks/03-node-details-configuration.spec.ts b/packages/testing/playwright/tests/ui/building-blocks/03-node-details-configuration.spec.ts index a6deda8bd0..1256c3d423 100644 --- a/packages/testing/playwright/tests/ui/building-blocks/03-node-details-configuration.spec.ts +++ b/packages/testing/playwright/tests/ui/building-blocks/03-node-details-configuration.spec.ts @@ -47,4 +47,42 @@ test.describe('03 - Node Details Configuration', () => { await expect(n8n.ndv.getParameterInputField('path')).toHaveValue('explicit-types'); }); + + test('should configure Edit Fields node with single field', async ({ n8n }) => { + await n8n.canvas.addNode('Edit Fields (Set)'); + + await n8n.ndv.editFields.setSingleFieldValue('testField', 'string', 'Hello World'); + + const nameInput = n8n.ndv.getAssignmentName('assignments', 0).getByRole('textbox'); + await expect(nameInput).toHaveValue('testField'); + }); + + test('should configure Edit Fields node with multiple fields', async ({ n8n }) => { + await n8n.canvas.addNode('Edit Fields (Set)'); + + await n8n.ndv.editFields.setFieldsValues([ + { name: 'stringField', type: 'string', value: 'Test String' }, + { name: 'numberField', type: 'number', value: 123 }, + { name: 'booleanField', type: 'boolean', value: true }, + ]); + + await expect( + n8n.ndv.getAssignmentCollectionContainer('assignments').getByTestId('assignment'), + ).toHaveCount(3); + }); + + test('should configure Edit Fields node with all field types', async ({ n8n }) => { + await n8n.canvas.addNode('Edit Fields (Set)'); + + await n8n.ndv.editFields.setFieldsValues([ + { name: 'myString', type: 'string', value: 'Hello' }, + { name: 'myNumber', type: 'number', value: 42 }, + { name: 'myBoolean', type: 'boolean', value: false }, + { name: 'myArray', type: 'array', value: '["item1", "item2"]' }, + ]); + + await expect( + n8n.ndv.getAssignmentCollectionContainer('assignments').getByTestId('assignment'), + ).toHaveCount(4); + }); });