Skip to content

Commit b62c957

Browse files
authored
test: Add Edit Fields node helper and tests (n8n-io#18726)
1 parent 0a4c3c3 commit b62c957

File tree

3 files changed

+163
-0
lines changed

3 files changed

+163
-0
lines changed

packages/testing/playwright/pages/NodeDetailsViewPage.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@ import { expect } from '@playwright/test';
33

44
import { BasePage } from './BasePage';
55
import { NodeParameterHelper } from '../helpers/NodeParameterHelper';
6+
import { EditFieldsNode } from './nodes/EditFieldsNode';
67

78
export class NodeDetailsViewPage extends BasePage {
89
readonly setupHelper: NodeParameterHelper;
10+
readonly editFields: EditFieldsNode;
911

1012
constructor(page: Page) {
1113
super(page);
1214
this.setupHelper = new NodeParameterHelper(this);
15+
this.editFields = new EditFieldsNode(page);
1316
}
1417

1518
async clickBackToCanvasButton() {
@@ -443,4 +446,15 @@ export class NodeDetailsViewPage extends BasePage {
443446
);
444447
}
445448
}
449+
450+
getAssignmentCollectionContainer(paramName: string) {
451+
return this.page.getByTestId(`assignment-collection-${paramName}`);
452+
}
453+
454+
getAssignmentName(paramName: string, index = 0) {
455+
return this.getAssignmentCollectionContainer(paramName)
456+
.getByTestId('assignment')
457+
.nth(index)
458+
.getByTestId('assignment-name');
459+
}
446460
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import type { Locator, Page } from '@playwright/test';
2+
3+
import { BasePage } from '../BasePage';
4+
5+
export class EditFieldsNode extends BasePage {
6+
constructor(page: Page) {
7+
super(page);
8+
}
9+
10+
async setFieldsValues(
11+
fields: Array<{
12+
name: string;
13+
type: 'string' | 'number' | 'boolean' | 'array' | 'object';
14+
value: string | number | boolean;
15+
}>,
16+
paramName = 'assignments',
17+
): Promise<void> {
18+
const container = this.page.getByTestId(`assignment-collection-${paramName}`);
19+
20+
for (let i = 0; i < fields.length; i++) {
21+
await this.ensureFieldExists(container, i);
22+
const assignment = container.getByTestId('assignment').nth(i);
23+
24+
await this.setFieldName(assignment, fields[i].name);
25+
await this.setFieldType(assignment, fields[i].type);
26+
await this.setFieldValue(assignment, fields[i].type, fields[i].value);
27+
}
28+
}
29+
30+
async setSingleFieldValue(
31+
name: string,
32+
type: 'string' | 'number' | 'boolean' | 'array' | 'object',
33+
value: string | number | boolean,
34+
paramName = 'assignments',
35+
): Promise<void> {
36+
await this.setFieldsValues([{ name, type, value }], paramName);
37+
}
38+
39+
private async ensureFieldExists(container: Locator, index: number): Promise<void> {
40+
if (index > 0) {
41+
await container.getByTestId('assignment-collection-drop-area').click();
42+
await container.getByTestId('assignment').nth(index).waitFor({ state: 'visible' });
43+
} else {
44+
const existingFields = await container.getByTestId('assignment').count();
45+
if (existingFields === 0) {
46+
await container.getByTestId('assignment-collection-drop-area').click();
47+
await container.getByTestId('assignment').first().waitFor({ state: 'visible' });
48+
}
49+
}
50+
}
51+
52+
private async setFieldName(assignment: Locator, name: string): Promise<void> {
53+
const nameInput = assignment.getByTestId('assignment-name').getByRole('textbox');
54+
await nameInput.waitFor({ state: 'visible' });
55+
await nameInput.fill(name);
56+
await nameInput.blur();
57+
}
58+
59+
private async setFieldType(assignment: Locator, type: string): Promise<void> {
60+
const typeSelect = assignment.getByTestId('assignment-type-select');
61+
await typeSelect.waitFor({ state: 'visible' });
62+
await typeSelect.click();
63+
64+
const typeOptionText = this.getTypeOptionText(type);
65+
const option = this.page.getByRole('option', { name: typeOptionText });
66+
await option.waitFor({ state: 'visible' });
67+
await option.click();
68+
}
69+
70+
private async setFieldValue(
71+
assignment: Locator,
72+
type: string,
73+
value: string | number | boolean,
74+
): Promise<void> {
75+
const valueContainer = assignment.getByTestId('assignment-value');
76+
await valueContainer.waitFor({ state: 'visible' });
77+
78+
if (type === 'boolean') {
79+
await this.setBooleanValue(valueContainer, value as boolean);
80+
} else {
81+
await this.setTextValue(valueContainer, String(value));
82+
}
83+
}
84+
85+
private getTypeOptionText(type: string): string {
86+
const typeMap = new Map([
87+
['string', 'String'],
88+
['number', 'Number'],
89+
['boolean', 'Boolean'],
90+
['array', 'Array'],
91+
['object', 'Object'],
92+
]);
93+
return typeMap.get(type) ?? 'String';
94+
}
95+
96+
private async setTextValue(valueContainer: Locator, value: string): Promise<void> {
97+
const input = valueContainer
98+
.getByRole('textbox')
99+
.or(valueContainer.locator('input, textarea, [contenteditable]').first());
100+
await input.waitFor({ state: 'visible' });
101+
await input.fill(value);
102+
}
103+
104+
private async setBooleanValue(valueContainer: Locator, value: boolean): Promise<void> {
105+
await valueContainer.click();
106+
const booleanValue = value ? 'True' : 'False';
107+
const option = this.page.getByRole('option', { name: booleanValue });
108+
await option.waitFor({ state: 'visible' });
109+
await option.click();
110+
}
111+
}

packages/testing/playwright/tests/ui/building-blocks/03-node-details-configuration.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,42 @@ test.describe('03 - Node Details Configuration', () => {
4747

4848
await expect(n8n.ndv.getParameterInputField('path')).toHaveValue('explicit-types');
4949
});
50+
51+
test('should configure Edit Fields node with single field', async ({ n8n }) => {
52+
await n8n.canvas.addNode('Edit Fields (Set)');
53+
54+
await n8n.ndv.editFields.setSingleFieldValue('testField', 'string', 'Hello World');
55+
56+
const nameInput = n8n.ndv.getAssignmentName('assignments', 0).getByRole('textbox');
57+
await expect(nameInput).toHaveValue('testField');
58+
});
59+
60+
test('should configure Edit Fields node with multiple fields', async ({ n8n }) => {
61+
await n8n.canvas.addNode('Edit Fields (Set)');
62+
63+
await n8n.ndv.editFields.setFieldsValues([
64+
{ name: 'stringField', type: 'string', value: 'Test String' },
65+
{ name: 'numberField', type: 'number', value: 123 },
66+
{ name: 'booleanField', type: 'boolean', value: true },
67+
]);
68+
69+
await expect(
70+
n8n.ndv.getAssignmentCollectionContainer('assignments').getByTestId('assignment'),
71+
).toHaveCount(3);
72+
});
73+
74+
test('should configure Edit Fields node with all field types', async ({ n8n }) => {
75+
await n8n.canvas.addNode('Edit Fields (Set)');
76+
77+
await n8n.ndv.editFields.setFieldsValues([
78+
{ name: 'myString', type: 'string', value: 'Hello' },
79+
{ name: 'myNumber', type: 'number', value: 42 },
80+
{ name: 'myBoolean', type: 'boolean', value: false },
81+
{ name: 'myArray', type: 'array', value: '["item1", "item2"]' },
82+
]);
83+
84+
await expect(
85+
n8n.ndv.getAssignmentCollectionContainer('assignments').getByTestId('assignment'),
86+
).toHaveCount(4);
87+
});
5088
});

0 commit comments

Comments
 (0)