mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-08 08:53:51 +00:00
523 lines
12 KiB
TypeScript
523 lines
12 KiB
TypeScript
import { OntimeEvent, SupportedEvent } from 'ontime-types';
|
|
import { MILLIS_PER_HOUR, MILLIS_PER_MINUTE } from 'ontime-utils';
|
|
|
|
import { loadRoll } from '../rollUtils.js';
|
|
|
|
const baseEvent = {
|
|
type: SupportedEvent.Event,
|
|
skip: false,
|
|
};
|
|
|
|
function makeOntimeEvent(patch: Partial<OntimeEvent>): OntimeEvent {
|
|
return {
|
|
...baseEvent,
|
|
...patch,
|
|
} as OntimeEvent;
|
|
}
|
|
|
|
function prepareTimedEvents(events: Partial<OntimeEvent>[]): OntimeEvent[] {
|
|
return events.map(makeOntimeEvent);
|
|
}
|
|
|
|
describe('loadRoll()', () => {
|
|
const eventlist = [
|
|
{
|
|
id: '1',
|
|
timeStart: 5,
|
|
timeEnd: 10,
|
|
isPublic: false,
|
|
},
|
|
{
|
|
id: '2',
|
|
timeStart: 10,
|
|
timeEnd: 20,
|
|
isPublic: false,
|
|
},
|
|
{
|
|
id: '3',
|
|
timeStart: 20,
|
|
timeEnd: 30,
|
|
isPublic: false,
|
|
},
|
|
{
|
|
id: '4',
|
|
timeStart: 30,
|
|
timeEnd: 40,
|
|
isPublic: false,
|
|
},
|
|
{
|
|
id: '5',
|
|
timeStart: 40,
|
|
timeEnd: 50,
|
|
isPublic: true,
|
|
},
|
|
{
|
|
id: '6',
|
|
timeStart: 50,
|
|
timeEnd: 60,
|
|
isPublic: false,
|
|
},
|
|
{
|
|
id: '7',
|
|
timeStart: 60,
|
|
timeEnd: 70,
|
|
isPublic: true,
|
|
},
|
|
{
|
|
id: '8',
|
|
timeStart: 70,
|
|
timeEnd: 80,
|
|
isPublic: false,
|
|
},
|
|
];
|
|
const timedEvents = prepareTimedEvents(eventlist);
|
|
|
|
it('should roll to the day after if timer is at 100', () => {
|
|
const now = 100;
|
|
const expected = {
|
|
event: timedEvents[0],
|
|
index: 0,
|
|
isPending: true,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('should be waiting to start if timer is at 0', () => {
|
|
const now = 0;
|
|
const expected = {
|
|
event: timedEvents[0],
|
|
index: 0,
|
|
isPending: true,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('should start the first event if timer is at 5', () => {
|
|
const now = 5;
|
|
const expected = {
|
|
event: timedEvents[0],
|
|
index: 0,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('should start the second event if timer is at 15', () => {
|
|
const now = 15;
|
|
const expected = {
|
|
event: timedEvents[1],
|
|
index: 1,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('should start the third event if timer is at 10', () => {
|
|
const now = 20;
|
|
const expected = {
|
|
event: timedEvents[2],
|
|
index: 2,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('should start the fifth event if timer is at 49', () => {
|
|
const now = 49;
|
|
const expected = {
|
|
event: timedEvents[4],
|
|
index: 4,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('should start the seventh event if timer is at 63', () => {
|
|
const now = 63;
|
|
const expected = {
|
|
event: timedEvents[6],
|
|
index: 6,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('should start the eight event if timer is at 75', () => {
|
|
const now = 75;
|
|
const expected = {
|
|
event: timedEvents[7],
|
|
index: 7,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
});
|
|
|
|
describe('loadRoll() handle edge cases with midnight', () => {
|
|
it('should find an event that crosses midnight', () => {
|
|
const now = 23 * MILLIS_PER_HOUR;
|
|
const eventlist = [
|
|
{
|
|
id: '0',
|
|
timeStart: 9 * MILLIS_PER_HOUR,
|
|
timeEnd: 10 * MILLIS_PER_HOUR,
|
|
isPublic: true,
|
|
},
|
|
{
|
|
id: '1',
|
|
timeStart: 20 * MILLIS_PER_HOUR,
|
|
timeEnd: 22 * MILLIS_PER_HOUR,
|
|
isPublic: true,
|
|
},
|
|
{
|
|
id: '2',
|
|
timeStart: 22 * MILLIS_PER_HOUR,
|
|
timeEnd: 1 * MILLIS_PER_HOUR,
|
|
isPublic: true,
|
|
},
|
|
{
|
|
id: '3',
|
|
timeStart: 1 * MILLIS_PER_HOUR,
|
|
timeEnd: 1 * MILLIS_PER_HOUR + 10 * MILLIS_PER_MINUTE,
|
|
isPublic: true,
|
|
},
|
|
{
|
|
id: '4',
|
|
timeStart: 1 * MILLIS_PER_HOUR,
|
|
timeEnd: 2 * MILLIS_PER_HOUR,
|
|
isPublic: true,
|
|
},
|
|
];
|
|
const timedEvents = prepareTimedEvents(eventlist);
|
|
|
|
const expected = {
|
|
event: timedEvents[2],
|
|
index: 2,
|
|
};
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('should not skip to the second day', () => {
|
|
/**
|
|
* NOTE: this is a potentially contentious decision
|
|
*
|
|
* The idea here is that it makes no sense for us to jump to the second / third day on activating roll
|
|
* if the user wants to skip a portion of the rundown, they can manually jump to the event and activate roll
|
|
*
|
|
* On our side, this simplifies logic and makes behaviour more predictable
|
|
*/
|
|
const now = 8 * MILLIS_PER_HOUR;
|
|
const eventlist = [
|
|
{
|
|
id: '0',
|
|
timeStart: 21 * MILLIS_PER_HOUR,
|
|
timeEnd: 22 * MILLIS_PER_HOUR,
|
|
},
|
|
{
|
|
id: '1',
|
|
timeStart: 22 * MILLIS_PER_HOUR,
|
|
timeEnd: 3 * MILLIS_PER_HOUR,
|
|
},
|
|
{
|
|
id: '2',
|
|
timeStart: 3 * MILLIS_PER_HOUR,
|
|
timeEnd: 10 * MILLIS_PER_HOUR,
|
|
},
|
|
];
|
|
const timedEvents = prepareTimedEvents(eventlist);
|
|
const expected = {
|
|
event: timedEvents[0],
|
|
index: 0,
|
|
isPending: true,
|
|
};
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
});
|
|
|
|
describe('loadRoll() handle rundowns with several days', () => {
|
|
it('should find the correct event, when we have many days', () => {
|
|
const now = 11 * MILLIS_PER_HOUR + 30 * MILLIS_PER_MINUTE;
|
|
const timedEvents = [
|
|
{
|
|
id: '0',
|
|
timeStart: 10 * MILLIS_PER_HOUR,
|
|
timeEnd: 11 * MILLIS_PER_HOUR,
|
|
},
|
|
{
|
|
id: '2',
|
|
timeStart: 11 * MILLIS_PER_HOUR,
|
|
timeEnd: 12 * MILLIS_PER_HOUR,
|
|
},
|
|
{
|
|
id: '3',
|
|
timeStart: 12 * MILLIS_PER_HOUR,
|
|
timeEnd: 13 * MILLIS_PER_HOUR,
|
|
},
|
|
{
|
|
id: '4',
|
|
timeStart: 11 * MILLIS_PER_HOUR,
|
|
timeEnd: 12 * MILLIS_PER_HOUR,
|
|
},
|
|
];
|
|
|
|
const state = loadRoll(prepareTimedEvents(timedEvents), now);
|
|
const expected = {
|
|
event: timedEvents[1],
|
|
index: 1,
|
|
};
|
|
expect(state).toMatchObject(expected);
|
|
});
|
|
|
|
it('should find the correct event, when we have events of zero duration', () => {
|
|
const now = 20 * MILLIS_PER_HOUR + 37 * MILLIS_PER_MINUTE;
|
|
const timedEvents = [
|
|
{
|
|
id: '0',
|
|
timeStart: 18 * MILLIS_PER_HOUR,
|
|
timeEnd: 19 * MILLIS_PER_HOUR,
|
|
},
|
|
{
|
|
id: '1 no duration',
|
|
timeStart: 0,
|
|
timeEnd: 0,
|
|
},
|
|
{
|
|
id: '2',
|
|
timeStart: 19 * MILLIS_PER_HOUR,
|
|
timeEnd: 20 * MILLIS_PER_HOUR,
|
|
},
|
|
{
|
|
id: '3 no duration',
|
|
timeStart: 0,
|
|
timeEnd: 0,
|
|
},
|
|
{
|
|
id: '4',
|
|
timeStart: 20 * MILLIS_PER_HOUR,
|
|
timeEnd: 21 * MILLIS_PER_HOUR,
|
|
},
|
|
];
|
|
|
|
const state = loadRoll(prepareTimedEvents(timedEvents), now);
|
|
const expected = {
|
|
event: timedEvents[4],
|
|
index: 4,
|
|
};
|
|
expect(state).toMatchObject(expected);
|
|
});
|
|
});
|
|
|
|
describe('loadRoll() handle edge cases with before and after start', () => {
|
|
it('should prepare first event, if we are not yet in the rundown start', () => {
|
|
const now = 7 * MILLIS_PER_HOUR;
|
|
const singleEventList = [
|
|
makeOntimeEvent({
|
|
id: '1',
|
|
timeStart: 10 * MILLIS_PER_HOUR,
|
|
timeEnd: 11 * MILLIS_PER_HOUR,
|
|
isPublic: true,
|
|
}),
|
|
];
|
|
|
|
const expected = {
|
|
event: singleEventList[0],
|
|
index: 0,
|
|
isPending: true,
|
|
};
|
|
const state = loadRoll(singleEventList, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('should prepare first event, if we are over the rundown end', () => {
|
|
const now = 18 * MILLIS_PER_HOUR;
|
|
const singleEventList = [
|
|
makeOntimeEvent({
|
|
id: '1',
|
|
timeStart: 10 * MILLIS_PER_HOUR,
|
|
timeEnd: 11 * MILLIS_PER_HOUR,
|
|
isPublic: true,
|
|
}),
|
|
];
|
|
|
|
const expected = {
|
|
event: singleEventList[0],
|
|
index: 0,
|
|
isPending: true,
|
|
};
|
|
const state = loadRoll(singleEventList, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('should account for a rundown that goes through midnight', () => {
|
|
const now = 1 * MILLIS_PER_HOUR;
|
|
const singleEventList = [
|
|
makeOntimeEvent({
|
|
id: '1',
|
|
timeStart: 10 * MILLIS_PER_HOUR,
|
|
timeEnd: 2 * MILLIS_PER_HOUR,
|
|
isPublic: true,
|
|
}),
|
|
];
|
|
const expected = {
|
|
event: singleEventList[0],
|
|
index: 0,
|
|
};
|
|
const state = loadRoll(singleEventList, now);
|
|
expect(state.isPending).toBeUndefined(); // we are playing the event
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('loads upcoming event while waiting to roll', () => {
|
|
const now = 6000; // 00:01
|
|
const singleEventList = [
|
|
makeOntimeEvent({
|
|
id: '1',
|
|
timeStart: 72000000, // 20:00
|
|
timeEnd: 72010000, // 20:10
|
|
isPublic: true,
|
|
}),
|
|
];
|
|
const expected = {
|
|
event: singleEventList[0],
|
|
index: 0,
|
|
isPending: true,
|
|
};
|
|
const state = loadRoll(singleEventList, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
});
|
|
|
|
describe('loadRoll() test that roll behaviour with overlapping times', () => {
|
|
const eventlist = [
|
|
{
|
|
id: '1',
|
|
timeStart: 10,
|
|
timeEnd: 10,
|
|
isPublic: false,
|
|
},
|
|
{
|
|
id: '2',
|
|
timeStart: 10,
|
|
timeEnd: 20,
|
|
isPublic: true,
|
|
},
|
|
{
|
|
id: '3',
|
|
timeStart: 10,
|
|
timeEnd: 30,
|
|
isPublic: false,
|
|
},
|
|
];
|
|
const timedEvents = prepareTimedEvents(eventlist);
|
|
|
|
it('if timer is at 0', () => {
|
|
const now = 0;
|
|
const expected = {
|
|
event: timedEvents[0],
|
|
index: 0,
|
|
isPending: true,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('if timer is at 10, it ignores events with 0 duration', () => {
|
|
const now = 10;
|
|
const expected = {
|
|
event: timedEvents[1],
|
|
index: 1,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('if timer is at 15', () => {
|
|
const now = 15;
|
|
const expected = {
|
|
event: timedEvents[1],
|
|
index: 1,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('if timer is at 20', () => {
|
|
const now = 20;
|
|
const expected = {
|
|
event: timedEvents[2],
|
|
index: 2,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('if timer is at 25', () => {
|
|
const now = 25;
|
|
const expected = {
|
|
event: timedEvents[2],
|
|
index: 2,
|
|
};
|
|
|
|
const state = loadRoll(timedEvents, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
});
|
|
|
|
// issue #58
|
|
describe('loadRoll() test that roll behaviour multi day event edge cases', () => {
|
|
it('should recognise a playing event where its schedule spans over midnight', () => {
|
|
const now = 66600000; // 19:30
|
|
const eventlist = [
|
|
makeOntimeEvent({
|
|
id: '1',
|
|
timeStart: 66000000, // 19:20
|
|
timeEnd: 54600000, // 16:10
|
|
isPublic: false,
|
|
}),
|
|
];
|
|
const expected = {
|
|
event: eventlist[0],
|
|
index: 0,
|
|
};
|
|
|
|
const state = loadRoll(eventlist, now);
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
|
|
it('if the start time is the day after end time, and both are later than now', () => {
|
|
const now = 66840000; // 19:34
|
|
const eventlist = [
|
|
makeOntimeEvent({
|
|
id: '1',
|
|
timeStart: 67200000, // 19:40
|
|
timeEnd: 66900000, // 19:35
|
|
isPublic: false,
|
|
}),
|
|
];
|
|
const expected = {
|
|
event: eventlist[0],
|
|
index: 0,
|
|
};
|
|
|
|
const state = loadRoll(eventlist, now);
|
|
expect(state.isPending).toBeUndefined(); // we are playing the event
|
|
expect(state).toStrictEqual(expected);
|
|
});
|
|
});
|