xingyun/__tests__/main.test.ts

216 lines
6.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { beforeEach, describe, expect, it, vi } from 'vitest';
const {
sendJsonRpcMock,
getDeviceIdMock,
getDeviceModelMock,
getChannelIdMock,
ensureApiClientReadyMock,
buglyInitMock,
buglySetDeviceIdMock,
buglySetDeviceModelMock,
setOaidMock,
setPrivacyStatusMock,
initBaiduOcpcMock,
} = vi.hoisted(() => ({
sendJsonRpcMock: vi.fn(),
getDeviceIdMock: vi.fn(),
getDeviceModelMock: vi.fn(),
getChannelIdMock: vi.fn(),
ensureApiClientReadyMock: vi.fn(),
buglyInitMock: vi.fn(),
buglySetDeviceIdMock: vi.fn(),
buglySetDeviceModelMock: vi.fn(),
setOaidMock: vi.fn(),
setPrivacyStatusMock: vi.fn(),
initBaiduOcpcMock: vi.fn(),
}));
vi.mock('react-native-gesture-handler', () => ({}));
vi.mock('react-native-url-polyfill/auto', () => ({}));
vi.mock('../src/App', () => ({
__esModule: true,
default: () => null,
}));
vi.mock('@telescope/api-client', () => ({
sendJsonRpc: sendJsonRpcMock,
}));
vi.mock('@telescope/oaid', () => ({
getDeviceId: getDeviceIdMock,
}));
vi.mock('@telescope/device-model', () => ({
getDeviceModel: getDeviceModelMock,
}));
vi.mock('@telescope/channel-id', () => ({
getChannelId: getChannelIdMock,
}));
vi.mock('../src/api', () => ({
ensureApiClientReady: ensureApiClientReadyMock,
}));
vi.mock('@telescope/bugly', () => ({
__esModule: true,
default: {
init: buglyInitMock,
setDeviceId: buglySetDeviceIdMock,
setDeviceModel: buglySetDeviceModelMock,
},
BuglyTransport: class BuglyTransport {
options: unknown;
constructor(options?: unknown) {
this.options = options;
}
},
}));
vi.mock('@telescope/baidu-ocpc', () => ({
setOaid: setOaidMock,
setPrivacyStatus: setPrivacyStatusMock,
init: initBaiduOcpcMock,
BaiduOcpcTransport: class BaiduOcpcTransport {
options: unknown;
constructor(options?: unknown) {
this.options = options;
}
},
}));
async function flushPromises(times = 4) {
for (let i = 0; i < times; i += 1) {
await Promise.resolve();
}
}
describe('main', () => {
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
(globalThis as any).__DEV__ = true;
sendJsonRpcMock.mockResolvedValue(undefined);
getDeviceIdMock.mockResolvedValue('device-1');
getDeviceModelMock.mockResolvedValue('Pixel-9');
getChannelIdMock.mockResolvedValue('GF');
ensureApiClientReadyMock.mockResolvedValue(undefined);
initBaiduOcpcMock.mockResolvedValue(undefined);
setOaidMock.mockResolvedValue(undefined);
setPrivacyStatusMock.mockResolvedValue(undefined);
});
it('会初始化 logger/第三方 SDK 并注册 RN 入口', async () => {
await import('../src/main');
await flushPromises();
const { initLogger } = await import('@telescope/logger');
const { AppRegistry } = await import('react-native');
expect(initLogger).toHaveBeenCalledTimes(1);
const [loggerConfig] = (initLogger as any).mock.calls[0] as [{ transports: unknown[] }];
expect(loggerConfig.transports).toHaveLength(4);
expect(AppRegistry.registerComponent).toHaveBeenCalledWith('XingyunReactNative', expect.any(Function));
// Bugly 初始化在 App.tsx 的 useEffect 中main.tsx 只注册 BuglyTransport
// 所以此处不验证 Bugly.init在 App.test.tsx 中测试)。
expect(buglyInitMock).not.toHaveBeenCalled();
expect(setOaidMock).not.toHaveBeenCalled();
expect(setPrivacyStatusMock).not.toHaveBeenCalled();
});
it('BD 渠道会先设置 OAID 再上报隐私同意状态', async () => {
// OCPC 逻辑在 App.tsx 的 useEffect 中main.tsx 只注册组件工厂不渲染,
// 所以此场景在 App.test.tsx 中测试。此处仅验证 main 不会抛错。
getChannelIdMock.mockResolvedValue('BD');
await import('../src/main');
await flushPromises();
expect(getChannelIdMock).toHaveBeenCalled();
});
it('BatchTransport.submit 会做等待、脱敏与截断后上报', async () => {
await import('../src/main');
await flushPromises();
const { initLogger } = await import('@telescope/logger');
const { InteractionManager } = await import('react-native');
const [loggerConfig] = (initLogger as any).mock.calls[0] as [{ transports: Array<{ options?: any }> }];
const batchTransport = loggerConfig.transports[3];
const submit = batchTransport.options.submit as (entries: any[]) => Promise<void>;
ensureApiClientReadyMock.mockClear();
getDeviceIdMock.mockClear();
sendJsonRpcMock.mockClear();
InteractionManager.runAfterInteractions.mockClear();
const circular: Record<string, unknown> = { id: 'self' };
circular.self = circular;
await submit([
{
message: 'm'.repeat(250),
context: {
token: 'plain-token',
password: 'plain-password',
normal: 'ok',
arr: ['x'.repeat(1100)],
deep: { a: { b: { c: { d: { e: { f: { g: 'too-deep' } } } } } } },
circular,
err: new Error('E'.repeat(1100)),
},
timestamp: 0,
},
{
message: 'short',
context: 42,
timestamp: 1000,
},
]);
expect(ensureApiClientReadyMock).toHaveBeenCalledTimes(1);
expect(InteractionManager.runAfterInteractions).toHaveBeenCalledTimes(1);
expect(getDeviceIdMock).toHaveBeenCalledTimes(1);
expect(sendJsonRpcMock).toHaveBeenCalledTimes(1);
const [method, payload] = sendJsonRpcMock.mock.calls[0] as [string, { items: Array<Record<string, any>> }];
expect(method).toBe('BatchSubmitCrmTrackLog');
expect(payload.items).toHaveLength(2);
expect(payload.items[0].uniqueId).toBe('device-1');
expect(payload.items[0].event).toBe(`${'m'.repeat(200)}...`);
expect(payload.items[0].time).toBe(new Date(0).toISOString());
expect(payload.items[0].params.token).toBe('[REDACTED]');
expect(payload.items[0].params.password).toBe('[REDACTED]');
expect(payload.items[0].params.arr[0].length).toBe(1003);
expect(payload.items[0].params.circular.self).toBe('[Circular]');
expect(payload.items[0].params.deep.a.b.c.d.e.f).toBe('[MaxDepth]');
expect(payload.items[0].params.err.name).toBe('Error');
expect(payload.items[0].params.err.message.length).toBe(1003);
expect(payload.items[1].event).toBe('short');
expect(payload.items[1].params).toEqual({ value: 42 });
expect(payload.items[1].time).toBe(new Date(1000).toISOString());
});
it('Bugly / 百度 OCPC 初始化失败时不抛错', async () => {
getChannelIdMock.mockRejectedValue(new Error('channel-failed'));
setOaidMock.mockRejectedValueOnce(new Error('oaid-failed'));
await expect(import('../src/main')).resolves.toBeDefined();
await flushPromises();
expect(buglyInitMock).not.toHaveBeenCalled();
expect(setPrivacyStatusMock).not.toHaveBeenCalled();
});
});