11/** @jest -environment jsdom */
22
3+ import { BakeQueue } from "@canvas/players/bake-queue" ;
34import { Html5Player , bakeScaleFor , captureFramesInFlight } from "@canvas/players/html5-player" ;
45import type { Edit } from "@core/edit-session" ;
56import { EditEvent , InternalEvent } from "@core/events/edit-events" ;
@@ -33,6 +34,14 @@ jest.mock("@canvas/players/player", () => ({
3334 return this . resolvedTiming . length ;
3435 }
3536
37+ getStart ( ) {
38+ return this . resolvedTiming . start ;
39+ }
40+
41+ getEnd ( ) {
42+ return this . resolvedTiming . start + this . resolvedTiming . length ;
43+ }
44+
3645 setResolvedTiming ( timing : { start : number ; length : number } ) {
3746 this . resolvedTiming = { ...timing } ;
3847 this . clipConfiguration . start = timing . start ;
@@ -119,6 +128,7 @@ function createPlayer() {
119128 const edit = {
120129 size : { width : 1080 , height : 1920 } ,
121130 isPlaying : false ,
131+ playbackTime : 0 ,
122132 getHtml5Prerender : ( ) => "on-load" ,
123133 getInternalEvents : ( ) => events ,
124134 getResolvedEdit : ( ) => ( { output : { resolution : "hd" } } ) ,
@@ -1083,17 +1093,34 @@ it("reports a failed re-bake and keeps the frames it has", async () => {
10831093 expect ( harness . capturedFrames ) . toBe ( frames ) ;
10841094} ) ;
10851095
1096+ type SharedQueue = { bakes : BakeQueue } ;
1097+
1098+ // Stands in for another clip's bake at the head of the shared queue.
1099+ function holdCaptureQueue ( ) : { drain : ( ) => void ; restore : ( ) => void } {
1100+ const { bakes } = Html5Player as unknown as SharedQueue ;
1101+ const held = new BakeQueue ( ) ;
1102+ let drain = ( ) => { } ;
1103+ const gate = new Promise < void > ( resolve => {
1104+ drain = resolve ;
1105+ } ) ;
1106+ // Under the playhead from the start, so it runs ahead of any clip that joins after it.
1107+ held . add ( { start : ( ) => 0 , end : ( ) => Infinity , playhead : ( ) => 0 } , ( ) => gate ) ;
1108+ Object . assign ( Html5Player , { bakes : held } ) ;
1109+ return { drain, restore : ( ) => Object . assign ( Html5Player , { bakes } ) } ;
1110+ }
1111+
1112+ // Earlier tests can leave a bake that never settles on the shared queue.
1113+ function emptyCaptureQueue ( ) : ( ) => void {
1114+ const { bakes } = Html5Player as unknown as SharedQueue ;
1115+ Object . assign ( Html5Player , { bakes : new BakeQueue ( ) } ) ;
1116+ return ( ) => Object . assign ( Html5Player , { bakes } ) ;
1117+ }
1118+
10861119it ( "drops a re-bake queued for content that has since changed, leaving the newer capture in flight" , async ( ) => {
10871120 const { player, harness } = createStillPlayer ( ) ;
10881121 Object . assign ( harness , { capturedFrames : [ new Blob ( [ "baked 0" ] ) ] , capturedHash : "original@0.5" , capturedScale : 0.5 , mode : "playback" } ) ;
10891122 shownAt ( harness , 0.8 ) ;
1090- const { captureChain } = Html5Player as unknown as { captureChain : Promise < unknown > } ;
1091- let drain = ( ) => { } ;
1092- Object . assign ( Html5Player , {
1093- captureChain : new Promise < void > ( resolve => {
1094- drain = resolve ;
1095- } )
1096- } ) ;
1123+ const queue = holdCaptureQueue ( ) ;
10971124 try {
10981125 player . update ( 0 , 0 ) ;
10991126 const rebake = harness . captureInFlight ! ;
@@ -1103,7 +1130,7 @@ it("drops a re-bake queued for content that has since changed, leaving the newer
11031130 await player . reloadAsset ( ) ;
11041131 const newer = harness . captureInFlight ;
11051132 expect ( newer ) . not . toBeNull ( ) ;
1106- drain ( ) ;
1133+ queue . drain ( ) ;
11071134 await rebake ;
11081135 expect ( harness . captureInFlight ) . toBe ( newer ) ;
11091136
@@ -1112,29 +1139,10 @@ it("drops a re-bake queued for content that has since changed, leaving the newer
11121139 expect ( html5CachePut ) . toHaveBeenCalledWith ( "changed@0.5" , expect . anything ( ) ) ;
11131140 expect ( html5CachePut ) . not . toHaveBeenCalledWith ( "changed@1" , expect . anything ( ) ) ;
11141141 } finally {
1115- Object . assign ( Html5Player , { captureChain } ) ;
1142+ queue . restore ( ) ;
11161143 }
11171144} ) ;
11181145
1119- // Stands in for another clip's bake at the head of the shared queue.
1120- function holdCaptureQueue ( ) : { drain : ( ) => void ; restore : ( ) => void } {
1121- const { captureChain } = Html5Player as unknown as { captureChain : Promise < unknown > } ;
1122- let drain = ( ) => { } ;
1123- Object . assign ( Html5Player , {
1124- captureChain : new Promise < void > ( resolve => {
1125- drain = resolve ;
1126- } )
1127- } ) ;
1128- return { drain, restore : ( ) => Object . assign ( Html5Player , { captureChain } ) } ;
1129- }
1130-
1131- // Earlier tests can leave a capture that never settles on the shared queue.
1132- function emptyCaptureQueue ( ) : ( ) => void {
1133- const { captureChain } = Html5Player as unknown as { captureChain : Promise < unknown > } ;
1134- Object . assign ( Html5Player , { captureChain : Promise . resolve ( ) } ) ;
1135- return ( ) => Object . assign ( Html5Player , { captureChain } ) ;
1136- }
1137-
11381146it ( "adopts cached frames while another clip's bake holds the queue" , async ( ) => {
11391147 const { harness, events } = createStillPlayer ( ) ;
11401148 const queue = holdCaptureQueue ( ) ;
@@ -1202,15 +1210,26 @@ it("keeps the queue's bakes in order when a cached clip sits between them", asyn
12021210 }
12031211} ) ;
12041212
1205- it ( "leaves no capture's frames on the shared queue once it settles" , async ( ) => {
1206- const { harness } = createStillPlayer ( ) ;
1207- cacheLib . html5CacheGet . mockResolvedValueOnce ( { pngs : [ new Blob ( [ "cached 0" ] ) ] , fps : 1 , frameCount : 1 } ) ;
1213+ it ( "bakes the clip that plays first ahead of one whose page loaded first" , async ( ) => {
1214+ const later = createStillPlayer ( ) ;
1215+ const opening = createStillPlayer ( ) ;
1216+ Object . assign ( later . player , { getStart : ( ) => 54.4 , getEnd : ( ) => 80.37 } ) ;
1217+ Object . assign ( opening . player , { getStart : ( ) => 0 , getEnd : ( ) => 24.4 } ) ;
1218+ const baked : string [ ] = [ ] ;
1219+ const bake = ( name : string ) => async ( ) => {
1220+ baked . push ( name ) ;
1221+ return null ;
1222+ } ;
1223+ jest . spyOn ( later . harness , "runCapture" ) . mockImplementation ( bake ( "later" ) ) ;
1224+ jest . spyOn ( opening . harness , "runCapture" ) . mockImplementation ( bake ( "opening" ) ) ;
1225+ jest . spyOn ( console , "warn" ) . mockImplementation ( ( ) => { } ) ;
12081226 const restore = emptyCaptureQueue ( ) ;
12091227 try {
1210- harness . beginCapture ( ) ;
1211- await until ( ( ) => harness . capturedFrames !== null ) ;
1228+ later . harness . beginCapture ( ) ;
1229+ opening . harness . beginCapture ( ) ;
1230+ await until ( ( ) => baked . length === 2 ) ;
12121231
1213- await expect ( ( Html5Player as unknown as { captureChain : Promise < unknown > } ) . captureChain ) . resolves . toBeUndefined ( ) ;
1232+ expect ( baked ) . toEqual ( [ "opening" , "later" ] ) ;
12141233 } finally {
12151234 restore ( ) ;
12161235 }
0 commit comments