openclaw/scripts/lib/vitest-cli.mts
Peter Steinberger 0be787a80c
fix(test): preserve boolean options in focused test runs (#133449)
* fix(test): preserve native arguments through focused runs

Preserve native scalar validation, boolean operands, config spellings, and
original argument occurrences through direct and delegated test runs.
Keep the documented strict direct-config and delegated opt-in policies.

Classify native execution before preparing runtime, browser, or compiled
worker prerequisites. Keep pre-install planning dependency-free by moving
launch helpers into their existing owners, without lifecycle changes.

Fixes #133432.

* fix(test): preserve native runner cleanup boundaries

Keep shared native argument admission free of process-management imports.
Move unchanged exit and failure-trailer helpers into the existing Vitest
process owner so UI configuration no longer imports Windows process code.

Preserve the actual child exit signal until strict cleanup selects its
existing policy, then normalize the final return to a shell status. Numeric
143 remains a numeric exit and still rejects lingering descendants. Owner
cancellation, non-strict close-based release, positive group/pipe/claim
verification, and all finalizer timing constants remain unchanged.

The real-process regression failed before this owner repair and passes
after it, with descendant SIGTERM receipt, drained output, released claims,
and strict numeric-exit controls. The full candidate received fresh
independent Codex review at the configured P0 threshold.

Refs #133449, #133432.
2026-09-02 17:44:08 -07:00

79 lines
2.8 KiB
TypeScript

import { VITEST_SUBCOMMANDS } from "./vitest-cli-mode.mts";
function nativeHelpRequested(args: string[], parseCLI: typeof import("vitest/node").parseCLI) {
const controls: string[] = [];
for (const [index, original] of args.entries()) {
if (original === "--") {
break;
}
// CAC treats every prefix except exactly two dashes as a short-option group.
const arg = original.replace(/^---+/u, "-");
// Project only help onto native watch's boolean/short-alias grammar.
// parseCLI(help) prints and skips validation; only the real child may do that.
const projected = arg.startsWith("--")
? arg.replace(
/^--(no-)?(help|h)(?=[.=]|$)/u,
(_, no: string | undefined, name: string) =>
`--${no ?? ""}${name === "h" ? "w" : "watch"}`,
)
: arg.startsWith("-no-")
? arg.replace(/^-no-(help|h)$/u, (_, name: string) =>
name === "h" ? "-no-w" : "-no-watch",
)
: arg.replace(
/^-(?!-)([^=]+)/u,
(_, flags: string) => `-${flags.replace(/[^h]/gu, "x").replaceAll("h", "w")}`,
);
if (projected === arg || !/watch|w/u.test(projected)) {
continue;
}
controls.push(projected);
const value = args[index + 1];
if (value && !value.startsWith("-")) {
controls.push(value);
}
}
return Boolean(
parseCLI(["vitest", "run", ...controls], { allowUnknownOptions: true }).options.watch,
);
}
/** Validate admission without printing help/version or taking the real child's error ownership. */
export function parseVitestExecutionArgs(
args: string[],
parseCLI: typeof import("vitest/node").parseCLI,
) {
try {
if (nativeHelpRequested(args, parseCLI)) {
return null;
}
// The silent run prefix lets native operand parsing expose the original first
// positional; dotted booleans must not consume a real command such as run.
const probe = parseCLI(["vitest", "run", ...args], { allowUnknownOptions: true });
const command = probe.filter[0];
const namedCommand = command !== undefined && VITEST_SUBCOMMANDS.has(command);
if (
command === "list" ||
command === "init" ||
(!namedCommand && "version" in probe.options && probe.options.version)
) {
return null;
}
// Unlike --run --version, a named run with --version executes tests.
const parsed = parseCLI(["vitest", ...(namedCommand ? [] : ["run"]), ...args]);
const { options, filter } = parsed;
if (
options.listTags ||
options.clearCache ||
options.mergeReports ||
(options.standalone && !filter.length)
) {
return null;
}
return parsed;
} catch {
// Repeated help is truthy to CAC but the projection rejects repeated scalars.
// That and native invalid input must reach the real child without preparing builds.
return null;
}
}