fix: fail smoke:web:app on async crashes, not just sync page errors
Copilot's second pass (suppressed comments) caught a real gap: the smoke listened only for `pageerror`, which is the *synchronous* half of the uncaught-crash class. Its async twin — an unhandled rejection, or a failed dynamic import (this app lazy-loads chunks) — is not a `pageerror`; Chromium reports it on the console channel. So an async crash during the app-open path could leave the smoke green. smoke-web-browser.mjs already solved this; this now mirrors it exactly — same FATAL_CONSOLE pattern, same split between hard failures and diagnostics, so benign noise (a font-CDN miss, a React warning) still can't flake CI. Fatal console errors are now included both in the catch-block diagnostics and in the final assertion, which is where they were missing. Confirmed working on a passing run: a benign 409 subresource error is reported as a non-fatal diagnostic rather than failing the smoke. Full `npm run ci` green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XAR2Nr9kXbrywFNUVoTe9F
This commit is contained in:
parent
3f4f68699e
commit
556aaca298
1 changed files with 36 additions and 3 deletions
|
|
@ -89,6 +89,12 @@ const HOST = "127.0.0.1";
|
|||
const PORT = process.env.SMOKE_WEB_APP_PORT ?? "6297";
|
||||
const TOKEN = "smoke-web-app-token";
|
||||
const APP_TOOL = "mcp_app_demo";
|
||||
// Console messages that are the async half of the uncaught-crash class (an
|
||||
// unhandled rejection or a failed dynamic import). Hard failures; every other
|
||||
// console error is a diagnostic, so benign font-CDN / React-warning noise can't
|
||||
// flake CI. Kept identical to smoke-web-browser.mjs, which documents the
|
||||
// reasoning at length.
|
||||
const FATAL_CONSOLE = /^Uncaught\b|Failed to fetch dynamically imported module/;
|
||||
// The URL the test server announces on startup. NOT derived from the config's
|
||||
// port: createTestServerHttp resolves its port with findAvailablePort(), which
|
||||
// walks UPWARD from the configured value when it's taken — so the config port is
|
||||
|
|
@ -219,10 +225,19 @@ try {
|
|||
browser = await loadChromium();
|
||||
const page = await browser.newPage();
|
||||
|
||||
// Uncaught *synchronous* page errors. Their *async* twin — an unhandled
|
||||
// rejection or a failed dynamic import — is not a `pageerror`; Chromium
|
||||
// reports it on the console channel instead, so both are captured and both
|
||||
// are hard failures. Same split as smoke:web:browser; see FATAL_CONSOLE there.
|
||||
const pageErrors = [];
|
||||
const consoleErrors = [];
|
||||
page.on("pageerror", (err) =>
|
||||
pageErrors.push(err instanceof Error ? err.message : String(err)),
|
||||
);
|
||||
page.on("console", (msg) => {
|
||||
if (msg.type() === "error") consoleErrors.push(msg.text());
|
||||
});
|
||||
const fatalConsole = () => consoleErrors.filter((m) => FATAL_CONSOLE.test(m));
|
||||
|
||||
// Deep link: connect, switch to the Apps tab, pre-select the app tool, and
|
||||
// fire "Open App". autoConnect/autoOpen must equal the session token (CSRF
|
||||
|
|
@ -285,15 +300,33 @@ try {
|
|||
try {
|
||||
await Promise.race([server.whenChildExits(), drive()]);
|
||||
} catch (err) {
|
||||
const diagnostics = [
|
||||
...pageErrors,
|
||||
...fatalConsole().map((m) => `console: ${m}`),
|
||||
];
|
||||
await fail(
|
||||
`${err instanceof Error ? err.message : String(err)}${
|
||||
pageErrors.length ? ` — page errors: ${pageErrors.join("; ")}` : ""
|
||||
diagnostics.length
|
||||
? ` — page diagnostics: ${diagnostics.join("; ")}`
|
||||
: ""
|
||||
}`,
|
||||
);
|
||||
}
|
||||
|
||||
if (pageErrors.length > 0) {
|
||||
await fail(`app logged uncaught error(s): ${pageErrors.join("; ")}`);
|
||||
// Hard failures: any uncaught sync page error, plus the console errors that
|
||||
// are the async half of the same class.
|
||||
const fatal = [...pageErrors, ...fatalConsole()];
|
||||
if (fatal.length > 0) {
|
||||
await fail(`app logged uncaught error(s): ${fatal.join("; ")}`);
|
||||
}
|
||||
|
||||
// Non-fatal console errors: surface them so a real problem isn't invisible,
|
||||
// without failing on benign subresource/warning noise.
|
||||
const benignConsole = consoleErrors.filter((m) => !FATAL_CONSOLE.test(m));
|
||||
if (benignConsole.length > 0) {
|
||||
console.log(
|
||||
`smoke:web:app note — ${benignConsole.length} non-fatal console error(s): ${benignConsole.join("; ")}`,
|
||||
);
|
||||
}
|
||||
|
||||
console.log(
|
||||
|
|
|
|||
Loading…
Reference in a new issue