/* @vitest-environment jsdom */ import { act, render, screen } from '@testing-library/react'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { PlatformProfileQrScannerModal } from './PlatformProfileQrScannerModal'; type MockTrack = { stop: ReturnType; }; type MockStream = { getTracks: () => MockTrack[]; }; const originalBarcodeDetector = ( globalThis as typeof globalThis & { BarcodeDetector?: unknown; } ).BarcodeDetector; describe('PlatformProfileQrScannerModal', () => { beforeEach(() => { vi.useFakeTimers(); vi.spyOn(HTMLMediaElement.prototype, 'play').mockResolvedValue(undefined); Object.defineProperty(HTMLMediaElement.prototype, 'readyState', { configurable: true, get: () => 4, }); }); afterEach(() => { vi.runOnlyPendingTimers(); vi.useRealTimers(); vi.restoreAllMocks(); if (originalBarcodeDetector === undefined) { delete ( globalThis as typeof globalThis & { BarcodeDetector?: unknown; } ).BarcodeDetector; } else { ( globalThis as typeof globalThis & { BarcodeDetector?: unknown; } ).BarcodeDetector = originalBarcodeDetector; } }); test('detects qr result and stops camera tracks', async () => { const stop = vi.fn(); const stream = buildStream([{ stop }]); const getUserMedia = vi.fn().mockResolvedValue(stream); const detect = vi.fn().mockResolvedValue([{ rawValue: ' hello-world ' }]); const onResult = vi.fn(); installMediaDevices(getUserMedia); installBarcodeDetector(detect); render( , ); await act(async () => { await flushPromises(); }); expect(getUserMedia).toHaveBeenCalledTimes(1); await act(async () => { vi.advanceTimersByTime(360); await flushPromises(); }); expect(onResult).toHaveBeenCalledWith('hello-world'); expect(detect).toHaveBeenCalledTimes(1); expect(stop).toHaveBeenCalledTimes(1); }); test('releases camera resource when modal unmounts before recognition', async () => { const stop = vi.fn(); const stream = buildStream([{ stop }]); const getUserMedia = vi.fn().mockResolvedValue(stream); const detect = vi.fn().mockResolvedValue([]); installMediaDevices(getUserMedia); installBarcodeDetector(detect); const { unmount } = render( , ); await act(async () => { await flushPromises(); }); expect(getUserMedia).toHaveBeenCalledTimes(1); unmount(); expect(stop).toHaveBeenCalledTimes(1); expect(screen.queryByRole('dialog', { name: '扫码' })).toBeNull(); }); }); function buildStream(tracks: MockTrack[]): MockStream { return { getTracks: () => tracks, }; } function installMediaDevices(getUserMedia: ReturnType) { Object.defineProperty(globalThis.navigator, 'mediaDevices', { configurable: true, value: { getUserMedia }, }); } function installBarcodeDetector(detect: ReturnType) { class MockBarcodeDetector { detect = detect; } ( globalThis as typeof globalThis & { BarcodeDetector?: unknown; } ).BarcodeDetector = MockBarcodeDetector; } async function flushPromises() { await Promise.resolve(); }