// tests/welcome/inline-svg.test.ts — Plan 04-06 Wave 0 RED → GREEN unit test // for the UI-SPEC dark-logo `currentColor` strategy (Option A). // // SCOPE — source-contract pin only (node-env file-read + string assertions). // // vitest is configured with `environment: 'node'` (vitest.config.ts:18) and // the project ships no DOM-emulation library — neither jsdom, happy-dom nor // linkedom is in node_modules. This test pins the SOURCE TEXT contract of // the inline-SVG injection strategy (the SVG source carries the // `currentColor` stroke, welcome.ts uses the `?raw` import + DOMParser + // replaceChildren, globals.d.ts declares the `*.svg?raw` ambient module). // // It does NOT instantiate any document, does NOT call populateMark, does // NOT invoke DOMParser at runtime. The runtime behavior (live // injection into the welcome page DOM and the currentColor CSS cascade // through `.welcome-hero__mark { color: var(--mks-fg-inverse); }`) is // verified by the host-side UAT harness assertion A35 (driveA35 in // tests/uat/lib/harness-page-driver.ts) — A35 opens welcome.html as a real // Puppeteer tab so populateMark() actually runs, then reads // `getComputedStyle().stroke` on the injected to prove the cascade // resolves. This split mirrors the project's two-layer test approach // (source contract under vitest + live-DOM behavior under the Puppeteer // harness) — verified at re-plan iter-2 (BLOCKER 1 resolution). // // References: // - .planning/phases/04-harden-clean-up-optional/04-UI-SPEC.md // §"Implementation amendment" (currentColor + inline technique) // - .planning/phases/04-harden-clean-up-optional/04-RESEARCH.md // §SVG currentColor cascade (W3C SVG2 §13.3) // - tests/i18n/manifest-i18n.test.ts (the canonical node-env file-read // + string-assertion scaffold this test mirrors). // - https://vite.dev/guide/assets.html#importing-asset-as-string // (Vite `?raw` query suffix returns module content as a string). import { describe, expect, it } from 'vitest'; import { readFileSync } from 'node:fs'; import { resolve as resolvePath } from 'node:path'; const MARK_SVG_PATH = resolvePath( process.cwd(), 'src/shared/brand/mokosh-mark.svg', ); const WELCOME_TS_PATH = resolvePath(process.cwd(), 'src/welcome/welcome.ts'); const GLOBALS_DTS_PATH = resolvePath(process.cwd(), 'globals.d.ts'); describe('UI-SPEC dark-logo currentColor strategy — source-level contract', () => { it('mokosh-mark.svg: root uses stroke="currentColor" + canonical viewBox; legacy #181b2a stroke removed', () => { const text = readFileSync(MARK_SVG_PATH, 'utf8'); expect(text).toContain('stroke="currentColor"'); expect(text).toContain('viewBox="0 0 32 32"'); expect(text).not.toContain('stroke="#181b2a"'); }); it('welcome.ts: uses `?raw` import + DOMParser + replaceChildren; no `?url` import, no innerHTML (MV3 CSP discipline / T-04-06-01)', () => { const text = readFileSync(WELCOME_TS_PATH, 'utf8'); expect(text).toContain('mokosh-mark.svg?raw'); expect(text).toContain('DOMParser'); expect(text).toContain('replaceChildren'); expect(text).not.toContain('mokosh-mark.svg?url'); expect(text).not.toContain('innerHTML'); }); it('globals.d.ts: declares the `*.svg?raw` ambient module (mirror of the existing `*.svg?url` block)', () => { const text = readFileSync(GLOBALS_DTS_PATH, 'utf8'); expect(text).toContain("declare module '*.svg?raw'"); }); });