Skip to content

Commit b5f257e

Browse files
Tests polished
1 parent 1ba8f5d commit b5f257e

File tree

3 files changed

+112
-54
lines changed

3 files changed

+112
-54
lines changed
Lines changed: 41 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import { test, expect } from '@playwright/test';
2-
import { expectExecutionProgressBarSucceeded, expectToMatchTimestamp } from './utils/expect';
3-
import { readFromCodeEditor, readFromCodeEditorAsJson } from './utils/editor';
4-
import { getLabeledValueText } from './utils/labeledValue';
2+
import {
3+
expectExecutionProgressBarSucceeded,
4+
expectExecutionDetails,
5+
expectExecutionTimings,
6+
expectExecutionInputs,
7+
expectExecutionOutputs,
8+
expectOutputTexts,
9+
expectOutputFileDownload,
10+
} from './utils/expect';
11+
import { readFromCodeEditor } from './utils/editor';
512

613
test.describe('Manual Scripts', () => {
714
test('Execute CSV Generation With I/O', async ({ page }) => {
@@ -10,9 +17,9 @@ test.describe('Manual Scripts', () => {
1017
await page.getByText('example/ACME-203_output-csv').click();
1118
await page.getByRole('button', { name: 'Execute' }).click();
1219

13-
await page.getByRole('textbox', { name: 'Input \'count\' Users to' }).fill('5000');
14-
await page.getByRole('textbox', { name: 'Input \'firstNames\' First names' }).fill('John\nJane\nJack\nAlice\nBob\nRobert');
15-
await page.getByRole('textbox', { name: 'Input \'lastNames\' Last names' }).fill('Doe\nSmith\nBrown\nJohnson\nWhite\nJordan');
20+
await page.getByRole('textbox', { name: 'Users to' }).fill('5000');
21+
await page.getByRole('textbox', { name: 'First names' }).fill('John\nJane\nJack\nAlice\nBob\nRobert');
22+
await page.getByRole('textbox', { name: 'Last names' }).fill('Doe\nSmith\nBrown\nJohnson\nWhite\nJordan');
1623

1724
await page.getByRole('button', { name: 'Start' }).click();
1825
await expectExecutionProgressBarSucceeded(page);
@@ -21,67 +28,50 @@ test.describe('Manual Scripts', () => {
2128
expect(output).toContain('[SUCCESS] Users CSV report generation ended successfully');
2229

2330
await page.getByRole('tab', { name: 'Details' }).click();
31+
32+
await page.waitForTimeout(1000);
33+
await page.screenshot();
2434

25-
const executionStatus = page.locator('#execution-status');
26-
const executionId = await getLabeledValueText(page, 'ID', executionStatus);
27-
expect(executionId).toMatch(/^2025\/\d+\/\d+\/\d+\/\d+\//);
28-
29-
await expect(page.getByText('admin')).toBeVisible();
30-
await expect(executionStatus.getByRole('presentation').filter({ hasText: 'succeeded' })).toBeVisible();
31-
32-
const executionTiming = page.locator('#execution-timing');
33-
const startedAt = await getLabeledValueText(page, 'Started At', executionTiming);
34-
expectToMatchTimestamp(startedAt);
35-
36-
const duration = await getLabeledValueText(page, 'Duration', executionTiming);
37-
expect(duration).toMatch(/\d+ ms \(\d+ seconds?\)/);
38-
39-
const endedAt = await getLabeledValueText(page, 'Ended At', executionTiming);
40-
expectToMatchTimestamp(endedAt);
35+
await expectExecutionDetails(page);
36+
await expectExecutionTimings(page);
4137

42-
const inputs = await readFromCodeEditorAsJson<Record<string, any>>(page, 'Execution Inputs JSON');
43-
expect(inputs).toEqual({
38+
await expectExecutionInputs(page, {
4439
count: 5000,
4540
firstNames: 'John\nJane\nJack\nAlice\nBob\nRobert',
4641
lastNames: 'Doe\nSmith\nBrown\nJohnson\nWhite\nJordan',
4742
});
4843

49-
const outputs = await readFromCodeEditorAsJson<Array<any>>(page, 'Execution Outputs JSON');
50-
expect(outputs).toHaveLength(2);
51-
expect(outputs[0]).toMatchObject({
52-
type: 'FILE',
53-
name: 'report',
54-
label: 'Report',
55-
downloadName: 'report.csv',
56-
});
57-
expect(outputs[1]).toMatchObject({
58-
type: 'TEXT',
59-
name: 'summary',
60-
value: 'Processed 5000 user(s)',
61-
});
44+
await expectExecutionOutputs(page, [
45+
{
46+
type: 'FILE',
47+
name: 'report',
48+
label: 'Report',
49+
downloadName: 'report.csv',
50+
},
51+
{
52+
type: 'TEXT',
53+
name: 'summary',
54+
value: 'Processed 5000 user(s)',
55+
},
56+
]);
6257

6358
await page.getByRole('tab', { name: 'Output' }).click();
59+
60+
await page.getByRole('button', { name: 'Review' }).click();
61+
await page.getByRole('tab', { name: 'Texts' }).click();
62+
await expectOutputTexts(page, ['Processed 5000 user(s)']);
63+
await page.getByTestId('modal').getByRole('button', { name: 'Close' }).click();
64+
6465
await page.getByRole('button', { name: 'Review' }).click();
65-
await expect(page.getByText('Processed 5000 user(s)')).toBeVisible();
6666
await page.getByRole('tab', { name: 'Files' }).click();
67-
68-
const downloadArchivePromise = page.waitForEvent('download');
69-
await page.getByRole('button', { name: 'Download Archive' }).click();
70-
const downloadArchive = await downloadArchivePromise;
71-
expect(downloadArchive.suggestedFilename()).toMatch(/\.(zip)$/);
67+
await expectOutputFileDownload(page, 'Download Archive', /\.(zip)$/);
7268

7369
await page.getByRole('button', { name: 'Review' }).click();
7470
await page.getByRole('tab', { name: 'Files' }).click();
75-
const downloadConsolePromise = page.waitForEvent('download');
76-
await page.getByRole('button', { name: 'Download Console' }).click();
77-
const downloadConsole = await downloadConsolePromise;
78-
expect(downloadConsole.suggestedFilename()).toMatch(/\.console\.log$/);
71+
await expectOutputFileDownload(page, 'Download Console', /\.console\.log$/);
7972

8073
await page.getByRole('button', { name: 'Review' }).click();
8174
await page.getByRole('tab', { name: 'Files' }).click();
82-
const downloadReportPromise = page.waitForEvent('download');
83-
await page.getByRole('button', { name: 'Download Report' }).click();
84-
const downloadReport = await downloadReportPromise;
85-
expect(downloadReport.suggestedFilename()).toMatch(/\.csv$/);
75+
await expectOutputFileDownload(page, 'Download Report', /\.csv$/);
8676
});
8777
});

test/e2e/utils/expect.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { expect, Page } from '@playwright/test';
22
import { Strings } from './lang';
3+
import { readFromCodeEditorAsJson } from './editor';
4+
import { getLabeledValueText } from './labeledValue';
35

46
export async function expectCompilationSucceeded(page: Page) {
57
await expect(async () => {
@@ -32,4 +34,57 @@ export function expectToHaveMultilineText(actual: string, expected: string) {
3234

3335
export function expectToMatchTimestamp(actual: string) {
3436
expect(actual).toMatch(/\d+ \w+ \d{4} at \d+:\d+( \(.+\))?/);
37+
}
38+
39+
export async function expectExecutionDetails(page: Page, options: { user?: string; status?: string } = {}) {
40+
const { user = 'admin', status = 'succeeded' } = options;
41+
42+
const executionStatus = page.locator('#execution-status');
43+
const executionId = await getLabeledValueText(page, 'ID', executionStatus);
44+
expect(executionId).toMatch(/^\d{4}\/\d+\/\d+\/\d+\/\d+\//);
45+
46+
const userName = await getLabeledValueText(page, 'User', executionStatus);
47+
expect(userName).toBe(user);
48+
49+
const statusBadge = await getLabeledValueText(page, 'Status', executionStatus);
50+
expect(statusBadge.toLowerCase()).toBe(status.toLowerCase());
51+
}
52+
53+
export async function expectExecutionTimings(page: Page) {
54+
const executionTiming = page.locator('#execution-timing');
55+
56+
const startedAt = await getLabeledValueText(page, 'Started At', executionTiming);
57+
expectToMatchTimestamp(startedAt);
58+
59+
const duration = await getLabeledValueText(page, 'Duration', executionTiming);
60+
expect(duration).toMatch(/\d+ ms \(\d+ seconds?\)/);
61+
62+
const endedAt = await getLabeledValueText(page, 'Ended At', executionTiming);
63+
expectToMatchTimestamp(endedAt);
64+
}
65+
66+
export async function expectExecutionInputs(page: Page, expectedInputs: Record<string, any>) {
67+
const inputs = await readFromCodeEditorAsJson<Record<string, any>>(page, 'Execution Inputs JSON');
68+
expect(inputs).toEqual(expectedInputs);
69+
}
70+
71+
export async function expectExecutionOutputs(page: Page, expectedOutputs: Array<any>) {
72+
const outputs = await readFromCodeEditorAsJson<Array<any>>(page, 'Execution Outputs JSON');
73+
expect(outputs).toHaveLength(expectedOutputs.length);
74+
expectedOutputs.forEach((expected, index) => {
75+
expect(outputs[index]).toMatchObject(expected);
76+
});
77+
}
78+
79+
export async function expectOutputTexts(page: Page, expectedTexts: string[]) {
80+
for (const text of expectedTexts) {
81+
await expect(page.getByText(text)).toBeVisible();
82+
}
83+
}
84+
85+
export async function expectOutputFileDownload(page: Page, buttonName: string, filenamePattern: RegExp) {
86+
const downloadPromise = page.waitForEvent('download');
87+
await page.getByRole('button', { name: buttonName }).click();
88+
const download = await downloadPromise;
89+
expect(download.suggestedFilename()).toMatch(filenamePattern);
3590
}

test/e2e/utils/labeledValue.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,23 @@ import { Locator, Page } from '@playwright/test';
22

33
/**
44
* Gets the value text from a LabeledValue by its label
5+
* Works with various HTML structures by:
6+
* 1. Finding any element containing the label text
7+
* 2. Getting its parent container
8+
* 3. Finding the first non-empty sibling with the value
59
*/
610
export async function getLabeledValueText(page: Page, label: string, scope?: Locator): Promise<string> {
711
const container = scope || page;
8-
const labelSpan = container.locator('span[class*="FieldLabel"]:text-is("' + label + '")');
9-
const valueSpan = labelSpan.locator('..').locator('span[class*="Field-field"]');
10-
return await valueSpan.textContent() || '';
12+
13+
// Find the label element (could be label, span, or any element with the text)
14+
const labelElement = container.locator(`:text-is("${label}")`).first();
15+
16+
// Get the parent container (the Field wrapper)
17+
const fieldContainer = labelElement.locator('..');
18+
19+
// Get all text content from the container and remove the label text
20+
const fullText = await fieldContainer.textContent() || '';
21+
const valueText = fullText.replace(label, '').trim();
22+
23+
return valueText;
1124
}

0 commit comments

Comments
 (0)