import { describe, expect, test } from 'vitest'; import { resolveMobileShellExternalUrl, resolveMobileShellWebViewUrl, shouldAcceptMobileShellHostBridgeMessage, shouldOpenInMobileShellWebView, } from './mobileShellNavigation'; describe('shouldOpenInMobileShellWebView', () => { test('只允许主站同源页面留在移动壳 WebView 内', () => { const allowedOrigin = 'https://app.genarrative.world'; expect( shouldOpenInMobileShellWebView( 'https://app.genarrative.world/works/detail?work=PZ-1', allowedOrigin, ), ).toBe(true); expect( shouldOpenInMobileShellWebView('/creation/puzzle', allowedOrigin), ).toBe(true); expect( shouldOpenInMobileShellWebView( 'http://app.genarrative.world/works/detail?work=PZ-1', allowedOrigin, ), ).toBe(false); expect( shouldOpenInMobileShellWebView('about:blank', allowedOrigin), ).toBe(true); }); test('外链和非网页协议必须离开带 HostBridge 的 WebView', () => { const allowedOrigin = 'https://app.genarrative.world'; expect( shouldOpenInMobileShellWebView('https://example.com/', allowedOrigin), ).toBe(false); expect( shouldOpenInMobileShellWebView('mailto:hi@example.com', allowedOrigin), ).toBe(false); expect( shouldOpenInMobileShellWebView('//example.com/evil', allowedOrigin), ).toBe(false); expect( shouldOpenInMobileShellWebView('javascript:alert(1)', allowedOrigin), ).toBe(false); expect(shouldOpenInMobileShellWebView('not a url', allowedOrigin)).toBe( false, ); }); test('只有允许协议能交给系统外部应用打开', () => { expect(resolveMobileShellExternalUrl(' https://example.com/path ')).toBe( 'https://example.com/path', ); expect(resolveMobileShellExternalUrl('mailto:hi@example.com')).toBe( 'mailto:hi@example.com', ); expect(resolveMobileShellExternalUrl('tel:+12345678')).toBe( 'tel:+12345678', ); expect(resolveMobileShellExternalUrl('javascript:alert(1)')).toBeNull(); expect(resolveMobileShellExternalUrl('file:///etc/passwd')).toBeNull(); expect(resolveMobileShellExternalUrl('/relative/path')).toBeNull(); }); test('HostBridge 主动导航只解析同源网页目标', () => { const allowedOrigin = 'https://app.genarrative.world'; expect( resolveMobileShellWebViewUrl('/works/detail?work=PZ-1', allowedOrigin), ).toBe('https://app.genarrative.world/works/detail?work=PZ-1'); expect( resolveMobileShellWebViewUrl( 'https://app.genarrative.world/creation/puzzle#draft', allowedOrigin, ), ).toBe('https://app.genarrative.world/creation/puzzle#draft'); expect( resolveMobileShellWebViewUrl('https://example.com/', allowedOrigin), ).toBeNull(); expect( resolveMobileShellWebViewUrl('about:blank', allowedOrigin), ).toBeNull(); }); test('HostBridge 消息只接受同源主站页面', () => { const allowedOrigin = 'https://app.genarrative.world'; expect( shouldAcceptMobileShellHostBridgeMessage( 'https://app.genarrative.world/creation/puzzle', allowedOrigin, ), ).toBe(true); expect( shouldAcceptMobileShellHostBridgeMessage('about:blank', allowedOrigin), ).toBe(false); expect( shouldAcceptMobileShellHostBridgeMessage( 'https://example.com/evil', allowedOrigin, ), ).toBe(false); expect( shouldAcceptMobileShellHostBridgeMessage( 'javascript:alert(1)', allowedOrigin, ), ).toBe(false); }); });