import { describe, expect, test } from 'vitest'; import { isHostBridgeCapability, normalizeHostBridgeBadgeCount, normalizeHostBridgeColorScheme, normalizeHostBridgeExportFileName, normalizeHostBridgeExternalUrl, normalizeHostBridgeLifecycleState, } from './hostBridge'; describe('HostBridge shared contract helpers', () => { test('只允许明确的外链协议交给宿主打开', () => { expect(normalizeHostBridgeExternalUrl(' https://example.com/a ')).toBe( 'https://example.com/a', ); expect(normalizeHostBridgeExternalUrl('mailto:hi@example.com')).toBe( 'mailto:hi@example.com', ); expect(normalizeHostBridgeExternalUrl('tel:+12345678')).toBe( 'tel:+12345678', ); }); test('拒绝空值、控制字符和危险协议', () => { expect(normalizeHostBridgeExternalUrl('')).toBeNull(); expect(normalizeHostBridgeExternalUrl('javascript:alert(1)')).toBeNull(); expect(normalizeHostBridgeExternalUrl('file:///etc/passwd')).toBeNull(); expect( normalizeHostBridgeExternalUrl('https://example.com/\nnext'), ).toBeNull(); expect(normalizeHostBridgeExternalUrl('/relative/path')).toBeNull(); }); test('归一化宿主导出文件名', () => { expect(normalizeHostBridgeExportFileName(' 作品:记录?.txt ')).toBe( '作品-记录-.txt', ); expect(normalizeHostBridgeExportFileName('../secret.txt')).toBe( 'secret.txt', ); expect(normalizeHostBridgeExportFileName('')).toBe( 'genarrative-export.txt', ); expect(normalizeHostBridgeExportFileName('a'.repeat(140))).toHaveLength( 120, ); }); test('识别 HostBridge 能力白名单', () => { expect(isHostBridgeCapability('appearance.getColorScheme')).toBe(true); expect(isHostBridgeCapability('share.open')).toBe(true); expect(isHostBridgeCapability('app.lifecycle')).toBe(true); expect(isHostBridgeCapability('app.setBadgeCount')).toBe(true); expect(isHostBridgeCapability('navigation.canGoBack')).toBe(true); expect(isHostBridgeCapability('unknown.capability')).toBe(false); expect(isHostBridgeCapability(null)).toBe(false); }); test('归一化宿主角标数量', () => { expect(normalizeHostBridgeBadgeCount(0)).toBe(0); expect(normalizeHostBridgeBadgeCount(12)).toBe(12); expect(normalizeHostBridgeBadgeCount(99999)).toBe(99999); expect(normalizeHostBridgeBadgeCount(-1)).toBeNull(); expect(normalizeHostBridgeBadgeCount(1.5)).toBeNull(); expect(normalizeHostBridgeBadgeCount(100000)).toBeNull(); expect(normalizeHostBridgeBadgeCount('1')).toBeNull(); }); test('归一化宿主配色模式', () => { expect(normalizeHostBridgeColorScheme('light')).toBe('light'); expect(normalizeHostBridgeColorScheme('dark')).toBe('dark'); expect(normalizeHostBridgeColorScheme('unspecified')).toBe('unknown'); expect(normalizeHostBridgeColorScheme(null)).toBe('unknown'); }); test('归一化宿主生命周期状态', () => { expect(normalizeHostBridgeLifecycleState('active')).toBe('active'); expect(normalizeHostBridgeLifecycleState('background')).toBe('background'); expect(normalizeHostBridgeLifecycleState('extension')).toBe('inactive'); expect(normalizeHostBridgeLifecycleState(null)).toBe('inactive'); }); });