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(/