| import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' |
| import { renderHook, act, waitFor } from '@testing-library/react' |
| import { useCamera } from './useCamera' |
|
|
| function fakeStream() { |
| const tracks = [ |
| { stop: vi.fn(), kind: 'video', getSettings: () => ({ deviceId: 'cam1' }) }, |
| { stop: vi.fn(), kind: 'video', getSettings: () => ({ deviceId: 'cam1' }) }, |
| ] |
| return { |
| _tracks: tracks, |
| getTracks: () => tracks, |
| getVideoTracks: () => tracks, |
| } as unknown as MediaStream & { _tracks: { stop: ReturnType<typeof vi.fn> }[] } |
| } |
|
|
| describe('useCamera — track lifecycle', () => { |
| let stream: ReturnType<typeof fakeStream> |
| beforeEach(() => { |
| stream = fakeStream() |
| Object.defineProperty(navigator, 'mediaDevices', { |
| configurable: true, |
| value: { getUserMedia: vi.fn().mockResolvedValue(stream), enumerateDevices: vi.fn().mockResolvedValue([]) }, |
| }) |
| }) |
| afterEach(() => vi.restoreAllMocks()) |
|
|
| it('start() acquires a stream and stop() stops ALL tracks', async () => { |
| const { result } = renderHook(() => useCamera()) |
| await act(async () => { await result.current.start() }) |
| await waitFor(() => expect(result.current.status).toBe('live')) |
| act(() => result.current.stop()) |
| for (const tr of stream._tracks) expect(tr.stop).toHaveBeenCalled() |
| expect(result.current.status).toBe('idle') |
| }) |
|
|
| it('maps NotAllowedError to denied', async () => { |
| const gum = navigator.mediaDevices.getUserMedia as ReturnType<typeof vi.fn> |
| gum.mockRejectedValueOnce(Object.assign(new Error('no'), { name: 'NotAllowedError' })) |
| const { result } = renderHook(() => useCamera()) |
| await act(async () => { await result.current.start() }) |
| await waitFor(() => expect(result.current.status).toBe('denied')) |
| }) |
|
|
| it('reports nocamera when getUserMedia is unavailable', async () => { |
| Object.defineProperty(navigator, 'mediaDevices', { configurable: true, value: undefined }) |
| const { result } = renderHook(() => useCamera()) |
| await act(async () => { await result.current.start() }) |
| expect(result.current.status).toBe('nocamera') |
| }) |
| }) |
|
|