import { describe, it } from 'node:test'; import assert from 'node:assert/strict'; import { readFileSync } from 'node:fs'; const html = readFileSync(new URL('../src/dashboard/index.html', import.meta.url), 'utf8'); // Extract an App method body by name (brace-slicing convention) so we can // exercise the real logic. function method(name) { const start = html.indexOf(` ${name}(`); const end = html.indexOf('\n },', start); assert.ok(start >= 0 && end > start, `expected ${name} method in index.html`); return html.slice(start, end + 4).trim(); } describe('dashboard a11y — segmented controls are radio groups (D3a)', () => { it('no seg-group still declares role="tablist"', () => { const segGroups = html.match(/
]*>/g) || []; for (const g of segGroups) { assert.doesNotMatch(g, /role="tablist"/, 'single-select seg-groups must be radiogroup, not tablist'); } // The merged add-account modal IS a real tablist (tabs -> tabpanels), and it // must stay the only one so this guard keeps policing the seg-groups. assert.equal((html.match(/role="tablist"/g) || []).length, 1, 'only the add-account modal may declare a tablist'); assert.match(html, /class="add-tabs" role="tablist"/); assert.match(html, /role="tabpanel"/); }); it('every seg-group is a labelled radiogroup', () => { const groups = html.match(/
]*>/g) || []; assert.ok(groups.length >= 3, `expected >=3 seg-groups, found ${groups.length}`); for (const g of groups) { assert.match(g, /role="radiogroup"/, `seg-group missing role=radiogroup: ${g}`); assert.match(g, /aria-label="[^"]+"/, `seg-group missing aria-label: ${g}`); assert.match(g, /data-i18n-aria-label="aria\./, `seg-group aria-label not i18n: ${g}`); } }); it('every seg-btn is role=radio with an aria-checked state', () => { const btns = html.match(/