返回 DeepSeek-Reasonix
run-tests.mjs
根目录 / desktop / frontend / scripts / run-tests.mjs
1 #!/usr/bin/env node
2 // Discovery-based test runner: every src/__tests__/*.test.ts{,x} runs as its
3 // own tsx process (suites install their own jsdom/globals and exit non-zero
4 // on failure), so adding a test file needs no package.json registration.
5 // Suites owned by a dedicated pnpm script are excluded here, named with their
6 // owner, and their absence fails the run so a rename cannot silently drop
7 // coverage. Fail-fast by default; --keep-going runs everything and summarizes.
8 import { spawnSync } from "node:child_process";
9 import { readdirSync } from "node:fs";
10 import { join, resolve } from "node:path";
11 import { pathToFileURL } from "node:url";
12
13 const TESTS_DIR = "src/__tests__";
14 const SCRIPTS_DIR = "scripts";
15
16 const OWNED_ELSEWHERE = new Map(Object.entries({
17 "terminal-events.test.ts": "test:terminal",
18 "terminal-store.test.ts": "test:terminal",
19 "terminal-output.test.ts": "test:terminal",
20 "terminal-theme.test.ts": "test:terminal",
21 "task-monitor-navigation.test.ts": "test:task-monitor",
22 "workspace-refresh-store.test.ts": "test:workspace",
23 "workspace-selection-isolation.test.tsx": "test:workspace",
24 "workspace-changes-errors.test.tsx": "test:workspace",
25 "workspace-preview-css.test.ts": "test:workspace",
26 "workspace-context-menu.test.tsx": "test:workspace",
27 "workspace-resize-interaction.test.tsx": "test:workspace",
28 "rich-composer-selection.test.tsx": "pretest",
29 "context-center-contract.test.ts": "pretest",
30 "provider-model-cache.test.ts": "pretest",
31 "format-tokens.test.ts": "pretest",
32 "usage-stats-format.test.ts": "test:usage-stats",
33 "usage-stats-panel.test.tsx": "test:usage-stats",
34 "settings-responsive-layout.test.ts": "test:settings-responsive",
35 "raf-batch.test.ts": "test:stream",
36 "stream-delta-batch.test.ts": "test:stream",
37 "use-controller-stream-progress.test.ts": "test:stream",
38 "nested-scroll-handoff.test.ts": "test:transcript",
39 "typography-overflow-contract.test.ts": "test:transcript",
40 "composer-menu-viewport.test.ts": "test:composer-menu-viewport",
41 "virtual-menu-identity.test.tsx": "test:composer-menu-viewport",
42 "remote-workspace-launch.test.ts": "test:remote",
43 "remote-store.test.ts": "test:remote",
44 "remote-error-ux.test.tsx": "test:remote",
45 "remote-hosts-page.test.tsx": "test:remote",
46 "remote-connect-wizard.test.tsx": "test:remote (needs the css stub register)",
47 "add-project-entries.test.ts": "test:remote",
48 "remote-secret-dialog.test.tsx": "test:remote",
49 "remote-server-panel.test.tsx": "test:remote (needs the svg stub register)",
50 "remote-session-surface.test.tsx": "test:remote (needs the svg stub register)",
51 "remote-running-reconcile.test.ts": "test:remote",
52 "remote-project-tree.test.tsx": "test:remote",
53 "statusbar-workspace.test.tsx": "test:remote",
54 "updater-shared-state.test.tsx": "test:updater",
55 "window-state-ordering.test.ts": "test:window-state",
56 }));
57
58 const keepGoing = process.argv.includes("--keep-going");
59 const files = readdirSync(TESTS_DIR)
60 .filter((name) => /\.test\.tsx?$/.test(name))
61 .sort();
62
63 for (const [name, owner] of OWNED_ELSEWHERE) {
64 if (!files.includes(name)) {
65 console.error(`run-tests: excluded suite ${name} (owner: ${owner}) no longer exists — update OWNED_ELSEWHERE`);
66 process.exit(1);
67 }
68 }
69
70 // Browser assets are not executable Node code. Use the same SVG/image/CSS
71 // loader as dedicated component suites so discovered transitive imports work.
72 // Asset rendering remains covered by the browser and stylesheet gates.
73 const assetArgs = ["--import", pathToFileURL(resolve(SCRIPTS_DIR, "svg-stub-register.mjs")).href];
74
75 const suites = files.filter((name) => !OWNED_ELSEWHERE.has(name));
76 console.log(`run-tests: ${suites.length} discovered suites (${OWNED_ELSEWHERE.size} owned by dedicated scripts)`);
77
78 const failures = [];
79 for (const name of suites) {
80 const path = join(TESTS_DIR, name);
81 console.log(`\n▶ ${path}`);
82 // English locale mirrors the Go convention (LANG=en_US.UTF-8 go test):
83 // Node's built-in navigator.language follows the machine's ICU locale, and
84 // suites assert English UI strings.
85 const env = { ...process.env, LANG: "en_US.UTF-8", LC_ALL: "en_US.UTF-8" };
86 // Register asset hooks before tsx and the suite. Passing --import after the
87 // tsx CLI can leave lazy CSS imports unhandled in Node tests.
88 const result = spawnSync(process.execPath, [...assetArgs, "--import", "tsx", path], { stdio: "inherit", env });
89 if (result.error) console.error(`run-tests: spawn failed for ${path}: ${result.error.message}`);
90 if (result.status !== 0) {
91 if (!keepGoing) {
92 console.error(`\nrun-tests: FAILED at ${path}`);
93 process.exit(result.status ?? 1);
94 }
95 failures.push(name);
96 }
97 }
98
99 if (failures.length > 0) {
100 console.error(`\nrun-tests: ${failures.length}/${suites.length} suites failed:`);
101 for (const name of failures) console.error(` FAIL ${name}`);
102 process.exit(1);
103 }
104 console.log(`\nrun-tests: all ${suites.length} suites passed`);
105
105 lines Plain Text