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:
Carlos Valente
2022-01-12 22:41:12 +01:00
committed by GitHub
parent c3f18feaae
commit 48093b8651
90 changed files with 2787 additions and 763 deletions
+12 -4
View File
@@ -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 };
+36 -12
View File
@@ -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,
};
}
}
+48 -53
View File
@@ -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,
+27 -2
View File
@@ -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);
});
+1 -1
View File
@@ -13,7 +13,7 @@ export const config = {
port: 8888,
portOut: 9999,
targetIP: '127.0.0.1',
enabled: true,
inputEnabled: true,
},
http: {
user: '',
+19 -13
View File
@@ -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;
}
+14 -1
View File
@@ -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 -1
View File
@@ -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);
});
});
});
+4
View File
@@ -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);
+1 -1
View File
@@ -13,7 +13,7 @@ import {
pbPrevious,
pbNext,
pbUnload,
pbReload
pbReload,
} from '../controllers/playbackController.js';
// create route between controller and '/playback/' endpoint
+3 -4
View File
@@ -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 = {