Setting the file. One moment.
Frame Packets Test · Product Launch Video · heygen-com/hyperframes · Skills Docs
ContentsBack to the top of the page (opens in a new tab)
scripts/ frame-packets.test.mjs
JavaScript · 144 lines · 7 KB
9 function write ( path , contents ) {
10 mkdirSync ( dirname (path), { recursive: true });
11 writeFileSync (path, contents);
12 }
13
14 test ( "packets inline the blueprint body and the Scene-cited rule recipes" , () => {
15 const project = mkdtempSync ( join ( tmpdir (), "plv-packets-" ));
16 write ( join (project, "frame.md" ), "# tokens \n " );
17 write (
18 join (project, "STORYBOARD.md" ),
19 `--- \n format: 1920x1080 \n --- \n\n ## Frame 1 — Hook \n\n - duration: 3s \n - src: compositions/frames/01-hook.html \n - blueprint: dataviz-countup \n - scene: hero stat punches in \n\n Scene 1 (0.0–1.5s): the stat enters via spring-pop-entrance, then counting-dynamic-scale runs the tally. \n\n ## Frame 2 — Freeform \n\n - duration: 4s \n - src: compositions/frames/02-freeform.html \n - blueprint: compose \n\n Scene 1 (0.0–4.0s): a quiet hold, no named motion. \n ` ,
20 );
21
22 const result = buildFramePackets ({ projectDir: project });
23 assert. equal (result. length , 2 );
24
25 const hook = readFileSync (result[ 0 ].path, "utf8" );
26 assert. match (hook, /## Selected blueprint: dataviz-countup/ );
27 assert. match (hook, /## Selected motion rule: spring-pop-entrance/ );
28 assert. match (hook, /## Selected motion rule: counting-dynamic-scale/ );
29 assert. match (hook, /RULES_DIR: / );
30
31 const freeform = readFileSync (result[ 1 ].path, "utf8" );
32 assert. doesNotMatch (freeform, /## Selected blueprint/ );
33 assert. doesNotMatch (freeform, /## Selected motion rule/ );
34 });
35
36 test ( "_role.md is the core contract + this workflow's delta, verbatim" , () => {
37 const project = mkdtempSync ( join ( tmpdir (), "plv-role-" ));
38 write ( join (project, "frame.md" ), "# tokens \n " );
39 write (
40 join (project, "STORYBOARD.md" ),
41 `--- \n format: 1920x1080 \n --- \n\n ## Frame 1 — Hook \n\n - duration: 3s \n - src: compositions/frames/01-hook.html \n ` ,
42 );
43
44 buildFramePackets ({ projectDir: project });
45 const rolePath = join (project, ".hyperframes" , "frame-packets" , "_role.md" );
46 assert. ok ( existsSync (rolePath));
47 const role = readFileSync (rolePath, "utf8" );
48 assert. match (role, /# Frame worker — core contract/ );
49 assert. match (role, /# Frame worker — product-launch delta/ );
50 });
51
52 test ( "packet validation is atomic and leaves no partial output on overflow" , () => {
53 const project = mkdtempSync ( join ( tmpdir (), "plv-atomic-" ));
54 const outDir = join (project, ".hyperframes" , "frame-packets" );
55 write ( join (project, "frame.md" ), "# tokens \n " );
56 write (
57 join (project, "STORYBOARD.md" ),
58 `--- \n format: 1920x1080 \n --- \n\n ## Frame 1 — Big \n\n - duration: 3s \n - src: compositions/frames/01-big.html \n\n ${"padding line \n " . repeat ( 300 ) }` ,
59 );
60
61 assert. throws (
62 () => buildFramePackets ({ projectDir: project, outDir, maxPacketBytes: 2_000 }),
63 /limit 2000/ ,
64 );
65 assert. equal ( existsSync (outDir), false );
66 });
67
68 // ── the blueprint qualifier ──────────────────────────────────────────────────
69 // Regression: visual-design.md documents `blueprint:` as the id plus a
70 // `(Reproduce)` / `(Adapt)` qualifier, and prints `dataviz-countup (Adapt)` as
71 // its worked example. The resolver used the raw field as the filename, so every
72 // qualified blueprint looked for a file that cannot exist and inlined "" —
73 // packets shipped without the document the frame was designed against, and the
74 // run still reported success. The cases above only ever used bare ids.
75
76 test ( "a qualified blueprint resolves to the same body as the bare id" , () => {
77 const project = mkdtempSync ( join ( tmpdir (), "plv-blueprint-qualified-" ));
78 write ( join (project, "frame.md" ), "# tokens \n " );
79 write (
80 join (project, "STORYBOARD.md" ),
81 `--- \n format: 1920x1080 \n --- \n\n ## Frame 1 — Adapted \n\n - duration: 3s \n - src: compositions/frames/01-adapted.html \n - blueprint: device-surface-showcase (Adapt) \n\n ## Frame 2 — Reproduced \n\n - duration: 3s \n - src: compositions/frames/02-reproduced.html \n - blueprint: device-surface-showcase (Reproduce) \n\n ## Frame 3 — Bare \n\n - duration: 3s \n - src: compositions/frames/03-bare.html \n - blueprint: device-surface-showcase \n ` ,
82 );
83
84 const packets = buildFramePackets ({ projectDir: project });
85 const blueprintSections = packets. map (( packet ) => {
86 const body = readFileSync (packet.path, "utf8" );
87 const start = body. indexOf ( "## Selected blueprint:" );
88 assert. notEqual (start, - 1 , `${ packet . frameId } inlined no blueprint` );
89 return body. slice (start);
90 });
91
92 assert. match (blueprintSections[ 0 ], /## Selected blueprint: device-surface-showcase \n / );
93 // The qualifier is direction for the worker, not a different document: all
94 // three frames must inline byte-identical blueprint bodies.
95 assert. equal ( new Set (blueprintSections).size, 1 );
96 });
97
98 test ( "a qualified `compose` still selects no blueprint" , () => {
99 const project = mkdtempSync ( join ( tmpdir (), "plv-blueprint-compose-" ));
100 write ( join (project, "frame.md" ), "# tokens \n " );
101 write (
102 join (project, "STORYBOARD.md" ),
103 `--- \n format: 1920x1080 \n --- \n\n ## Frame 1 — Freeform \n\n - duration: 3s \n - src: compositions/frames/01-freeform.html \n - blueprint: compose (Adapt) \n ` ,
104 );
105
106 const [ packet ] = buildFramePackets ({ projectDir: project });
107
108 assert. doesNotMatch ( readFileSync (packet.path, "utf8" ), /## Selected blueprint/ );
109 });
110
111 test ( "a blueprint with no file fails the run instead of shipping an empty section" , () => {
112 const project = mkdtempSync ( join ( tmpdir (), "plv-blueprint-missing-" ));
113 const outDir = join (project, ".hyperframes" , "frame-packets" );
114 write ( join (project, "frame.md" ), "# tokens \n " );
115 write (
116 join (project, "STORYBOARD.md" ),
117 `--- \n format: 1920x1080 \n --- \n\n ## Frame 1 — Typo \n\n - duration: 3s \n - src: compositions/frames/01-typo.html \n - blueprint: device-surface-showcses \n ` ,
118 );
119
120 assert. throws (
121 () => buildFramePackets ({ projectDir: project, outDir }),
122 /01-typo: blueprint "device-surface-showcses" has no file/ ,
123 );
124 assert. equal ( existsSync (outDir), false );
125 });
126
127 test ( "an uninstalled animation skill degrades with a warning, it does not fail the run" , () => {
128 // hyperframes-animation installs on demand, so an absent blueprints/ means the
129 // library isn't there yet — not that the frame named a bad id. Matches how an
130 // absent rules/ already behaves.
131 const project = mkdtempSync ( join ( tmpdir (), "plv-blueprint-uninstalled-" ));
132 write ( join (project, "frame.md" ), "# tokens \n " );
133 write (
134 join (project, "STORYBOARD.md" ),
135 `--- \n format: 1920x1080 \n --- \n\n ## Frame 1 — Hook \n\n - duration: 3s \n - src: compositions/frames/01-hook.html \n - blueprint: dataviz-countup (Adapt) \n ` ,
136 );
137
138 const [ packet ] = buildFramePackets ({
139 projectDir: project,
140 animationDir: join (project, "absent-animation-skill" ),
141 });
142
143 assert. doesNotMatch ( readFileSync (packet.path, "utf8" ), /## Selected blueprint/ );
144 });