Files
tibi-svelte-starter/tests/fixtures/console-monitor.ts

118 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Console/Network-Error-Monitor für Playwright-Tests.
*
* Überwacht Browser-Pages auf:
* - `pageerror` Uncaught Exceptions
* - `console.error` Alle console.error()-Aufrufe
* - `requestfailed` Fehlgeschlagene Netzwerk-Requests (nicht absichtlich abgebrochene)
*
* Verwendung in Fixtures:
* ```ts
* const monitor = attachConsoleMonitor(page)
* // … setup …
* await use(page)
* monitor.assertNoErrors()
* ```
*
* Fehler werden NICHT ignoriert, sondern in der App gefixt.
* IGNORED_PATTERNS nur für Infrastructure-Rauschen, das nicht in der App liegt.
*/
import type { Page } from "@playwright/test"
// ── Whitelist (nur Infrastructure-Rauschen, keine App-Fehler) ────────
const IGNORED_PATTERNS: RegExp[] = [
/browser-sync/i,
/cc\.webmakers\.de/i,
/sentry/i,
/Failed to load resource: net::ERR_/i,
/Failed to load resource: the server responded with a status of/i,
/TypeError: Failed to fetch/i,
]
// ── Types ────────────────────────────────────────────────────────────
interface ConsoleError {
type: "pageerror" | "console.error" | "request-failed"
message: string
}
export interface ConsoleMonitor {
getErrors(): ConsoleError[]
assertNoErrors(): void
}
// ── Implementation ───────────────────────────────────────────────────
function isIgnored(text: string): boolean {
return IGNORED_PATTERNS.some((pattern) => pattern.test(text))
}
export function attachConsoleMonitor(page: Page): ConsoleMonitor {
const errors: ConsoleError[] = []
// Inject script to capture detailed unhandled rejection info
page.addInitScript(() => {
window.addEventListener("unhandledrejection", (event) => {
const reason = event.reason
if (reason && typeof reason === "object" && !(reason instanceof Error)) {
try {
const detail = JSON.stringify(
reason,
(_key, val) => {
if (val instanceof Response) return `[Response ${val.status} ${val.url}]`
if (val instanceof Request) return `[Request ${val.method} ${val.url}]`
return val
},
2
)
console.error(`[unhandled-rejection-detail] ${detail}`)
} catch {
console.error(`[unhandled-rejection-detail] ${String(reason)}`)
}
}
})
})
page.on("pageerror", (error) => {
const msg = error.stack || error.message || String(error)
if (!isIgnored(msg)) {
errors.push({ type: "pageerror", message: msg })
}
})
page.on("console", (msg) => {
if (msg.type() === "error") {
const text = msg.text()
if (!isIgnored(text)) {
errors.push({ type: "console.error", message: text })
}
}
})
page.on("requestfailed", (request) => {
const failure = request.failure()?.errorText || "unknown"
if (failure === "net::ERR_ABORTED" || failure === "net::ERR_FAILED") return
const url = request.url()
if (isIgnored(url)) return
errors.push({
type: "request-failed",
message: `${request.method()} ${url} ${failure}`,
})
})
return {
getErrors: () => [...errors],
assertNoErrors() {
if (errors.length > 0) {
const summary = errors.map((e) => ` [${e.type}] ${e.message}`).join("\n")
throw new Error(
`Browser-Fehler während des Tests erkannt (${errors.length}):\n${summary}\n\n` +
`→ Fehler in der App fixen, NICHT in IGNORED_PATTERNS aufnehmen!`
)
}
},
}
}