Skip to content

Commit d52395f

Browse files
committed
fix(web): make e2e-ui command windows compatible
1 parent 9a7cf42 commit d52395f

3 files changed

Lines changed: 148 additions & 1 deletion

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"acceptance:phase1": "pnpm --dir e2e exec playwright test --config playwright.config.ts --grep @phase1",
2424
"acceptance:phase1:update-baseline": "pnpm --dir e2e exec playwright test --config playwright.config.ts --grep @phase1 --update-snapshots",
2525
"acceptance:phase1:report": "node e2e/fixtures/report-writer.ts phase-1",
26-
"e2e-ui": "sh -c 'rm -rf e2e-ui/output e2e-ui/test-results; pnpm --dir e2e-ui exec playwright test --config playwright.config.ts; status=$?; pnpm --dir e2e-ui exec tsx report/build-report.ts; exit $status'",
26+
"e2e-ui": "tsx scripts/e2e-ui.ts",
2727
"lint": "biome lint .",
2828
"lint:fix": "biome lint --write .",
2929
"format": "biome format --write .",

scripts/e2e-ui.test.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
import { runE2eUi } from "./e2e-ui.js";
3+
4+
describe("runE2eUi", () => {
5+
it("cleans stale output directories before running playwright and report generation", async () => {
6+
const removeDir = vi.fn<(_: string) => Promise<void>>().mockResolvedValue(undefined);
7+
const runCommand = vi
8+
.fn<(command: string, args?: string[], options?: { cwd?: string }) => Promise<void>>()
9+
.mockResolvedValue(undefined);
10+
11+
const code = await runE2eUi({
12+
repoRoot: "/repo",
13+
removeDir,
14+
runCommand,
15+
});
16+
17+
expect(code).toBe(0);
18+
expect(removeDir).toHaveBeenCalledTimes(2);
19+
expect(removeDir).toHaveBeenNthCalledWith(1, "/repo/e2e-ui/output");
20+
expect(removeDir).toHaveBeenNthCalledWith(2, "/repo/e2e-ui/test-results");
21+
expect(runCommand).toHaveBeenNthCalledWith(
22+
1,
23+
"pnpm",
24+
["--dir", "e2e-ui", "exec", "playwright", "test", "--config", "playwright.config.ts"],
25+
{ cwd: "/repo" }
26+
);
27+
expect(runCommand).toHaveBeenNthCalledWith(
28+
2,
29+
"pnpm",
30+
["--dir", "e2e-ui", "exec", "tsx", "report/build-report.ts"],
31+
{ cwd: "/repo" }
32+
);
33+
});
34+
35+
it("still builds the report when the playwright run fails and returns a non-zero exit code", async () => {
36+
const runCommand = vi
37+
.fn<(command: string, args?: string[], options?: { cwd?: string }) => Promise<void>>()
38+
.mockRejectedValueOnce(new Error("playwright failed"))
39+
.mockResolvedValueOnce(undefined);
40+
41+
const code = await runE2eUi({
42+
repoRoot: "/repo",
43+
removeDir: vi.fn().mockResolvedValue(undefined),
44+
runCommand,
45+
});
46+
47+
expect(code).toBe(1);
48+
expect(runCommand).toHaveBeenCalledTimes(2);
49+
expect(runCommand).toHaveBeenNthCalledWith(
50+
2,
51+
"pnpm",
52+
["--dir", "e2e-ui", "exec", "tsx", "report/build-report.ts"],
53+
{ cwd: "/repo" }
54+
);
55+
});
56+
57+
it("returns a non-zero exit code when report generation fails after a successful playwright run", async () => {
58+
const runCommand = vi
59+
.fn<(command: string, args?: string[], options?: { cwd?: string }) => Promise<void>>()
60+
.mockResolvedValueOnce(undefined)
61+
.mockRejectedValueOnce(new Error("report failed"));
62+
63+
const code = await runE2eUi({
64+
repoRoot: "/repo",
65+
removeDir: vi.fn().mockResolvedValue(undefined),
66+
runCommand,
67+
});
68+
69+
expect(code).toBe(1);
70+
expect(runCommand).toHaveBeenCalledTimes(2);
71+
});
72+
});

scripts/e2e-ui.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import { rm } from "node:fs/promises";
2+
import { join } from "node:path";
3+
import { error, info, success } from "./shared/logger.js";
4+
import { ROOT_DIR } from "./shared/paths.js";
5+
import { isDirectExecution, run } from "./shared/process.js";
6+
7+
export interface E2eUiRunnerDeps {
8+
repoRoot?: string;
9+
removeDir?: (target: string) => Promise<void>;
10+
runCommand?: (command: string, args?: string[], options?: { cwd?: string }) => Promise<void>;
11+
}
12+
13+
const E2E_UI_DIR = "e2e-ui";
14+
const PLAYWRIGHT_ARGS = [
15+
"--dir",
16+
E2E_UI_DIR,
17+
"exec",
18+
"playwright",
19+
"test",
20+
"--config",
21+
"playwright.config.ts",
22+
];
23+
const REPORT_ARGS = ["--dir", E2E_UI_DIR, "exec", "tsx", "report/build-report.ts"];
24+
25+
export async function runE2eUi(deps: E2eUiRunnerDeps = {}): Promise<number> {
26+
const repoRoot = deps.repoRoot ?? ROOT_DIR;
27+
const removeDir =
28+
deps.removeDir ??
29+
(async (target: string) => {
30+
await rm(target, { recursive: true, force: true });
31+
});
32+
const runCommand =
33+
deps.runCommand ??
34+
((command: string, args: string[] = [], options?: { cwd?: string }) =>
35+
run(command, args, { cwd: options?.cwd }));
36+
37+
const outputDir = join(repoRoot, E2E_UI_DIR, "output");
38+
const testResultsDir = join(repoRoot, E2E_UI_DIR, "test-results");
39+
40+
await Promise.all([removeDir(outputDir), removeDir(testResultsDir)]);
41+
42+
let exitCode = 0;
43+
44+
try {
45+
info("Running e2e-ui Playwright capture suite...");
46+
await runCommand("pnpm", PLAYWRIGHT_ARGS, { cwd: repoRoot });
47+
success("e2e-ui Playwright capture suite finished.");
48+
} catch (cause) {
49+
exitCode = 1;
50+
error(
51+
cause instanceof Error
52+
? cause.message
53+
: "e2e-ui Playwright capture suite failed unexpectedly."
54+
);
55+
}
56+
57+
try {
58+
info("Building e2e-ui report...");
59+
await runCommand("pnpm", REPORT_ARGS, { cwd: repoRoot });
60+
success("e2e-ui report generated.");
61+
} catch (cause) {
62+
if (exitCode === 0) {
63+
exitCode = 1;
64+
}
65+
error(cause instanceof Error ? cause.message : "e2e-ui report generation failed unexpectedly.");
66+
}
67+
68+
return exitCode;
69+
}
70+
71+
if (isDirectExecution(import.meta.url)) {
72+
void runE2eUi().then((code) => {
73+
process.exit(code);
74+
});
75+
}

0 commit comments

Comments
 (0)