Open History to see the last commit
1import { describe, expect, it } from "@effect/vitest";
2
3import { buildProof, readSide } from "./prove-scenario.ts";
4
5const report = (
6 exitCode: number,
7 error?: { code: string; message: string },
8 status = error ? "failed" : "passed",
9) => ({
10 run: {
11 results: [
12 { titlePath: ["Another scenario the run did not select"], attempts: [] },
13 {
14 titlePath: ["Tab reaches the submit button"],
15 attempts: [
16 {
17 status,
18 steps: [
19 { api: "app.open", label: "/pair#token=secret", status: "passed" },
20 { api: "expect.toBeFocused", label: "Continue", status: error ? "failed" : "passed" },
21 ],
22 ...(error === undefined ? {} : { error }),
23 artifacts: [
24 { kind: "screenshot", path: "tab/attempt-1/screenshots/001-after-tab.png" },
25 { kind: "video", path: "tab/attempt-1/video/video.webm" },
26 ],
27 },
28 ],
29 },
30 ],
31 },
32});
33
34const passed = report(0);
35const failed = report(1, { code: "ASSERTION_FAILED", message: "expected: focused" });
36// The control the scenario clicks is missing: the scenario is broken, the bug is not shown.
37const brokenScenario = report(1, { code: "LOCATOR_NOT_FOUND", message: "no node" });
38// A file with two tests: the first passes, so reading only it would hide the second.
39const twoTests = { run: { results: [...passed.run.results, ...failed.run.results] } };
40// e2e exits 0 when the test skips itself at runtime.
41const skipped = report(0, undefined, "skipped");
42
43describe("prove-scenario", () => {
44 it.each([
45 { base: failed, head: passed, failsOnBase: true, passesOnHead: true, verdict: /^Proven/ },
46 // A "fix" for behavior that never happens: the scenario must fail before it can prove.
47 {
48 base: passed,
49 head: passed,
50 failsOnBase: false,
51 passesOnHead: true,
52 verdict: /passes on base/,
53 },
54 { base: failed, head: failed, failsOnBase: true, passesOnHead: false, verdict: /still fails/ },
55 {
56 base: brokenScenario,
57 head: passed,
58 failsOnBase: false,
59 passesOnHead: true,
60 verdict: /could not run on base/,
61 },
62 {
63 base: failed,
64 head: skipped,
65 failsOnBase: true,
66 passesOnHead: false,
67 verdict: /could not run on head/,
68 },
69 {
70 base: failed,
71 head: twoTests,
72 failsOnBase: true,
73 passesOnHead: false,
74 verdict: /could not run on head .*exactly one/,
75 },
76 // e2e writes no report when the app does not start.
77 {
78 base: undefined,
79 head: passed,
80 failsOnBase: false,
81 passesOnHead: true,
82 verdict: /could not run on base/,
83 },
84 ])("reports $verdict", (row) => {
85 const proof = buildProof(
86 readSide("aaaaaaaaaaaa", "/out/base/results", row.base),
87 readSide("bbbbbbbbbbbb", "/out/head/results", row.head),
88 (path) => path,
89 );
90 expect(proof).toMatchObject({ failsOnBase: row.failsOnBase, passesOnHead: row.passesOnHead });
91 expect(proof.verdict).toMatch(row.verdict);
92 });
93
94 it("labels captures with their side and commit and keeps the pairing token out", () => {
95 const proof = buildProof(
96 readSide("aaaaaaaaaaaa", "/out/base/results", failed),
97 readSide("bbbbbbbbbbbb", "/out/head/results", passed),
98 (path) => `https://files.example${path}`,
99 );
100 expect(proof.markdown).toContain("| | Base `aaaaaaaaaa` | Head `bbbbbbbbbb` |");
101 expect(proof.markdown).toContain(
102 "",
103 );
104 expect(proof.videos).toEqual([
105 "/out/base/results/tab/attempt-1/video/video.webm",
106 "/out/head/results/tab/attempt-1/video/video.webm",
107 ]);
108 expect(JSON.stringify(proof)).not.toContain("secret");
109 });
110});