| import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' |
| import { renderHook, act, waitFor } from '@testing-library/react' |
| import { useRecorder } from './useRecorder' |
|
|
| function setupAudio() { |
| const tracks = [{ stop: vi.fn() }] |
| const stream = { getTracks: () => tracks } as unknown as MediaStream |
| Object.defineProperty(navigator, 'mediaDevices', { |
| configurable: true, value: { getUserMedia: vi.fn().mockResolvedValue(stream) }, |
| }) |
| const node = { connect: vi.fn(), disconnect: vi.fn(), onaudioprocess: null as ((e: unknown) => void) | null } |
| const source = { connect: vi.fn(), disconnect: vi.fn() } |
| class FakeAC { |
| sampleRate = 16000 |
| destination = {} |
| createMediaStreamSource = () => source |
| createScriptProcessor = () => node |
| close = vi.fn() |
| } |
| const w = window as unknown as Record<string, unknown> |
| w.AudioContext = FakeAC |
| return { tracks, node } |
| } |
|
|
| describe('useRecorder (WAV)', () => { |
| beforeEach(() => setupAudio()) |
| afterEach(() => vi.restoreAllMocks()) |
|
|
| it('records PCM and produces a WAV data URI, stopping mic tracks', async () => { |
| const { tracks, node } = setupAudio() |
| const { result } = renderHook(() => useRecorder()) |
| await act(async () => { await result.current.start() }) |
| await waitFor(() => expect(result.current.status).toBe('recording')) |
| |
| act(() => node.onaudioprocess?.({ inputBuffer: { getChannelData: () => new Float32Array([0.1, -0.2, 0.3]) } })) |
| act(() => result.current.stop()) |
| await waitFor(() => expect(result.current.status).toBe('recorded')) |
| await waitFor(() => expect(result.current.dataUri).toMatch(/^data:audio\/wav/)) |
| expect(tracks[0].stop).toHaveBeenCalled() |
| }) |
|
|
| it('reports unsupported when WebAudio is unavailable', async () => { |
| Object.defineProperty(navigator, 'mediaDevices', { configurable: true, value: { getUserMedia: vi.fn() } }) |
| const w = window as unknown as Record<string, unknown> |
| w.AudioContext = undefined |
| w.webkitAudioContext = undefined |
| const { result } = renderHook(() => useRecorder()) |
| await act(async () => { await result.current.start() }) |
| expect(result.current.status).toBe('unsupported') |
| }) |
| }) |
|
|