/** * render-svg.test — pure inline-SVG builders: accessibility hooks, token-driven * classes, zero-total safety, determinism. */ import { describe, it, expect } from 'vitest'; import { barsSvg, donutSvg, type BandBar, type DonutSegment } from '../render-svg.js'; const SEGMENTS: DonutSegment[] = [ { label: 'passed', count: 12, cssClass: 'seg-pass' }, { label: 'failed', count: 3, cssClass: 'seg-fail' }, { label: 'skipped', count: 5, cssClass: 'seg-skip' }, ]; const BANDS: BandBar[] = [ { label: '< 500 ms', count: 9, cssClass: 'band-fast' }, { label: '< 1000 ms', count: 4, cssClass: 'band-ok' }, { label: '< 3000 ms', count: 2, cssClass: 'band-warn' }, { label: '≥ 3000 ms', count: 1, cssClass: 'band-slow' }, ]; describe('donutSvg', () => { it('emits an accessible svg with one ring segment per non-zero count', () => { const svg = donutSvg(SEGMENTS, '80%', 'API checks'); expect(svg).toMatch(/^API checks'); expect(svg).toContain('aria-label="API checks — passed 12 · failed 3 · skipped 5"'); expect(svg).toContain('class="seg-pass"'); expect(svg).toContain('class="seg-fail"'); expect(svg).toContain('class="seg-skip"'); expect(svg).toContain('>80%'); }); it('skips zero-count segments and survives an all-zero total without NaN', () => { const svg = donutSvg( [ { label: 'passed', count: 0, cssClass: 'seg-pass' }, { label: 'failed', count: 0, cssClass: 'seg-fail' }, ], '—', 'Empty', ); expect(svg).not.toContain('NaN'); expect(svg).not.toContain('seg-pass'); // no segment ring, only the track expect(svg).toContain('seg-track'); }); it('escapes hostile labels', () => { const svg = donutSvg([{ label: '', 'x"y'); expect(svg).not.toContain('