mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-17 13:23:35 +00:00
Ux/54 help (#88)
* ux/54-help tooltips * ux/54-help refetch after mutation * ux/54-help broadcast event index * ux/54-help prevent multiple keypresses when key held * ux/54-help fat clock * ux/54-help progress bar user defined * ux/54-help onAir is button * ux/54-help OSC: add missing presenter message * ux/54-help OSC: enable / disable OSC * ux/54-help handle timezone in excel date import * ux/54-help tray left click to show app * ux/54-help create queriable endpoint * ux/54-help fix memoisation in lower thirds * ux/54-help revise icons * ux/54-help clarify text * ux/54-help forgiving text parsing * ux/54-help add smart keywords * ux/54-help version bump
This commit is contained in:
+12
-4
@@ -123,6 +123,7 @@ const osc = data.osc;
|
||||
const oscIP = osc?.targetIP || config.osc.targetIP;
|
||||
const oscOutPort = osc?.portOut || config.osc.portOut;
|
||||
const oscInPort = osc?.port || config.osc.port;
|
||||
const oscInEnabled = osc?.enabled !== undefined ? osc.enabled : config.osc.inputEnabled;
|
||||
|
||||
const serverPort = data.settings.serverPort || config.server.port;
|
||||
|
||||
@@ -130,14 +131,19 @@ const serverPort = data.settings.serverPort || config.server.port;
|
||||
import { initiateOSC, shutdownOSCServer } from './controllers/OscController.js';
|
||||
|
||||
export const startOSCServer = async (overrideConfig = null) => {
|
||||
|
||||
if (!oscInEnabled) {
|
||||
global.timer.info('RX', 'OSC Input Disabled')
|
||||
return;
|
||||
}
|
||||
|
||||
// Setup default port
|
||||
const oscSettings = {
|
||||
port: overrideConfig?.port || oscInPort,
|
||||
ipOut: oscIP,
|
||||
portOut: oscOutPort,
|
||||
};
|
||||
|
||||
// Start OSC Server
|
||||
global.timer.info('RX', `Starting OSC Server on port: ${oscInPort}`)
|
||||
initiateOSC(oscSettings);
|
||||
};
|
||||
|
||||
@@ -156,7 +162,7 @@ export const startServer = async (overrideConfig = null) => {
|
||||
|
||||
// Start server
|
||||
const returnMessage = `Ontime is listening on port ${port}`;
|
||||
server.listen(port, '0.0.0.0', () => console.log(returnMessage));
|
||||
server.listen(port, '0.0.0.0');
|
||||
|
||||
// OSC Config
|
||||
const oscConfig = {
|
||||
@@ -167,7 +173,7 @@ export const startServer = async (overrideConfig = null) => {
|
||||
// init timer
|
||||
global.timer = new EventTimer(server, config.timer, oscConfig, data.http);
|
||||
global.timer.setupWithEventList(data.events);
|
||||
|
||||
global.timer.info('SERVER', returnMessage);
|
||||
return returnMessage;
|
||||
};
|
||||
|
||||
@@ -185,3 +191,5 @@ export const shutdown = async () => {
|
||||
// shutdown timer
|
||||
global.timer.shutdown();
|
||||
};
|
||||
|
||||
export { server, app };
|
||||
@@ -306,8 +306,11 @@ export class EventTimer extends Timer {
|
||||
// _finish at is only set when an event is loaded
|
||||
if (this._finishAt > 0) {
|
||||
this.sendOsc(this.osc.implemented.play);
|
||||
this.sendOsc(
|
||||
this.osc.implemented.eventNumber,
|
||||
this.selectedEventIndex || 0
|
||||
);
|
||||
}
|
||||
|
||||
// check integrations - http
|
||||
if (h?.onLoad?.enabled) {
|
||||
if (h?.onLoad?.url != null || h?.onStart?.url !== '') {
|
||||
@@ -335,6 +338,10 @@ export class EventTimer extends Timer {
|
||||
this.osc.implemented.title,
|
||||
this.titles?.titleNow || ''
|
||||
);
|
||||
this.sendOsc(
|
||||
this.osc.implemented.presenter,
|
||||
this.titles?.presenterNow || ''
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -504,13 +511,13 @@ export class EventTimer extends Timer {
|
||||
switch (action) {
|
||||
/*******************************************/
|
||||
// Presenter message
|
||||
case 'set-presenter-text':
|
||||
case 'set-timer-text':
|
||||
this.presenter.text = payload;
|
||||
this.broadcastThis('messages-presenter', this.presenter);
|
||||
this.broadcastThis('messages-timer', this.presenter);
|
||||
break;
|
||||
case 'set-presenter-visible':
|
||||
case 'set-timer-visible':
|
||||
this.presenter.visible = payload;
|
||||
this.broadcastThis('messages-presenter', this.presenter);
|
||||
this.broadcastThis('messages-timer', this.presenter);
|
||||
break;
|
||||
|
||||
/*******************************************/
|
||||
@@ -679,22 +686,22 @@ export class EventTimer extends Timer {
|
||||
|
||||
// Messages
|
||||
socket.on('get-messages', () => {
|
||||
this.broadcastThis('messages-presenter', this.presenter);
|
||||
this.broadcastThis('messages-timer', this.presenter);
|
||||
this.broadcastThis('messages-public', this.public);
|
||||
this.broadcastThis('messages-lower', this.lower);
|
||||
});
|
||||
|
||||
// Presenter message
|
||||
socket.on('set-presenter-text', (data) => {
|
||||
this._setTitles('set-presenter-text', data);
|
||||
socket.on('set-timer-text', (data) => {
|
||||
this._setTitles('set-timer-text', data);
|
||||
});
|
||||
|
||||
socket.on('set-presenter-visible', (data) => {
|
||||
this._setTitles('set-presenter-visible', data);
|
||||
socket.on('set-timer-visible', (data) => {
|
||||
this._setTitles('set-timer-visible', data);
|
||||
});
|
||||
|
||||
socket.on('get-presenter', () => {
|
||||
this.broadcastThis('messages-presenter', this.presenter);
|
||||
socket.on('get-timer', () => {
|
||||
this.broadcastThis('messages-timer', this.presenter);
|
||||
});
|
||||
/*******************************************/
|
||||
// Public message
|
||||
@@ -1390,6 +1397,7 @@ export class EventTimer extends Timer {
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Logger logic
|
||||
* -------------
|
||||
@@ -1453,6 +1461,7 @@ export class EventTimer extends Timer {
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/**
|
||||
* Integrations
|
||||
* -------------
|
||||
@@ -1473,4 +1482,19 @@ export class EventTimer extends Timer {
|
||||
this.error('TX', reply.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds sync object
|
||||
* @returns {{running: number, timer: (null|string|*), presenter: null, playback: string, clock: null, title: null}}
|
||||
*/
|
||||
poll() {
|
||||
return {
|
||||
clock: this.clock,
|
||||
running: Timer.toSeconds(this.current),
|
||||
timer: this.timeTag,
|
||||
playback: this.state,
|
||||
title: this.titles.titleNow,
|
||||
presenter: this.titles.presenterNow,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,26 +4,26 @@ import {
|
||||
replacePlaceholder,
|
||||
normaliseEndTime,
|
||||
sortArrayByProperty,
|
||||
updateRoll
|
||||
updateRoll,
|
||||
} from '../classUtils.js';
|
||||
|
||||
// test sortArrayByProperty()
|
||||
describe('sort simple arrays of objects', () => {
|
||||
it('sort array 1-5', () => {
|
||||
const arr1 = [
|
||||
{timeStart: 1},
|
||||
{timeStart: 5},
|
||||
{timeStart: 3},
|
||||
{timeStart: 2},
|
||||
{timeStart: 4},
|
||||
{ timeStart: 1 },
|
||||
{ timeStart: 5 },
|
||||
{ timeStart: 3 },
|
||||
{ timeStart: 2 },
|
||||
{ timeStart: 4 },
|
||||
];
|
||||
|
||||
const arr1Expected = [
|
||||
{timeStart: 1},
|
||||
{timeStart: 2},
|
||||
{timeStart: 3},
|
||||
{timeStart: 4},
|
||||
{timeStart: 5},
|
||||
{ timeStart: 1 },
|
||||
{ timeStart: 2 },
|
||||
{ timeStart: 3 },
|
||||
{ timeStart: 4 },
|
||||
{ timeStart: 5 },
|
||||
];
|
||||
|
||||
const sorted = sortArrayByProperty(arr1, 'timeStart');
|
||||
@@ -32,21 +32,21 @@ describe('sort simple arrays of objects', () => {
|
||||
|
||||
it('sort array 1-5 with null', () => {
|
||||
const arr1 = [
|
||||
{timeStart: 1},
|
||||
{timeStart: 5},
|
||||
{timeStart: 3},
|
||||
{timeStart: 2},
|
||||
{timeStart: 4},
|
||||
{timeStart: null},
|
||||
{ timeStart: 1 },
|
||||
{ timeStart: 5 },
|
||||
{ timeStart: 3 },
|
||||
{ timeStart: 2 },
|
||||
{ timeStart: 4 },
|
||||
{ timeStart: null },
|
||||
];
|
||||
|
||||
const arr1Expected = [
|
||||
{timeStart: null},
|
||||
{timeStart: 1},
|
||||
{timeStart: 2},
|
||||
{timeStart: 3},
|
||||
{timeStart: 4},
|
||||
{timeStart: 5},
|
||||
{ timeStart: null },
|
||||
{ timeStart: 1 },
|
||||
{ timeStart: 2 },
|
||||
{ timeStart: 3 },
|
||||
{ timeStart: 4 },
|
||||
{ timeStart: 5 },
|
||||
];
|
||||
|
||||
const sorted = sortArrayByProperty(arr1, 'timeStart');
|
||||
@@ -393,78 +393,76 @@ describe('test that roll behaviour with overlapping times', () => {
|
||||
// test replacePlaceholder()
|
||||
describe('test that it replaces data correctly', () => {
|
||||
const values = {
|
||||
$timer: "timer",
|
||||
$title: "title",
|
||||
$presenter: "presenter",
|
||||
$subtitle: "subtitle",
|
||||
"$next-title": "next title",
|
||||
"$next-presenter": "next presenter",
|
||||
"$next-subtitle": "next subtitle"
|
||||
$timer: 'timer',
|
||||
$title: 'title',
|
||||
$presenter: 'presenter',
|
||||
$subtitle: 'subtitle',
|
||||
'$next-title': 'next title',
|
||||
'$next-presenter': 'next presenter',
|
||||
'$next-subtitle': 'next subtitle',
|
||||
};
|
||||
|
||||
it('replaces timer', () => {
|
||||
const test = '___1232132 $timer';
|
||||
const expected = '___1232132 timer';
|
||||
const s = replacePlaceholder(test, values)
|
||||
const s = replacePlaceholder(test, values);
|
||||
expect(s).toBe(expected);
|
||||
});
|
||||
|
||||
it('replaces title', () => {
|
||||
const test = '___1232132 $title';
|
||||
const expected = '___1232132 title';
|
||||
const s = replacePlaceholder(test, values)
|
||||
const s = replacePlaceholder(test, values);
|
||||
expect(s).toBe(expected);
|
||||
});
|
||||
|
||||
it('replaces presenter', () => {
|
||||
const test = '___1232132 $presenter';
|
||||
const expected = '___1232132 presenter';
|
||||
const s = replacePlaceholder(test, values)
|
||||
const s = replacePlaceholder(test, values);
|
||||
expect(s).toBe(expected);
|
||||
});
|
||||
|
||||
it('replaces subtitle', () => {
|
||||
const test = '___1232132 $subtitle';
|
||||
const expected = '___1232132 subtitle';
|
||||
const s = replacePlaceholder(test, values)
|
||||
const s = replacePlaceholder(test, values);
|
||||
expect(s).toBe(expected);
|
||||
});
|
||||
|
||||
it('replaces next next title', () => {
|
||||
const test = '___1232132 $next-title';
|
||||
const expected = '___1232132 next title';
|
||||
const s = replacePlaceholder(test, values)
|
||||
const s = replacePlaceholder(test, values);
|
||||
expect(s).toBe(expected);
|
||||
});
|
||||
|
||||
it('replaces next presenter', () => {
|
||||
const test = '___1232132 $next-presenter';
|
||||
const expected = '___1232132 next presenter';
|
||||
const s = replacePlaceholder(test, values)
|
||||
const s = replacePlaceholder(test, values);
|
||||
expect(s).toBe(expected);
|
||||
});
|
||||
|
||||
it('replaces next subtitle', () => {
|
||||
const test = '___1232132 $next-subtitle';
|
||||
const expected = '___1232132 next subtitle';
|
||||
const s = replacePlaceholder(test, values)
|
||||
const s = replacePlaceholder(test, values);
|
||||
expect(s).toBe(expected);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// test getSelectionByRoll() on issue #58
|
||||
describe('test that roll behaviour multi day event edge cases', () => {
|
||||
|
||||
it('if the start time is the day after end time, and start time is earlier than now', () => {
|
||||
const now = 66600000; // 19:30
|
||||
const now = 66600000; // 19:30
|
||||
const eventlist = [
|
||||
{
|
||||
id: 1,
|
||||
timeStart: 66000000, // 19:20
|
||||
timeEnd: 54600000, // 16:10
|
||||
timeStart: 66000000, // 19:20
|
||||
timeEnd: 54600000, // 16:10
|
||||
isPublic: false,
|
||||
}
|
||||
},
|
||||
];
|
||||
const expected = {
|
||||
nowIndex: 0,
|
||||
@@ -486,14 +484,14 @@ describe('test that roll behaviour multi day event edge cases', () => {
|
||||
});
|
||||
|
||||
it('if the start time is the day after end time, and both are later than now', () => {
|
||||
const now = 66840000; // 19:34
|
||||
const now = 66840000; // 19:34
|
||||
const eventlist = [
|
||||
{
|
||||
id: 1,
|
||||
timeStart: 67200000, // 19:40
|
||||
timeEnd: 66900000, // 19:35
|
||||
timeStart: 67200000, // 19:40
|
||||
timeEnd: 66900000, // 19:35
|
||||
isPublic: false,
|
||||
}
|
||||
},
|
||||
];
|
||||
const expected = {
|
||||
nowIndex: null,
|
||||
@@ -512,11 +510,10 @@ describe('test that roll behaviour multi day event edge cases', () => {
|
||||
|
||||
// test normaliseEndTime() on issue #58
|
||||
test('test typical scenarios', () => {
|
||||
|
||||
const t1 = {
|
||||
start: 10,
|
||||
end: 20,
|
||||
}
|
||||
};
|
||||
const t1_expected = 20;
|
||||
|
||||
expect(normaliseEndTime(t1.start, t1.end)).toBe(t1_expected);
|
||||
@@ -524,7 +521,7 @@ test('test typical scenarios', () => {
|
||||
const t2 = {
|
||||
start: 10 + DAY_TO_MS,
|
||||
end: 20,
|
||||
}
|
||||
};
|
||||
const t2_expected = 20 + DAY_TO_MS;
|
||||
|
||||
expect(normaliseEndTime(t2.start, t2.end)).toBe(t2_expected);
|
||||
@@ -532,16 +529,14 @@ test('test typical scenarios', () => {
|
||||
const t3 = {
|
||||
start: 10,
|
||||
end: 10,
|
||||
}
|
||||
};
|
||||
const t3_expected = 10;
|
||||
|
||||
expect(normaliseEndTime(t3.start, t3.end)).toBe(t3_expected);
|
||||
});
|
||||
|
||||
|
||||
// test updateRoll()
|
||||
describe('typical scenarios', () => {
|
||||
|
||||
it('it updates running events correctly', () => {
|
||||
const timers = {
|
||||
selectedEventId: 1,
|
||||
|
||||
@@ -24,6 +24,7 @@ export class OSCIntegration {
|
||||
time: 'time',
|
||||
overtime: 'overtime',
|
||||
title: 'title',
|
||||
eventNumber: 'eventNumber',
|
||||
presenter: 'presenter',
|
||||
};
|
||||
}
|
||||
@@ -36,6 +37,15 @@ export class OSCIntegration {
|
||||
*/
|
||||
init(oscConfig) {
|
||||
const { ip, port } = oscConfig;
|
||||
const validateType = typeof ip !== 'string' || typeof port !== 'number';
|
||||
const validateNull = ip == null || port == null;
|
||||
|
||||
if (validateType || validateNull) {
|
||||
return {
|
||||
success: false,
|
||||
message: `Config options incorrect`,
|
||||
};
|
||||
}
|
||||
try {
|
||||
this.oscClient = new Client(ip, port);
|
||||
return {
|
||||
@@ -45,7 +55,7 @@ export class OSCIntegration {
|
||||
} catch (error) {
|
||||
this.oscClient = null;
|
||||
return {
|
||||
success: true,
|
||||
success: false,
|
||||
message: `Failed initialising OSC Client: ${error}`,
|
||||
};
|
||||
}
|
||||
@@ -101,9 +111,24 @@ export class OSCIntegration {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'eventNumber':
|
||||
if (payload != null && payload !== '') {
|
||||
// Send event number of current event
|
||||
this.oscClient.send(`${this.ADDRESS}/eventNumber`, payload, (err) => {
|
||||
if (err) {
|
||||
reply.success = false;
|
||||
reply.message = err;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
reply.success = false;
|
||||
reply.message = 'Missing message data';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'presenter':
|
||||
if (payload != null && payload !== '') {
|
||||
// Send presenter data on current event
|
||||
// Send timer data on current event
|
||||
this.oscClient.send(`${this.ADDRESS}/presenter`, payload, (err) => {
|
||||
if (err) {
|
||||
reply.success = false;
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
import { OSCIntegration } from '../Osc';
|
||||
import { Server } from 'node-osc';
|
||||
|
||||
test('Class initialises correctly', () => {
|
||||
const osc = new OSCIntegration();
|
||||
expect(osc.ADDRESS).toBe('/ontime');
|
||||
expect(osc.oscClient).toBe(null);
|
||||
|
||||
// defined objects
|
||||
expect(osc.implemented.play).toBeDefined();
|
||||
expect(osc.implemented.pause).toBeDefined();
|
||||
expect(osc.implemented.stop).toBeDefined();
|
||||
expect(osc.implemented.previous).toBeDefined();
|
||||
expect(osc.implemented.next).toBeDefined();
|
||||
expect(osc.implemented.reload).toBeDefined();
|
||||
expect(osc.implemented.finished).toBeDefined();
|
||||
expect(osc.implemented.time).toBeDefined();
|
||||
expect(osc.implemented.overtime).toBeDefined();
|
||||
expect(osc.implemented.title).toBeDefined();
|
||||
expect(osc.implemented.eventNumber).toBeDefined();
|
||||
expect(osc.implemented.presenter).toBeDefined();
|
||||
|
||||
// initialise client succeeds
|
||||
const { ip, port } = { ip: '127.0.0.1', port: 12345 };
|
||||
const init = osc.init({ ip, port });
|
||||
expect(init.message).toBe(`Initialised OSC Client at ${ip}:${port}`);
|
||||
expect(init.success).toBe(true);
|
||||
expect(osc.oscClient).not.toBe(null);
|
||||
|
||||
// object shutdown as expected
|
||||
osc.shutdown();
|
||||
expect(osc.oscClient).toBe(null);
|
||||
});
|
||||
|
||||
describe('OSC fails to initialise when incorrect data is given', () => {
|
||||
test('IP of wrong type', () => {
|
||||
const osc = new OSCIntegration();
|
||||
const init = osc.init({ ip: 123, port: 8888 });
|
||||
expect(init.message).toBe('Config options incorrect');
|
||||
expect(init.success).toBe(false);
|
||||
expect(osc.oscClient).toBe(null);
|
||||
});
|
||||
|
||||
test('IP is null', () => {
|
||||
const osc = new OSCIntegration();
|
||||
const init = osc.init({ ip: null, port: 8888 });
|
||||
expect(init.message).toBe('Config options incorrect');
|
||||
expect(init.success).toBe(false);
|
||||
expect(osc.oscClient).toBe(null);
|
||||
});
|
||||
|
||||
test('Port of wrong type', () => {
|
||||
const osc = new OSCIntegration();
|
||||
const init = osc.init({ ip: 'localhost', port: 'test' });
|
||||
expect(init.message).toBe('Config options incorrect');
|
||||
expect(init.success).toBe(false);
|
||||
expect(osc.oscClient).toBe(null);
|
||||
});
|
||||
|
||||
test('Port is null', () => {
|
||||
const osc = new OSCIntegration();
|
||||
const init = osc.init({ ip: 'localhost', port: null });
|
||||
expect(init.message).toBe('Config options incorrect');
|
||||
expect(init.success).toBe(false);
|
||||
expect(osc.oscClient).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
test('Test messages sending', async () => {
|
||||
const testPort = 9999;
|
||||
const testIP = 'localhost';
|
||||
const testPayload = 'test';
|
||||
const osc = new OSCIntegration();
|
||||
|
||||
const messages = [];
|
||||
|
||||
// prepare dummy server to receive messages
|
||||
const oscServer = new Server(testPort, testIP);
|
||||
|
||||
oscServer.on('message', (m) => {
|
||||
messages.push({ yay: m });
|
||||
});
|
||||
|
||||
// try and send a message before initialising
|
||||
const test = await osc.send('test');
|
||||
expect(test.success).toBe(false);
|
||||
expect(test.message).toBe('Client not initialised');
|
||||
|
||||
// initialise osc
|
||||
osc.init({ ip: testIP, port: testPort });
|
||||
|
||||
// try and send unrecognised message
|
||||
const test2 = await osc.send('test');
|
||||
expect(test2.success).toBe(true);
|
||||
|
||||
// send play message
|
||||
const playAddress = osc.implemented.play;
|
||||
const playSent = await osc.send(playAddress);
|
||||
expect(playSent.success).toBe(true);
|
||||
|
||||
// send pause message
|
||||
const pauseAddress = osc.implemented.pause;
|
||||
const pauseSent = await osc.send(pauseAddress);
|
||||
expect(pauseSent.success).toBe(true);
|
||||
|
||||
// send stop message
|
||||
const stopAddress = osc.implemented.stop;
|
||||
const stopSent = await osc.send(stopAddress);
|
||||
expect(stopSent.success).toBe(true);
|
||||
|
||||
// send previous message
|
||||
const previousAddress = osc.implemented.previous;
|
||||
const previousSent = await osc.send(previousAddress);
|
||||
expect(previousSent.success).toBe(true);
|
||||
|
||||
// send next message
|
||||
const nextAddress = osc.implemented.next;
|
||||
const nextSent = await osc.send(nextAddress);
|
||||
expect(nextSent.success).toBe(true);
|
||||
|
||||
// send reload message
|
||||
const reloadAddress = osc.implemented.reload;
|
||||
const reloadSent = await osc.send(reloadAddress);
|
||||
expect(reloadSent.success).toBe(true);
|
||||
|
||||
// send finished message
|
||||
const finishedAddress = osc.implemented.finished;
|
||||
const finishedSent = await osc.send(finishedAddress);
|
||||
expect(finishedSent.success).toBe(true);
|
||||
|
||||
// send time message
|
||||
const timeAddress = osc.implemented.time;
|
||||
const timeSent = await osc.send(timeAddress);
|
||||
expect(timeSent.success).toBe(true);
|
||||
|
||||
// send overtime message
|
||||
const overtimeAddress = osc.implemented.overtime;
|
||||
const overtimeSent = await osc.send(overtimeAddress, testPayload);
|
||||
expect(overtimeSent.success).toBe(true);
|
||||
|
||||
// send title message
|
||||
const titleAddress = osc.implemented.title;
|
||||
const titleSent = await osc.send(titleAddress, testPayload);
|
||||
expect(titleSent.success).toBe(true);
|
||||
|
||||
// send eventNumber message
|
||||
const eventNumberAddress = osc.implemented.eventNumber;
|
||||
const eventNumberSent = await osc.send(eventNumberAddress, testPayload);
|
||||
expect(eventNumberSent.success).toBe(true);
|
||||
|
||||
// send timer message
|
||||
const presenterAddress = osc.implemented.presenter;
|
||||
const presenterSent = await osc.send(presenterAddress, testPayload);
|
||||
expect(presenterSent.success).toBe(true);
|
||||
|
||||
// cleanup
|
||||
await osc.shutdown();
|
||||
await oscServer.close();
|
||||
|
||||
// see messagesObject
|
||||
// expect(messages.length).toBe(5);
|
||||
});
|
||||
@@ -13,7 +13,7 @@ export const config = {
|
||||
port: 8888,
|
||||
portOut: 9999,
|
||||
targetIP: '127.0.0.1',
|
||||
enabled: true,
|
||||
inputEnabled: true,
|
||||
},
|
||||
http: {
|
||||
user: '',
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
// get database
|
||||
import { db, data } from '../app.js';
|
||||
import { data, db } from '../app.js';
|
||||
|
||||
// utils
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
import {
|
||||
event as eventDef,
|
||||
delay as delayDef,
|
||||
block as blockDef,
|
||||
delay as delayDef,
|
||||
event as eventDef,
|
||||
} from '../models/eventsDefinition.js';
|
||||
|
||||
async function _insertAt(entry, index) {
|
||||
@@ -73,9 +73,14 @@ export const eventsGetAll = async (req, res) => {
|
||||
// Create controller for GET request to '/events/:eventId'
|
||||
// Returns -
|
||||
export const eventsGetById = async (req, res) => {
|
||||
const e = data.events.find({ id: req.params.eventId }).value();
|
||||
console.log('event by id', e);
|
||||
res.json(e);
|
||||
const id = req.params?.eventId;
|
||||
|
||||
if (id == null) {
|
||||
res.status(400).send(`No eventId found in request`);
|
||||
} else {
|
||||
const event = data.events.find((e) => e.id === id);
|
||||
res.json(event);
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for POST request to '/events/'
|
||||
@@ -89,23 +94,24 @@ export const eventsPost = async (req, res) => {
|
||||
|
||||
// ensure structure
|
||||
let newEvent = {};
|
||||
req.body.id = generateId();
|
||||
let id = req.body.id;
|
||||
if (data.events.find((e) => e.id === id)) {
|
||||
id = generateId();
|
||||
}
|
||||
|
||||
switch (req.body.type) {
|
||||
case 'event':
|
||||
newEvent = { ...eventDef, ...req.body };
|
||||
newEvent = { ...eventDef, ...req.body, id };
|
||||
break;
|
||||
case 'delay':
|
||||
newEvent = { ...delayDef, ...req.body };
|
||||
newEvent = { ...delayDef, ...req.body, id };
|
||||
break;
|
||||
case 'block':
|
||||
newEvent = { ...blockDef, ...req.body };
|
||||
newEvent = { ...blockDef, ...req.body, id };
|
||||
break;
|
||||
|
||||
default:
|
||||
res
|
||||
.status(400)
|
||||
.send(`Object type missing or unrecognised: ${req.body.type}`);
|
||||
res.status(400).send(`Object type missing or unrecognised: ${req.body.type}`);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
// get database
|
||||
import { db, data } from '../app.js';
|
||||
import { data, db } from '../app.js';
|
||||
import { networkInterfaces } from 'os';
|
||||
import { fileHandler } from '../utils/parser.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
@@ -15,6 +15,19 @@ function getEventTitle() {
|
||||
return data.event.title;
|
||||
}
|
||||
|
||||
// Create controller for GET request to '/ontime/poll'
|
||||
// Returns data for current state
|
||||
export const poll = async (req, res) => {
|
||||
try {
|
||||
const s = global.timer.poll();
|
||||
res.status(200).send(s);
|
||||
} catch (error) {
|
||||
res.status(500).send({
|
||||
message: `Could not get sync data: ${error}`,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for GET request to '/ontime/db'
|
||||
// Returns -
|
||||
export const dbDownload = async (req, res) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Create controller for GET request to '/playback'
|
||||
// Returns ACK message
|
||||
export const pbGet = async (req, res) => {
|
||||
res.send(global.timer.playState);
|
||||
res.send({ playback: global.timer.state });
|
||||
};
|
||||
|
||||
// Create controller for GET request to '/playback/onAir'
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import { server, shutdown, startServer } from '../../app.js';
|
||||
import supertest from 'supertest';
|
||||
|
||||
beforeAll(() => startServer());
|
||||
afterAll(() => shutdown());
|
||||
|
||||
describe('When a GET request request is sent', () => {
|
||||
test('GET /event returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get('/event')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
expect(response.body).toBeDefined();
|
||||
expect(typeof response.body.title).toBe('string');
|
||||
expect(typeof response.body.url).toBe('string');
|
||||
expect(typeof response.body.publicInfo).toBe('string');
|
||||
expect(typeof response.body.backstageInfo).toBe('string');
|
||||
expect(typeof response.body.endMessage).toBe('string');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import { server, shutdown, startServer } from '../../app.js';
|
||||
import supertest from 'supertest';
|
||||
|
||||
beforeAll(() => startServer());
|
||||
afterAll(() => shutdown());
|
||||
|
||||
const testEvent = {
|
||||
title: 'API test event',
|
||||
subtitle: 'test title',
|
||||
presenter: 'test presenter',
|
||||
note: 'test note',
|
||||
timeStart: 0,
|
||||
timeEnd: 42,
|
||||
isPublic: false,
|
||||
type: 'event',
|
||||
id: 'superSpecial12',
|
||||
};
|
||||
|
||||
describe('When a POST request is sent', () => {
|
||||
test('POST /event should return a 201', async () => {
|
||||
await supertest(server)
|
||||
.post('/events')
|
||||
.send(testEvent)
|
||||
.expect(201)
|
||||
});
|
||||
});
|
||||
|
||||
describe('When a GET request request is sent', () => {
|
||||
test('GET /events returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get('/events')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
expect(response.body).toBeDefined();
|
||||
expect(typeof response.body).toBe('object');
|
||||
});
|
||||
});
|
||||
test('GET /events/:eventId returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get(`/events/${testEvent.id}`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
expect(response.body).toBeDefined();
|
||||
expect(response.body.title).toBe(testEvent.title);
|
||||
expect(response.body.subtitle).toBe(testEvent.subtitle);
|
||||
expect(response.body.presenter).toBe(testEvent.presenter);
|
||||
expect(response.body.note).toBe(testEvent.note);
|
||||
expect(response.body.timeStart).toBe(testEvent.timeStart);
|
||||
expect(response.body.timeEnd).toBe(testEvent.timeEnd);
|
||||
expect(response.body.isPublic).toBe(testEvent.isPublic);
|
||||
expect(response.body.type).toBe(testEvent.type);
|
||||
expect(response.body.id).toBe(testEvent.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,98 @@
|
||||
import { server, shutdown, startServer } from '../../app.js';
|
||||
import supertest from 'supertest';
|
||||
|
||||
beforeAll(() => startServer());
|
||||
afterAll(() => shutdown());
|
||||
|
||||
describe('When a GET request request is sent', () => {
|
||||
test('GET /ontime/poll returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get('/ontime/poll')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
expect(response.body).toBeDefined();
|
||||
expect(typeof response.body.clock).toBe('number');
|
||||
expect(typeof response.body.running).toBe('number');
|
||||
expect(typeof response.body.timer).toBe('string');
|
||||
expect(typeof response.body.playback).toBe('string');
|
||||
expect(typeof response.body.title).toBe('string');
|
||||
expect(typeof response.body.presenter).toBe('string');
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /ontime/db returns a JSON object', async () => {
|
||||
await supertest(server)
|
||||
.get('/ontime/db')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
expect(response.body).toBeDefined();
|
||||
expect(response.headers['content-type']).toContain('json');
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /ontime/info returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get('/ontime/info')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
expect(response.body).toBeDefined();
|
||||
expect(typeof response.body.networkInterfaces).toBe('object');
|
||||
expect(typeof response.body.version).toBe('number');
|
||||
expect(typeof response.body.serverPort).toBe('number');
|
||||
expect(typeof response.body.osc).toBe('object');
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /ontime/aliases returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get('/ontime/aliases')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
expect(response.body).toBeDefined();
|
||||
expect(typeof response.body).toBe('object');
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /ontime/settings returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get('/ontime/settings')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
expect(response.body).toBeDefined();
|
||||
expect(typeof response.body.version).toBe('number');
|
||||
expect(typeof response.body.serverPort).toBe('number');
|
||||
expect(typeof response.body.pinCode).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /ontime/osc returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get('/ontime/osc')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
expect(response.body).toBeDefined();
|
||||
expect(typeof response.body.port).toBe('number');
|
||||
expect(typeof response.body.portOut).toBe('number');
|
||||
expect(typeof response.body.targetIP).toBe('string');
|
||||
expect(typeof response.body.enabled).toBe('boolean');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Any other returns the app', () => {
|
||||
test('GET / returns ontime app', async () => {
|
||||
await supertest(server)
|
||||
.get('/')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.body).toBeDefined();
|
||||
expect(response.text.includes('<!doctype html>')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,129 @@
|
||||
import { server, shutdown, startServer } from '../../app.js';
|
||||
import supertest from 'supertest';
|
||||
|
||||
beforeAll(() => startServer());
|
||||
afterAll(() => shutdown());
|
||||
|
||||
describe('When a GET request request is sent', () => {
|
||||
test('GET /playback returns a valid object', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
expect(response.body).toBeDefined();
|
||||
expect(typeof response.body.playback).toBe('string');
|
||||
expect(response.body.playback).toBe('stop' || 'start' || 'pause' || 'roll');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('When a GET state change is sent', () => {
|
||||
test('GET /playback/onAir returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/onAir')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/offAir returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/offAir')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/start returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/start')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/play returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/play')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/pause returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/pause')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/stop returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/stop')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/roll returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/roll')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/previous returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/previous')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/next returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/next')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/unload returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/unload')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/reload returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/reload')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET of unknown request returns app', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/madeup')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -15,8 +15,12 @@ import {
|
||||
postSettings,
|
||||
getAliases,
|
||||
postAliases,
|
||||
poll,
|
||||
} from '../controllers/ontimeController.js';
|
||||
|
||||
// create route between controller and '/ontime/sync' endpoint
|
||||
router.get('/poll', poll);
|
||||
|
||||
// create route between controller and '/ontime/db' endpoint
|
||||
router.get('/db', dbDownload);
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
pbPrevious,
|
||||
pbNext,
|
||||
pbUnload,
|
||||
pbReload
|
||||
pbReload,
|
||||
} from '../controllers/playbackController.js';
|
||||
|
||||
// create route between controller and '/playback/' endpoint
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import fs from 'fs';
|
||||
import xlsx from 'node-xlsx';
|
||||
import {
|
||||
event as eventDef,
|
||||
delay as delayDef,
|
||||
block as blockDef,
|
||||
delay as delayDef,
|
||||
event as eventDef,
|
||||
} from '../models/eventsDefinition.js';
|
||||
import { dbModelv1 } from '../models/dataModel.js';
|
||||
import { generateId } from 'ontime-utils/generate_id.js';
|
||||
@@ -183,7 +183,6 @@ export const parseExcel_v1 = async (excelData) => {
|
||||
events.push({ ...event, type: 'event' });
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
events,
|
||||
event: eventData,
|
||||
@@ -429,7 +428,7 @@ export const parseOsc_v1 = (data, enforce) => {
|
||||
if (s.port) osc.port = s.port;
|
||||
if (s.portOut) osc.portOut = s.portOut;
|
||||
if (s.targetIP) osc.targetIP = s.targetIP;
|
||||
if (s.enabled) osc.enabled = s.enabled;
|
||||
if (s.enabled !== undefined) osc.enabled = s.enabled;
|
||||
|
||||
// write to db
|
||||
newOsc = {
|
||||
|
||||
Reference in New Issue
Block a user