import { describe, it, expect } from 'vitest'; import baseline from '../../../apps/web/lib/parity/baseline.json' assert { type: 'json' }; import type { BannerSpec, ArtboardSpec } from '@banner-studio/types'; import { buildRuntimeHtml } from '../src/build-runtime-html.js'; // Wrap the parity baseline into the full BannerSpec envelope. The baseline // JSON is intentionally partial (no campaign_id/version_id/etc.) — those // envelope fields are slice-only and not relevant to layout parity. The // build-runtime-html test owns the responsibility of providing them. function makeSpec(): BannerSpec { // Deep-clone via JSON so per-test mutations (e.g. content edits) don't // bleed into other tests through the shared imported JSON module. const artboard = JSON.parse( JSON.stringify(baseline.artboards[0]) ) as ArtboardSpec; return { template_id: 'demo-300x250', template_version: 1, campaign_id: 'demo-campaign', version_id: 'v1', copy_variant: 'A', generated_at: '2026-05-15T00:00:00.000Z', ai_reasoning: { asset_selection: 'hero crop chosen for visual punch', copy_rationale: 'concise headline', variant_selection: 'A', animation_rationale: 'staggered fade-in keeps focus' }, artboards: [artboard], ad_server_profile: 'iab_standard', click_destinations: [{ id: 'cta', url: 'https://example.com/buy' }] }; } const FAKE_GSAP = '/* fake gsap.min.js */ var gsap = { timeline: function(){ return { to: function(){return this;} }; } };'; describe('buildRuntimeHtml', () => { it('inlines the spec JSON in a typed script tag', () => { const html = buildRuntimeHtml({ spec: makeSpec(), gsapSource: FAKE_GSAP }); expect(html).toContain('Hello & "world"'; const html = buildRuntimeHtml({ spec, gsapSource: FAKE_GSAP }); // The visible headline must be rendered as escaped text, not as a real script. expect(html).toContain('<script>alert(1)</script>Hello &'); // It must NOT contain a literal outside the inline // JSON (the JSON also escapes <\/script>). const stripped = html.replace(/