@@ -1069,4 +1069,348 @@ describe('findLookaheadObjectsForPart', () => {
10691069 )
10701070 expect ( rehearsalInRehearsal ) . toHaveLength ( 1 )
10711071 } )
1072+
1073+ describe ( 'single piece with multiple objects on the same layer' , ( ) => {
1074+ const rundownId : RundownId = protectString ( 'rundown0' )
1075+ const layer0 = 'layer0'
1076+ const partInstanceId = protectString ( 'partInstance0' )
1077+
1078+ test ( 'all objects from a single piece on the same layer are returned' , ( ) => {
1079+ // A single piece that contributes two objects to the same layer should expose both
1080+ // objects in the lookahead result
1081+ const partInfo = {
1082+ part : definePart ( rundownId ) ,
1083+ usesInTransition : false ,
1084+ pieces : literal < PieceInstance [ ] > ( [
1085+ {
1086+ ...defaultPieceInstanceProps ,
1087+ rundownId,
1088+ piece : {
1089+ ...defaultPieceInstanceProps . piece ,
1090+ timelineObjectsString : serializePieceTimelineObjectsBlob ( [
1091+ {
1092+ id : 'obj0' ,
1093+ enable : { start : 0 } ,
1094+ layer : layer0 ,
1095+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1096+ priority : 0 ,
1097+ } ,
1098+ {
1099+ id : 'obj1' ,
1100+ enable : { start : 100 } ,
1101+ layer : layer0 ,
1102+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1103+ priority : 0 ,
1104+ } ,
1105+ ] ) ,
1106+ } ,
1107+ } ,
1108+ ] ) ,
1109+ }
1110+
1111+ const objects = findLookaheadObjectsForPart (
1112+ context ,
1113+ null ,
1114+ layer0 ,
1115+ undefined ,
1116+ partInfo ,
1117+ partInstanceId ,
1118+ DEFAULT_PLAYOUT_STATE
1119+ )
1120+
1121+ expect ( stripObjectProperties ( objects ) ) . toStrictEqual ( [
1122+ {
1123+ id : 'obj0' ,
1124+ layer : layer0 ,
1125+ pieceInstanceId : 'piece0_instance' ,
1126+ infinitePieceInstanceId : undefined ,
1127+ partInstanceId : partInstanceId ,
1128+ } ,
1129+ {
1130+ id : 'obj1' ,
1131+ layer : layer0 ,
1132+ pieceInstanceId : 'piece0_instance' ,
1133+ infinitePieceInstanceId : undefined ,
1134+ partInstanceId : partInstanceId ,
1135+ } ,
1136+ ] )
1137+ } )
1138+
1139+ test ( 'all objects from a start=0 Normal piece are filtered when the transition has an object on this layer' , ( ) => {
1140+ // Note: this is sane, but maybe not 100% correct. There are times when it would be desirable to preserve the Normal piece's objects if they start in the part after the transition will have ended
1141+ const partInfo = {
1142+ part : definePart ( rundownId ) ,
1143+ usesInTransition : true ,
1144+ pieces : literal < PieceInstance [ ] > ( [
1145+ {
1146+ ...defaultPieceInstanceProps ,
1147+ rundownId,
1148+ piece : {
1149+ ...defaultPieceInstanceProps . piece ,
1150+ timelineObjectsString : serializePieceTimelineObjectsBlob ( [
1151+ {
1152+ id : 'obj0' ,
1153+ enable : { start : 0 } ,
1154+ layer : layer0 ,
1155+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1156+ priority : 0 ,
1157+ } ,
1158+ {
1159+ id : 'obj1' ,
1160+ enable : { start : 100 } ,
1161+ layer : layer0 ,
1162+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1163+ priority : 0 ,
1164+ } ,
1165+ ] ) ,
1166+ } ,
1167+ } ,
1168+ {
1169+ // Transition piece with an object on the same layer.
1170+ ...defaultPieceInstanceProps ,
1171+ _id : protectString ( 'piece1_instance' ) ,
1172+ rundownId,
1173+ piece : {
1174+ ...defaultPieceInstanceProps . piece ,
1175+ _id : protectString ( 'piece1' ) ,
1176+ pieceType : IBlueprintPieceType . InTransition ,
1177+ timelineObjectsString : serializePieceTimelineObjectsBlob ( [
1178+ {
1179+ id : 'trans0' ,
1180+ enable : { start : 0 } ,
1181+ layer : layer0 ,
1182+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1183+ priority : 0 ,
1184+ } ,
1185+ ] ) ,
1186+ } ,
1187+ } ,
1188+ ] ) ,
1189+ }
1190+
1191+ const previousPart : DBPart = { disableNextInTransition : false , classesForNext : undefined } as any
1192+ const objects = findLookaheadObjectsForPart (
1193+ context ,
1194+ partInstanceId ,
1195+ layer0 ,
1196+ previousPart ,
1197+ partInfo ,
1198+ partInstanceId ,
1199+ DEFAULT_PLAYOUT_STATE
1200+ )
1201+
1202+ // obj0 and obj1 are both filtered because their parent piece is Normal + start=0 and
1203+ // `hasTransitionObj` is truthy. Only the transition object should remain.
1204+ expect ( stripObjectProperties ( objects ) ) . toStrictEqual ( [
1205+ {
1206+ id : 'trans0' ,
1207+ layer : layer0 ,
1208+ pieceInstanceId : 'piece1_instance' ,
1209+ infinitePieceInstanceId : undefined ,
1210+ partInstanceId : partInstanceId ,
1211+ } ,
1212+ ] )
1213+ } )
1214+
1215+ test ( 'all objects from a start=0 Normal piece are included when the transition has no object on this layer' , ( ) => {
1216+ const partInfo = {
1217+ part : definePart ( rundownId ) ,
1218+ usesInTransition : true ,
1219+ pieces : literal < PieceInstance [ ] > ( [
1220+ {
1221+ ...defaultPieceInstanceProps ,
1222+ rundownId,
1223+ piece : {
1224+ ...defaultPieceInstanceProps . piece ,
1225+ timelineObjectsString : serializePieceTimelineObjectsBlob ( [
1226+ {
1227+ id : 'obj0' ,
1228+ enable : { start : 0 } ,
1229+ layer : layer0 ,
1230+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1231+ priority : 0 ,
1232+ } ,
1233+ {
1234+ id : 'obj1' ,
1235+ enable : { start : 100 } ,
1236+ layer : layer0 ,
1237+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1238+ priority : 0 ,
1239+ } ,
1240+ ] ) ,
1241+ } ,
1242+ } ,
1243+ {
1244+ // Transition piece whose only object is on a *different* layer.
1245+ ...defaultPieceInstanceProps ,
1246+ _id : protectString ( 'piece1_instance' ) ,
1247+ rundownId,
1248+ piece : {
1249+ ...defaultPieceInstanceProps . piece ,
1250+ _id : protectString ( 'piece1' ) ,
1251+ pieceType : IBlueprintPieceType . InTransition ,
1252+ timelineObjectsString : serializePieceTimelineObjectsBlob ( [
1253+ {
1254+ id : 'trans0' ,
1255+ enable : { start : 0 } ,
1256+ layer : 'other_layer' ,
1257+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1258+ priority : 0 ,
1259+ } ,
1260+ ] ) ,
1261+ } ,
1262+ } ,
1263+ ] ) ,
1264+ }
1265+
1266+ const previousPart : DBPart = { disableNextInTransition : false , classesForNext : undefined } as any
1267+ const objects = findLookaheadObjectsForPart (
1268+ context ,
1269+ partInstanceId ,
1270+ layer0 ,
1271+ previousPart ,
1272+ partInfo ,
1273+ partInstanceId ,
1274+ DEFAULT_PLAYOUT_STATE
1275+ )
1276+
1277+ // `hasTransitionObj` is falsy for layer0, so the Normal piece's objects must not be
1278+ // skipped. Both obj0 and obj1 should appear.
1279+ expect ( stripObjectProperties ( objects ) ) . toStrictEqual ( [
1280+ {
1281+ id : 'obj0' ,
1282+ layer : layer0 ,
1283+ pieceInstanceId : 'piece0_instance' ,
1284+ infinitePieceInstanceId : undefined ,
1285+ partInstanceId : partInstanceId ,
1286+ } ,
1287+ {
1288+ id : 'obj1' ,
1289+ layer : layer0 ,
1290+ pieceInstanceId : 'piece0_instance' ,
1291+ infinitePieceInstanceId : undefined ,
1292+ partInstanceId : partInstanceId ,
1293+ } ,
1294+ ] )
1295+ } )
1296+
1297+ test ( 'multiple objects from a later-starting piece all appear alongside filtered start=0 objects' , ( ) => {
1298+ // Three pieces: one Normal at start=0 (should be filtered), one Normal at start=500
1299+ // that contributes TWO objects (both should appear), and an InTransition piece with an
1300+ // object on the layer (should appear + triggers the start=0 filter).
1301+ const partInfo = {
1302+ part : definePart ( rundownId ) ,
1303+ usesInTransition : true ,
1304+ // Sort so that InTransition comes first in the iteration order, matching production behaviour.
1305+ pieces : sortPieceInstancesByStart (
1306+ literal < PieceInstance [ ] > ( [
1307+ {
1308+ ...defaultPieceInstanceProps ,
1309+ rundownId,
1310+ piece : {
1311+ ...defaultPieceInstanceProps . piece ,
1312+ enable : { start : 0 } ,
1313+ timelineObjectsString : serializePieceTimelineObjectsBlob ( [
1314+ {
1315+ id : 'obj0' ,
1316+ enable : { start : 0 } ,
1317+ layer : layer0 ,
1318+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1319+ priority : 0 ,
1320+ } ,
1321+ ] ) ,
1322+ } ,
1323+ } ,
1324+ {
1325+ // Normal piece at start=500 — contributes TWO objects.
1326+ ...defaultPieceInstanceProps ,
1327+ _id : protectString ( 'piece1_instance' ) ,
1328+ rundownId,
1329+ piece : {
1330+ ...defaultPieceInstanceProps . piece ,
1331+ _id : protectString ( 'piece1' ) ,
1332+ enable : { start : 500 } ,
1333+ timelineObjectsString : serializePieceTimelineObjectsBlob ( [
1334+ {
1335+ id : 'obj1' ,
1336+ enable : { start : 0 } ,
1337+ layer : layer0 ,
1338+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1339+ priority : 0 ,
1340+ } ,
1341+ {
1342+ id : 'obj2' ,
1343+ enable : { start : 0 } ,
1344+ layer : layer0 ,
1345+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1346+ priority : 0 ,
1347+ } ,
1348+ ] ) ,
1349+ } ,
1350+ } ,
1351+ {
1352+ // Transition piece. InTransition sorts before Normal pieces at the same start.
1353+ ...defaultPieceInstanceProps ,
1354+ _id : protectString ( 'piece2_instance' ) ,
1355+ rundownId,
1356+ piece : {
1357+ ...defaultPieceInstanceProps . piece ,
1358+ _id : protectString ( 'piece2' ) ,
1359+ pieceType : IBlueprintPieceType . InTransition ,
1360+ enable : { start : 0 } ,
1361+ timelineObjectsString : serializePieceTimelineObjectsBlob ( [
1362+ {
1363+ id : 'trans0' ,
1364+ enable : { start : 0 } ,
1365+ layer : layer0 ,
1366+ content : { deviceType : TSR . DeviceType . ABSTRACT } ,
1367+ priority : 0 ,
1368+ } ,
1369+ ] ) ,
1370+ } ,
1371+ } ,
1372+ ] ) ,
1373+ 0
1374+ ) ,
1375+ }
1376+
1377+ const previousPart : DBPart = { disableNextInTransition : false , classesForNext : undefined } as any
1378+ const objects = findLookaheadObjectsForPart (
1379+ context ,
1380+ partInstanceId ,
1381+ layer0 ,
1382+ previousPart ,
1383+ partInfo ,
1384+ partInstanceId ,
1385+ DEFAULT_PLAYOUT_STATE
1386+ )
1387+
1388+ // obj0 (Normal, start=0) is filtered because `hasTransitionObj` is truthy.
1389+ // trans0 (InTransition) is always included.
1390+ // obj1 and obj2 (Normal, start=500) are not filtered since start != 0.
1391+ expect ( stripObjectProperties ( objects ) ) . toStrictEqual ( [
1392+ {
1393+ id : 'trans0' ,
1394+ layer : layer0 ,
1395+ pieceInstanceId : 'piece2_instance' ,
1396+ infinitePieceInstanceId : undefined ,
1397+ partInstanceId : partInstanceId ,
1398+ } ,
1399+ {
1400+ id : 'obj1' ,
1401+ layer : layer0 ,
1402+ pieceInstanceId : 'piece1_instance' ,
1403+ infinitePieceInstanceId : undefined ,
1404+ partInstanceId : partInstanceId ,
1405+ } ,
1406+ {
1407+ id : 'obj2' ,
1408+ layer : layer0 ,
1409+ pieceInstanceId : 'piece1_instance' ,
1410+ infinitePieceInstanceId : undefined ,
1411+ partInstanceId : partInstanceId ,
1412+ } ,
1413+ ] )
1414+ } )
1415+ } )
10721416} )
0 commit comments