Two findings from Copilot's suppressed block.
1. resolveBrowserName treated a present-but-empty SMOKE_BROWSER as unset
and returned chromium. The test that covered it even documented WHY
that is dangerous — GitHub Actions renders an undefined matrix key as
"" rather than omitting the variable — and then asserted the unsafe
behavior. A misspelled matrix key would run chromium inside a job named
for another engine and report green: the same false-coverage outcome
the unrecognized-value branch exists to prevent, reached by a typo
instead of a bad value.
Only an absent variable now selects the default. Empty throws, naming
the likely cause and the actual remedy ('unset it', which is not the
same as setting it to nothing). The workflow always renders a real
value, so nothing in CI is affected.
2. install-smoke-browser.mjs had no tests. Its I/O half runs on every
smoke and fails loudly; its argument half does not — every caller
passes nothing or the literal 'chromium', so the precedence rule is
asserted by nothing at runtime. It matters: pack:verify passes chromium
BECAUSE it launches chromium, and if the environment won,
SMOKE_BROWSER=webkit npm run pack:verify would install one engine and
launch another, surfacing as a misleading "Executable doesn't exist".
resolveRequestedBrowser is now exported and pure; execution moved
behind main() with the entrypoint guard verify-typecheck-coverage.mjs
uses, so importing the module for tests cannot trigger an install.
Both mutation-verified: restoring either behavior fails the tests that
claim to cover it.
Addresses the suppressed comments on Copilot's fifth review of #2133.
Signed-off-by: cliffhall <cliff@futurescale.com>
88 lines
3.2 KiB
JavaScript
88 lines
3.2 KiB
JavaScript
/**
|
|
* Unit tests for the smoke browser installer's argument resolution (#2086).
|
|
*
|
|
* The installer's I/O half — resolving the Playwright CLI and spawning it — is
|
|
* exercised on every `npm run smoke:web:*`, so it fails loudly the moment it
|
|
* breaks. Its *argument* half is not: every caller in the repo passes either
|
|
* nothing or the literal `chromium`, so the precedence rule below is asserted by
|
|
* nothing at runtime, and getting it wrong produces a mismatch (install one
|
|
* engine, launch another) that surfaces as a misleading "Executable doesn't
|
|
* exist" rather than as the wiring error it is.
|
|
*
|
|
* Importing this module must not run an install; `main()` is behind an
|
|
* entrypoint guard for exactly that reason, and these tests are also what would
|
|
* catch that guard regressing — an unguarded module would try to install a
|
|
* browser the moment the suite imported it.
|
|
*/
|
|
|
|
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import { resolveRequestedBrowser } from "./install-smoke-browser.mjs";
|
|
import {
|
|
BROWSER_ENV_VAR,
|
|
DEFAULT_BROWSER,
|
|
SUPPORTED_BROWSERS,
|
|
} from "./lib/headless-browser.mjs";
|
|
|
|
describe("resolveRequestedBrowser", () => {
|
|
it("falls back to the environment when no argument is given", () => {
|
|
assert.equal(resolveRequestedBrowser([], {}), DEFAULT_BROWSER);
|
|
assert.equal(
|
|
resolveRequestedBrowser([], { [BROWSER_ENV_VAR]: "webkit" }),
|
|
"webkit",
|
|
);
|
|
});
|
|
|
|
it("lets an explicit argument beat the environment", () => {
|
|
// The `pack:verify` invariant. It passes `chromium` because it LAUNCHES
|
|
// chromium; if the environment won here, `SMOKE_BROWSER=webkit npm run
|
|
// pack:verify` would install WebKit and then launch Chromium.
|
|
assert.equal(
|
|
resolveRequestedBrowser(["chromium"], { [BROWSER_ENV_VAR]: "webkit" }),
|
|
"chromium",
|
|
);
|
|
for (const name of SUPPORTED_BROWSERS) {
|
|
assert.equal(
|
|
resolveRequestedBrowser([name], { [BROWSER_ENV_VAR]: "firefox" }),
|
|
name,
|
|
);
|
|
}
|
|
});
|
|
|
|
it("rejects an unsupported argument, naming it and the allowed set", () => {
|
|
assert.throws(
|
|
() => resolveRequestedBrowser(["safari"], {}),
|
|
(err) => {
|
|
assert.match(err.message, /unsupported browser "safari"/);
|
|
SUPPORTED_BROWSERS.forEach((name) =>
|
|
assert.ok(err.message.includes(name)),
|
|
);
|
|
return true;
|
|
},
|
|
);
|
|
});
|
|
|
|
it("does not let a bad argument fall through to the environment", () => {
|
|
// Silently using SMOKE_BROWSER after ignoring a typo'd argument would
|
|
// install an engine nobody asked for, and the npm script's typo would
|
|
// survive unnoticed.
|
|
assert.throws(
|
|
() =>
|
|
resolveRequestedBrowser(["chrome"], { [BROWSER_ENV_VAR]: "firefox" }),
|
|
/unsupported browser "chrome"/,
|
|
);
|
|
});
|
|
|
|
it("propagates the environment's own validation, empty included", () => {
|
|
// Delegation, not a second copy of the rules — so the deny-by-default
|
|
// behavior cannot drift between the two entry points.
|
|
assert.throws(
|
|
() => resolveRequestedBrowser([], { [BROWSER_ENV_VAR]: "safari" }),
|
|
/is not a supported browser/,
|
|
);
|
|
assert.throws(
|
|
() => resolveRequestedBrowser([], { [BROWSER_ENV_VAR]: "" }),
|
|
/set but empty/,
|
|
);
|
|
});
|
|
});
|