Files
millionco__react-doctor/packages/react-doctor/tests/strip-unknown-cli-flags.test.ts
Aiden Bai 1971506440 feat(cli): add React-aware runtime traces (#1653)
* feat(cli): record React-aware browser traces

Add an interactive Playwright scanner that streams DevTools traces to disk and reports React rendering hotspots for humans and agents.

* fix(cli): use shared telemetry anonymizer

Keep runtime URL redaction on the canonical core utility after the telemetry helper consolidation.

* feat(cli): visualize component renders during traces

Flash labeled component outlines during runtime scans so users can connect slow interactions to the React work they trigger.

* feat(skill): add runtime performance workflow

Guide agents through repeatable React traces so performance findings stay tied to measured interactions.

* docs(cli): guide runtime scan setup

Explain the recording flow, browser isolation, authenticated sessions, output formats, and trace privacy directly in command help.

* fix(cli): harden runtime trace capture

Preserve evidence across navigations while keeping structured output, telemetry, trace files, and attached-browser cleanup safe under failures and cancellation.

* fix(cli): preserve latest navigation evidence

Keep cross-document layout shifts comparable and retain the most recent bounded performance entries so the recorded interaction remains represented.

* fix(cli): bound runtime scan browser resources

Prevent trace deadlocks, cross-tab capture, and unbounded browser evidence while preserving navigation data through an isolated validated relay.

* fix(cli): preserve busy-page runtime snapshots

Stop tracing concurrently with final probe collection and allow busy pages enough time to flush their bounded performance evidence.

* feat(cli): prompt for runtime scan URL

Let people start an interactive runtime scan without remembering the URL argument while keeping agents and CI deterministic.

* feat(cli): suggest local apps for runtime scans

Replace the bare URL field with a polished chooser that discovers common localhost development servers while preserving custom URL entry.

* fix(cli): use scan terminology in runtime flow

Keep the interactive copy aligned with the command users invoked instead of switching between recording and profiling language.

* fix(cli): center runtime chooser in full terminal

Render the interactive URL chooser as a terminal-sized view so it no longer appears anchored to the previous cursor position.

* fix(cli): harden runtime scan edge cases

Capture popup evidence without leaking browser pages, support redirected interactive output and IPv6 localhost apps, and keep bounded reports honest.
2026-08-16 19:34:13 -07:00

265 lines
8.3 KiB
TypeScript

import { describe, expect, it } from "vite-plus/test";
import { stripUnknownCliFlags } from "../src/cli/utils/strip-unknown-cli-flags.js";
const stripUserArguments = (userArguments: ReadonlyArray<string>): string[] =>
stripUnknownCliFlags(["node", "react-doctor", ...userArguments]).slice(2);
describe("stripUnknownCliFlags", () => {
it("drops unknown root flags before Commander can treat them as directory arguments", () => {
expect(stripUserArguments(["--offline", "."])).toEqual(["."]);
expect(stripUserArguments([".", "--offline"])).toEqual(["."]);
});
it("keeps known root flags and their values", () => {
expect(
stripUserArguments([
".",
"--debug",
"--no-score",
"--project",
"web",
"--changed-files-from",
"/tmp/react-doctor-changed-files.txt",
"--category",
"Security",
"--diff",
"main",
"--fail-on=warning",
]),
).toEqual([
".",
"--debug",
"--no-score",
"--project",
"web",
"--changed-files-from",
"/tmp/react-doctor-changed-files.txt",
"--category",
"Security",
"--diff",
"main",
"--fail-on=warning",
]);
});
it("keeps --scope / --base and consumes their values (no value leaks as a positional)", () => {
// Regression: the action invokes `react-doctor . --scope changed --changed-files-from <f>`.
// If --scope isn't a known value-taking flag, its value `changed` leaks as a 2nd
// positional and Commander throws "too many arguments".
expect(
stripUserArguments([".", "--scope", "changed", "--changed-files-from", "/tmp/changed.txt"]),
).toEqual([".", "--scope", "changed", "--changed-files-from", "/tmp/changed.txt"]);
expect(stripUserArguments([".", "--scope", "lines", "--base", "main"])).toEqual([
".",
"--scope",
"lines",
"--base",
"main",
]);
});
it("keeps --output-dir and consumes its value (no value leaks as a positional)", () => {
expect(stripUserArguments([".", "--output-dir", "./doctor-report"])).toEqual([
".",
"--output-dir",
"./doctor-report",
]);
expect(stripUserArguments(["--output-dir=./doctor-report"])).toEqual([
"--output-dir=./doctor-report",
]);
});
it("drops unknown install flags while keeping install options", () => {
expect(stripUserArguments(["install", "--offline", "--cwd", ".", "--agent-hooks"])).toEqual([
"install",
"--cwd",
".",
"--agent-hooks",
]);
});
it("keeps a trailing optional-value flag without pushing undefined", () => {
expect(stripUserArguments(["--diff"])).toEqual(["--diff"]);
expect(stripUserArguments([".", "--diff"])).toEqual([".", "--diff"]);
});
it("keeps an optional-value flag followed by another flag", () => {
expect(stripUserArguments(["--diff", "--json"])).toEqual(["--diff", "--json"]);
});
it("keeps the --color / --no-color flags so the color resolver can see them", () => {
expect(stripUserArguments([".", "--color"])).toEqual([".", "--color"]);
expect(stripUserArguments([".", "--no-color"])).toEqual([".", "--no-color"]);
expect(stripUserArguments(["install", "--no-color", "--cwd", "."])).toEqual([
"install",
"--no-color",
"--cwd",
".",
]);
expect(stripUserArguments(["experimental-tui", ".", "--color"])).toEqual([
"experimental-tui",
".",
"--color",
]);
expect(stripUserArguments(["experimental-tui", ".", "--no-color"])).toEqual([
"experimental-tui",
".",
"--no-color",
]);
});
it("keeps the blocking level on the experimental TUI command", () => {
expect(stripUserArguments(["experimental-tui", ".", "--blocking", "none"])).toEqual([
"experimental-tui",
".",
"--blocking",
"none",
]);
});
it("keeps the max-duration budget on the experimental TUI command", () => {
expect(stripUserArguments(["experimental-tui", ".", "--max-duration", "30"])).toEqual([
"experimental-tui",
".",
"--max-duration",
"30",
]);
});
it("keeps the supply-chain opt-out on the experimental TUI command", () => {
expect(stripUserArguments(["experimental-tui", ".", "--no-supply-chain"])).toEqual([
"experimental-tui",
".",
"--no-supply-chain",
]);
});
it("keeps the --no-telemetry alias for --no-score", () => {
expect(stripUserArguments([".", "--no-telemetry"])).toEqual([".", "--no-telemetry"]);
});
it("keeps the global cache opt-out", () => {
expect(stripUserArguments([".", "--no-cache"])).toEqual([".", "--no-cache"]);
expect(stripUserArguments(["experimental-tui", ".", "--no-cache"])).toEqual([
"experimental-tui",
".",
"--no-cache",
]);
});
it("keeps the phase opt-out flags so Commander can toggle each scan phase", () => {
expect(stripUserArguments([".", "--no-lint", "--no-dead-code", "--no-supply-chain"])).toEqual([
".",
"--no-lint",
"--no-dead-code",
"--no-supply-chain",
]);
expect(stripUserArguments([".", "--supply-chain"])).toEqual([".", "--supply-chain"]);
});
it("keeps standard scan flags on the design subcommand", () => {
expect(
stripUserArguments(["design", "./apps/web", "--verbose", "--scope", "changed", "--json"]),
).toEqual(["design", "./apps/web", "--verbose", "--scope", "changed", "--json"]);
});
it("keeps color flags on the version subcommand and drops unknown ones", () => {
expect(stripUserArguments(["version", "--no-color"])).toEqual(["version", "--no-color"]);
expect(stripUserArguments(["version", "--color"])).toEqual(["version", "--color"]);
expect(stripUserArguments(["version", "--offline"])).toEqual(["version"]);
});
it("keeps rules subcommand options and positionals", () => {
expect(
stripUserArguments(["rules", "explain", "react-doctor/no-danger", "-c", "/tmp/project"]),
).toEqual(["rules", "explain", "react-doctor/no-danger", "-c", "/tmp/project"]);
expect(
stripUserArguments(["rules", "list", "--category", "Performance", "--configured", "--json"]),
).toEqual(["rules", "list", "--category", "Performance", "--configured", "--json"]);
expect(
stripUserArguments(["rules", "enable", "no-danger", "--severity", "error", "--offline"]),
).toEqual(["rules", "enable", "no-danger", "--severity", "error"]);
});
it("keeps the why subcommand positional and options, dropping unknown ones", () => {
expect(
stripUserArguments(["why", "src/App.tsx:42", "--project", "web", "-c", "/tmp/project"]),
).toEqual(["why", "src/App.tsx:42", "--project", "web", "-c", "/tmp/project"]);
expect(stripUserArguments(["why", "src/App.tsx:42", "--offline"])).toEqual([
"why",
"src/App.tsx:42",
]);
});
it("keeps runtime scan options and their values", () => {
expect(
stripUserArguments([
"scan",
"http://localhost:3000",
"--format",
"jsonl",
"--cdp",
"http://127.0.0.1:9222",
"--trace-out",
"/tmp/runtime.json.gz",
"--offline",
]),
).toEqual([
"scan",
"http://localhost:3000",
"--format",
"jsonl",
"--cdp",
"http://127.0.0.1:9222",
"--trace-out",
"/tmp/runtime.json.gz",
]);
});
it("keeps color flags on rules subcommands so the color resolver can see them", () => {
expect(stripUserArguments(["rules", "list", "--no-color"])).toEqual([
"rules",
"list",
"--no-color",
]);
expect(stripUserArguments(["rules", "explain", "no-danger", "--color"])).toEqual([
"rules",
"explain",
"no-danger",
"--color",
]);
});
it("keeps ci subcommand options (including the gate toggles) and drops unknown ones", () => {
expect(
stripUserArguments([
"ci",
"config",
"--blocking",
"error",
"--scope",
"full",
"--no-comment",
"--review-comments",
"--offline",
]),
).toEqual([
"ci",
"config",
"--blocking",
"error",
"--scope",
"full",
"--no-comment",
"--review-comments",
]);
expect(stripUserArguments(["ci", "install", "--provider", "gitlab-ci", "--pr"])).toEqual([
"ci",
"install",
"--provider",
"gitlab-ci",
"--pr",
]);
});
});