@paretools/test 0.8.5 → 0.10.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.
Files changed (47) hide show
  1. package/README.md +35 -18
  2. package/dist/index.js +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/lib/formatters.d.ts +3 -0
  5. package/dist/lib/formatters.d.ts.map +1 -1
  6. package/dist/lib/formatters.js +23 -6
  7. package/dist/lib/formatters.js.map +1 -1
  8. package/dist/lib/parsers/jest.d.ts +4 -0
  9. package/dist/lib/parsers/jest.d.ts.map +1 -1
  10. package/dist/lib/parsers/jest.js +39 -2
  11. package/dist/lib/parsers/jest.js.map +1 -1
  12. package/dist/lib/parsers/mocha.d.ts +4 -0
  13. package/dist/lib/parsers/mocha.d.ts.map +1 -1
  14. package/dist/lib/parsers/mocha.js +47 -2
  15. package/dist/lib/parsers/mocha.js.map +1 -1
  16. package/dist/lib/parsers/playwright.d.ts.map +1 -1
  17. package/dist/lib/parsers/playwright.js +4 -0
  18. package/dist/lib/parsers/playwright.js.map +1 -1
  19. package/dist/lib/parsers/pytest.d.ts +4 -0
  20. package/dist/lib/parsers/pytest.d.ts.map +1 -1
  21. package/dist/lib/parsers/pytest.js +35 -2
  22. package/dist/lib/parsers/pytest.js.map +1 -1
  23. package/dist/lib/parsers/vitest.d.ts +4 -0
  24. package/dist/lib/parsers/vitest.d.ts.map +1 -1
  25. package/dist/lib/parsers/vitest.js +39 -2
  26. package/dist/lib/parsers/vitest.js.map +1 -1
  27. package/dist/lib/timeouts.d.ts +3 -0
  28. package/dist/lib/timeouts.d.ts.map +1 -0
  29. package/dist/lib/timeouts.js +3 -0
  30. package/dist/lib/timeouts.js.map +1 -0
  31. package/dist/schemas/index.d.ts +19 -0
  32. package/dist/schemas/index.d.ts.map +1 -1
  33. package/dist/schemas/index.js +14 -0
  34. package/dist/schemas/index.js.map +1 -1
  35. package/dist/tools/coverage.d.ts +11 -1
  36. package/dist/tools/coverage.d.ts.map +1 -1
  37. package/dist/tools/coverage.js +258 -35
  38. package/dist/tools/coverage.js.map +1 -1
  39. package/dist/tools/playwright.d.ts +21 -0
  40. package/dist/tools/playwright.d.ts.map +1 -1
  41. package/dist/tools/playwright.js +149 -24
  42. package/dist/tools/playwright.js.map +1 -1
  43. package/dist/tools/run.d.ts +19 -1
  44. package/dist/tools/run.d.ts.map +1 -1
  45. package/dist/tools/run.js +263 -39
  46. package/dist/tools/run.js.map +1 -1
  47. package/package.json +6 -3
@@ -1,4 +1,25 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ /** Build extra CLI args for the `playwright` tool. Exported for unit testing. */
3
+ export declare function buildPlaywrightExtraArgs(opts: {
4
+ filter?: string;
5
+ project?: string;
6
+ grep?: string;
7
+ browser?: string;
8
+ shard?: string;
9
+ trace?: "on" | "off" | "retain-on-failure";
10
+ config?: string;
11
+ headed?: boolean;
12
+ updateSnapshots?: boolean;
13
+ workers?: number;
14
+ retries?: number;
15
+ maxFailures?: number;
16
+ timeout?: number;
17
+ lastFailed?: boolean;
18
+ onlyChanged?: boolean;
19
+ forbidOnly?: boolean;
20
+ passWithNoTests?: boolean;
21
+ args?: string[];
22
+ }): string[];
2
23
  /** Registers the `playwright` tool on the given MCP server. */
3
24
  export declare function registerPlaywrightTool(server: McpServer): void;
4
25
  //# sourceMappingURL=playwright.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/tools/playwright.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAUzE,+DAA+D;AAC/D,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAqHvD"}
1
+ {"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/tools/playwright.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAWzE,iFAAiF;AACjF,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,IAAI,GAAG,KAAK,GAAG,mBAAmB,CAAC;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,GAAG,MAAM,EAAE,CAiEX;AAED,+DAA+D;AAC/D,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,SAAS,QAwMvD"}
@@ -7,17 +7,70 @@ import { compactDualOutput, run, assertNoFlagInjection, INPUT_LIMITS } from "@pa
7
7
  import { parsePlaywrightJson } from "../lib/parsers/playwright.js";
8
8
  import { formatPlaywrightResult, compactPlaywrightResultMap, formatPlaywrightResultCompact, } from "../lib/formatters.js";
9
9
  import { PlaywrightResultSchema } from "../schemas/index.js";
10
+ import { TEST_CLI_TIMEOUT_MS } from "../lib/timeouts.js";
11
+ /** Build extra CLI args for the `playwright` tool. Exported for unit testing. */
12
+ export function buildPlaywrightExtraArgs(opts) {
13
+ const extraArgs = [...(opts.args || [])];
14
+ if (opts.filter) {
15
+ extraArgs.push(opts.filter);
16
+ }
17
+ if (opts.project) {
18
+ extraArgs.push("--project", opts.project);
19
+ }
20
+ if (opts.grep) {
21
+ extraArgs.push("--grep", opts.grep);
22
+ }
23
+ if (opts.browser) {
24
+ extraArgs.push("--browser", opts.browser);
25
+ }
26
+ if (opts.shard) {
27
+ extraArgs.push("--shard", opts.shard);
28
+ }
29
+ if (opts.trace) {
30
+ extraArgs.push("--trace", opts.trace);
31
+ }
32
+ if (opts.config) {
33
+ extraArgs.push("--config", opts.config);
34
+ }
35
+ if (opts.headed) {
36
+ extraArgs.push("--headed");
37
+ }
38
+ if (opts.updateSnapshots) {
39
+ extraArgs.push("--update-snapshots");
40
+ }
41
+ if (opts.workers !== undefined) {
42
+ extraArgs.push(`--workers=${opts.workers}`);
43
+ }
44
+ if (opts.retries !== undefined) {
45
+ extraArgs.push(`--retries=${opts.retries}`);
46
+ }
47
+ if (opts.maxFailures !== undefined) {
48
+ extraArgs.push(`--max-failures=${opts.maxFailures}`);
49
+ }
50
+ if (opts.timeout !== undefined) {
51
+ extraArgs.push(`--timeout=${opts.timeout}`);
52
+ }
53
+ if (opts.lastFailed) {
54
+ extraArgs.push("--last-failed");
55
+ }
56
+ if (opts.onlyChanged) {
57
+ extraArgs.push("--only-changed");
58
+ }
59
+ if (opts.forbidOnly) {
60
+ extraArgs.push("--forbid-only");
61
+ }
62
+ if (opts.passWithNoTests) {
63
+ extraArgs.push("--pass-with-no-tests");
64
+ }
65
+ return extraArgs;
66
+ }
10
67
  /** Registers the `playwright` tool on the given MCP server. */
11
68
  export function registerPlaywrightTool(server) {
12
69
  server.registerTool("playwright", {
13
70
  title: "Playwright Tests",
14
- description: "Runs Playwright tests with JSON reporter and returns structured results with pass/fail status, duration, and error messages. Use instead of running `npx playwright test` in the terminal.",
71
+ description: "Runs Playwright tests with JSON reporter and returns structured results with pass/fail status, duration, and error messages.",
15
72
  inputSchema: {
16
- path: z
17
- .string()
18
- .max(INPUT_LIMITS.PATH_MAX)
19
- .optional()
20
- .describe("Project root path (default: cwd)"),
73
+ path: z.string().max(INPUT_LIMITS.PATH_MAX).optional().describe("Project root path"),
21
74
  filter: z
22
75
  .string()
23
76
  .max(INPUT_LIMITS.SHORT_STRING_MAX)
@@ -28,52 +81,124 @@ export function registerPlaywrightTool(server) {
28
81
  .max(INPUT_LIMITS.SHORT_STRING_MAX)
29
82
  .optional()
30
83
  .describe("Playwright project name (e.g., 'chromium', 'firefox', 'webkit')"),
84
+ grep: z
85
+ .string()
86
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
87
+ .optional()
88
+ .describe("Regex pattern for matching tests by title (--grep)"),
89
+ browser: z
90
+ .string()
91
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
92
+ .optional()
93
+ .describe('Browser to use (e.g., "chromium", "firefox", "webkit") via --browser'),
94
+ shard: z
95
+ .string()
96
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
97
+ .optional()
98
+ .describe('Shard spec for distributed E2E test execution (e.g., "1/3") via --shard'),
99
+ trace: z
100
+ .enum(["on", "off", "retain-on-failure"])
101
+ .optional()
102
+ .describe("Trace recording mode (--trace)"),
103
+ config: z
104
+ .string()
105
+ .max(INPUT_LIMITS.PATH_MAX)
106
+ .optional()
107
+ .describe("Path to non-default Playwright config file (--config)"),
31
108
  headed: z.boolean().optional().default(false).describe("Run tests in headed browser mode"),
32
109
  updateSnapshots: z
33
110
  .boolean()
34
111
  .optional()
35
112
  .default(false)
36
113
  .describe("Update snapshots (adds --update-snapshots flag)"),
114
+ workers: z
115
+ .number()
116
+ .optional()
117
+ .describe("Number of parallel workers for test execution (maps to --workers)"),
118
+ retries: z
119
+ .number()
120
+ .optional()
121
+ .describe("Number of retries for failed tests (maps to --retries)"),
122
+ maxFailures: z
123
+ .number()
124
+ .optional()
125
+ .describe("Stop after this many test failures (maps to --max-failures)"),
126
+ timeout: z
127
+ .number()
128
+ .optional()
129
+ .describe("Per-test timeout in milliseconds (maps to --timeout)"),
130
+ lastFailed: z
131
+ .boolean()
132
+ .optional()
133
+ .describe("Re-run only previously failed tests (maps to --last-failed)"),
134
+ onlyChanged: z
135
+ .boolean()
136
+ .optional()
137
+ .describe("Run only tests affected by recent changes (maps to --only-changed)"),
138
+ forbidOnly: z
139
+ .boolean()
140
+ .optional()
141
+ .describe("Fail if test.only is found — CI safety check (maps to --forbid-only)"),
142
+ passWithNoTests: z
143
+ .boolean()
144
+ .optional()
145
+ .describe("Exit successfully when no tests are found (maps to --pass-with-no-tests)"),
37
146
  args: z
38
147
  .array(z.string().max(INPUT_LIMITS.STRING_MAX))
39
148
  .max(INPUT_LIMITS.ARRAY_MAX)
40
149
  .optional()
41
150
  .default([])
42
151
  .describe("Additional arguments to pass to Playwright test runner"),
43
- compact: z
44
- .boolean()
45
- .optional()
46
- .default(true)
47
- .describe("Auto-compact when structured output exceeds raw CLI tokens. Set false to always get full schema."),
152
+ compact: z.boolean().optional().default(true).describe("Prefer compact output"),
48
153
  },
49
154
  outputSchema: PlaywrightResultSchema,
50
- }, async ({ path, filter, project, headed, updateSnapshots, args, compact }) => {
51
- for (const a of args ?? []) {
52
- assertNoFlagInjection(a, "args");
53
- }
54
- const cwd = path || process.cwd();
55
- const extraArgs = [...(args || [])];
155
+ }, async ({ path, filter, project, grep, browser, shard, trace, config, headed, updateSnapshots, workers, retries, maxFailures, timeout, lastFailed, onlyChanged, forbidOnly, passWithNoTests, args, compact, }) => {
56
156
  if (filter) {
57
157
  assertNoFlagInjection(filter, "filter");
58
- extraArgs.push(filter);
59
158
  }
60
159
  if (project) {
61
160
  assertNoFlagInjection(project, "project");
62
- extraArgs.push("--project", project);
63
161
  }
64
- if (headed) {
65
- extraArgs.push("--headed");
162
+ if (grep) {
163
+ assertNoFlagInjection(grep, "grep");
66
164
  }
67
- if (updateSnapshots) {
68
- extraArgs.push("--update-snapshots");
165
+ if (browser) {
166
+ assertNoFlagInjection(browser, "browser");
69
167
  }
168
+ if (shard) {
169
+ assertNoFlagInjection(shard, "shard");
170
+ }
171
+ if (config) {
172
+ assertNoFlagInjection(config, "config");
173
+ }
174
+ const cwd = path || process.cwd();
175
+ const extraArgs = buildPlaywrightExtraArgs({
176
+ filter,
177
+ project,
178
+ grep,
179
+ browser,
180
+ shard,
181
+ trace,
182
+ config,
183
+ headed,
184
+ updateSnapshots,
185
+ workers,
186
+ retries,
187
+ maxFailures,
188
+ timeout,
189
+ lastFailed,
190
+ onlyChanged,
191
+ forbidOnly,
192
+ passWithNoTests,
193
+ args,
194
+ });
70
195
  // Write JSON output to a temp file to avoid stdout parsing issues on Windows
71
196
  const tempPath = join(tmpdir(), `pare-playwright-${randomUUID()}.json`);
72
197
  const cmdArgs = ["playwright", "test", `--reporter=json`, ...extraArgs];
73
198
  // Set PLAYWRIGHT_JSON_OUTPUT_NAME env var to direct JSON to temp file
74
199
  const result = await run("npx", cmdArgs, {
75
200
  cwd,
76
- timeout: 180_000,
201
+ timeout: TEST_CLI_TIMEOUT_MS,
77
202
  env: { PLAYWRIGHT_JSON_OUTPUT_NAME: tempPath },
78
203
  });
79
204
  let jsonStr;
@@ -1 +1 @@
1
- {"version":3,"file":"playwright.js","sourceRoot":"","sources":["../../src/tools/playwright.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,iBAAiB,EAAE,GAAG,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAChG,OAAO,EAAE,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,6BAA6B,GAC9B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,+DAA+D;AAC/D,MAAM,UAAU,sBAAsB,CAAC,MAAiB;IACtD,MAAM,CAAC,YAAY,CACjB,YAAY,EACZ;QACE,KAAK,EAAE,kBAAkB;QACzB,WAAW,EACT,4LAA4L;QAC9L,WAAW,EAAE;YACX,IAAI,EAAE,CAAC;iBACJ,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,QAAQ,CAAC;iBAC1B,QAAQ,EAAE;iBACV,QAAQ,CAAC,kCAAkC,CAAC;YAC/C,MAAM,EAAE,CAAC;iBACN,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,gBAAgB,CAAC;iBAClC,QAAQ,EAAE;iBACV,QAAQ,CAAC,2DAA2D,CAAC;YACxE,OAAO,EAAE,CAAC;iBACP,MAAM,EAAE;iBACR,GAAG,CAAC,YAAY,CAAC,gBAAgB,CAAC;iBAClC,QAAQ,EAAE;iBACV,QAAQ,CAAC,iEAAiE,CAAC;YAC9E,MAAM,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,kCAAkC,CAAC;YAC1F,eAAe,EAAE,CAAC;iBACf,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,OAAO,CAAC,KAAK,CAAC;iBACd,QAAQ,CAAC,iDAAiD,CAAC;YAC9D,IAAI,EAAE,CAAC;iBACJ,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC;iBAC9C,GAAG,CAAC,YAAY,CAAC,SAAS,CAAC;iBAC3B,QAAQ,EAAE;iBACV,OAAO,CAAC,EAAE,CAAC;iBACX,QAAQ,CAAC,wDAAwD,CAAC;YACrE,OAAO,EAAE,CAAC;iBACP,OAAO,EAAE;iBACT,QAAQ,EAAE;iBACV,OAAO,CAAC,IAAI,CAAC;iBACb,QAAQ,CACP,kGAAkG,CACnG;SACJ;QACD,YAAY,EAAE,sBAAsB;KACrC,EACD,KAAK,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE;QAC1E,KAAK,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC;YAC3B,qBAAqB,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACnC,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAClC,MAAM,SAAS,GAAa,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;QAE9C,IAAI,MAAM,EAAE,CAAC;YACX,qBAAqB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;YACxC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;QAED,IAAI,OAAO,EAAE,CAAC;YACZ,qBAAqB,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC1C,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACvC,CAAC;QAED,IAAI,MAAM,EAAE,CAAC;YACX,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC7B,CAAC;QAED,IAAI,eAAe,EAAE,CAAC;YACpB,SAAS,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACvC,CAAC;QAED,6EAA6E;QAC7E,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,UAAU,EAAE,OAAO,CAAC,CAAC;QAExE,MAAM,OAAO,GAAG,CAAC,YAAY,EAAE,MAAM,EAAE,iBAAiB,EAAE,GAAG,SAAS,CAAC,CAAC;QAExE,sEAAsE;QACtE,MAAM,MAAM,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE;YACvC,GAAG;YACH,OAAO,EAAE,OAAO;YAChB,GAAG,EAAE,EAAE,2BAA2B,EAAE,QAAQ,EAAE;SAC/C,CAAC,CAAC;QAEH,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;QAAC,MAAM,CAAC;YACP,4DAA4D;YAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC;YACpD,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAClC,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,KAAK,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,IAAI,GAAG,IAAI,KAAK,EAAE,CAAC;gBAC/C,MAAM,IAAI,KAAK,CACb,qGAAqG,CACtG,CAAC;YACJ,CAAC;YACD,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC;QACzC,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;YACzB,CAAC;YAAC,MAAM,CAAC;gBACP,2BAA2B;YAC7B,CAAC;QACH,CAAC;QAED,MAAM,gBAAgB,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAEtD,OAAO,iBAAiB,CACtB,gBAAgB,EAChB,MAAM,CAAC,MAAM,EACb,sBAAsB,EACtB,0BAA0B,EAC1B,6BAA6B,EAC7B,OAAO,KAAK,KAAK,CAClB,CAAC;IACJ,CAAC,CACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"playwright.js","sourceRoot":"","sources":["../../src/tools/playwright.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,28 @@
1
1
  import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { type Framework } from "../lib/detect.js";
3
3
  /** Exported for unit testing. */
4
- export declare function getRunCommand(framework: Framework, args: string[]): {
4
+ export declare function getRunCommand(framework: Framework, args: string[], opts?: {
5
+ coverage?: boolean;
6
+ }): {
5
7
  cmd: string;
6
8
  cmdArgs: string[];
7
9
  };
10
+ /** Build the extra CLI args for the `run` tool. Exported for unit testing. */
11
+ export declare function buildRunExtraArgs(framework: Framework, opts: {
12
+ filter?: string;
13
+ shard?: string;
14
+ config?: string;
15
+ updateSnapshots?: boolean;
16
+ coverage?: boolean;
17
+ onlyChanged?: boolean;
18
+ exitFirst?: boolean;
19
+ passWithNoTests?: boolean;
20
+ bail?: number | boolean;
21
+ testNamePattern?: string;
22
+ workers?: number;
23
+ timeout?: number;
24
+ args?: string[];
25
+ }): string[];
8
26
  /** Registers the `run` tool on the given MCP server. */
9
27
  export declare function registerRunTool(server: McpServer): void;
10
28
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/tools/run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAQnE,iCAAiC;AACjC,wBAAgB,aAAa,CAC3B,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,EAAE,GACb;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAWpC;AAED,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,QA6HhD;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAatF;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAUlD"}
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../src/tools/run.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEzE,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,kBAAkB,CAAC;AASnE,iCAAiC;AACjC,wBAAgB,aAAa,CAC3B,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE,MAAM,EAAE,EACd,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAwBpC;AAED,8EAA8E;AAC9E,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,EACpB,IAAI,EAAE;IACJ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB,GACA,MAAM,EAAE,CAmLV;AAED,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,QAoMhD;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAatF;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAUlD"}
package/dist/tools/run.js CHANGED
@@ -11,8 +11,9 @@ import { parseVitestJson } from "../lib/parsers/vitest.js";
11
11
  import { parseMochaJson } from "../lib/parsers/mocha.js";
12
12
  import { formatTestRun, compactTestRunMap, formatTestRunCompact } from "../lib/formatters.js";
13
13
  import { TestRunSchema } from "../schemas/index.js";
14
+ import { TEST_CLI_TIMEOUT_MS } from "../lib/timeouts.js";
14
15
  /** Exported for unit testing. */
15
- export function getRunCommand(framework, args) {
16
+ export function getRunCommand(framework, args, opts) {
16
17
  switch (framework) {
17
18
  case "pytest":
18
19
  return { cmd: "python", cmdArgs: ["-m", "pytest", "-v", ...args] };
@@ -21,20 +22,197 @@ export function getRunCommand(framework, args) {
21
22
  case "vitest":
22
23
  return { cmd: "npx", cmdArgs: ["vitest", "run", "--reporter=json", ...args] };
23
24
  case "mocha":
24
- return { cmd: "npx", cmdArgs: ["mocha", "--reporter", "json", ...args] };
25
+ return opts?.coverage
26
+ ? {
27
+ cmd: "npx",
28
+ cmdArgs: [
29
+ "nyc",
30
+ "--reporter=text",
31
+ "--reporter=json-summary",
32
+ "mocha",
33
+ "--reporter",
34
+ "json",
35
+ ...args,
36
+ ],
37
+ }
38
+ : { cmd: "npx", cmdArgs: ["mocha", "--reporter", "json", ...args] };
25
39
  }
26
40
  }
41
+ /** Build the extra CLI args for the `run` tool. Exported for unit testing. */
42
+ export function buildRunExtraArgs(framework, opts) {
43
+ const extraArgs = [...(opts.args || [])];
44
+ if (opts.filter) {
45
+ switch (framework) {
46
+ case "pytest":
47
+ extraArgs.push("-k", opts.filter);
48
+ break;
49
+ case "jest":
50
+ extraArgs.push("--testPathPattern", opts.filter);
51
+ break;
52
+ case "vitest":
53
+ extraArgs.push(opts.filter);
54
+ break;
55
+ case "mocha":
56
+ extraArgs.push("--grep", opts.filter);
57
+ break;
58
+ }
59
+ }
60
+ // Shard support (jest/vitest)
61
+ if (opts.shard) {
62
+ switch (framework) {
63
+ case "jest":
64
+ case "vitest":
65
+ extraArgs.push("--shard", opts.shard);
66
+ break;
67
+ case "pytest":
68
+ case "mocha":
69
+ // pytest/mocha don't have native --shard; ignore silently
70
+ break;
71
+ }
72
+ }
73
+ // Config file support
74
+ if (opts.config) {
75
+ switch (framework) {
76
+ case "pytest":
77
+ extraArgs.push(`--override-ini=config=${opts.config}`);
78
+ break;
79
+ case "jest":
80
+ extraArgs.push("--config", opts.config);
81
+ break;
82
+ case "vitest":
83
+ extraArgs.push("--config", opts.config);
84
+ break;
85
+ case "mocha":
86
+ extraArgs.push("--config", opts.config);
87
+ break;
88
+ }
89
+ }
90
+ if (opts.updateSnapshots && (framework === "vitest" || framework === "jest")) {
91
+ extraArgs.push("-u");
92
+ }
93
+ if (opts.coverage) {
94
+ switch (framework) {
95
+ case "vitest":
96
+ case "jest":
97
+ extraArgs.push("--coverage");
98
+ break;
99
+ case "pytest":
100
+ extraArgs.push("--cov");
101
+ break;
102
+ case "mocha":
103
+ break;
104
+ }
105
+ }
106
+ if (opts.onlyChanged) {
107
+ switch (framework) {
108
+ case "pytest":
109
+ extraArgs.push("--lf");
110
+ break;
111
+ case "jest":
112
+ extraArgs.push("--onlyChanged");
113
+ break;
114
+ case "vitest":
115
+ extraArgs.push("--changed");
116
+ break;
117
+ case "mocha":
118
+ break;
119
+ }
120
+ }
121
+ if (opts.exitFirst) {
122
+ switch (framework) {
123
+ case "pytest":
124
+ extraArgs.push("-x");
125
+ break;
126
+ case "jest":
127
+ case "vitest":
128
+ extraArgs.push("--bail=1");
129
+ break;
130
+ case "mocha":
131
+ extraArgs.push("-b");
132
+ break;
133
+ }
134
+ }
135
+ if (opts.passWithNoTests && (framework === "jest" || framework === "vitest")) {
136
+ extraArgs.push("--passWithNoTests");
137
+ }
138
+ // Bail: fail-fast with optional count
139
+ if (opts.bail !== undefined && opts.bail !== false) {
140
+ const n = opts.bail === true ? 1 : opts.bail;
141
+ switch (framework) {
142
+ case "pytest":
143
+ extraArgs.push(`--maxfail=${n}`);
144
+ break;
145
+ case "jest":
146
+ extraArgs.push(`--bail=${n}`);
147
+ break;
148
+ case "vitest":
149
+ extraArgs.push(`--bail=${n}`);
150
+ break;
151
+ case "mocha":
152
+ extraArgs.push("--bail");
153
+ break;
154
+ }
155
+ }
156
+ // Test name pattern: filter tests by name
157
+ if (opts.testNamePattern) {
158
+ switch (framework) {
159
+ case "pytest":
160
+ extraArgs.push("-k", opts.testNamePattern);
161
+ break;
162
+ case "jest":
163
+ extraArgs.push(`--testNamePattern=${opts.testNamePattern}`);
164
+ break;
165
+ case "vitest":
166
+ extraArgs.push(`--grep=${opts.testNamePattern}`);
167
+ break;
168
+ case "mocha":
169
+ extraArgs.push("--grep", opts.testNamePattern);
170
+ break;
171
+ }
172
+ }
173
+ // Workers: parallel execution
174
+ if (opts.workers !== undefined) {
175
+ switch (framework) {
176
+ case "pytest":
177
+ extraArgs.push("-n", String(opts.workers));
178
+ break;
179
+ case "jest":
180
+ extraArgs.push(`--maxWorkers=${opts.workers}`);
181
+ break;
182
+ case "vitest":
183
+ extraArgs.push(`--pool.threads.maxThreads=${opts.workers}`);
184
+ break;
185
+ case "mocha":
186
+ extraArgs.push("--jobs", String(opts.workers));
187
+ break;
188
+ }
189
+ }
190
+ // Timeout: per-test timeout
191
+ if (opts.timeout !== undefined) {
192
+ switch (framework) {
193
+ case "pytest":
194
+ extraArgs.push(`--timeout=${opts.timeout}`);
195
+ break;
196
+ case "jest":
197
+ extraArgs.push(`--testTimeout=${opts.timeout}`);
198
+ break;
199
+ case "vitest":
200
+ extraArgs.push(`--testTimeout=${opts.timeout}`);
201
+ break;
202
+ case "mocha":
203
+ extraArgs.push("--timeout", String(opts.timeout));
204
+ break;
205
+ }
206
+ }
207
+ return extraArgs;
208
+ }
27
209
  /** Registers the `run` tool on the given MCP server. */
28
210
  export function registerRunTool(server) {
29
211
  server.registerTool("run", {
30
212
  title: "Run Tests",
31
- description: "Auto-detects test framework (pytest/jest/vitest/mocha), runs tests, returns structured results with failures. Use instead of running pytest/jest/vitest/mocha in the terminal.",
213
+ description: "Auto-detects test framework (pytest/jest/vitest/mocha), runs tests, returns structured results with failures.",
32
214
  inputSchema: {
33
- path: z
34
- .string()
35
- .max(INPUT_LIMITS.PATH_MAX)
36
- .optional()
37
- .describe("Project root path (default: cwd)"),
215
+ path: z.string().max(INPUT_LIMITS.PATH_MAX).optional().describe("Project root path"),
38
216
  framework: z
39
217
  .enum(["pytest", "jest", "vitest", "mocha"])
40
218
  .optional()
@@ -44,63 +222,109 @@ export function registerRunTool(server) {
44
222
  .max(INPUT_LIMITS.SHORT_STRING_MAX)
45
223
  .optional()
46
224
  .describe("Test filter pattern (file path or test name pattern)"),
225
+ shard: z
226
+ .string()
227
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
228
+ .optional()
229
+ .describe('Shard spec for distributed test execution (e.g., "1/3") via --shard (jest/vitest)'),
230
+ config: z
231
+ .string()
232
+ .max(INPUT_LIMITS.PATH_MAX)
233
+ .optional()
234
+ .describe("Path to test config file (--config for all frameworks)"),
47
235
  updateSnapshots: z
48
236
  .boolean()
49
237
  .optional()
50
238
  .default(false)
51
239
  .describe("Update snapshots (vitest/jest only, adds -u flag)"),
240
+ coverage: z
241
+ .boolean()
242
+ .optional()
243
+ .default(false)
244
+ .describe("Run with coverage (adds --coverage for vitest/jest, --cov for pytest)"),
245
+ onlyChanged: z
246
+ .boolean()
247
+ .optional()
248
+ .describe("Run only tests affected by recent changes (maps to --lf for pytest, --onlyChanged for jest, --changed for vitest)"),
249
+ exitFirst: z
250
+ .boolean()
251
+ .optional()
252
+ .describe("Stop on first test failure (maps to -x for pytest, --bail=1 for jest/vitest, -b for mocha)"),
253
+ passWithNoTests: z
254
+ .boolean()
255
+ .optional()
256
+ .describe("Exit successfully when no tests are found (maps to --passWithNoTests for jest/vitest)"),
257
+ bail: z
258
+ .union([z.number().int().min(1), z.boolean()])
259
+ .optional()
260
+ .describe("Fail fast after N failures (maps to --maxfail=N for pytest, --bail=N for jest/vitest, --bail for mocha). Pass true for 1."),
261
+ testNamePattern: z
262
+ .string()
263
+ .max(INPUT_LIMITS.SHORT_STRING_MAX)
264
+ .optional()
265
+ .describe("Filter tests by name pattern (maps to -k for pytest, --testNamePattern for jest, --grep for vitest/mocha)"),
266
+ workers: z
267
+ .number()
268
+ .int()
269
+ .min(1)
270
+ .optional()
271
+ .describe("Number of parallel workers (maps to -n for pytest-xdist, --maxWorkers for jest, --pool.threads.maxThreads for vitest, --jobs for mocha)"),
272
+ timeout: z
273
+ .number()
274
+ .int()
275
+ .min(0)
276
+ .optional()
277
+ .describe("Per-test timeout in milliseconds (maps to --timeout for pytest-timeout, --testTimeout for jest/vitest, --timeout for mocha)"),
52
278
  args: z
53
279
  .array(z.string().max(INPUT_LIMITS.STRING_MAX))
54
280
  .max(INPUT_LIMITS.ARRAY_MAX)
55
281
  .optional()
56
282
  .default([])
57
283
  .describe("Additional arguments to pass to the test runner"),
58
- compact: z
59
- .boolean()
60
- .optional()
61
- .default(true)
62
- .describe("Auto-compact when structured output exceeds raw CLI tokens. Set false to always get full schema."),
284
+ compact: z.boolean().optional().default(true).describe("Prefer compact output"),
63
285
  },
64
286
  outputSchema: TestRunSchema,
65
- }, async ({ path, framework, filter, updateSnapshots, args, compact }) => {
66
- for (const a of args ?? []) {
67
- assertNoFlagInjection(a, "args");
68
- }
69
- const cwd = path || process.cwd();
70
- const detected = framework || (await detectFramework(cwd));
71
- const extraArgs = [...(args || [])];
287
+ }, async ({ path, framework, filter, shard, config, updateSnapshots, coverage, onlyChanged, exitFirst, passWithNoTests, bail, testNamePattern, workers, timeout, args, compact, }) => {
72
288
  if (filter) {
73
289
  assertNoFlagInjection(filter, "filter");
74
- switch (detected) {
75
- case "pytest":
76
- extraArgs.push("-k", filter);
77
- break;
78
- case "jest":
79
- extraArgs.push("--testPathPattern", filter);
80
- break;
81
- case "vitest":
82
- extraArgs.push(filter);
83
- break;
84
- case "mocha":
85
- extraArgs.push("--grep", filter);
86
- break;
87
- }
88
290
  }
89
- // Snapshot update support (vitest/jest only)
90
- if (updateSnapshots && (detected === "vitest" || detected === "jest")) {
91
- extraArgs.push("-u");
291
+ if (shard) {
292
+ assertNoFlagInjection(shard, "shard");
293
+ }
294
+ if (config) {
295
+ assertNoFlagInjection(config, "config");
92
296
  }
297
+ if (testNamePattern) {
298
+ assertNoFlagInjection(testNamePattern, "testNamePattern");
299
+ }
300
+ const cwd = path || process.cwd();
301
+ const detected = framework || (await detectFramework(cwd));
302
+ const extraArgs = buildRunExtraArgs(detected, {
303
+ filter,
304
+ shard,
305
+ config,
306
+ updateSnapshots,
307
+ coverage,
308
+ onlyChanged,
309
+ exitFirst,
310
+ passWithNoTests,
311
+ bail,
312
+ testNamePattern,
313
+ workers,
314
+ timeout,
315
+ args,
316
+ });
93
317
  // For vitest/jest, write JSON to a temp file instead of relying on
94
318
  // stdout capture. On Windows, npx.cmd can swallow or mangle stdout,
95
319
  // causing "No JSON output found" errors.
96
320
  // Mocha outputs JSON to stdout, so we don't use --outputFile for it.
97
321
  const useOutputFile = detected === "jest" || detected === "vitest";
98
322
  const tempPath = useOutputFile ? join(tmpdir(), `pare-test-${randomUUID()}.json`) : "";
99
- const { cmd, cmdArgs } = getRunCommand(detected, extraArgs);
323
+ const { cmd, cmdArgs } = getRunCommand(detected, extraArgs, { coverage });
100
324
  if (useOutputFile) {
101
325
  cmdArgs.push(`--outputFile=${tempPath}`);
102
326
  }
103
- const result = await run(cmd, cmdArgs, { cwd, timeout: 180_000 });
327
+ const result = await run(cmd, cmdArgs, { cwd, timeout: TEST_CLI_TIMEOUT_MS });
104
328
  // Combine stdout and stderr for parsing (some frameworks write to stderr)
105
329
  const output = result.stdout + "\n" + result.stderr;
106
330
  let testRun;