8bd8363ffa
将原生壳公开主站统一为 www.genarrative.world 调整 Tauri release 直接加载线上主站并移除 frontendDist 打包 补齐创作入口配置开发代理与回归测试 同步原生壳方案文档和共享记忆
147 lines
4.6 KiB
TypeScript
147 lines
4.6 KiB
TypeScript
import { describe, expect, test } from 'vitest';
|
|
|
|
import {
|
|
buildMobileShellUrlFromDeepLink,
|
|
resolveMobileShellUrlFromDeepLink,
|
|
} from './deepLink';
|
|
|
|
const options = {
|
|
platform: 'ios' as const,
|
|
hostVersion: '0.1.0',
|
|
capabilities: ['host.getRuntime' as const],
|
|
};
|
|
|
|
describe('buildMobileShellUrlFromDeepLink', () => {
|
|
test('把原生 scheme deep link 映射成同源 H5 目标页', () => {
|
|
const url = new URL(
|
|
buildMobileShellUrlFromDeepLink(
|
|
'genarrative://open/works/detail?work=PZ-1',
|
|
'https://www.genarrative.world/',
|
|
options,
|
|
),
|
|
);
|
|
|
|
expect(url.origin).toBe('https://www.genarrative.world');
|
|
expect(url.pathname).toBe('/works/detail');
|
|
expect(url.searchParams.get('work')).toBe('PZ-1');
|
|
expect(url.searchParams.get('clientRuntime')).toBe('native_app');
|
|
expect(url.searchParams.get('hostShell')).toBe('expo_mobile');
|
|
});
|
|
|
|
test('把同源 universal link 映射成带宿主上下文的 H5 URL', () => {
|
|
const url = new URL(
|
|
buildMobileShellUrlFromDeepLink(
|
|
'https://www.genarrative.world/creation/puzzle#draft',
|
|
'https://www.genarrative.world/',
|
|
options,
|
|
),
|
|
);
|
|
|
|
expect(url.pathname).toBe('/creation/puzzle');
|
|
expect(url.hash).toBe('#draft');
|
|
expect(url.searchParams.get('hostCapabilities')).toBe('host.getRuntime');
|
|
});
|
|
|
|
test('外域和危险协议回退到默认首页', () => {
|
|
const external = new URL(
|
|
buildMobileShellUrlFromDeepLink(
|
|
'https://example.com/works/detail?work=PZ-1',
|
|
'https://www.genarrative.world/',
|
|
options,
|
|
),
|
|
);
|
|
const unsafe = new URL(
|
|
buildMobileShellUrlFromDeepLink(
|
|
'javascript:alert(1)',
|
|
'https://www.genarrative.world/',
|
|
options,
|
|
),
|
|
);
|
|
|
|
expect(external.origin).toBe('https://www.genarrative.world');
|
|
expect(external.pathname).toBe('/');
|
|
expect(unsafe.origin).toBe('https://www.genarrative.world');
|
|
expect(unsafe.pathname).toBe('/');
|
|
});
|
|
|
|
test('协议相对外域 URL 不会被装进移动壳 WebView', () => {
|
|
const url = new URL(
|
|
buildMobileShellUrlFromDeepLink(
|
|
'//example.com/works/detail?work=PZ-1',
|
|
'https://www.genarrative.world/',
|
|
options,
|
|
),
|
|
);
|
|
|
|
expect(url.origin).toBe('https://www.genarrative.world');
|
|
expect(url.pathname).toBe('/');
|
|
});
|
|
|
|
test('基准 H5 URL 配置非法或外域时回退到默认启动地址', () => {
|
|
const url = new URL(
|
|
buildMobileShellUrlFromDeepLink(
|
|
'genarrative://open/works/detail?work=PZ-1',
|
|
'https://example.com/app',
|
|
options,
|
|
),
|
|
);
|
|
|
|
expect(url.origin).toBe('https://www.genarrative.world');
|
|
expect(url.pathname).toBe('/works/detail');
|
|
expect(url.searchParams.get('work')).toBe('PZ-1');
|
|
expect(url.searchParams.get('clientRuntime')).toBe('native_app');
|
|
});
|
|
|
|
test('基准 H5 URL 只在显式开发模式保留本机 deep link 入口', () => {
|
|
const productionUrl = new URL(
|
|
buildMobileShellUrlFromDeepLink(
|
|
'http://127.0.0.1:3000/works/detail?work=PZ-1',
|
|
'http://127.0.0.1:3000/',
|
|
options,
|
|
),
|
|
);
|
|
const developmentUrl = new URL(
|
|
buildMobileShellUrlFromDeepLink(
|
|
'http://127.0.0.1:3000/works/detail?work=PZ-1',
|
|
'http://127.0.0.1:3000/',
|
|
options,
|
|
{
|
|
allowLocalDevelopment: true,
|
|
},
|
|
),
|
|
);
|
|
|
|
expect(productionUrl.origin).toBe('https://www.genarrative.world');
|
|
expect(productionUrl.pathname).toBe('/');
|
|
expect(developmentUrl.origin).toBe('http://127.0.0.1:3000');
|
|
expect(developmentUrl.pathname).toBe('/works/detail');
|
|
expect(developmentUrl.searchParams.get('work')).toBe('PZ-1');
|
|
expect(developmentUrl.searchParams.get('clientRuntime')).toBe('native_app');
|
|
});
|
|
|
|
test('返回 deep link 解析状态用于壳层记录拒绝路径', () => {
|
|
const mapped = resolveMobileShellUrlFromDeepLink(
|
|
'genarrative://open/works/detail?work=PZ-1',
|
|
'https://www.genarrative.world/',
|
|
options,
|
|
);
|
|
const rejected = resolveMobileShellUrlFromDeepLink(
|
|
'https://example.com/works/detail?work=PZ-1',
|
|
'https://www.genarrative.world/',
|
|
options,
|
|
);
|
|
const empty = resolveMobileShellUrlFromDeepLink(
|
|
null,
|
|
'https://www.genarrative.world/',
|
|
options,
|
|
);
|
|
|
|
expect(mapped.status).toBe('mapped');
|
|
expect(new URL(mapped.url).pathname).toBe('/works/detail');
|
|
expect(rejected.status).toBe('rejected');
|
|
expect(new URL(rejected.url).pathname).toBe('/');
|
|
expect(empty.status).toBe('default');
|
|
expect(new URL(empty.url).pathname).toBe('/');
|
|
});
|
|
});
|