import { readFile } from 'node:fs/promises'; import { join } from 'node:path'; import { test } from 'node:test'; import assert from 'node:assert/strict'; import { BASE_PATH as BASE, distRoot, launchBrowser, serveDist } from './website-browser-support.mjs'; /** * The home page "Flip through the app" channel switcher. * * The structural half reads the built HTML: a vertical tablist with one * selected channel, roving tabindex, and a panel per channel. The * interaction half serves the build over HTTP and drives it in Chromium: * autoplay advances, hover and focus pause independently, arrow keys move * the selection together with the panel, caption and on-screen badge, and * `prefers-reduced-motion` turns autoplay off. Without a Chromium the browser * half is skipped locally and fails in CI (see website-browser-support.mjs). */ const CHANNELS = ['dashboard', 'live-tv', 'epg', 'movies', 'downloads', 'settings']; test('showcase markup: a vertical tablist with one selected channel and a panel each', async () => { const html = await readFile(join(distRoot, 'index.html'), 'utf8'); assert.match(html, /
]*aria-orientation="vertical"/); const tabs = [...html.matchAll(/]*role="tab"[^>]*>/g)].map((m) => m[0]); assert.equal(tabs.length, CHANNELS.length, 'one tab per channel'); for (const [i, tab] of tabs.entries()) { assert.match(tab, new RegExp(`data-channel="${CHANNELS[i]}"`)); assert.match(tab, new RegExp(`aria-controls="screen-panel-${CHANNELS[i]}"`)); assert.match(tab, new RegExp(`aria-selected="${i === 0 ? 'true' : 'false'}"`)); assert.match(tab, new RegExp(`tabindex="${i === 0 ? '0' : '-1'}"`)); } const panels = [...html.matchAll(/]*role="tabpanel"[^>]*>/g)].map((m) => m[0]); assert.equal(panels.length, CHANNELS.length, 'one panel per channel'); for (const [i, panel] of panels.entries()) { assert.match(panel, new RegExp(`id="screen-panel-${CHANNELS[i]}"`)); assert.match(panel, new RegExp(`aria-labelledby="screen-tab-${CHANNELS[i]}"`)); assert.match(panel, new RegExp(`aria-hidden="${i === 0 ? 'false' : 'true'}"`)); } assert.match(html, /href="\/iptvnator\/features\/epg\/"/, 'captions link into the feature pages'); const toggleTag = html.match(/]*data-autoplay-toggle[^>]*>/)?.[0]; assert.ok(toggleTag && /aria-label="Pause auto-advance"/.test(toggleTag), 'a persistent pause control ships in the markup'); assert.doesNotMatch(toggleTag, /aria-pressed/, 'the control is an action button, not a toggle with a changing name'); assert.match(html, /channel-osd[^"]*motion-reduce:transition-none/, 'the OSD slide is off under reduced motion'); assert.match(html, /channel-panel[^"]*motion-reduce:transition-none/, 'the panel fade is off under reduced motion'); const tablist = html.match(/
/)?.[0]; assert.ok(tablist, 'tablist element'); assert.equal((tablist.match(/