@@ -543,6 +543,75 @@ describe("shadow reload store ownership and readiness", () => {
543543 unmount ( root ) ;
544544 } ) ;
545545
546+ it ( "takes a section the edit removed off the timeline, though the old document still has it" , ( ) => {
547+ const { getApi, gen, liveIframe, shadowIframe, root } = setup ( ) ;
548+ const host = ( id : string ) =>
549+ `<div id="${ id } " data-composition-id="${ id } " data-composition-src="compositions/${ id } .html" data-start="0" data-duration="3"></div>` ;
550+ liveIframe . contentDocument ! . body . innerHTML = `<div data-composition-id="main">${ host ( "benefit" ) } <p id="a"></p></div>` ;
551+ shadowIframe . contentDocument ! . body . innerHTML = `<div data-composition-id="main"><p id="a"></p></div>` ;
552+ const a = { id : "a" , domId : "a" , tag : "p" , start : 0 , duration : 3 , track : 0 } ;
553+ const benefit = {
554+ id : "benefit" ,
555+ domId : "benefit" ,
556+ tag : "div" ,
557+ kind : "composition" as const ,
558+ start : 0 ,
559+ duration : 3 ,
560+ track : 1 ,
561+ compositionSrc : "compositions/benefit.html" ,
562+ } ;
563+ act ( ( ) => usePlayerStore . getState ( ) . setElements ( [ a , benefit ] ) ) ;
564+ ( shadowIframe . contentWindow as Record < string , unknown > ) . __clipManifest = {
565+ source : "hf-preview" ,
566+ type : "timeline" ,
567+ durationInFrames : 1260 ,
568+ clips : [
569+ { id : "a" , label : "A" , start : 0 , duration : 3 , track : 0 , kind : "element" , tagName : "p" } ,
570+ ] ,
571+ } ;
572+ act ( ( ) => getApi ( ) . onShadowIframeLoad ( gen ) ) ;
573+ act ( ( ) => getApi ( ) . onShadowReadyChange ( gen , true ) ) ;
574+ expect ( usePlayerStore . getState ( ) . elements . map ( ( element ) => element . id ) ) . toEqual ( [ "a" ] ) ;
575+ unmount ( root ) ;
576+ } ) ;
577+
578+ it ( "keeps a section the new document still hosts but its manifest misses, without dropping it once" , ( ) => {
579+ const { getApi, gen, liveIframe, shadowIframe, root } = setup ( ) ;
580+ const film = `<div data-composition-id="main"><div id="benefit" data-composition-id="benefit"
581+ data-composition-src="compositions/benefit.html" data-start="0" data-duration="3"></div><p id="a"></p></div>` ;
582+ liveIframe . contentDocument ! . body . innerHTML = film ;
583+ shadowIframe . contentDocument ! . body . innerHTML = film ;
584+ const a = { id : "a" , domId : "a" , tag : "p" , start : 0 , duration : 3 , track : 0 } ;
585+ // As enrichment adds it: no kind.
586+ const benefit = {
587+ id : "benefit" ,
588+ domId : "benefit" ,
589+ tag : "div" ,
590+ start : 0 ,
591+ duration : 3 ,
592+ track : 1 ,
593+ compositionSrc : "compositions/benefit.html" ,
594+ } ;
595+ act ( ( ) => usePlayerStore . getState ( ) . setElements ( [ a , benefit ] ) ) ;
596+ const seen : string [ ] [ ] = [ ] ;
597+ const stop = usePlayerStore . subscribe ( ( state ) =>
598+ seen . push ( state . elements . map ( ( element ) => element . id ) ) ,
599+ ) ;
600+ ( shadowIframe . contentWindow as Record < string , unknown > ) . __clipManifest = {
601+ source : "hf-preview" ,
602+ type : "timeline" ,
603+ durationInFrames : 1260 ,
604+ clips : [
605+ { id : "a" , label : "A" , start : 0 , duration : 3 , track : 0 , kind : "element" , tagName : "p" } ,
606+ ] ,
607+ } ;
608+ act ( ( ) => getApi ( ) . onShadowIframeLoad ( gen ) ) ;
609+ act ( ( ) => getApi ( ) . onShadowReadyChange ( gen , true ) ) ;
610+ stop ( ) ;
611+ expect ( seen . every ( ( ids ) => ids . includes ( "benefit" ) ) ) . toBe ( true ) ;
612+ unmount ( root ) ;
613+ } ) ;
614+
546615 it ( "never writes a failed shadow's timeline into the store" , ( ) => {
547616 vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => { } ) ;
548617 const { getApi, gen, root } = setup ( ) ;
0 commit comments