feat: publish av-tools 0.1.0
This commit is contained in:
114
tests/unit/waveform-runtime-canvas.test.ts
Normal file
114
tests/unit/waveform-runtime-canvas.test.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
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 };
|
||||
}
|
||||
Reference in New Issue
Block a user