chore: add demo project

This commit is contained in:
Carlos Valente
2025-08-05 18:45:50 +02:00
committed by Carlos Valente
parent fef9ed4fcc
commit ad9db4edd3
5 changed files with 316 additions and 496 deletions
@@ -1,5 +1,7 @@
/* eslint-disable no-console -- we are mocking the console */
import { SupportedEntry } from 'ontime-types';
import { demoDb } from '../../../models/demoProject.js';
import { parseDatabaseModel } from '../db.parser.js';
@@ -22,9 +24,17 @@ describe('test parseDatabaseModel() with demo project (valid)', () => {
const filteredDemoProject = structuredClone(demoDb);
const { data } = parseDatabaseModel(filteredDemoProject);
it('has 17 events with 12 top level events', () => {
expect(data.rundowns.default.order.length).toBe(12);
expect(Object.keys(data.rundowns.default.entries).length).toBe(17);
it('has 14 entries, with 6 events and 4 top level groups', () => {
expect(data.rundowns.default.order.length).toBe(4);
expect(Object.keys(data.rundowns.default.entries).length).toBe(14);
let numEntries = 0;
data.rundowns.default.flatOrder.forEach((entryId) => {
const entry = data.rundowns.default.entries[entryId];
if (entry.type === SupportedEntry.Event) {
numEntries++;
}
});
expect(numEntries).toBe(6);
});
it('is the same as the demo project since all data is valid', () => {
@@ -32,7 +42,33 @@ describe('test parseDatabaseModel() with demo project (valid)', () => {
delete filteredDemoProject.settings.version;
// @ts-expect-error -- its ok
delete data.settings.version;
expect(data).toMatchObject(filteredDemoProject);
// remove time-related fields from the comparison
// these are not calculated in the parser
Object.values(filteredDemoProject.rundowns.default.entries).forEach((entry: any) => {
if (entry.type === SupportedEntry.Block) {
delete entry.timeStart;
delete entry.timeEnd;
delete entry.duration;
delete entry.isFirstLinked;
}
});
Object.values(data.rundowns.default.entries).forEach((entry: any) => {
if (entry.type === SupportedEntry.Block) {
delete entry.timeStart;
delete entry.timeEnd;
delete entry.duration;
delete entry.isFirstLinked;
}
});
expect(data.automation).toMatchObject(filteredDemoProject.automation);
expect(data.customFields).toMatchObject(filteredDemoProject.customFields);
expect(data.project).toMatchObject(filteredDemoProject.project);
expect(data.rundowns).toMatchObject(filteredDemoProject.rundowns);
expect(data.settings).toMatchObject(filteredDemoProject.settings);
expect(data.urlPresets).toMatchObject(filteredDemoProject.urlPresets);
expect(data.viewSettings).toMatchObject(filteredDemoProject.viewSettings);
});
});
@@ -97,12 +97,12 @@ describe('processRundown()', () => {
it('generates metadata from given rundown', () => {
const initResult = processRundown(demoDb.rundowns.default, demoDb.customFields);
expect(initResult.order.length).toBe(12);
expect(initResult.flatEntryOrder.length).toBe(17);
expect(initResult.timedEventOrder.length).toBe(14);
expect(initResult.playableEventOrder.length).toBe(14);
expect(initResult.firstStart).toBe(36000000);
expect(initResult.lastEnd).toBe(61800000);
expect(initResult.order.length).toBe(4);
expect(initResult.flatEntryOrder.length).toBe(14);
expect(initResult.timedEventOrder.length).toBe(6);
expect(initResult.playableEventOrder.length).toBe(6);
expect(initResult.firstStart).toBe(MILLIS_PER_HOUR * 10);
expect(initResult.lastEnd).toBe(MILLIS_PER_HOUR * 14);
expect(initResult.totalDays).toBe(0);
expect(initResult.totalDelay).toBe(0);
});
@@ -118,18 +118,22 @@ export function parseRundown(
if (isOntimeEvent(nestedEvent)) {
newNestedEvent = createEvent(nestedEvent, eventIndex);
// skip if event is invalid
if (newNestedEvent == null) {
emitError?.('Skipping event without payload');
continue;
}
newNestedEvent.parent = event.id;
cleanupCustomFields(newNestedEvent.custom, parsedCustomFields);
eventIndex += 1;
} else if (isOntimeDelay(nestedEvent)) {
newNestedEvent = { ...delayDef, duration: nestedEvent.duration, id: nestedEventId };
newNestedEvent.parent = event.id;
} else if (isOntimeMilestone(nestedEvent)) {
newNestedEvent = createMilestone({ ...nestedEvent, id: nestedEventId });
newNestedEvent.parent = event.id;
cleanupCustomFields(newNestedEvent.custom, parsedCustomFields);
}
@@ -54,10 +54,10 @@ describe('safeMerge', () => {
expect(mergedData.project).toStrictEqual({
title: 'new title',
description: 'Turin 2022',
url: 'www.github.com/cpvalente/ontime',
description: demoDb.project.description,
url: demoDb.project.url,
info: 'new backstage info',
logo: null,
logo: demoDb.project.logo,
custom: [
{
title: 'new custom title',
+263 -483
View File
@@ -4,524 +4,311 @@ export const demoDb: DatabaseModel = {
rundowns: {
default: {
id: 'default',
title: 'Eurovision Demo',
order: [
'block',
'01e85',
'1c420',
'b7737',
'd3a80',
'8276c',
'2340b',
'cb90b',
'503c4',
'5e965',
'bab4a',
'd3eb1',
],
title: 'Demo project',
order: ['e2163f', '7eaf99', 'f60403', '6b0edb'],
flatOrder: [
'block',
'32d31',
'21cd2',
'0b371',
'3cd28',
'e457f',
'01e85',
'1c420',
'b7737',
'd3a80',
'8276c',
'2340b',
'cb90b',
'503c4',
'5e965',
'bab4a',
'd3eb1',
'e2163f',
'7eaf99',
'9bf60f',
'bf71a2',
'c2697f',
'fa593e',
'a8b0b3',
'f60403',
'0aaa7d',
'6b0edb',
'02afca',
'75ce86',
'e10ed9',
'07df89',
],
entries: {
block: {
type: SupportedEntry.Block,
entries: ['32d31', '21cd2', '0b371', '3cd28', 'e457f'],
id: 'block',
title: 'Test Block',
note: '',
targetDuration: null,
colour: 'hotpink',
revision: 0,
timeStart: null,
timeEnd: null,
duration: 0,
isFirstLinked: false,
e2163f: {
id: 'e2163f',
type: SupportedEntry.Milestone,
cue: 'Demo',
title: 'Clear all, or Create New Project to start fresh',
note: 'Go to Settings cog wheel top left of Editor, Project > Create',
colour: '#9d9d9d',
custom: {
Song: 'Sekret',
Artist: 'Ronela Hajati',
Custom_Field:
'Create Custom Fields to store specific text information or images.\n\nGo to Settings > Project data > Custom fields',
Images: 'https://www.getontime.no/images/icons/ontime-logo.png',
},
parent: null,
revision: 0,
},
'32d31': {
type: SupportedEntry.Event,
flag: false,
id: '32d31',
cue: 'SF1.01',
title: 'Albania',
note: 'SF1.01',
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
'7eaf99': {
id: '7eaf99',
type: SupportedEntry.Block,
title: 'Morning Sessions',
note: '',
entries: ['9bf60f', 'bf71a2', 'c2697f', 'fa593e', 'a8b0b3'],
targetDuration: null,
colour: '#339E4E',
custom: {},
revision: 0,
timeStart: 36000000,
timeEnd: 37200000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'Sekret',
Artist: 'Ronela Hajati',
},
},
'21cd2': {
type: SupportedEntry.Event,
flag: false,
id: '21cd2',
cue: 'SF1.02',
title: 'Latvia',
note: 'SF1.02',
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 37500000,
timeEnd: 38700000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'Eat Your Salad',
Artist: 'Citi Zeni',
},
},
'0b371': {
type: SupportedEntry.Event,
flag: false,
id: '0b371',
cue: 'SF1.03',
title: 'Lithuania',
note: 'SF1.03',
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 39000000,
timeEnd: 40200000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'Sentimentai',
Artist: 'Monika Liu',
},
},
'3cd28': {
type: SupportedEntry.Event,
flag: false,
id: '3cd28',
cue: 'SF1.04',
title: 'Switzerland',
note: 'SF1.04',
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 40500000,
timeEnd: 41700000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'Boys Do Cry',
Artist: 'Marius Bear',
},
},
e457f: {
type: SupportedEntry.Event,
flag: false,
id: 'e457f',
cue: 'SF1.05',
title: 'Slovenia',
note: 'SF1.05',
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 42000000,
timeEnd: 43200000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'Disko',
Artist: 'LPS',
},
},
/// <----- BLOCK
'01e85': {
// TODO: this should be a marker type
type: SupportedEntry.Block,
id: '01e85',
title: 'Lunch break',
note: '',
colour: '',
entries: [],
targetDuration: null,
custom: {},
revision: 0,
timeStart: null,
timeEnd: null,
duration: 0,
duration: 7200000,
isFirstLinked: false,
},
'1c420': {
'9bf60f': {
id: '9bf60f',
type: SupportedEntry.Event,
flag: false,
id: '1c420',
cue: 'SF1.06',
title: 'Ukraine',
note: 'SF1.06',
title: 'Pre-show Countdown',
timeStart: 36000000,
timeEnd: 39600000,
duration: 3600000,
timeStrategy: TimeStrategy.LockEnd,
linkStart: false,
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 47100000,
timeEnd: 48300000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
note: 'Music plays, holding slide on screens',
colour: '#77C785',
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
cue: '1',
parent: '7eaf99',
revision: 0,
timeWarning: 600000,
timeDanger: 300000,
custom: {
Song: 'Stefania',
Artist: 'Kalush Orchestra',
Custom_Field: 'Put additional info here',
},
triggers: [],
},
b7737: {
bf71a2: {
id: 'bf71a2',
type: SupportedEntry.Milestone,
cue: 'Standby',
title: '10:45 - Presenters ready side stage',
note: '',
colour: '#A790F5',
revision: 0,
custom: {},
parent: '7eaf99',
},
c2697f: {
id: 'c2697f',
type: SupportedEntry.Event,
flag: false,
id: 'b7737',
cue: 'SF1.07',
title: 'Bulgaria',
note: 'SF1.07',
title: 'Welcome',
timeStart: 39600000,
timeEnd: 40200000,
duration: 600000,
timeStrategy: TimeStrategy.LockDuration,
linkStart: true,
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 48600000,
skip: false,
note: 'Emma Thompson',
colour: '#FFCC78',
delay: 0,
dayOffset: 0,
gap: 0,
cue: '1.1',
parent: '7eaf99',
revision: 0,
timeWarning: 120000,
timeDanger: 60000,
custom: {},
triggers: [],
},
fa593e: {
id: 'fa593e',
type: SupportedEntry.Event,
flag: true,
title: 'Session 1',
timeStart: 40200000,
timeEnd: 43200000,
duration: 3000000,
timeStrategy: TimeStrategy.LockDuration,
linkStart: true,
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
skip: false,
note: 'Liam Carter, Sophia Patel + PowerPoint',
colour: '#77C785',
delay: 0,
dayOffset: 0,
gap: 0,
cue: '1.2',
parent: '7eaf99',
revision: 0,
timeWarning: 120000,
timeDanger: 60000,
custom: {},
triggers: [],
},
a8b0b3: {
id: 'a8b0b3',
type: SupportedEntry.Milestone,
cue: 'House',
title: '11:30 - House staff setup lunch in lobby',
note: '',
colour: '#A790F5',
revision: 0,
custom: {},
parent: '7eaf99',
},
f60403: {
id: 'f60403',
type: SupportedEntry.Block,
title: 'Lunch',
note: '',
entries: ['0aaa7d'],
targetDuration: null,
colour: '#3E75E8',
custom: {},
revision: 0,
timeStart: 43200000,
timeEnd: 46800000,
duration: 3600000,
isFirstLinked: true,
},
'0aaa7d': {
id: '0aaa7d',
type: SupportedEntry.Event,
flag: false,
title: 'Lunch / Countdown to next session',
timeStart: 43200000,
timeEnd: 46800000,
duration: 3600000,
timeStrategy: TimeStrategy.LockDuration,
linkStart: true,
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
skip: false,
note: 'Buffet in lobby\nMusic plays, holding slide on screens',
colour: '#779BE7',
delay: 0,
dayOffset: 0,
gap: 0,
cue: '2.1',
parent: 'f60403',
revision: 0,
timeWarning: 120000,
timeDanger: 60000,
custom: {},
triggers: [],
},
'6b0edb': {
id: '6b0edb',
type: SupportedEntry.Block,
title: 'Afternoon Sessions',
note: '',
entries: ['02afca', '75ce86', 'e10ed9', '07df89'],
targetDuration: null,
colour: '#339E4E',
custom: {},
revision: 0,
timeStart: 46800000,
timeEnd: 50400000,
duration: 3600000,
isFirstLinked: true,
},
'02afca': {
id: '02afca',
type: SupportedEntry.Milestone,
cue: 'Standby',
title: '12:45 - Presenters ready side stage',
note: '',
colour: '#A790F5',
revision: 0,
custom: {},
parent: '6b0edb',
},
'75ce86': {
id: '75ce86',
type: SupportedEntry.Event,
flag: false,
title: 'Session 2',
timeStart: 46800000,
timeEnd: 49800000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'Intention',
Artist: 'Intelligent Music Project',
},
},
d3a80: {
type: SupportedEntry.Event,
flag: false,
id: 'd3a80',
cue: 'SF1.08',
title: 'Netherlands',
note: 'SF1.08',
duration: 3000000,
timeStrategy: TimeStrategy.LockDuration,
linkStart: true,
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 50100000,
timeEnd: 51300000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
note: 'Ethan Brooks + PowerPoint + Video playback',
colour: '#77C785',
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'De Diepte',
Artist: 'S10',
},
},
'8276c': {
type: SupportedEntry.Event,
flag: false,
id: '8276c',
cue: 'SF1.09',
title: 'Moldova',
note: 'SF1.09',
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 51600000,
timeEnd: 52800000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
cue: '3.1',
parent: '6b0edb',
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'Trenuletul',
Artist: 'Zdob si Zdub',
},
},
'2340b': {
type: SupportedEntry.Event,
flag: false,
id: '2340b',
cue: 'SF1.10',
title: 'Portugal',
note: 'SF1.10',
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 53100000,
timeEnd: 54300000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'Saudade Saudade',
Artist: 'Maro',
},
},
/// <----- BLOCK
cb90b: {
// TODO: This should be a marker type
type: SupportedEntry.Block,
id: 'cb90b',
title: 'Afternoon break',
note: '',
colour: '',
entries: [],
targetDuration: null,
timeWarning: 120000,
timeDanger: 60000,
custom: {},
revision: 0,
timeStart: null,
timeEnd: null,
duration: 0,
isFirstLinked: false,
triggers: [],
},
'503c4': {
e10ed9: {
id: 'e10ed9',
type: SupportedEntry.Event,
flag: false,
id: '503c4',
cue: 'SF1.11',
title: 'Croatia',
note: 'SF1.11',
title: 'Wrap up',
timeStart: 49800000,
timeEnd: 50400000,
duration: 600000,
timeStrategy: TimeStrategy.LockDuration,
linkStart: false,
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 56100000,
timeEnd: 57300000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
note: 'Lucas Bennett',
colour: '#FFCC78',
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
cue: '3.2',
parent: '6b0edb',
revision: 0,
timeWarning: 120000,
timeDanger: 60000,
custom: {},
triggers: [],
custom: {
Song: 'Guilty Pleasure',
Artist: 'Mia Dimsic',
},
},
'5e965': {
type: SupportedEntry.Event,
flag: false,
id: '5e965',
cue: 'SF1.12',
title: 'Denmark',
note: 'SF1.12',
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 57600000,
timeEnd: 58800000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
'07df89': {
id: '07df89',
type: SupportedEntry.Milestone,
cue: 'Strike',
title: '14:00 - AV & Room strike',
note: '',
colour: '#A790F5',
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'The Show',
Artist: 'Reddi',
},
},
bab4a: {
type: SupportedEntry.Event,
flag: false,
id: 'bab4a',
cue: 'SF1.13',
title: 'Austria',
note: 'SF1.13',
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 59100000,
timeEnd: 60300000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'Halo',
Artist: 'LUM!X & Pia Maria',
},
},
d3eb1: {
type: SupportedEntry.Event,
flag: false,
id: 'd3eb1',
cue: 'SF1.14',
title: 'Greece',
note: 'SF1.14',
endAction: EndAction.None,
timerType: TimerType.CountDown,
countToEnd: false,
linkStart: false,
timeStrategy: TimeStrategy.LockEnd,
timeStart: 60600000,
timeEnd: 61800000,
duration: 1200000,
skip: false,
colour: '',
parent: null,
revision: 0,
delay: 0,
dayOffset: 0,
gap: 0,
timeWarning: 500000,
timeDanger: 100000,
triggers: [],
custom: {
Song: 'Die Together',
Artist: 'Amanda Tenfjord',
},
custom: {},
parent: '6b0edb',
},
},
revision: 0,
},
},
project: {
title: 'Eurovision Song Contest',
description: 'Turin 2022',
url: 'www.github.com/cpvalente/ontime',
info: 'Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal',
logo: null,
custom: [],
title: 'Ontime Demo Project',
description: 'Demo Project to get you started',
url: 'https://docs.getontime.no/',
info: 'Use Project info to share information to various Ontime views.\nie. Venue info, wifi, staff details, etc.',
logo: 'ontime-logo.png',
custom: [
{
title: 'Custom data',
value:
'Add additional, custom data fields to the project along with optional links to images. \nThe image will be rendered in the views',
url: '',
},
],
},
settings: {
version: '-',
@@ -537,37 +324,30 @@ export const demoDb: DatabaseModel = {
overrideStyles: false,
warningColor: '#ffa528',
},
customFields: {
Song: {
label: 'Song',
type: 'text',
colour: '#339E4E',
},
Artist: {
label: 'Artist',
type: 'text',
colour: '#3E75E8',
},
},
urlPresets: [
{
enabled: true,
alias: 'clock',
target: 'timer',
search:
'showLeadingZeros=true&timerType=clock&hideClock=true&hideCards=true&hideProgress=true&hideMessage=true&hideSecondary=true&hideLogo=true',
},
{
enabled: true,
alias: 'minimal',
target: 'timer',
search:
'showLeadingZeros=true&hideClock=true&hideCards=true&hideProgress=true&hideMessage=true&hideSecondary=true&hideLogo=true',
'hideclock=true&hidecards=true&hideprogress=true&hidemessage=true&hidesecondary=true&hidelogo=true&font=arial+black&keycolour=00ff00&textcolour=ffffff',
},
],
customFields: {
Custom_Field: {
type: 'text',
colour: '#3E75E8',
label: 'Custom Field',
},
Images: {
type: 'image',
colour: '#ED3333',
label: 'Images',
},
},
automation: {
enabledAutomations: false,
enabledOscIn: true,
enabledOscIn: false,
oscPortIn: 8888,
triggers: [],
automations: {},