import { describe, expect, it, vi } from 'vitest'; import { aggregatePcmPeaks, describeWaveform, renderWaveformToCanvas, } from '../../src/waveform'; describe('waveform canvas rendering', () => { it('renders bounded columns and supplies a text alternative', () => { const peaks = aggregatePcmPeaks( { container: 'raw', data: new Int16Array([-32_768, 0, 16_384, 32_767]), encoding: 's16le', sampleRate: 2, }, { bucketCount: 4 } ); const { canvas, context, attributes } = createCanvas(200, 80); const rendered = renderWaveformToCanvas(canvas, peaks, { devicePixelRatio: 2, startBucket: 1, endBucket: 4, selection: { startFraction: 0.25, endFraction: 0.75 }, }); expect(canvas.width).toBe(400); expect(canvas.height).toBe(160); expect(rendered.startBucket).toBe(1); expect(rendered.endBucket).toBe(4); expect(rendered.accessibleLabel).toContain('Audio waveform'); expect(attributes.get('role')).toBe('img'); expect(attributes.get('aria-label')).toBe(rendered.accessibleLabel); expect(context.moveTo).toHaveBeenCalled(); expect(context.lineTo).toHaveBeenCalled(); expect(context.fillRect).toHaveBeenCalledWith(50, 0, 100, 80); }); it('describes empty waveforms and respects an explicit accessible label', () => { const empty = aggregatePcmPeaks( { container: 'raw', data: new Uint8Array(), encoding: 's16le', sampleRate: 8_000, }, { bucketCount: 1 } ); const { canvas, attributes } = createCanvas(100, 40); const result = renderWaveformToCanvas(canvas, empty, { accessibleLabel: 'No audible content', }); expect(describeWaveform(empty)).toBe('Empty audio waveform.'); expect(result.accessibleLabel).toBe('No audible content'); expect(attributes.get('aria-label')).toBe('No audible content'); }); it('rejects unsafe dimensions, ranges, and selections', () => { const peaks = aggregatePcmPeaks( { container: 'raw', data: new Int16Array([1, 2]), encoding: 's16le', sampleRate: 8_000, }, { bucketCount: 2 } ); const { canvas } = createCanvas(100, 40); expect(() => renderWaveformToCanvas(canvas, peaks, { width: 100_000 }) ).toThrow(/safe limits/u); expect(() => renderWaveformToCanvas(canvas, peaks, { startBucket: 2, endBucket: 1, }) ).toThrow(/bucket range/u); expect(() => renderWaveformToCanvas(canvas, peaks, { selection: { startFraction: -1, endFraction: 0.5 }, }) ).toThrow(/selection/u); }); }); function createCanvas(width: number, height: number) { const attributes = new Map(); const context = { setTransform: vi.fn(), clearRect: vi.fn(), fillRect: vi.fn(), beginPath: vi.fn(), moveTo: vi.fn(), lineTo: vi.fn(), stroke: vi.fn(), fillStyle: '', strokeStyle: '', lineWidth: 1, }; const canvas = { width, height, clientWidth: width, clientHeight: height, getContext: vi.fn(() => context), setAttribute: vi.fn((name: string, value: string) => { attributes.set(name, value); }), } as unknown as HTMLCanvasElement; return { canvas, context, attributes }; }