From 710c9d8dedaa9bd1a84abb88988b06f1f91b7cba Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Tue, 20 Jan 2026 00:26:45 -0500 Subject: [PATCH] Add shared Playwright UI test helpers --- tests/e2e/helpers/uiTestUtils.ts | 38 ++++++++++++++++++++++++++++++++ tests/e2e/popover.spec.ts | 16 +++++--------- tests/e2e/toast-service.spec.ts | 4 +++- tests/e2e/ui-modals.spec.ts | 30 +++++++++---------------- 4 files changed, 56 insertions(+), 32 deletions(-) create mode 100644 tests/e2e/helpers/uiTestUtils.ts diff --git a/tests/e2e/helpers/uiTestUtils.ts b/tests/e2e/helpers/uiTestUtils.ts new file mode 100644 index 00000000..c0fcc44a --- /dev/null +++ b/tests/e2e/helpers/uiTestUtils.ts @@ -0,0 +1,38 @@ +import type { Page } from "@playwright/test"; + +export async function applyReducedMotion(page: Page): Promise { + await page.emulateMedia({ reducedMotion: "reduce" }); + await page.addStyleTag({ + content: ` + * { + animation-duration: 0s !important; + animation-delay: 0s !important; + transition-duration: 0s !important; + transition-delay: 0s !important; + } + `, + }); +} + +export function failOnConsoleErrors(page: Page): void { + page.on("console", (message) => { + if (message.type() === "error") { + throw new Error(`Console error: ${message.text()}`); + } + }); + + page.on("pageerror", (error) => { + throw error; + }); +} + +export async function forceOpenModal(page: Page, selector: string): Promise { + await page.evaluate((modalSelector) => { + const element = document.querySelector(modalSelector); + if (!element) { + return; + } + element.classList.remove("hidden"); + element.setAttribute("data-open", "true"); + }, selector); +} diff --git a/tests/e2e/popover.spec.ts b/tests/e2e/popover.spec.ts index 329c8fbe..b43acdfa 100644 --- a/tests/e2e/popover.spec.ts +++ b/tests/e2e/popover.spec.ts @@ -1,9 +1,11 @@ import { expect, test } from "@playwright/test"; import type { Locator } from "@playwright/test"; +import { applyReducedMotion, failOnConsoleErrors } from "./helpers/uiTestUtils"; test.describe("popover layout scenarios", () => { test.beforeEach(async ({ page }) => { - await page.emulateMedia({ reducedMotion: "reduce" }); + await applyReducedMotion(page); + failOnConsoleErrors(page); await page.goto("/docs/popover-scenarios.html", { waitUntil: "networkidle" }); }); @@ -150,13 +152,6 @@ test.describe("popover layout scenarios", () => { }); test("stress tests popover triggers without console errors", async ({ page }) => { - const consoleErrors: string[] = []; - page.on("console", (message) => { - if (message.type() === "error") { - consoleErrors.push(message.text()); - } - }); - await page.locator('[data-test-open-modal]').click(); await expect(page.locator('[data-test-modal]')).toBeVisible(); @@ -180,15 +175,14 @@ test.describe("popover layout scenarios", () => { await page.click("body"); await page.waitForTimeout(60); await expect(page.locator('[data-popover-state="open"]')).toHaveCount(0); - expect(consoleErrors).toHaveLength(0); - consoleErrors.length = 0; } }); }); test.describe("popover demo alignments", () => { test.beforeEach(async ({ page }) => { - await page.emulateMedia({ reducedMotion: "reduce" }); + await applyReducedMotion(page); + failOnConsoleErrors(page); await page.goto("/views/dev/popover-demo.html", { waitUntil: "networkidle" }); }); diff --git a/tests/e2e/toast-service.spec.ts b/tests/e2e/toast-service.spec.ts index 9d7ad051..59c0b137 100644 --- a/tests/e2e/toast-service.spec.ts +++ b/tests/e2e/toast-service.spec.ts @@ -1,8 +1,10 @@ import { expect, test } from "@playwright/test"; +import { applyReducedMotion, failOnConsoleErrors } from "./helpers/uiTestUtils"; test.describe("toast service in full DOM", () => { test("renders, expires, and dismisses toasts", async ({ page }) => { - await page.emulateMedia({ reducedMotion: "reduce" }); + await applyReducedMotion(page); + failOnConsoleErrors(page); await page.goto("/docs/kitchen-sink.html", { waitUntil: "networkidle" }); await page.addScriptTag({ diff --git a/tests/e2e/ui-modals.spec.ts b/tests/e2e/ui-modals.spec.ts index 28b8cfee..e1021bc9 100644 --- a/tests/e2e/ui-modals.spec.ts +++ b/tests/e2e/ui-modals.spec.ts @@ -1,19 +1,15 @@ import { expect, test } from "@playwright/test"; +import { + applyReducedMotion, + failOnConsoleErrors, + forceOpenModal, +} from "./helpers/uiTestUtils"; test.describe("login modal flows", () => { test.beforeEach(async ({ page }) => { - await page.emulateMedia({ reducedMotion: "reduce" }); + await applyReducedMotion(page); + failOnConsoleErrors(page); await page.goto("/components/login-modal.html", { waitUntil: "networkidle" }); - await page.addStyleTag({ - content: ` - * { - animation-duration: 0s !important; - animation-delay: 0s !important; - transition-duration: 0s !important; - transition-delay: 0s !important; - } - `, - }); await page.addScriptTag({ type: "module", @@ -175,7 +171,8 @@ test.describe("component modal pages", () => { for (const pageName of modalPages) { test(`closes ${pageName} modal`, async ({ page }) => { - await page.emulateMedia({ reducedMotion: "reduce" }); + await applyReducedMotion(page); + failOnConsoleErrors(page); await page.goto(`/components/${pageName}.html`, { waitUntil: "networkidle", }); @@ -183,14 +180,7 @@ test.describe("component modal pages", () => { const modal = page.locator(".bv-modal"); await expect(modal).toHaveCount(1); - await page.evaluate(() => { - const element = document.querySelector(".bv-modal"); - if (!element) { - return; - } - element.classList.remove("hidden"); - element.setAttribute("data-open", "true"); - }); + await forceOpenModal(page, ".bv-modal"); await expect(modal).not.toHaveClass(/hidden/);