From f0b88d4d17f9f06297c371cafaca156690a4fcb0 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 26 May 2026 07:52:41 +0200 Subject: [PATCH] =?UTF-8?q?test(04-06):=20Wave=200=20=E2=80=94=20inline-SV?= =?UTF-8?q?G=20source-contract=20RED=20+=20cursor-visibility=20regression?= =?UTF-8?q?=20pin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tests/welcome/inline-svg.test.ts (NEW; 3 tests, node-env source-contract): - Test A: mokosh-mark.svg carries stroke="currentColor" + viewBox="0 0 32 32" (currently RED — SVG still has stroke="#181b2a"). - Test B: welcome.ts uses ?raw import + DOMParser + replaceChildren and does NOT use innerHTML (MV3 CSP discipline / T-04-06-01). Currently RED — welcome.ts still ?url + . - Test C: globals.d.ts declares the *.svg?raw ambient module. Currently RED — only *.svg?url + *.webm?url declared. - tests/build/cursor-visibility.test.ts (NEW; 1 test, node-env file-grep): - GREEN-on-arrival regression pin for the cursor: 'always' literal at src/offscreen/recorder.ts:285 (shipped opportunistically Plan 01-09). - Mirrors the canonical tests/i18n/manifest-i18n.test.ts scaffold (readFileSync + expect(text).toContain(...)) — vitest is environment:'node' and the project ships no DOM-emulation library, so the inline-svg test pins source TEXT only; the live-DOM injection + currentColor cascade is verified by the host-side harness assertion A35 (Task 3). --- tests/build/cursor-visibility.test.ts | 45 ++++++++++++++++++ tests/welcome/inline-svg.test.ts | 67 +++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 tests/build/cursor-visibility.test.ts create mode 100644 tests/welcome/inline-svg.test.ts diff --git a/tests/build/cursor-visibility.test.ts b/tests/build/cursor-visibility.test.ts new file mode 100644 index 0000000..5289520 --- /dev/null +++ b/tests/build/cursor-visibility.test.ts @@ -0,0 +1,45 @@ +// tests/build/cursor-visibility.test.ts — Plan 04-06 cursor-visibility +// defensive regression pin (RESEARCH Finding 4 closure). +// +// Pins that src/offscreen/recorder.ts:285 carries the `cursor: 'always'` +// getDisplayMedia constraint — the original Phase-5 deferral was lifted +// opportunistically in Plan 01-09 (recorder.ts:285) and the back-patched +// Plan 04-06 closes the audit loop by both (a) re-verifying the literal +// via this defensive grep test and (b) flipping the stale "deferred to +// Phase 5" lines in .planning/phases/01-stabilize-video-pipeline/ +// 01-07-SUMMARY.md (Task 3 of Plan 04-06). +// +// Polarity at land: GREEN-on-arrival — the literal already exists at +// recorder.ts:285 (verified at 04-06 re-plan iter-2; confirmed via +// `grep -n "cursor: 'always'" src/offscreen/recorder.ts`). +// +// SCOPE: node-env file-grep regression pin. Does NOT instantiate the +// MediaStream pipeline (out of scope for unit tests; the runtime visibility +// behavior is operator-empirical and out of the automated test loop — +// per UI-SPEC § the operator confirms the cursor appears in +// `video/last_30sec.webm` playback after a SAVE). +// +// References: +// - .planning/phases/04-harden-clean-up-optional/04-RESEARCH.md +// §Finding 4 (cursor visibility verification scope). +// - .planning/phases/01-stabilize-video-pipeline/01-07-SUMMARY.md +// (back-patched in Plan 04-06 Task 3 to remove the stale Phase-5 +// deferral framing — the constraint shipped in Plan 01-09). +// - https://www.w3.org/TR/screen-capture/#dom-mediatrackconstraintset-cursor +// (W3C Screen Capture spec — `cursor` constraint values). + +import { describe, expect, it } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { resolve as resolvePath } from 'node:path'; + +const RECORDER_PATH = resolvePath( + process.cwd(), + 'src/offscreen/recorder.ts', +); + +describe("cursor visibility constraint shipped (Plan 01-09 -> verified Plan 04-06)", () => { + it("src/offscreen/recorder.ts contains the cursor: 'always' getDisplayMedia constraint literal", () => { + const text = readFileSync(RECORDER_PATH, 'utf8'); + expect(text).toContain("cursor: 'always'"); + }); +}); diff --git a/tests/welcome/inline-svg.test.ts b/tests/welcome/inline-svg.test.ts new file mode 100644 index 0000000..168439d --- /dev/null +++ b/tests/welcome/inline-svg.test.ts @@ -0,0 +1,67 @@ +// 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'"); + }); +});