Add multi-player and remote bridge support
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const vm = require('node:vm');
|
||||
|
||||
require('../src/common');
|
||||
|
||||
@@ -16,12 +18,203 @@ test('dashboard onboarding link uses the player base url', () => {
|
||||
playlists: [],
|
||||
clients: [],
|
||||
slides: [],
|
||||
connectedClientsCount: 0
|
||||
connectedClientsCount: 0,
|
||||
connectedPlayersCount: 0
|
||||
},
|
||||
'',
|
||||
null
|
||||
{ id: 1, permissions: ['screens.read', 'clients.read'] }
|
||||
);
|
||||
|
||||
assert.match(html, /href="http:\/\/player\.local"/);
|
||||
assert.doesNotMatch(html, /http:\/\/player\.local\//);
|
||||
});
|
||||
});
|
||||
|
||||
test('dashboard screen snapshot omits player links and duplicate connection counts', () => {
|
||||
const html = renderDashboardPage(
|
||||
{
|
||||
screens: [
|
||||
{
|
||||
id: 7,
|
||||
name: 'Demo Conference',
|
||||
slug: 'demo-conference',
|
||||
playlist_name: 'Main Loop',
|
||||
player_connection_count: 3,
|
||||
public_base_url: 'http://player.local/'
|
||||
}
|
||||
],
|
||||
playlists: [],
|
||||
clients: [],
|
||||
slides: [],
|
||||
connectedClientsCount: 3,
|
||||
connectedPlayersCount: 3
|
||||
},
|
||||
'',
|
||||
{ id: 1, permissions: ['screens.read', 'clients.read'] }
|
||||
);
|
||||
|
||||
assert.match(html, /Screen group snapshot/);
|
||||
assert.match(html, /players/);
|
||||
assert.doesNotMatch(html, /dashboard-screen-link/);
|
||||
assert.doesNotMatch(html, /Connections<\/dt>/);
|
||||
assert.match(html, /3\s+live/);
|
||||
assert.doesNotMatch(html, />3 connected/);
|
||||
});
|
||||
|
||||
test('dashboard kiosk launcher includes connected player choices', () => {
|
||||
const html = renderDashboardPage(
|
||||
{
|
||||
screens: [],
|
||||
playlists: [],
|
||||
kioskPlayers: [
|
||||
{
|
||||
player_identifier: 'player-alpha',
|
||||
player_url: 'http://player-a.example'
|
||||
},
|
||||
{
|
||||
player_identifier: 'player-beta',
|
||||
player_url: 'http://player-b.example'
|
||||
}
|
||||
],
|
||||
slides: [],
|
||||
connectedClientsCount: 1,
|
||||
connectedPlayersCount: 1
|
||||
},
|
||||
'',
|
||||
{ id: 1, permissions: ['screens.read', 'clients.read', 'dashboard.allow'] }
|
||||
);
|
||||
|
||||
assert.match(html, /dashboard-kiosk-launcher-player-select/);
|
||||
assert.match(html, /<option value="http:\/\/player-a\.example" data-player-identifier="player-alpha">player-alpha - http:\/\/player-a\.example<\/option>/);
|
||||
assert.match(html, /<option value="http:\/\/player-b\.example" data-player-identifier="player-beta">player-beta - http:\/\/player-b\.example<\/option>/);
|
||||
assert.match(html, /data-kiosk-launcher-download-base="\/downloads\/kiosk\/pulse-signage-kiosk\.bat"/);
|
||||
});
|
||||
|
||||
test('dashboard kiosk launcher requires both confirmation and a player selection', () => {
|
||||
const script = fs.readFileSync(require.resolve('../src/web/public/js/dashboard/dashboard-page.js'), 'utf8');
|
||||
const downloadLink = {
|
||||
classList: {
|
||||
classes: new Set(['disabled']),
|
||||
add(name) { this.classes.add(name); },
|
||||
remove(name) { this.classes.delete(name); },
|
||||
contains(name) { return this.classes.has(name); }
|
||||
},
|
||||
attributes: {
|
||||
href: '',
|
||||
'data-kiosk-launcher-download-base': '/downloads/kiosk/pulse-signage-kiosk.bat',
|
||||
'aria-disabled': 'true',
|
||||
tabindex: '-1'
|
||||
},
|
||||
setAttribute(name, value) {
|
||||
this.attributes[name] = String(value);
|
||||
},
|
||||
getAttribute(name) {
|
||||
return Object.prototype.hasOwnProperty.call(this.attributes, name) ? this.attributes[name] : '';
|
||||
},
|
||||
removeAttribute(name) {
|
||||
delete this.attributes[name];
|
||||
}
|
||||
};
|
||||
const checkbox = {
|
||||
checked: false,
|
||||
listeners: {},
|
||||
addEventListener(type, handler) {
|
||||
this.listeners[type] = handler;
|
||||
}
|
||||
};
|
||||
const select = {
|
||||
value: '',
|
||||
options: [
|
||||
{ value: '', textContent: 'Select a player' },
|
||||
{ value: 'http://player-a.example', textContent: 'player-alpha' }
|
||||
],
|
||||
listeners: {},
|
||||
innerHTML: '',
|
||||
addEventListener(type, handler) {
|
||||
this.listeners[type] = handler;
|
||||
}
|
||||
};
|
||||
const modal = {
|
||||
querySelectorAll(selector) {
|
||||
return selector === '[data-kiosk-launcher-download]' ? [downloadLink] : [];
|
||||
},
|
||||
querySelector(selector) {
|
||||
if (selector === '[data-kiosk-launcher-confirm]') {
|
||||
return checkbox;
|
||||
}
|
||||
if (selector === '[data-kiosk-launcher-player-select]') {
|
||||
return select;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
addEventListener(type, handler) {
|
||||
this.listeners = this.listeners || {};
|
||||
this.listeners[type] = handler;
|
||||
}
|
||||
};
|
||||
const context = {
|
||||
document: {
|
||||
getElementById(id) {
|
||||
if (id === 'dashboard-kiosk-launcher-modal') {
|
||||
return modal;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
querySelector() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
window: {
|
||||
webUiHelpers: {
|
||||
escapeHtml(value) { return String(value); },
|
||||
formatDashboardDate(value) { return String(value); },
|
||||
getClientRowKey() { return ''; },
|
||||
getClientDisplayName() { return ''; },
|
||||
setButtonVariant() {},
|
||||
normalizeDisplayIp(value) { return String(value); }
|
||||
},
|
||||
WebSocket: null,
|
||||
location: {
|
||||
protocol: 'http:',
|
||||
host: 'example.test'
|
||||
},
|
||||
setTimeout() { return 1; },
|
||||
clearTimeout() {},
|
||||
alert() {},
|
||||
prompt() {
|
||||
return null;
|
||||
},
|
||||
webHandleDashboardState() {}
|
||||
},
|
||||
WebSocket: function MockWebSocket() {},
|
||||
JSON: JSON,
|
||||
Number: Number,
|
||||
String: String,
|
||||
Boolean: Boolean,
|
||||
Array: Array,
|
||||
Object: Object,
|
||||
Math: Math,
|
||||
Set: Set,
|
||||
URLSearchParams: URLSearchParams,
|
||||
FormData: function FormData() {},
|
||||
setTimeout() {},
|
||||
clearTimeout() {},
|
||||
console: console
|
||||
};
|
||||
context.window.document = context.document;
|
||||
context.window.WebSocket = context.WebSocket;
|
||||
|
||||
vm.runInNewContext(script, context);
|
||||
|
||||
assert.equal(downloadLink.classList.contains('disabled'), true);
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(downloadLink.attributes, 'href'), false);
|
||||
|
||||
checkbox.checked = true;
|
||||
checkbox.listeners.change();
|
||||
assert.equal(downloadLink.classList.contains('disabled'), true);
|
||||
assert.equal(Object.prototype.hasOwnProperty.call(downloadLink.attributes, 'href'), false);
|
||||
|
||||
select.value = 'http://player-a.example';
|
||||
select.listeners.change();
|
||||
assert.equal(downloadLink.classList.contains('disabled'), false);
|
||||
assert.equal(downloadLink.attributes.href, '/downloads/kiosk/pulse-signage-kiosk.bat?playerUrl=http%3A%2F%2Fplayer-a.example');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user