cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

ci/pipeline-ergonomics
goq / portal / roster-ui.test.mjs
roster-ui.test.mjs 3.2K
1import test from 'node:test';2import assert from 'node:assert/strict';34import { renderRoster, rosterPresentation } from './roster-ui.mjs';56function snapshot({ focus = { state: 'vacant', slot: 0 } } = {}) {7 return {8 revision: 4,9 self_presence_id: 'viewer-two',10 viewers: [11 { presence_id: 'viewer-one', session_id: 10, input_capable: true, you: false },12 { presence_id: 'viewer-two', session_id: 11, input_capable: false, you: true },13 { presence_id: 'viewer-three', session_id: 12, input_capable: true, you: false },14 ],15 focus,16 };17}1819test('presentation exposes only opaque roster, coarse eligibility, and focus state', () => {20 const presentation = rosterPresentation(snapshot({21 focus: {22 state: 'held',23 slot: 0,24 holder: 'viewer-one',25 session_id: 10,26 focus_generation: 3,27 },28 }));29 assert.equal(presentation.count, 3);30 assert.equal(presentation.shell, '3 viewers · viewer-one holds control');31 assert.deepEqual(32 presentation.rows.map(({ fullHandle, role, you, adaptive }) => ({ fullHandle, role, you, adaptive })),33 [34 { fullHandle: 'viewer-one', role: 'holds control', you: false, adaptive: 'shared stream' },35 { fullHandle: 'viewer-two', role: 'view only', you: true, adaptive: 'shared stream' },36 { fullHandle: 'viewer-three', role: 'input eligible', you: false, adaptive: 'shared stream' },37 ],38 );39});4041test('presentation rejects duplicate, unbounded, and inconsistent identities', () => {42 const duplicate = snapshot();43 duplicate.viewers[2].presence_id = 'viewer-one';44 assert.throws(() => rosterPresentation(duplicate), /duplicate/);45 const rawPeer = snapshot();46 rawPeer.viewers[0].presence_id = 'a'.repeat(64);47 assert.throws(() => rosterPresentation(rawPeer), /bounded opaque/);48 const missingHolder = snapshot({49 focus: { state: 'held', slot: 0, holder: 'viewer-gone', session_id: 90, focus_generation: 1 },50 });51 assert.throws(() => rosterPresentation(missingHolder), /absent/);52});5354class FakeElement {55 constructor(tagName, document) {56 this.tagName = tagName;57 this.ownerDocument = document;58 this.children = [];59 this.attributes = new Map();60 this.className = '';61 this.classList = { add: (...names) => { this.className += ` ${names.join(' ')}`; } };62 this.textContent = '';63 }64 appendChild(child) { this.children.push(child); return child; }65 replaceChildren(...children) { this.children = children; }66 setAttribute(name, value) { this.attributes.set(name, value); }67}6869test('renderer uses text nodes and emits a non-color-only accessible summary', () => {70 const document = {71 createElement: (tag) => new FakeElement(tag, document),72 createDocumentFragment: () => new FakeElement('#fragment', document),73 };74 const root = new FakeElement('ul', document);75 const presentation = renderRoster(root, snapshot());76 assert.equal(presentation.shell, '3 viewers · control available');77 assert.equal(root.attributes.get('aria-label'), '3 connected viewers. control available');78 const rows = root.children[0].children;79 assert.equal(rows.length, 3);80 assert.match(rows[1].attributes.get('aria-label'), /you, view only/);81 assert.equal('innerHTML' in rows[0], false);82});