收口个人中心状态弹层与扫码组件
新增 PlatformStatusDialog 统一支付结果与确认中状态弹层 新增 PlatformProfileQrScannerModal 统一个人中心扫码面板 RpgEntryHomeView 改用共享组件并删除内联支付与扫码实现 更新 PlatformUiKit 收口文档与团队决策记录
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/* @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<typeof vi.fn>;
|
||||
};
|
||||
|
||||
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(
|
||||
<PlatformProfileQrScannerModal
|
||||
error={null}
|
||||
result={null}
|
||||
onClose={vi.fn()}
|
||||
onError={vi.fn()}
|
||||
onResult={onResult}
|
||||
/>,
|
||||
);
|
||||
|
||||
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(
|
||||
<PlatformProfileQrScannerModal
|
||||
error={null}
|
||||
result={null}
|
||||
onClose={vi.fn()}
|
||||
onError={vi.fn()}
|
||||
onResult={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
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<typeof vi.fn>) {
|
||||
Object.defineProperty(globalThis.navigator, 'mediaDevices', {
|
||||
configurable: true,
|
||||
value: { getUserMedia },
|
||||
});
|
||||
}
|
||||
|
||||
function installBarcodeDetector(detect: ReturnType<typeof vi.fn>) {
|
||||
class MockBarcodeDetector {
|
||||
detect = detect;
|
||||
}
|
||||
|
||||
(
|
||||
globalThis as typeof globalThis & {
|
||||
BarcodeDetector?: unknown;
|
||||
}
|
||||
).BarcodeDetector = MockBarcodeDetector;
|
||||
}
|
||||
|
||||
async function flushPromises() {
|
||||
await Promise.resolve();
|
||||
}
|
||||
Reference in New Issue
Block a user