inspector/scripts/lib/win-shell-args.test.mjs
cliffhall c93552139c fix(scripts): keep the resolver local, complete the cmd.exe quoting
Copilot review round 3, all three real:

- `require.resolve.paths()` also appends Node's GLOBAL FOLDERS
  ($HOME/.node_modules, $HOME/.node_libraries, $PREFIX/lib/node) and any
  NODE_PATH entries, so round 2's lookup could satisfy a missing repo
  install from a GLOBALLY installed typescript/vite — measuring the
  programs with the wrong compiler instead of producing the actionable
  "run npm install" this helper promises. Filter to the node_modules
  directories on fromDir's own ancestor chain.
- The quoting predicate missed characters cmd.exe still parses, notably
  parentheses (`C:\Temp(1)`), and quoting cannot suppress `%VAR%` at all
  — expansion happens before quotes are processed. Extracted to
  lib/win-shell-args.mjs with a broad predicate and a hard error on `%`
  rather than a silently different path. `platform` is a parameter so the
  Windows behavior is exercised on the POSIX machines that run the suite.
- AGENTS.md claimed pack-and-verify spawns via `process.execPath`; it does
  not, and cannot — its children are `npm` and the installed `.bin` shim.
  State the exception and why.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01387r3MotGk52hrkfjzHFsN
Signed-off-by: cliffhall <cliff@futurescale.com>
2026-08-17 00:18:40 -04:00

54 lines
2.1 KiB
JavaScript

// Tests for `win-shell-args.mjs` (#1939). The platform is a parameter rather
// than a read of `process.platform`, so the Windows behaviour is exercised on
// the Linux/macOS machines that actually run this suite — otherwise every
// assertion below would be skipped exactly where the bug lives.
import { test } from "node:test";
import assert from "node:assert/strict";
import { quoteWinArg, winShellArgs } from "./win-shell-args.mjs";
test("passes args through untouched off Windows", () => {
const args = ["pack", "--pack-destination", "/tmp/dir with spaces"];
assert.deepEqual(winShellArgs(args, "linux"), args);
assert.deepEqual(winShellArgs(args, "darwin"), args);
});
test("quotes a path with a space (the user-profile tmpdir case)", () => {
assert.equal(
quoteWinArg("C:\\Users\\First Last\\AppData\\Local\\Temp"),
'"C:\\Users\\First Last\\AppData\\Local\\Temp"',
);
});
test("quotes cmd.exe metacharacters, parentheses included", () => {
// Parentheses group in cmd.exe, so `C:\Temp(1)` is syntax, not a path — the
// case a space-only predicate misses.
for (const meta of ["(", ")", "&", "|", "<", ">", "^", "!", ";", ",", "="]) {
const arg = `C:\\Temp${meta}1`;
assert.equal(quoteWinArg(arg), `"${arg}"`, `unquoted: ${arg}`);
}
});
test("leaves an inert argument unquoted", () => {
for (const arg of ["pack", "--json", "C:\\Users\\dev\\AppData\\Temp"]) {
assert.equal(quoteWinArg(arg), arg);
}
});
test("doubles an embedded quote", () => {
assert.equal(quoteWinArg('a"b c'), '"a""b c"');
});
test("throws on `%` rather than silently mis-expanding it", () => {
// cmd.exe expands %NAME% before it processes quotes, so quoting cannot save
// this one — the child would receive a different path than we passed.
assert.throws(() => quoteWinArg("C:\\%TEMP%\\pack"), /cannot safely pass/);
assert.throws(() => winShellArgs(["ok", "%X%"], "win32"), /cannot safely/);
});
test("winShellArgs maps every element on win32", () => {
assert.deepEqual(
winShellArgs(["pack", "--pack-destination", "C:\\a b\\c"], "win32"),
["pack", "--pack-destination", '"C:\\a b\\c"'],
);
});