返回 CodeWhale
terminal-capture.test.ts
根目录 / web / lib / terminal-capture.test.ts
1 import { execFileSync } from "node:child_process";
2 import { mkdtempSync, readFileSync, rmSync } from "node:fs";
3 import { createHash } from "node:crypto";
4 import { tmpdir } from "node:os";
5 import { join } from "node:path";
6 import { describe, expect, it } from "vitest";
7 import { TERMINAL_SCREENSHOT } from "./media-manifest";
8 import { TERMINAL_CAPTURE_FRAMES, TERMINAL_CAPTURE_META } from "./terminal-capture.generated";
9
10 // The homepage terminal is live text drawn from real PTY cells. These checks
11 // keep the generated runs tied to the capture files, cell for cell.
12 const webRoot = new URL("../", import.meta.url);
13
14 describe("terminal capture", () => {
15 it("keeps the generated module current with the capture files", () => {
16 const out = execFileSync(process.execPath, ["scripts/render-terminal-capture.mjs", "--check"], {
17 cwd: webRoot,
18 encoding: "utf8",
19 });
20 expect(out).toContain("is current");
21 });
22
23 it("keeps every frame's text identical to its captured cells", () => {
24 for (const frame of Object.values(TERMINAL_CAPTURE_FRAMES)) {
25 const capture = JSON.parse(readFileSync(new URL(`../../${frame.file}`, import.meta.url), "utf8")) as {
26 rows: number;
27 cols: number;
28 cells: { text: string }[][];
29 };
30 expect(frame.lines).toHaveLength(capture.rows);
31 frame.lines.forEach((runs, row) => {
32 const text = runs.map(([t]) => t).join("");
33 expect(text, `${frame.file} row ${row}`).toBe(capture.cells[row].map((c) => c.text || " ").join(""));
34 expect([...text]).toHaveLength(capture.cols);
35 });
36 }
37 });
38
39 it("names its source and the build it came from", () => {
40 expect(TERMINAL_CAPTURE_META.source).toContain("website_current_terminal_capture");
41 expect(TERMINAL_CAPTURE_META.baseCommit).toBe(TERMINAL_SCREENSHOT.sourceCommit);
42 expect(`web/lib/terminal-captures/website-home-100x24.json`).toBe(TERMINAL_SCREENSHOT.capture);
43 });
44
45 it("keeps the native workbar and every recorded frame tied to their capture hashes", () => {
46 const manifest = JSON.parse(readFileSync(new URL("./terminal-captures/manifest.json", import.meta.url), "utf8"));
47 expect(manifest.binarySha256).toMatch(/^[0-9a-f]{64}$/);
48 expect(manifest.conditions.submittedPrompts).toBe(0);
49 expect(manifest.conditions.fixtureHistory).toBe(false);
50 for (const frame of manifest.frames) {
51 const bytes = readFileSync(new URL(`./terminal-captures/${frame.file}`, import.meta.url));
52 expect(createHash("sha256").update(bytes).digest("hex"), frame.file).toBe(frame.sha256);
53 const capture = JSON.parse(bytes.toString());
54 expect(capture.rows).toBe(frame.rows);
55 expect(capture.cols).toBe(frame.cols);
56 }
57 const workbar = TERMINAL_CAPTURE_FRAMES.workbar.lines.map((runs) => runs.map(([text]) => text).join("")).join("\n");
58 expect(workbar).toContain("no to-dos yet");
59 expect(workbar).toContain("~/my-project");
60 expect(workbar).not.toContain(".tmp");
61 });
62
63 it("exports native geometry and every whale braille dot without relying on font fallback", () => {
64 const directory = mkdtempSync(join(tmpdir(), "native-terminal-svg-"));
65 try {
66 const output = join(directory, "home.svg");
67 execFileSync(process.execPath, ["scripts/render-terminal-capture.mjs", "--svg", output], { cwd: webRoot });
68 const svg = readFileSync(output, "utf8");
69 expect(svg).toContain('viewBox="0 0 1000 480"');
70 const text = TERMINAL_CAPTURE_FRAMES.home.lines.flatMap((runs) => runs.map(([text]) => text)).join("");
71 let dots = 0;
72 for (const glyph of text) {
73 const code = glyph.codePointAt(0)!;
74 if (code >= 0x2800 && code <= 0x28ff) dots += (code - 0x2800).toString(2).replaceAll("0", "").length;
75 }
76 expect(dots).toBeGreaterThan(0);
77 expect(svg.match(/<circle /g)).toHaveLength(dots);
78 expect(svg).not.toContain("<script");
79 } finally {
80 rmSync(directory, { recursive: true, force: true });
81 }
82 });
83
84 it("renders as one labelled image inside a keyboard-scrollable region", () => {
85 const component = readFileSync(new URL("../components/terminal-capture.tsx", import.meta.url), "utf8");
86 expect(component).toContain('role="region"');
87 expect(component).toContain("tabIndex={0}");
88 expect(component).toContain('role="img"');
89 expect(component).toContain("aria-label={label}");
90 });
91 });
92
92 lines TYPESCRIPT