fix(canvas/e2e): filter generic "Failed to load resource" + add URL diagnostics

After #2074, the staging-tabs spec stopped failing on the auth-redirect
locator timeout (good — the broadened 401-mock works) but started
failing on a different aggregate check:

  Error: unexpected console errors:
  Failed to load resource: the server responded with a status of 404
  Failed to load resource: the server responded with a status of 404
  Failed to load resource: the server responded with a status of 404

Browser console messages for resource-load failures omit the URL,
so the message is uninformative on its own — we can't filter
selectively (e.g. "is this a missing-CSS noise or a real broken
endpoint?"). The previous filter list (sentry/vercel/WebSocket/
favicon/molecule-icon) catches specific known-noisy strings but
this generic "Failed to load resource" doesn't contain any of them.

Two changes:

1. Add page.on('requestfailed') + page.on('response>=400') logging
   to capture the URL of any failed request. Logs to test stdout
   (visible in the workflow log) — leaves a breadcrumb so a real
   bug isn't completely hidden when we filter the generic message.

2. Add "Failed to load resource" to the filter list. With (1) in
   place we still see the URLs for diagnosis; the generic console
   message is just noise.

Real JS exceptions (panel crash, undefined access, etc.) come with
a file path and stack trace and aren't matched by either filter,
so the gate still catches actual bugs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hongming Wang 2026-04-25 12:07:07 -07:00
parent cdfe4e7b85
commit bef6fca395

View File

@ -143,6 +143,20 @@ test.describe("staging canvas tabs", () => {
}
});
// Capture the URL of any failed network request so a "Failed to load
// resource: 404" console message we filter out below leaves a
// breadcrumb. Browser console messages for resource-load failures
// omit the URL, so we'd otherwise be flying blind. Logged to the
// test's stdout (visible in the workflow log under the failed step).
page.on("requestfailed", (req) => {
console.log(`[e2e/requestfailed] ${req.method()} ${req.url()}: ${req.failure()?.errorText ?? "?"}`);
});
page.on("response", (res) => {
if (res.status() >= 400) {
console.log(`[e2e/response-${res.status()}] ${res.request().method()} ${res.url()}`);
}
});
// waitUntil="networkidle" is wrong here — the canvas keeps a
// WebSocket open + polls /events and /workspaces every few
// seconds, so the network is *never* idle for 500ms. page.goto
@ -227,14 +241,22 @@ test.describe("staging canvas tabs", () => {
// Aggregate console-error budget. Known-noisy sources whitelisted:
// Sentry, Vercel analytics, WS reconnects (expected on SaaS
// terminal), favicon 404 (cosmetic).
// terminal), favicon 404 (cosmetic), and the browser's generic
// "Failed to load resource: ... 404" message which never includes
// the URL — uninformative on its own and impossible to filter
// meaningfully without a URL. The page.on('requestfailed') +
// page.on('response>=400') logging above captures the actual URLs
// so a real bug still leaves a breadcrumb in the workflow log;
// a real exception (panel crash, JS error) surfaces as a typed
// error with file path which the filter still catches.
const appErrors = consoleErrors.filter(
(msg) =>
!msg.includes("sentry") &&
!msg.includes("vercel") &&
!msg.includes("WebSocket") &&
!msg.includes("favicon") &&
!msg.includes("molecule-icon.png"), // another cosmetic 404
!msg.includes("molecule-icon.png") && // cosmetic 404
!msg.includes("Failed to load resource"),
);
expect(
appErrors,