rspace-online/e2e/helpers/console-collector.ts

63 lines
1.9 KiB
TypeScript

/**
* Collects console errors and uncaught exceptions from a Playwright page.
* Filters out expected noise so tests can assert "no real errors".
*/
import type { Page, ConsoleMessage } from "@playwright/test";
/** Patterns to ignore — expected noise from EncryptID, HMR, analytics, SW */
const IGNORE_PATTERNS = [
/encryptid/i,
/session/i,
/service.?worker/i,
/umami/i,
/HMR/i,
/hot.?update/i,
/favicon/i,
/\[vite\]/i,
/net::ERR_/, // network errors in background fetches
/ResizeObserver loop/i, // benign browser warning
/Failed to register.*SW/i, // service worker blocked by config
/attachShadow/i, // benign: duplicate shadow root from HMR/re-registration
/already hosts a shadow tree/i,
/Failed to resolve module specifier/i, // known importmap gaps (e.g. three-forcegraph)
/folk-graph-viewer.*Failed to load/i, // rNetwork 3d-force-graph load failure (known)
/Failed to load 3d-force-graph/i,
];
function isNoise(text: string): boolean {
return IGNORE_PATTERNS.some((re) => re.test(text));
}
export class ConsoleCollector {
readonly errors: string[] = [];
readonly pageErrors: string[] = [];
constructor(page: Page) {
page.on("console", (msg: ConsoleMessage) => {
if (msg.type() === "error") {
const text = msg.text();
if (!isNoise(text)) this.errors.push(text);
}
});
page.on("pageerror", (err) => {
const text = err.message || String(err);
if (!isNoise(text)) this.pageErrors.push(text);
});
}
/** Returns all collected real errors (console.error + uncaught exceptions). */
get allErrors(): string[] {
return [...this.errors, ...this.pageErrors];
}
/** Asserts no real errors were logged. */
assertNoErrors() {
if (this.allErrors.length > 0) {
throw new Error(
`Unexpected console errors:\n${this.allErrors.map((e) => ` - ${e}`).join("\n")}`
);
}
}
}