ptywright 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +459 -116
- package/dist/agent.mjs +2 -0
- package/dist/bin/ptywright.mjs +6 -0
- package/dist/cli-DIUx2w6X.mjs +3587 -0
- package/dist/cli.mjs +2 -0
- package/{src/index.ts → dist/index.mjs} +7 -9
- package/dist/mcp.mjs +2 -0
- package/dist/pty-cassette.mjs +24 -0
- package/dist/pty_like-Cpkh_O9B.mjs +404 -0
- package/dist/runner-DzZlFrt1.mjs +1897 -0
- package/dist/runner-zApMYWZx.mjs +3257 -0
- package/dist/script.mjs +2 -0
- package/dist/server-VHuEWWj_.mjs +3068 -0
- package/dist/session.mjs +2 -0
- package/dist/terminal_session-DopC7Xg6.mjs +893 -0
- package/package.json +28 -21
- package/schemas/ptywright-agent-cassette.schema.json +57 -0
- package/schemas/ptywright-agent-check.schema.json +122 -0
- package/schemas/ptywright-agent-manifest.schema.json +107 -0
- package/schemas/ptywright-agent-promote.schema.json +146 -0
- package/schemas/ptywright-agent-replay-summary.schema.json +140 -0
- package/schemas/ptywright-agent-run.schema.json +126 -0
- package/schemas/ptywright-agent.schema.json +182 -0
- package/schemas/ptywright-pty-cassette.schema.json +86 -0
- package/schemas/ptywright-script-manifest.schema.json +75 -0
- package/schemas/ptywright-script-run-summary.schema.json +114 -0
- package/schemas/ptywright-script.schema.json +55 -3
- package/skills/ptywright-testing/SKILL.md +53 -33
- package/bin/ptywright +0 -4
- package/src/cli.ts +0 -414
- package/src/generator/doc_parser.ts +0 -341
- package/src/generator/generate.ts +0 -161
- package/src/generator/index.ts +0 -10
- package/src/generator/script_generator.ts +0 -209
- package/src/generator/step_extractor.ts +0 -397
- package/src/mcp/http_server.ts +0 -174
- package/src/mcp/script_recording.ts +0 -238
- package/src/mcp/server.ts +0 -1348
- package/src/pty/bun_pty_adapter.ts +0 -34
- package/src/pty/bun_terminal_adapter.ts +0 -149
- package/src/pty/pty_adapter.ts +0 -31
- package/src/script/dsl.ts +0 -188
- package/src/script/module.ts +0 -43
- package/src/script/path.ts +0 -151
- package/src/script/run.ts +0 -108
- package/src/script/run_all.ts +0 -229
- package/src/script/runner.ts +0 -983
- package/src/script/schema.ts +0 -237
- package/src/script/steps/assert_snapshot_equals.ts +0 -21
- package/src/script/steps/index.ts +0 -2
- package/src/script/suite_report.ts +0 -626
- package/src/session/session_manager.ts +0 -145
- package/src/session/terminal_session.ts +0 -473
- package/src/terminal/ansi.ts +0 -142
- package/src/terminal/keys.ts +0 -180
- package/src/terminal/mask.ts +0 -70
- package/src/terminal/mouse.ts +0 -75
- package/src/terminal/snapshot.ts +0 -196
- package/src/terminal/style.ts +0 -121
- package/src/terminal/view.ts +0 -49
- package/src/trace/asciicast.ts +0 -20
- package/src/trace/asciinema_player_assets.ts +0 -44
- package/src/trace/cast_to_txt.ts +0 -116
- package/src/trace/recorder.ts +0 -110
- package/src/trace/report.ts +0 -2092
- package/src/types.ts +0 -86
- package/src/util/hash.ts +0 -8
- package/src/util/sleep.ts +0 -5
|
@@ -3,20 +3,23 @@ name: ptywright-testing
|
|
|
3
3
|
description: Terminal/TUI automation and regression testing using ptywright (PTY + xterm) via CLI or MCP tools. Use when you need to (1) drive a CLI/TUI app (send keys/mouse, wait, snapshot), (2) run scripted regressions (run/run-all) and review the HTML report (index.html + run.summary.json), or (3) record an interactive MCP-driven session into a replayable script with golden checkpoints.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Ptywright Testing
|
|
6
|
+
# Ptywright Testing
|
|
7
7
|
|
|
8
|
-
Use ptywright to run deterministic CLI/TUI regression tests with readable
|
|
8
|
+
Use ptywright to run deterministic CLI/TUI regression tests with readable "terminal screenshots" and a Playwright-like HTML report.
|
|
9
9
|
|
|
10
|
-
##
|
|
10
|
+
## Installation & Usage
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
```bash
|
|
13
|
+
# 推荐:bunx 一次性运行
|
|
14
|
+
bunx ptywright@latest <command>
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
# 或全局安装
|
|
17
|
+
bun add -g ptywright
|
|
18
|
+
ptywright <command>
|
|
19
|
+
|
|
20
|
+
# 本仓库内开发
|
|
21
|
+
bun run bin/ptywright <command>
|
|
22
|
+
```
|
|
20
23
|
|
|
21
24
|
## Choose the interface
|
|
22
25
|
|
|
@@ -25,42 +28,57 @@ This CLI is Bun-based (`#!/usr/bin/env bun`). For a released package, prefer `bu
|
|
|
25
28
|
|
|
26
29
|
## Start the MCP server
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
```bash
|
|
32
|
+
# stdio (default)
|
|
33
|
+
bunx ptywright@latest mcp
|
|
34
|
+
|
|
35
|
+
# 精简 tools(降低上下文压力)
|
|
36
|
+
bunx ptywright@latest mcp --caps core
|
|
37
|
+
|
|
38
|
+
# HTTP 模式
|
|
39
|
+
bunx ptywright@latest mcp-http --port 3000
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Capabilities (`--caps`): `all|core|debug|script|recording` (comma separated)
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
## Configure MCP Client
|
|
39
45
|
|
|
40
|
-
|
|
46
|
+
**Claude Desktop / Cursor** (`~/.config/claude/claude_desktop_config.json`):
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"ptywright": {
|
|
52
|
+
"command": "bunx",
|
|
53
|
+
"args": ["ptywright@latest", "mcp"]
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
41
58
|
|
|
42
59
|
## Run scripts (deterministic regression)
|
|
43
60
|
|
|
44
61
|
### Run the whole suite (preferred)
|
|
45
62
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
- Output to focus on:
|
|
50
|
-
- `reportPath` (open in a browser)
|
|
51
|
-
- `summaryPath` (`run.summary.json` for agents/CI)
|
|
63
|
+
```bash
|
|
64
|
+
bunx ptywright@latest run-all --dir scripts
|
|
65
|
+
```
|
|
52
66
|
|
|
53
|
-
|
|
67
|
+
Output to focus on:
|
|
68
|
+
- `reportPath` (open in a browser)
|
|
69
|
+
- `summaryPath` (`run.summary.json` for agents/CI)
|
|
54
70
|
|
|
71
|
+
MCP equivalent:
|
|
55
72
|
- `run_all_scripts` (defaults: `dir="scripts"`, suite report in `.tmp/run-all/`)
|
|
56
73
|
- Keep MCP output small: `run_all_scripts(includeEntries="failures", maxEntries=20)`
|
|
57
74
|
|
|
58
75
|
### Run one script
|
|
59
76
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
77
|
+
```bash
|
|
78
|
+
bunx ptywright@latest run <file.json|file.ts> [--artifacts-dir <dir>]
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
MCP: `run_script(scriptPath=...)`
|
|
64
82
|
|
|
65
83
|
## Debug a failure
|
|
66
84
|
|
|
@@ -85,7 +103,9 @@ Tip: for flaky waits, prefer `scope="buffer"` when the content may have scrolled
|
|
|
85
103
|
|
|
86
104
|
To verify ptywright MCP tool coverage without relying on external apps/network, run:
|
|
87
105
|
|
|
88
|
-
|
|
106
|
+
```bash
|
|
107
|
+
bun test tests/mcp_all_tools_smoke.test.ts
|
|
108
|
+
```
|
|
89
109
|
|
|
90
110
|
This exercises `core + debug + script + recording` tools end-to-end.
|
|
91
111
|
|
package/bin/ptywright
DELETED
package/src/cli.ts
DELETED
|
@@ -1,414 +0,0 @@
|
|
|
1
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2
|
-
|
|
3
|
-
import type { PtywrightCapability } from "./mcp/server";
|
|
4
|
-
import { createPtywrightServer } from "./mcp/server";
|
|
5
|
-
import { startPtywrightHttpServer } from "./mcp/http_server";
|
|
6
|
-
import { runAllScripts } from "./script/run_all";
|
|
7
|
-
import { runScriptPath } from "./script/path";
|
|
8
|
-
|
|
9
|
-
function usage(): string {
|
|
10
|
-
return [
|
|
11
|
-
"ptywright <command>",
|
|
12
|
-
"",
|
|
13
|
-
"Commands:",
|
|
14
|
-
" mcp Start the MCP server over stdio (default)",
|
|
15
|
-
" mcp-http Start the MCP server over Streamable HTTP",
|
|
16
|
-
" run <file> Run one script (JSON/TS) and write artifacts",
|
|
17
|
-
" run-all [dir] Run all scripts in a directory and write a suite report",
|
|
18
|
-
" help Show help",
|
|
19
|
-
"",
|
|
20
|
-
"Run options:",
|
|
21
|
-
" --artifacts-dir <dir> Override artifacts directory",
|
|
22
|
-
" --steps <module.ts> Inject custom step handlers",
|
|
23
|
-
" --update-goldens Update golden snapshots",
|
|
24
|
-
"",
|
|
25
|
-
"Run-all options:",
|
|
26
|
-
" --dir <dir> Directory to scan (default: scripts)",
|
|
27
|
-
" --artifacts-root <dir> Suite artifacts root (default: .tmp/run-all)",
|
|
28
|
-
" --steps <module.ts> Inject custom step handlers",
|
|
29
|
-
" --update-goldens Update golden snapshots",
|
|
30
|
-
"",
|
|
31
|
-
"MCP options:",
|
|
32
|
-
" --caps <list> Capabilities: all|core|debug|script|recording",
|
|
33
|
-
"",
|
|
34
|
-
"MCP HTTP options (mcp-http):",
|
|
35
|
-
" --host <host> Bind host (default: 127.0.0.1)",
|
|
36
|
-
" --port <port> Bind port (default: 3000)",
|
|
37
|
-
" --allowed-origins <list> Comma/space separated Origin allowlist",
|
|
38
|
-
" --no-cors Disable CORS headers",
|
|
39
|
-
].join("\n");
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function isHelp(arg: string | undefined): boolean {
|
|
43
|
-
return arg === "-h" || arg === "--help" || arg === "help";
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function logLines(lines: Array<string | null | undefined>, stderr: boolean): void {
|
|
47
|
-
const filtered = lines.map((l) => l?.trim()).filter(Boolean) as string[];
|
|
48
|
-
for (const line of filtered) {
|
|
49
|
-
// eslint-disable-next-line no-console
|
|
50
|
-
(stderr ? console.error : console.log)(line);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function parseCaps(value: string): PtywrightCapability[] {
|
|
55
|
-
const parts = value
|
|
56
|
-
.split(/[\s,]+/g)
|
|
57
|
-
.map((p) => p.trim().toLowerCase())
|
|
58
|
-
.filter(Boolean);
|
|
59
|
-
|
|
60
|
-
const out: PtywrightCapability[] = [];
|
|
61
|
-
for (const p of parts) {
|
|
62
|
-
if (p === "all") out.push("all");
|
|
63
|
-
else if (p === "core") out.push("core");
|
|
64
|
-
else if (p === "debug") out.push("debug");
|
|
65
|
-
else if (p === "script" || p === "scripts" || p === "runner" || p === "run") out.push("script");
|
|
66
|
-
else if (p === "recording" || p === "record" || p === "rec") out.push("recording");
|
|
67
|
-
else throw new Error(`unknown capability: ${p}`);
|
|
68
|
-
}
|
|
69
|
-
return out;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function parseRunArgs(argv: string[]): {
|
|
73
|
-
scriptPath: string;
|
|
74
|
-
artifactsDir?: string;
|
|
75
|
-
stepsPath?: string;
|
|
76
|
-
updateGoldens: boolean;
|
|
77
|
-
} {
|
|
78
|
-
const out: {
|
|
79
|
-
scriptPath?: string;
|
|
80
|
-
artifactsDir?: string;
|
|
81
|
-
stepsPath?: string;
|
|
82
|
-
updateGoldens: boolean;
|
|
83
|
-
} = { updateGoldens: false };
|
|
84
|
-
|
|
85
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
86
|
-
const arg = argv[i];
|
|
87
|
-
const next = argv[i + 1];
|
|
88
|
-
|
|
89
|
-
if (!out.scriptPath && arg && !arg.startsWith("-")) {
|
|
90
|
-
out.scriptPath = arg;
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
if (arg === "--artifacts-dir" && next) {
|
|
95
|
-
out.artifactsDir = next;
|
|
96
|
-
i += 1;
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (arg === "--steps" && next) {
|
|
101
|
-
out.stepsPath = next;
|
|
102
|
-
i += 1;
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (arg === "--update-goldens") {
|
|
107
|
-
out.updateGoldens = true;
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
throw new Error(`unknown arg: ${arg ?? ""}`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (!out.scriptPath) {
|
|
115
|
-
throw new Error("missing <file>\n\n" + usage());
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return out as {
|
|
119
|
-
scriptPath: string;
|
|
120
|
-
artifactsDir?: string;
|
|
121
|
-
stepsPath?: string;
|
|
122
|
-
updateGoldens: boolean;
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function parseRunAllArgs(argv: string[]): {
|
|
127
|
-
dir?: string;
|
|
128
|
-
artifactsRoot?: string;
|
|
129
|
-
stepsPath?: string;
|
|
130
|
-
updateGoldens: boolean;
|
|
131
|
-
} {
|
|
132
|
-
const out: {
|
|
133
|
-
dir?: string;
|
|
134
|
-
artifactsRoot?: string;
|
|
135
|
-
stepsPath?: string;
|
|
136
|
-
updateGoldens: boolean;
|
|
137
|
-
} = { updateGoldens: false };
|
|
138
|
-
|
|
139
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
140
|
-
const arg = argv[i];
|
|
141
|
-
const next = argv[i + 1];
|
|
142
|
-
|
|
143
|
-
if (!out.dir && arg && !arg.startsWith("-")) {
|
|
144
|
-
out.dir = arg;
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (arg === "--dir" && next) {
|
|
149
|
-
out.dir = next;
|
|
150
|
-
i += 1;
|
|
151
|
-
continue;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (arg === "--artifacts-root" && next) {
|
|
155
|
-
out.artifactsRoot = next;
|
|
156
|
-
i += 1;
|
|
157
|
-
continue;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
if (arg === "--steps" && next) {
|
|
161
|
-
out.stepsPath = next;
|
|
162
|
-
i += 1;
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
if (arg === "--update-goldens") {
|
|
167
|
-
out.updateGoldens = true;
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
throw new Error(`unknown arg: ${arg ?? ""}`);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return out as {
|
|
175
|
-
dir?: string;
|
|
176
|
-
artifactsRoot?: string;
|
|
177
|
-
stepsPath?: string;
|
|
178
|
-
updateGoldens: boolean;
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
async function cmdMcp(argv: string[]): Promise<void> {
|
|
183
|
-
let capabilities: PtywrightCapability[] | undefined;
|
|
184
|
-
|
|
185
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
186
|
-
const arg = argv[i];
|
|
187
|
-
const next = argv[i + 1];
|
|
188
|
-
|
|
189
|
-
if (isHelp(arg)) {
|
|
190
|
-
// eslint-disable-next-line no-console
|
|
191
|
-
console.log(usage());
|
|
192
|
-
return;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
if (arg === "--caps" && next) {
|
|
196
|
-
capabilities = parseCaps(next);
|
|
197
|
-
i += 1;
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
throw new Error(`unknown arg: ${arg ?? ""}`);
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const { server, sessions } = createPtywrightServer({ capabilities });
|
|
205
|
-
const transport = new StdioServerTransport();
|
|
206
|
-
await server.connect(transport);
|
|
207
|
-
|
|
208
|
-
function shutdown(): void {
|
|
209
|
-
sessions.closeAll();
|
|
210
|
-
void server.close();
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
process.on("SIGINT", shutdown);
|
|
214
|
-
process.on("SIGTERM", shutdown);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
async function cmdMcpHttp(argv: string[]): Promise<void> {
|
|
218
|
-
let capabilities: PtywrightCapability[] | undefined;
|
|
219
|
-
let hostname: string | undefined;
|
|
220
|
-
let port: number | undefined;
|
|
221
|
-
let allowedOrigins: string[] | undefined;
|
|
222
|
-
let cors = true;
|
|
223
|
-
|
|
224
|
-
for (let i = 0; i < argv.length; i += 1) {
|
|
225
|
-
const arg = argv[i];
|
|
226
|
-
const next = argv[i + 1];
|
|
227
|
-
|
|
228
|
-
if (isHelp(arg)) {
|
|
229
|
-
// eslint-disable-next-line no-console
|
|
230
|
-
console.log(usage());
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (arg === "--caps" && next) {
|
|
235
|
-
capabilities = parseCaps(next);
|
|
236
|
-
i += 1;
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if ((arg === "--host" || arg === "--hostname") && next) {
|
|
241
|
-
hostname = next;
|
|
242
|
-
i += 1;
|
|
243
|
-
continue;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
if (arg === "--port" && next) {
|
|
247
|
-
const value = Number.parseInt(next, 10);
|
|
248
|
-
if (!Number.isFinite(value) || value < 0) {
|
|
249
|
-
throw new Error(`invalid --port: ${next}`);
|
|
250
|
-
}
|
|
251
|
-
port = value;
|
|
252
|
-
i += 1;
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
if (arg === "--allowed-origins" && next) {
|
|
257
|
-
allowedOrigins = next
|
|
258
|
-
.split(/[\s,]+/g)
|
|
259
|
-
.map((v) => v.trim())
|
|
260
|
-
.filter(Boolean);
|
|
261
|
-
i += 1;
|
|
262
|
-
continue;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
if (arg === "--no-cors") {
|
|
266
|
-
cors = false;
|
|
267
|
-
continue;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
throw new Error(`unknown arg: ${arg ?? ""}`);
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const handle = await startPtywrightHttpServer({
|
|
274
|
-
hostname,
|
|
275
|
-
port,
|
|
276
|
-
capabilities,
|
|
277
|
-
allowedOrigins,
|
|
278
|
-
cors,
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
// eslint-disable-next-line no-console
|
|
282
|
-
console.log(`listening ${handle.url}`);
|
|
283
|
-
// eslint-disable-next-line no-console
|
|
284
|
-
console.log(`health http://${handle.hostname}:${handle.port}/health`);
|
|
285
|
-
|
|
286
|
-
function shutdown(): void {
|
|
287
|
-
void handle.close();
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
process.on("SIGINT", shutdown);
|
|
291
|
-
process.on("SIGTERM", shutdown);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
async function cmdRun(argv: string[]): Promise<number> {
|
|
295
|
-
const args = parseRunArgs(argv);
|
|
296
|
-
const result = await runScriptPath(args.scriptPath, {
|
|
297
|
-
artifactsDir: args.artifactsDir,
|
|
298
|
-
updateGoldens: args.updateGoldens,
|
|
299
|
-
stepsPath: args.stepsPath,
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
if (!result.ok) {
|
|
303
|
-
logLines(
|
|
304
|
-
[
|
|
305
|
-
result.error,
|
|
306
|
-
result.artifactsDir ? `artifacts=${result.artifactsDir}` : null,
|
|
307
|
-
result.reportPath ? `report=${result.reportPath}` : null,
|
|
308
|
-
result.castPath ? `cast=${result.castPath}` : null,
|
|
309
|
-
result.failureArtifacts?.lastViewPath
|
|
310
|
-
? `last=${result.failureArtifacts.lastViewPath}`
|
|
311
|
-
: null,
|
|
312
|
-
result.failureArtifacts?.errorPath ? `error=${result.failureArtifacts.errorPath}` : null,
|
|
313
|
-
],
|
|
314
|
-
true,
|
|
315
|
-
);
|
|
316
|
-
return 1;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
logLines(
|
|
320
|
-
[
|
|
321
|
-
`ok artifacts=${result.artifactsDir}`,
|
|
322
|
-
result.reportPath ? `report=${result.reportPath}` : null,
|
|
323
|
-
result.castPath ? `cast=${result.castPath}` : null,
|
|
324
|
-
],
|
|
325
|
-
false,
|
|
326
|
-
);
|
|
327
|
-
return 0;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
async function cmdRunAll(argv: string[]): Promise<number> {
|
|
331
|
-
const args = parseRunAllArgs(argv);
|
|
332
|
-
const result = await runAllScripts({
|
|
333
|
-
dir: args.dir,
|
|
334
|
-
artifactsRoot: args.artifactsRoot,
|
|
335
|
-
stepsPath: args.stepsPath,
|
|
336
|
-
updateGoldens: args.updateGoldens,
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
const failures = result.entries.filter((e) => !e.result.ok);
|
|
340
|
-
|
|
341
|
-
if (failures.length === 0) {
|
|
342
|
-
// eslint-disable-next-line no-console
|
|
343
|
-
console.log(
|
|
344
|
-
`ok count=${result.entries.length} dir=${result.dir}\nreport=${result.reportPath}\nsummary=${result.summaryPath}`,
|
|
345
|
-
);
|
|
346
|
-
return 0;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// eslint-disable-next-line no-console
|
|
350
|
-
console.error(
|
|
351
|
-
`failed count=${failures.length}/${result.entries.length} dir=${result.dir}\nreport=${result.reportPath}\nsummary=${result.summaryPath}`,
|
|
352
|
-
);
|
|
353
|
-
for (const f of failures) {
|
|
354
|
-
if (f.result.ok) continue;
|
|
355
|
-
// eslint-disable-next-line no-console
|
|
356
|
-
console.error(`- ${f.filePath}: ${f.result.error}`);
|
|
357
|
-
if (f.result.failureArtifacts) {
|
|
358
|
-
// eslint-disable-next-line no-console
|
|
359
|
-
console.error(` artifacts=${f.result.artifactsDir ?? ""}`);
|
|
360
|
-
// eslint-disable-next-line no-console
|
|
361
|
-
console.error(` last=${f.result.failureArtifacts.lastViewPath}`);
|
|
362
|
-
// eslint-disable-next-line no-console
|
|
363
|
-
console.error(` error=${f.result.failureArtifacts.errorPath}`);
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
return 1;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
export async function main(argv: string[] = process.argv.slice(2)): Promise<void> {
|
|
370
|
-
const [command, ...rest] = argv;
|
|
371
|
-
|
|
372
|
-
if (!command) {
|
|
373
|
-
await cmdMcp([]);
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
if (isHelp(command)) {
|
|
378
|
-
// eslint-disable-next-line no-console
|
|
379
|
-
console.log(usage());
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
if (command === "mcp") {
|
|
384
|
-
await cmdMcp(rest);
|
|
385
|
-
return;
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (command === "mcp-http") {
|
|
389
|
-
await cmdMcpHttp(rest);
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (command === "run") {
|
|
394
|
-
process.exitCode = await cmdRun(rest);
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
if (command === "run-all") {
|
|
399
|
-
process.exitCode = await cmdRunAll(rest);
|
|
400
|
-
return;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
throw new Error(`unknown command: ${command}\n\n` + usage());
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
if (import.meta.main) {
|
|
407
|
-
try {
|
|
408
|
-
await main();
|
|
409
|
-
} catch (error) {
|
|
410
|
-
// eslint-disable-next-line no-console
|
|
411
|
-
console.error((error as Error).message);
|
|
412
|
-
process.exitCode = 1;
|
|
413
|
-
}
|
|
414
|
-
}
|