From d458664b55df2be7f49c142716ff7c27d093ed38 Mon Sep 17 00:00:00 2001 From: gnezim Date: Sat, 25 Apr 2026 02:21:03 +0300 Subject: [PATCH] e2e: add console-error gate fixture with allowlist --- tests/e2e/fixtures/console-allowlist.json | 3 + tests/e2e/fixtures/console-gate.ts | 70 +++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 tests/e2e/fixtures/console-allowlist.json create mode 100644 tests/e2e/fixtures/console-gate.ts diff --git a/tests/e2e/fixtures/console-allowlist.json b/tests/e2e/fixtures/console-allowlist.json new file mode 100644 index 00000000..b5b010c3 --- /dev/null +++ b/tests/e2e/fixtures/console-allowlist.json @@ -0,0 +1,3 @@ +{ + "patterns": [] +} diff --git a/tests/e2e/fixtures/console-gate.ts b/tests/e2e/fixtures/console-gate.ts new file mode 100644 index 00000000..fb22c4e3 --- /dev/null +++ b/tests/e2e/fixtures/console-gate.ts @@ -0,0 +1,70 @@ +import { test as base, expect } from "@playwright/test"; +import fs from "node:fs"; +import path from "node:path"; + +interface AllowlistEntry { + pattern: string; + reason: string; +} + +interface Allowlist { + patterns: AllowlistEntry[]; +} + +const ALLOWLIST_PATH = path.join(__dirname, "console-allowlist.json"); + +function loadAllowlist(): RegExp[] { + const raw = fs.readFileSync(ALLOWLIST_PATH, "utf8"); + const parsed: Allowlist = JSON.parse(raw); + for (const entry of parsed.patterns) { + if (!entry.reason || entry.reason.trim() === "") { + throw new Error( + `console-allowlist.json: pattern ${JSON.stringify(entry.pattern)} has no reason` + ); + } + } + return parsed.patterns.map((e) => new RegExp(e.pattern)); +} + +const allowlist = loadAllowlist(); + +function isAllowed(message: string): boolean { + return allowlist.some((re) => re.test(message)); +} + +interface ConsoleGateFixtures { + consoleMessages: string[]; +} + +export const test = base.extend({ + consoleMessages: async ({ page }, use, testInfo) => { + const messages: string[] = []; + page.on("console", (msg) => { + const type = msg.type(); + if (type !== "error" && type !== "warning") return; + const text = `[${type}] ${msg.text()}`; + if (isAllowed(text)) return; + messages.push(text); + }); + page.on("pageerror", (err) => { + const text = `[pageerror] ${err.message}`; + if (!isAllowed(text)) messages.push(text); + }); + + await use(messages); + + if (messages.length > 0) { + testInfo.attachments.push({ + name: "console-violations.txt", + contentType: "text/plain", + body: Buffer.from(messages.join("\n"), "utf8"), + }); + throw new Error( + `Console gate: ${messages.length} disallowed message(s):\n` + + messages.map((m) => ` ${m}`).join("\n") + ); + } + }, +}); + +export { expect };