Files
pulse-signage/test/web-dashboard-page.test.js
T

221 lines
6.4 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const vm = require('node:vm');
require('../src/common');
const renderDashboardPage = require('../src/web/routes/signage/dashboard');
test('dashboard onboarding link uses the player base url', () => {
const html = renderDashboardPage(
{
screens: [
{
public_base_url: 'http://player.local/'
}
],
playlists: [],
clients: [],
slides: [],
connectedClientsCount: 0,
connectedPlayersCount: 0
},
'',
{ 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');
});