@@ -5,9 +5,13 @@ import { setupDefaultRundown, setupMockShowStyleCompound } from '../../__mocks__
55import { defaultRundownPlaylist } from '../../__mocks__/defaultCollectionObjects.js'
66import { getRandomId } from '@sofie-automation/corelib/dist/lib'
77import { DBPart } from '@sofie-automation/corelib/dist/dataModel/Part'
8- import { resolveQueuedAdlibInsertTarget } from '../adlibUtils.js'
8+ import { insertQueuedPartWithPieces , resolveQueuedAdlibInsertTarget } from '../adlibUtils.js'
99import { runJobWithPlayoutModel } from '../lock.js'
1010import { SegmentOrphanedReason } from '@sofie-automation/corelib/dist/dataModel/Segment'
11+ import { QuickLoopMarkerType } from '@sofie-automation/corelib/dist/dataModel/RundownPlaylist/RundownPlaylist'
12+ import { handleActivateRundownPlaylist } from '../activePlaylistJobs.js'
13+ import { handleTakeNextPart } from '../take.js'
14+ import { QueuePartTarget } from '@sofie-automation/blueprints-integration'
1115
1216describe ( 'adlibUtils' , ( ) => {
1317 async function setupActivatedPlaylist ( rundownId : RundownId , playlistId : RundownPlaylistId ) {
@@ -321,4 +325,206 @@ describe('adlibUtils', () => {
321325 ) . toThrow ( 'Cannot queue part: target is in orphaned segment' )
322326 } )
323327 } )
328+
329+ describe ( 'insertQueuedPartWithPieces quick loop' , ( ) => {
330+ async function setupPlayingPlaylist ( ) {
331+ const context = setupDefaultJobEnvironment ( )
332+ const playlistId : RundownPlaylistId = protectString ( 'playlist0' )
333+ const rundownId : RundownId = getRandomId ( )
334+
335+ await context . mockCollections . RundownPlaylists . insertOne ( {
336+ ...defaultRundownPlaylist ( playlistId , context . studioId ) ,
337+ } )
338+
339+ const showStyleCompound = await setupMockShowStyleCompound ( context )
340+ await setupDefaultRundown ( context , showStyleCompound , playlistId , rundownId )
341+
342+ await handleActivateRundownPlaylist ( context , { playlistId, rehearsal : true } )
343+ await handleTakeNextPart ( context , { playlistId, fromPartInstanceId : null } )
344+
345+ return { context, playlistId, rundownId }
346+ }
347+
348+ function queuedPart ( target ?: QueuePartTarget ) {
349+ return {
350+ part : {
351+ _id : getRandomId ( ) ,
352+ externalId : 'queued_adlib' ,
353+ title : 'Queued adlib' ,
354+ expectedDurationWithTransition : undefined ,
355+ } ,
356+ pieces : [ ] ,
357+ target,
358+ }
359+ }
360+
361+ test ( 'extends quick loop when inserted immediately after current' , async ( ) => {
362+ const { context, playlistId } = await setupPlayingPlaylist ( )
363+
364+ await runJobWithPlayoutModel ( context , { playlistId } , null , async ( playoutModel ) => {
365+ const currentPartInstance = playoutModel . currentPartInstance
366+ expect ( currentPartInstance ) . toBeTruthy ( )
367+ if ( ! currentPartInstance ) throw new Error ( 'currentPartInstance not found' )
368+
369+ playoutModel . setQuickLoopMarker ( 'start' , {
370+ type : QuickLoopMarkerType . PART ,
371+ id : currentPartInstance . partInstance . part . _id ,
372+ } )
373+ playoutModel . setQuickLoopMarker ( 'end' , {
374+ type : QuickLoopMarkerType . PART ,
375+ id : currentPartInstance . partInstance . part . _id ,
376+ } )
377+
378+ const setQuickLoopMarker = jest . spyOn ( playoutModel , 'setQuickLoopMarker' )
379+
380+ const newPartInstance = await insertQueuedPartWithPieces (
381+ context ,
382+ playoutModel ,
383+ currentPartInstance ,
384+ queuedPart ( ) ,
385+ undefined
386+ )
387+
388+ expect ( newPartInstance . partInstance . segmentId ) . toEqual ( currentPartInstance . partInstance . segmentId )
389+ expect ( newPartInstance . partInstance . part . _rank ) . toBeGreaterThan (
390+ currentPartInstance . partInstance . part . _rank
391+ )
392+ expect ( playoutModel . playlist . nextPartInfo ?. partInstanceId ) . toEqual ( newPartInstance . partInstance . _id )
393+ expect ( setQuickLoopMarker ) . toHaveBeenCalledWith (
394+ 'end' ,
395+ expect . objectContaining ( {
396+ type : QuickLoopMarkerType . PART ,
397+ id : newPartInstance . partInstance . part . _id ,
398+ overridenId : currentPartInstance . partInstance . part . _id ,
399+ } )
400+ )
401+ } )
402+ } )
403+
404+ test ( 'extends quick loop when explicit target is the next part in the same segment' , async ( ) => {
405+ const { context, playlistId } = await setupPlayingPlaylist ( )
406+
407+ await runJobWithPlayoutModel ( context , { playlistId } , null , async ( playoutModel ) => {
408+ const currentPartInstance = playoutModel . currentPartInstance
409+ expect ( currentPartInstance ) . toBeTruthy ( )
410+ if ( ! currentPartInstance ) throw new Error ( 'currentPartInstance not found' )
411+
412+ const nextPartInSegment = playoutModel
413+ . getAllOrderedParts ( )
414+ . find (
415+ ( part ) =>
416+ part . segmentId === currentPartInstance . partInstance . segmentId &&
417+ part . _rank > currentPartInstance . partInstance . part . _rank
418+ )
419+ expect ( nextPartInSegment ) . toBeTruthy ( )
420+ if ( ! nextPartInSegment ) throw new Error ( 'nextPartInSegment not found' )
421+
422+ playoutModel . setQuickLoopMarker ( 'start' , {
423+ type : QuickLoopMarkerType . PART ,
424+ id : currentPartInstance . partInstance . part . _id ,
425+ } )
426+ playoutModel . setQuickLoopMarker ( 'end' , {
427+ type : QuickLoopMarkerType . PART ,
428+ id : currentPartInstance . partInstance . part . _id ,
429+ } )
430+
431+ const setQuickLoopMarker = jest . spyOn ( playoutModel , 'setQuickLoopMarker' )
432+
433+ const newPartInstance = await insertQueuedPartWithPieces (
434+ context ,
435+ playoutModel ,
436+ currentPartInstance ,
437+ queuedPart ( { targetPartId : unprotectString ( nextPartInSegment . _id ) } ) ,
438+ undefined
439+ )
440+
441+ expect ( newPartInstance . partInstance . segmentId ) . toEqual ( currentPartInstance . partInstance . segmentId )
442+ expect ( playoutModel . playlist . nextPartInfo ?. partInstanceId ) . toEqual ( newPartInstance . partInstance . _id )
443+ expect ( setQuickLoopMarker ) . toHaveBeenCalledWith (
444+ 'end' ,
445+ expect . objectContaining ( {
446+ type : QuickLoopMarkerType . PART ,
447+ id : newPartInstance . partInstance . part . _id ,
448+ } )
449+ )
450+ } )
451+ } )
452+
453+ test ( 'skips extending quick loop for explicit target in another segment' , async ( ) => {
454+ const { context, playlistId, rundownId } = await setupPlayingPlaylist ( )
455+
456+ const targetPart = await context . mockCollections . Parts . findOne ( {
457+ externalId : 'MOCK_PART_1_1' ,
458+ rundownId,
459+ } )
460+ expect ( targetPart ) . toBeTruthy ( )
461+ if ( ! targetPart ) throw new Error ( 'targetPart not found' )
462+
463+ await runJobWithPlayoutModel ( context , { playlistId } , null , async ( playoutModel ) => {
464+ const currentPartInstance = playoutModel . currentPartInstance
465+ expect ( currentPartInstance ) . toBeTruthy ( )
466+ if ( ! currentPartInstance ) throw new Error ( 'currentPartInstance not found' )
467+
468+ playoutModel . setQuickLoopMarker ( 'start' , {
469+ type : QuickLoopMarkerType . PART ,
470+ id : currentPartInstance . partInstance . part . _id ,
471+ } )
472+ playoutModel . setQuickLoopMarker ( 'end' , {
473+ type : QuickLoopMarkerType . PART ,
474+ id : currentPartInstance . partInstance . part . _id ,
475+ } )
476+
477+ const setQuickLoopMarker = jest . spyOn ( playoutModel , 'setQuickLoopMarker' )
478+
479+ const newPartInstance = await insertQueuedPartWithPieces (
480+ context ,
481+ playoutModel ,
482+ currentPartInstance ,
483+ queuedPart ( { targetPartId : unprotectString ( targetPart . _id ) } ) ,
484+ undefined
485+ )
486+
487+ expect ( newPartInstance . partInstance . segmentId ) . toEqual ( targetPart . segmentId )
488+ expect ( newPartInstance . partInstance . part . _rank ) . toBeLessThan ( targetPart . _rank )
489+ expect ( playoutModel . playlist . nextPartInfo ?. partInstanceId ) . toEqual ( newPartInstance . partInstance . _id )
490+ expect ( setQuickLoopMarker ) . not . toHaveBeenCalled ( )
491+ } )
492+ } )
493+
494+ test ( 'skips extending quick loop for explicit target before current' , async ( ) => {
495+ const { context, playlistId } = await setupPlayingPlaylist ( )
496+
497+ await runJobWithPlayoutModel ( context , { playlistId } , null , async ( playoutModel ) => {
498+ const currentPartInstance = playoutModel . currentPartInstance
499+ expect ( currentPartInstance ) . toBeTruthy ( )
500+ if ( ! currentPartInstance ) throw new Error ( 'currentPartInstance not found' )
501+
502+ playoutModel . setQuickLoopMarker ( 'start' , {
503+ type : QuickLoopMarkerType . PART ,
504+ id : currentPartInstance . partInstance . part . _id ,
505+ } )
506+ playoutModel . setQuickLoopMarker ( 'end' , {
507+ type : QuickLoopMarkerType . PART ,
508+ id : currentPartInstance . partInstance . part . _id ,
509+ } )
510+
511+ const setQuickLoopMarker = jest . spyOn ( playoutModel , 'setQuickLoopMarker' )
512+
513+ const newPartInstance = await insertQueuedPartWithPieces (
514+ context ,
515+ playoutModel ,
516+ currentPartInstance ,
517+ queuedPart ( { targetPartId : unprotectString ( currentPartInstance . partInstance . part . _id ) } ) ,
518+ undefined
519+ )
520+
521+ expect ( newPartInstance . partInstance . segmentId ) . toEqual ( currentPartInstance . partInstance . segmentId )
522+ expect ( newPartInstance . partInstance . part . _rank ) . toBeLessThan (
523+ currentPartInstance . partInstance . part . _rank
524+ )
525+ expect ( playoutModel . playlist . nextPartInfo ?. partInstanceId ) . toEqual ( newPartInstance . partInstance . _id )
526+ expect ( setQuickLoopMarker ) . not . toHaveBeenCalled ( )
527+ } )
528+ } )
529+ } )
324530} )
0 commit comments