From 58b42348091290ebb7fe3d6274ccb6f9362fddc0 Mon Sep 17 00:00:00 2001 From: James Date: Sun, 3 May 2026 01:06:38 +0000 Subject: [PATCH] test(producer): add variables-prod regression test for the variables stack MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit End-to-end Docker regression test that exercises the full variables chain: meta.json renderConfig.variables → harness → createRenderJob → RenderConfig → CaptureOptions → engine evaluateOnNewDocument → window.__hfVariables → getVariables() → DOM text → rendered pixels. Fixture (packages/producer/tests/variables-prod/): - src/index.html: composition with three declared variables (title, subtitle, bgColor) read via window.__hyperframes.getVariables() and rendered as positioned text on a colored background. No animation — keeps the regression frame-stable so it isolates "did the variables flow through?" from motion concerns. - meta.json: tags ["variables", "composition"] (runs in the existing fast shard's tag filter), renderConfig.variables provides override values the baseline reflects ("Override Title", "Override subtitle", #0a3d62). Defaults would produce a visibly different frame, so a failing baseline that reflects defaults means the variables didn't propagate. - output/output.mp4: Docker-generated baseline per the project's CLAUDE.md golden-baseline rule. Harness change (packages/producer/src/regression-harness.ts): - TestMetadata.renderConfig gains an optional variables field, validated as a JSON object in the meta.json validator. - The createRenderJob call site forwards renderConfig.variables to RenderConfig.variables, which the engine already consumes via evaluateOnNewDocument (PR #600). Verified: docker:test variables-prod passes 100/100 visual checkpoints and audio correlation 1.000 against the committed baseline. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/producer/src/regression-harness.ts | 15 ++ .../producer/tests/variables-prod/meta.json | 17 +++ .../tests/variables-prod/output/compiled.html | 137 ++++++++++++++++++ .../tests/variables-prod/output/output.mp4 | 3 + .../tests/variables-prod/src/index.html | 96 ++++++++++++ .../tests/variables-prod/src/silence.wav | Bin 0 -> 288078 bytes 6 files changed, 268 insertions(+) create mode 100644 packages/producer/tests/variables-prod/meta.json create mode 100644 packages/producer/tests/variables-prod/output/compiled.html create mode 100644 packages/producer/tests/variables-prod/output/output.mp4 create mode 100644 packages/producer/tests/variables-prod/src/index.html create mode 100644 packages/producer/tests/variables-prod/src/silence.wav diff --git a/packages/producer/src/regression-harness.ts b/packages/producer/src/regression-harness.ts index e5f919c1a..27cb2098f 100644 --- a/packages/producer/src/regression-harness.ts +++ b/packages/producer/src/regression-harness.ts @@ -36,6 +36,14 @@ type TestMetadata = { workers?: number; // Optional: auto-calculates if omitted /** Force HDR in the harness; omitted/false preserves historical SDR-only test behavior. */ hdr?: boolean; + /** + * Render-time variable overrides, equivalent to `hyperframes render + * --variables ''`. Injected as `window.__hfVariables` before any + * page script runs so the runtime helper `getVariables()` returns the + * merged result of declared defaults (`data-composition-variables`) + * and these overrides. Omit when the test doesn't exercise variables. + */ + variables?: Record; }; }; @@ -160,6 +168,12 @@ function validateMetadata(meta: unknown): TestMetadata { if (rc.hdr !== undefined && typeof rc.hdr !== "boolean") { throw new Error("meta.json: 'renderConfig.hdr' must be a boolean (or omit for false)"); } + if ( + rc.variables !== undefined && + (rc.variables === null || typeof rc.variables !== "object" || Array.isArray(rc.variables)) + ) { + throw new Error("meta.json: 'renderConfig.variables' must be a JSON object (or omitted)"); + } return m as TestMetadata; } @@ -601,6 +615,7 @@ async function runTestSuite( useGpu: false, debug: false, hdrMode: suite.meta.renderConfig.hdr ? "force-hdr" : "force-sdr", + variables: suite.meta.renderConfig.variables, }); await executeRenderJob(job, tempSrcDir, renderedOutputPath); diff --git a/packages/producer/tests/variables-prod/meta.json b/packages/producer/tests/variables-prod/meta.json new file mode 100644 index 000000000..c1d56becc --- /dev/null +++ b/packages/producer/tests/variables-prod/meta.json @@ -0,0 +1,17 @@ +{ + "name": "Variables Prod", + "description": "End-to-end regression for the variables system. Composition declares title/subtitle/bgColor in data-composition-variables and reads them via window.__hyperframes.getVariables(). renderConfig.variables provides overrides that flow CLI → producer → engine evaluateOnNewDocument → window.__hfVariables → helper merge → DOM text → rendered pixels. If any link in that chain breaks, the rendered text/background diverges from the baseline and PSNR fails.", + "tags": ["variables", "composition"], + "minPsnr": 30, + "maxFrameFailures": 0, + "minAudioCorrelation": 0.9, + "maxAudioLagWindows": 120, + "renderConfig": { + "fps": 24, + "variables": { + "title": "Override Title", + "subtitle": "Override subtitle", + "bgColor": "#0a3d62" + } + } +} diff --git a/packages/producer/tests/variables-prod/output/compiled.html b/packages/producer/tests/variables-prod/output/compiled.html new file mode 100644 index 000000000..6d7acf5b2 --- /dev/null +++ b/packages/producer/tests/variables-prod/output/compiled.html @@ -0,0 +1,137 @@ + + + + + + + + + +
+ +

+

+
+ + + + diff --git a/packages/producer/tests/variables-prod/output/output.mp4 b/packages/producer/tests/variables-prod/output/output.mp4 new file mode 100644 index 000000000..ba8eaf31e --- /dev/null +++ b/packages/producer/tests/variables-prod/output/output.mp4 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:be9a5689e27272b191fa566843e45f88f728b36f677a3db49f89e9193627a667 +size 117111 diff --git a/packages/producer/tests/variables-prod/src/index.html b/packages/producer/tests/variables-prod/src/index.html new file mode 100644 index 000000000..7a3c9e489 --- /dev/null +++ b/packages/producer/tests/variables-prod/src/index.html @@ -0,0 +1,96 @@ + + + + + + + + + +
+ +

+

+
+ + + + diff --git a/packages/producer/tests/variables-prod/src/silence.wav b/packages/producer/tests/variables-prod/src/silence.wav new file mode 100644 index 0000000000000000000000000000000000000000..b9113c424ab100136f5039d76583b0a2ddd79850 GIT binary patch literal 288078 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 literal 0 HcmV?d00001