115 lines
3.3 KiB
TypeScript
115 lines
3.3 KiB
TypeScript
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<string, string>();
|
|
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 };
|
|
}
|