mirror of
https://github.com/cpvalente/ontime.git
synced 2026-07-26 10:38:55 +00:00
Feat/173 (#177)
* feat/173: update dependencies * feat/173: migrate to playback requests to POST * feat/173: HTTP startById and startByIndex * feat/173: HTTP loadById and loadByIndex * feat/173: add tests * feat/173: OSC API * feat/173: Websocket API
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ontime",
|
||||
"version": "1.3.2",
|
||||
"version": "1.4.0",
|
||||
"author": "Carlos Valente",
|
||||
"description": "Time keeping for live events",
|
||||
"repository": "https://github.com/cpvalente/ontime",
|
||||
|
||||
@@ -87,6 +87,11 @@ const oscInEnabled = osc?.enabled !== undefined ? osc.enabled : config.osc.input
|
||||
|
||||
const serverPort = data.settings.serverPort || config.server.port;
|
||||
|
||||
/**
|
||||
* @description starts OSC server
|
||||
* @param overrideConfig
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
export const startOSCServer = async (overrideConfig = null) => {
|
||||
if (!oscInEnabled) {
|
||||
global.timer.info('RX', 'OSC Input Disabled');
|
||||
@@ -106,6 +111,11 @@ export const startOSCServer = async (overrideConfig = null) => {
|
||||
// create HTTP server
|
||||
const server = http.createServer(app);
|
||||
|
||||
/**
|
||||
* @description Starts all necessary services
|
||||
* @param overrideConfig
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
export const startServer = async (overrideConfig = null) => {
|
||||
const port = 4001; // port hardcoded
|
||||
|
||||
@@ -126,6 +136,10 @@ export const startServer = async (overrideConfig = null) => {
|
||||
return returnMessage;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description clean shutdown app services
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
export const shutdown = async () => {
|
||||
console.log('Node service shutdown');
|
||||
// shutdown express server
|
||||
|
||||
@@ -183,79 +183,135 @@ export class EventTimer extends Timer {
|
||||
/**
|
||||
* @description Interface for triggering playback actions
|
||||
* @param {string} action - state to be triggered
|
||||
* @param {string|number} [payload] - optional action payload
|
||||
* @returns {boolean} Whether action was called
|
||||
*/
|
||||
trigger(action) {
|
||||
let reply = true;
|
||||
trigger(action, payload) {
|
||||
let success = true;
|
||||
switch (action) {
|
||||
case 'start':
|
||||
case 'start': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
// Call action and force update
|
||||
this.info('PLAYBACK', 'Play Mode Start');
|
||||
this.start();
|
||||
break;
|
||||
case 'pause':
|
||||
}
|
||||
case 'startById': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
const loaded = this.loadEventById(payload);
|
||||
if (loaded) {
|
||||
this.info('PLAYBACK', `Loaded event with ID ${payload}`);
|
||||
this.info('PLAYBACK', 'Play Mode Start');
|
||||
this.start();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'startByIndex': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
const loaded = this.loadEventByIndex(payload);
|
||||
if (loaded) {
|
||||
this.info('PLAYBACK', `Loaded event with index ${payload}`);
|
||||
this.info('PLAYBACK', 'Play Mode Start');
|
||||
this.start();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'pause': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
// Call action and force update
|
||||
this.info('PLAYBACK', 'Play Mode Pause');
|
||||
this.pause();
|
||||
break;
|
||||
case 'stop':
|
||||
}
|
||||
case 'stop': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
// Call action and force update
|
||||
this.info('PLAYBACK', 'Play Mode Stop');
|
||||
this.stop();
|
||||
break;
|
||||
case 'roll':
|
||||
}
|
||||
case 'roll': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
// Call action and force update
|
||||
this.info('PLAYBACK', 'Play Mode Roll');
|
||||
this.roll();
|
||||
break;
|
||||
case 'previous':
|
||||
}
|
||||
case 'previous': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
// Call action and force update
|
||||
this.info('PLAYBACK', 'Play Mode Previous');
|
||||
this.previous();
|
||||
break;
|
||||
case 'next':
|
||||
}
|
||||
case 'next': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
// Call action and force update
|
||||
this.info('PLAYBACK', 'Play Mode Next');
|
||||
this.next();
|
||||
break;
|
||||
case 'unload':
|
||||
}
|
||||
case 'loadById': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
const loaded = this.loadEventById(payload);
|
||||
if (loaded) {
|
||||
this.info('PLAYBACK', `Loaded event with ID ${payload}`);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'loadByIndex': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
const loaded = this.loadEventByIndex(payload);
|
||||
if (loaded) {
|
||||
this.info('PLAYBACK', `Loaded event with index ${payload}`);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'unload': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
// Call action and force update
|
||||
this.info('PLAYBACK', 'Events unloaded');
|
||||
this.unload();
|
||||
break;
|
||||
case 'reload':
|
||||
}
|
||||
case 'reload': {
|
||||
if (this.numEvents === 0 || this.numEvents == null) return false;
|
||||
// Call action and force update
|
||||
this.info('PLAYBACK', 'Reloaded event');
|
||||
this.reload();
|
||||
break;
|
||||
case 'onAir':
|
||||
}
|
||||
case 'onAir': {
|
||||
// Call action
|
||||
this.info('PLAYBACK', 'Going On Air');
|
||||
this.setonAir(true);
|
||||
break;
|
||||
case 'offAir':
|
||||
}
|
||||
case 'offAir': {
|
||||
// Call action and force update
|
||||
this.info('PLAYBACK', 'Going Off Air');
|
||||
this.setonAir(false);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
// Error, disable flag
|
||||
this.error('RX', `Unhandled action triggered ${action}`);
|
||||
reply = false;
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// update state
|
||||
this.runCycle();
|
||||
return reply;
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -553,9 +609,82 @@ export class EventTimer extends Timer {
|
||||
|
||||
/***************************************/
|
||||
/*** TIMER STATE GETTERS / SETTERS ***/
|
||||
/*** ------- WEBSOCKET API ------- ***/
|
||||
/*** ----------------------------- ***/
|
||||
/***************************************/
|
||||
|
||||
/*******************************************/
|
||||
socket.on('ontime-test', () => {
|
||||
socket.emit('hello');
|
||||
});
|
||||
|
||||
socket.on('set-start', () => {
|
||||
this.trigger('start');
|
||||
socket.emit('playstate', this.state);
|
||||
});
|
||||
|
||||
socket.on('set-startid', (data) => {
|
||||
this.trigger('startById', data);
|
||||
socket.emit('playstate', this.state);
|
||||
});
|
||||
|
||||
socket.on('set-startindex', (data) => {
|
||||
const eventIndex = Number(data);
|
||||
if (isNaN(eventIndex)) {
|
||||
return;
|
||||
}
|
||||
this.trigger('startByIndex', data);
|
||||
socket.emit('playstate', this.state);
|
||||
});
|
||||
|
||||
socket.on('set-pause', () => {
|
||||
this.trigger('pause');
|
||||
socket.emit('playstate', this.state);
|
||||
});
|
||||
|
||||
socket.on('set-stop', () => {
|
||||
this.trigger('stop');
|
||||
socket.emit('playstate', this.state);
|
||||
});
|
||||
|
||||
socket.on('set-reload', () => {
|
||||
this.trigger('reload');
|
||||
socket.emit('playstate', this.state);
|
||||
});
|
||||
|
||||
socket.on('set-previous', () => {
|
||||
this.trigger('previous');
|
||||
socket.emit('playstate', this.state);
|
||||
});
|
||||
|
||||
socket.on('set-next', () => {
|
||||
this.trigger('next');
|
||||
socket.emit('playstate', this.state);
|
||||
});
|
||||
|
||||
socket.on('set-roll', () => {
|
||||
this.trigger('roll');
|
||||
socket.emit('playstate', this.state);
|
||||
});
|
||||
|
||||
socket.on('set-delay', (data) => {
|
||||
const delayTime = Number(data);
|
||||
if (isNaN(delayTime)) {
|
||||
return;
|
||||
}
|
||||
this.increment(delayTime * 1000 * 60);
|
||||
});
|
||||
|
||||
socket.on('set-onAir', (data) => {
|
||||
try {
|
||||
const d = JSON.parse(data);
|
||||
this.onAir = !!d;
|
||||
this.broadcastThis('onAir', this.onAir);
|
||||
} catch (error) {
|
||||
this.error('RX', `Failed to parse message ${data}`);
|
||||
}
|
||||
});
|
||||
|
||||
/*******************************************/
|
||||
// general playback state
|
||||
socket.on('get-state', () => {
|
||||
@@ -593,11 +722,6 @@ export class EventTimer extends Timer {
|
||||
socket.emit('playstate', this.state);
|
||||
});
|
||||
|
||||
socket.on('set-onAir', (data) => {
|
||||
this.onAir = data;
|
||||
this.broadcastThis('onAir', this.onAir);
|
||||
});
|
||||
|
||||
socket.on('get-onAir', () => {
|
||||
socket.emit('onAir', this.onAir);
|
||||
});
|
||||
@@ -880,11 +1004,12 @@ export class EventTimer extends Timer {
|
||||
loadEventById(eventId) {
|
||||
const eventIndex = this._eventlist.findIndex((e) => e.id === eventId);
|
||||
|
||||
if (eventIndex === -1) return;
|
||||
if (eventIndex === -1) return false;
|
||||
this.pause();
|
||||
this.loadEvent(eventIndex, 'load');
|
||||
// run cycle
|
||||
this.runCycle();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -892,11 +1017,12 @@ export class EventTimer extends Timer {
|
||||
* @param {number} eventIndex - Index of event in eventlist
|
||||
*/
|
||||
loadEventByIndex(eventIndex) {
|
||||
if (eventIndex === -1 || eventIndex > this.numEvents) return;
|
||||
if (eventIndex === -1 || eventIndex > this.numEvents) return false;
|
||||
this.pause();
|
||||
this.loadEvent(eventIndex, 'load');
|
||||
// run cycle
|
||||
this.runCycle();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -905,7 +1031,7 @@ export class EventTimer extends Timer {
|
||||
* @param {string} [type='load'] - 'load' or 'reload', whether we are keeping running time
|
||||
*/
|
||||
loadEvent(eventIndex, type = 'load') {
|
||||
const e = this._eventlist[eventIndex];
|
||||
const e = this._eventlist?.[eventIndex];
|
||||
if (e == null) return;
|
||||
|
||||
const start = e.timeStart == null || e.timeStart === '' ? 0 : e.timeStart;
|
||||
|
||||
@@ -89,7 +89,7 @@ export class OSCIntegration {
|
||||
|
||||
// only specify special cases
|
||||
switch (payload) {
|
||||
case 'overtime':
|
||||
case 'overtime': {
|
||||
// Whether timer is negative
|
||||
this.oscClient.send(`${this.ADDRESS}/overtime`, payload, (err) => {
|
||||
if (err) {
|
||||
@@ -98,8 +98,8 @@ export class OSCIntegration {
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'title':
|
||||
}
|
||||
case 'title': {
|
||||
if (payload != null && payload !== '') {
|
||||
// Send Title of current event
|
||||
this.oscClient.send(`${this.ADDRESS}/title`, payload, (err) => {
|
||||
@@ -113,8 +113,8 @@ export class OSCIntegration {
|
||||
reply.message = 'Missing message data';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'eventNumber':
|
||||
}
|
||||
case 'eventNumber': {
|
||||
if (payload != null && payload !== '') {
|
||||
// Send event number of current event
|
||||
this.oscClient.send(`${this.ADDRESS}/eventNumber`, payload, (err) => {
|
||||
@@ -128,8 +128,8 @@ export class OSCIntegration {
|
||||
reply.message = 'Missing message data';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'presenter':
|
||||
}
|
||||
case 'presenter': {
|
||||
if (payload != null && payload !== '') {
|
||||
// Send timer data on current event
|
||||
this.oscClient.send(`${this.ADDRESS}/presenter`, payload, (err) => {
|
||||
@@ -143,11 +143,10 @@ export class OSCIntegration {
|
||||
reply.message = 'Missing message data';
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
default: {
|
||||
// catch all for messages, allows to add new messages
|
||||
// but should be used with the integrations definition
|
||||
// eslint-disable-next-line no-case-declarations
|
||||
const message = new Message(`${this.ADDRESS}/${messageType}`);
|
||||
if (payload != null) message.append(payload);
|
||||
this.oscClient.send(message, (err) => {
|
||||
@@ -157,6 +156,7 @@ export class OSCIntegration {
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
@@ -42,36 +42,67 @@ export const initiateOSC = (config) => {
|
||||
|
||||
// get second part (command)
|
||||
switch (path.toLowerCase()) {
|
||||
case 'onair':
|
||||
case 'onair': {
|
||||
global.timer.setonAir(true);
|
||||
break;
|
||||
case 'offair':
|
||||
}
|
||||
case 'offair': {
|
||||
global.timer.setonAir(false);
|
||||
break;
|
||||
case 'start':
|
||||
case 'play':
|
||||
}
|
||||
case 'play': {
|
||||
global.timer.trigger('start');
|
||||
break;
|
||||
case 'pause':
|
||||
}
|
||||
case 'start': {
|
||||
try {
|
||||
const eventIndex = Number(args);
|
||||
if (isNaN(eventIndex)) {
|
||||
global.timer.error('RX', `OSC IN: event index not recognised ${args}`);
|
||||
return;
|
||||
}
|
||||
const success = global.timer.trigger('startByIndex', eventIndex);
|
||||
if (!success) {
|
||||
global.timer.error('RX', `OSC IN: event index not recognised ${args}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('error parsing: ', error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'startid': {
|
||||
const success = global.timer.trigger('startById', args);
|
||||
if (!success) {
|
||||
global.timer.error('RX', `OSC IN: event ID not recognised ${args}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'pause': {
|
||||
global.timer.trigger('pause');
|
||||
break;
|
||||
case 'prev':
|
||||
}
|
||||
case 'prev': {
|
||||
global.timer.trigger('previous');
|
||||
break;
|
||||
case 'next':
|
||||
}
|
||||
case 'next': {
|
||||
global.timer.trigger('next');
|
||||
break;
|
||||
}
|
||||
case 'unload':
|
||||
case 'stop':
|
||||
case 'stop': {
|
||||
global.timer.trigger('unload');
|
||||
break;
|
||||
case 'reload':
|
||||
}
|
||||
case 'reload': {
|
||||
global.timer.trigger('reload');
|
||||
break;
|
||||
case 'roll':
|
||||
}
|
||||
case 'roll': {
|
||||
global.timer.trigger('roll');
|
||||
break;
|
||||
case 'delay':
|
||||
}
|
||||
case 'delay': {
|
||||
try {
|
||||
const t = parseInt(args, 10);
|
||||
if (isNaN(t)) {
|
||||
@@ -83,7 +114,9 @@ export const initiateOSC = (config) => {
|
||||
console.log('error parsing: ', error);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'goto':
|
||||
case 'load': {
|
||||
try {
|
||||
const eventIndex = parseInt(args, 10);
|
||||
if (isNaN(eventIndex) || eventIndex <= 0) {
|
||||
@@ -97,8 +130,9 @@ export const initiateOSC = (config) => {
|
||||
global.timer.error('RX', `OSC IN: error calling goto ${error}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'gotoid':
|
||||
console.log('calling gotoid with', args);
|
||||
case 'loadid': {
|
||||
if (args == null) {
|
||||
global.timer.error('RX', `OSC IN: event id not recognised or out of range ${args}}`);
|
||||
return;
|
||||
@@ -109,10 +143,12 @@ export const initiateOSC = (config) => {
|
||||
global.timer.error('RX', `OSC IN: error calling goto ${error}`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
default: {
|
||||
global.timer.warning('RX', `OSC IN: unhandled message ${path}`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -46,6 +46,13 @@ async function _insertAt(entry, index) {
|
||||
await db.write();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Inserts an entry after an element with given Id
|
||||
* @param entry
|
||||
* @param id
|
||||
* @return {Promise<void>}
|
||||
* @private
|
||||
*/
|
||||
async function _insertAfterId(entry, id) {
|
||||
const index = [...data.events].findIndex((event) => event.id === id);
|
||||
await _insertAt(entry, index + 1);
|
||||
|
||||
@@ -19,7 +19,23 @@ export const offAir = async (req, res) => {
|
||||
// Create controller for GET request to '/playback/start'
|
||||
// Starts timer object
|
||||
export const pbStart = async (req, res) => {
|
||||
global.timer.trigger('start') ? res.sendStatus(200) : res.sendStatus(400);
|
||||
const { eventId, eventIndex } = req.query;
|
||||
if (eventId) {
|
||||
global.timer.trigger('startById', eventId)
|
||||
? res.sendStatus(200)
|
||||
: res.status(400).send('Invalid event ID');
|
||||
} else if (eventIndex) {
|
||||
const index = Number(eventIndex);
|
||||
if (!isNaN(index)) {
|
||||
global.timer.trigger('startByIndex', index - 1)
|
||||
? res.sendStatus(200)
|
||||
: res.status(400).send('Invalid event index');
|
||||
} else {
|
||||
res.status(400).send('Invalid event index');
|
||||
}
|
||||
} else {
|
||||
global.timer.trigger('start') ? res.sendStatus(200) : res.sendStatus(400);
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for GET request to '/playback/pause'
|
||||
@@ -52,6 +68,28 @@ export const pbNext = async (req, res) => {
|
||||
global.timer.trigger('next') ? res.sendStatus(200) : res.sendStatus(400);
|
||||
};
|
||||
|
||||
// Create controller for GET request to '/playback/load'
|
||||
// Load requested event
|
||||
export const pbLoad = async (req, res) => {
|
||||
const { eventId, eventIndex } = req.query;
|
||||
if (eventId) {
|
||||
global.timer.trigger('loadById', eventId)
|
||||
? res.sendStatus(200)
|
||||
: res.status(400).send('Invalid event ID');
|
||||
} else if (eventIndex) {
|
||||
const index = Number(eventIndex);
|
||||
if (!isNaN(index)) {
|
||||
global.timer.trigger('loadByIndex', index - 1)
|
||||
? res.sendStatus(200)
|
||||
: res.status(400).send('Invalid event index');
|
||||
} else {
|
||||
res.status(400).send('Invalid event index');
|
||||
}
|
||||
} else {
|
||||
res.status(400).send('No event given');
|
||||
}
|
||||
};
|
||||
|
||||
// Create controller for GET request to '/playback/unload'
|
||||
// Unloads any events
|
||||
export const pbUnload = async (req, res) => {
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
{
|
||||
"name": "ontime-server",
|
||||
"type": "module",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.0",
|
||||
"dependencies": {
|
||||
"body-parser": "^1.20.0",
|
||||
"dotenv": "^16.0.0",
|
||||
"express": "^4.18.0",
|
||||
"express-session": "~1.17.1",
|
||||
"dotenv": "^16.0.1",
|
||||
"express": "^4.18.1",
|
||||
"express-session": "^1.17.3",
|
||||
"lowdb": "3.0.0",
|
||||
"multer": "^1.4.4",
|
||||
"nanoid": "^3.3.3",
|
||||
"node-osc": "^6.1.16",
|
||||
"node-xlsx": "^0.21.0",
|
||||
"passport": "^0.5.2",
|
||||
"passport": "^0.6.0",
|
||||
"passport-local": "~1.0.0",
|
||||
"socket-io": "^1.0.0",
|
||||
"socket.io": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -18,100 +18,91 @@ describe('When a GET request request is sent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('When a GET state change is sent', () => {
|
||||
test('GET /playback/onAir returns 200', async () => {
|
||||
describe('When a POST state change is sent', () => {
|
||||
test('POST /playback/onAir returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/onAir')
|
||||
.post('/playback/onAir')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/offAir returns 200', async () => {
|
||||
test('POST /playback/offAir returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/offAir')
|
||||
.post('/playback/offAir')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/start returns 200', async () => {
|
||||
test('POST /playback/start returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/start')
|
||||
.post('/playback/start')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/play returns 200', async () => {
|
||||
test('POST /playback/pause returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/play')
|
||||
.post('/playback/pause')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/pause returns 200', async () => {
|
||||
test('POST /playback/stop returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/pause')
|
||||
.post('/playback/stop')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/stop returns 200', async () => {
|
||||
test('POST /playback/roll returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/stop')
|
||||
.post('/playback/roll')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/roll returns 200', async () => {
|
||||
test('POST /playback/previous returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/roll')
|
||||
.post('/playback/previous')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/previous returns 200', async () => {
|
||||
test('POST /playback/next returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/previous')
|
||||
.post('/playback/next')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/next returns 200', async () => {
|
||||
test('POST /playback/unload returns 200', async () => {
|
||||
await supertest(server)
|
||||
.get('/playback/next')
|
||||
.post('/playback/unload')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
test('GET /playback/unload returns 200', async () => {
|
||||
test('POST /playback/reload 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')
|
||||
.post('/playback/reload')
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(false);
|
||||
@@ -126,4 +117,82 @@ describe('When a GET state change is sent', () => {
|
||||
expect(response.text.includes('<!doctype html>')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
test('POST of unknown request returns 404', async () => {
|
||||
await supertest(server).post('/playback/madeup').expect(404);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test POST requests with payload', () => {
|
||||
describe('Start given event', () => {
|
||||
test('POST /playback/start with correctly given ID', async () => {
|
||||
await supertest(server)
|
||||
.post('/playback/start')
|
||||
.query({eventId: '5946'})
|
||||
.expect(200);
|
||||
});
|
||||
test('POST /playback/start with correctly given index', async () => {
|
||||
await supertest(server)
|
||||
.post('/playback/start')
|
||||
.query({eventIndex: '2'})
|
||||
.expect(200);
|
||||
});
|
||||
test('POST /playback/start with incorrectly given ID', async () => {
|
||||
await supertest(server)
|
||||
.post('/playback/start')
|
||||
.query({
|
||||
eventId: 'doesntexist',
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
test('POST /playback/start with incorrectly given index', async () => {
|
||||
await supertest(server)
|
||||
.post('/playback/start')
|
||||
.query({
|
||||
eventIndex: '25',
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
test('POST /playback/start with incorrectly given index (NaN)', async () => {
|
||||
await supertest(server)
|
||||
.post('/playback/start')
|
||||
.query({
|
||||
eventIndex: 'notanumber',
|
||||
})
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Load given event', () => {
|
||||
test('POST /playback/load with correctly given ID', async () => {
|
||||
await supertest(server)
|
||||
.post('/playback/load')
|
||||
.query({eventId: '5946'})
|
||||
.expect(200);
|
||||
});
|
||||
test('POST /playback/load with correctly given index', async () => {
|
||||
await supertest(server)
|
||||
.post('/playback/load')
|
||||
.query({eventIndex: '2'})
|
||||
.expect(200);
|
||||
});
|
||||
test('POST /playback/load with incorrectly given ID', async () => {
|
||||
await supertest(server)
|
||||
.post('/playback/load')
|
||||
.query({eventId: 'doesntexist'})
|
||||
.expect(400);
|
||||
});
|
||||
test('POST /playback/load with incorrectly given index', async () => {
|
||||
await supertest(server)
|
||||
.post('/playback/load')
|
||||
.query({eventIndex: '25'})
|
||||
.expect(400);
|
||||
});
|
||||
test('POST /playback/load with incorrectly given index (NaN)', async () => {
|
||||
await supertest(server)
|
||||
.post('/playback/load')
|
||||
.query({ eventIndex: 'notanumber' })
|
||||
.expect(400);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
pbRoll,
|
||||
pbPrevious,
|
||||
pbNext,
|
||||
pbLoad,
|
||||
pbUnload,
|
||||
pbReload,
|
||||
} from '../controllers/playbackController.js';
|
||||
@@ -20,32 +21,34 @@ import {
|
||||
router.get('/', pbGet);
|
||||
|
||||
// create route between controller and '/playback/onAir' endpoint
|
||||
router.get('/onAir', onAir);
|
||||
router.post('/onAir', onAir);
|
||||
|
||||
// create route between controller and '/playback/offAir' endpoint
|
||||
router.get('/offAir', offAir);
|
||||
router.post('/offAir', offAir);
|
||||
|
||||
// create route between controller and '/playback/start' endpoint
|
||||
router.get('/start', pbStart);
|
||||
router.get('/play', pbStart);
|
||||
router.post('/start', pbStart);
|
||||
|
||||
// create route between controller and '/playback/pause' endpoint
|
||||
router.get('/pause', pbPause);
|
||||
router.post('/pause', pbPause);
|
||||
|
||||
// create route between controller and '/playback/stop' endpoint
|
||||
router.get('/stop', pbStop);
|
||||
router.post('/stop', pbStop);
|
||||
|
||||
// create route between controller and '/playback/roll' endpoint
|
||||
router.get('/roll', pbRoll);
|
||||
router.post('/roll', pbRoll);
|
||||
|
||||
// create route between controller and '/playback/previous' endpoint
|
||||
router.get('/previous', pbPrevious);
|
||||
router.post('/previous', pbPrevious);
|
||||
|
||||
// create route between controller and '/playback/next' endpoint
|
||||
router.get('/next', pbNext);
|
||||
router.post('/next', pbNext);
|
||||
|
||||
// create route between controller and '/playback/load' endpoint
|
||||
router.post('/load', pbLoad);
|
||||
|
||||
// create route between controller and '/playback/unload' endpoint
|
||||
router.get('/unload', pbUnload);
|
||||
router.post('/unload', pbUnload);
|
||||
|
||||
// create route between controller and '/playback/reload' endpoint
|
||||
router.get('/reload', pbReload);
|
||||
router.post('/reload', pbReload);
|
||||
|
||||
+28
-27
@@ -282,16 +282,21 @@ cookie-signature@1.0.6:
|
||||
resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
|
||||
integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
|
||||
|
||||
cookie@0.4.1, cookie@~0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
|
||||
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
|
||||
cookie@0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
|
||||
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
|
||||
|
||||
cookie@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b"
|
||||
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
|
||||
|
||||
cookie@~0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
|
||||
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
|
||||
@@ -373,10 +378,10 @@ doctrine@^3.0.0:
|
||||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
dotenv@^16.0.0:
|
||||
version "16.0.0"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411"
|
||||
integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==
|
||||
dotenv@^16.0.1:
|
||||
version "16.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.1.tgz#8f8f9d94876c35dac989876a5d3a82a267fdce1d"
|
||||
integrity sha512-1K6hR6wtk2FviQ4kEiSjFiH5rpzEVi8WW0x96aztHVMhEspNpc4DVOUTEHtEva5VThQ8IaBX1Pe4gSzpVVUsKQ==
|
||||
|
||||
ee-first@1.1.1:
|
||||
version "1.1.1"
|
||||
@@ -540,12 +545,12 @@ exit-on-epipe@~1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz#0bdd92e87d5285d267daa8171d0eb06159689692"
|
||||
integrity sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==
|
||||
|
||||
express-session@~1.17.1:
|
||||
version "1.17.2"
|
||||
resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.17.2.tgz#397020374f9bf7997f891b85ea338767b30d0efd"
|
||||
integrity sha512-mPcYcLA0lvh7D4Oqr5aNJFMtBMKPLl++OKKxkHzZ0U0oDq1rpKBnkR5f5vCHR26VeArlTOEF9td4x5IjICksRQ==
|
||||
express-session@^1.17.3:
|
||||
version "1.17.3"
|
||||
resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.17.3.tgz#14b997a15ed43e5949cb1d073725675dd2777f36"
|
||||
integrity sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==
|
||||
dependencies:
|
||||
cookie "0.4.1"
|
||||
cookie "0.4.2"
|
||||
cookie-signature "1.0.6"
|
||||
debug "2.6.9"
|
||||
depd "~2.0.0"
|
||||
@@ -554,10 +559,10 @@ express-session@~1.17.1:
|
||||
safe-buffer "5.2.1"
|
||||
uid-safe "~2.1.5"
|
||||
|
||||
express@^4.18.0:
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.18.0.tgz#7a426773325d0dd5406395220614c0db10b6e8e2"
|
||||
integrity sha512-EJEXxiTQJS3lIPrU1AE2vRuT7X7E+0KBbpm5GSoK524yl0K8X+er8zS2P14E64eqsVNoWbMCT7MpmQ+ErAhgRg==
|
||||
express@^4.18.1:
|
||||
version "4.18.1"
|
||||
resolved "https://registry.yarnpkg.com/express/-/express-4.18.1.tgz#7797de8b9c72c857b9cd0e14a5eea80666267caf"
|
||||
integrity sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==
|
||||
dependencies:
|
||||
accepts "~1.3.8"
|
||||
array-flatten "1.1.1"
|
||||
@@ -1051,13 +1056,14 @@ passport-strategy@1.x.x:
|
||||
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
|
||||
integrity sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ=
|
||||
|
||||
passport@^0.5.2:
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/passport/-/passport-0.5.2.tgz#0cb38dd8a71552c8390dfa6a9a6f7f3909954bcf"
|
||||
integrity sha512-w9n/Ot5I7orGD4y+7V3EFJCQEznE5RxHamUxcqLT2QoJY0f2JdN8GyHonYFvN0Vz+L6lUJfVhrk2aZz2LbuREw==
|
||||
passport@^0.6.0:
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/passport/-/passport-0.6.0.tgz#e869579fab465b5c0b291e841e6cc95c005fac9d"
|
||||
integrity sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==
|
||||
dependencies:
|
||||
passport-strategy "1.x.x"
|
||||
pause "0.0.1"
|
||||
utils-merge "^1.0.1"
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
@@ -1266,11 +1272,6 @@ side-channel@^1.0.4:
|
||||
get-intrinsic "^1.0.2"
|
||||
object-inspect "^1.9.0"
|
||||
|
||||
socket-io@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/socket-io/-/socket-io-1.0.0.tgz#fc566f4d30a6370224d07e65dcef386bd8f5d296"
|
||||
integrity sha1-/FZvTTCmNwIk0H5l3O84a9j10pY=
|
||||
|
||||
socket.io-adapter@~2.3.3:
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz#4d6111e4d42e9f7646e365b4f578269821f13486"
|
||||
@@ -1409,7 +1410,7 @@ util-deprecate@~1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
utils-merge@1.0.1:
|
||||
utils-merge@1.0.1, utils-merge@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||
|
||||
Reference in New Issue
Block a user