Files
Genarrative/apps/mobile-shell/src/shell/url.test.ts
T
kdletters 387a1c26e3 完成全量检查整改
清理已跟踪原始日志与个人本地配置
修复前后端有效门禁、测试和构建问题
补齐生产 Jenkins、SpacetimeDB 本地命令与文档约束
收紧图片编辑器状态、附件与媒体引用测试
排除已下线旧创作入口测试并清理 warning
2026-07-17 16:56:46 +08:00

187 lines
6.3 KiB
TypeScript

import { describe, expect, test } from 'vitest';
import {
HOST_BRIDGE_PUBLIC_WEB_ORIGIN,
HOST_BRIDGE_PUBLIC_WEB_URL,
HOST_BRIDGE_VERSION,
} from '../../../../packages/shared/src/contracts/hostBridge';
import {
ALLOWED_PRODUCTION_WEB_ORIGIN,
buildMobileShellUrl,
DEFAULT_MOBILE_SHELL_WEB_URL,
resolveMobileShellBaseWebUrl,
} from './url';
describe('buildMobileShellUrl', () => {
test('默认 H5 地址指向真实主站', () => {
expect(DEFAULT_MOBILE_SHELL_WEB_URL).toBe(HOST_BRIDGE_PUBLIC_WEB_URL);
expect(ALLOWED_PRODUCTION_WEB_ORIGIN).toBe(HOST_BRIDGE_PUBLIC_WEB_ORIGIN);
});
test('为 H5 附加原生移动壳上下文', () => {
const url = new URL(
buildMobileShellUrl('https://www.genarrative.world/works/detail?work=PZ-1', {
platform: 'ios',
hostVersion: '0.1.0',
capabilities: ['host.getRuntime', 'share.open'],
}),
);
expect(url.searchParams.get('clientRuntime')).toBe('native_app');
expect(url.searchParams.get('clientType')).toBe('native_app');
expect(url.searchParams.get('hostShell')).toBe('expo_mobile');
expect(url.searchParams.get('hostPlatform')).toBe('ios');
expect(url.searchParams.get('hostVersion')).toBe('0.1.0');
expect(url.searchParams.get('bridgeVersion')).toBe(
HOST_BRIDGE_VERSION.toString(),
);
expect(url.searchParams.get('hostCapabilities')).toBe(
'host.getRuntime,share.open',
);
expect(url.searchParams.get('work')).toBe('PZ-1');
});
test('附加宿主上下文前会清理旧移动壳 query', () => {
const rawUrl =
'https://www.genarrative.world/works/detail?clientRuntime=browser&hostShell=old_shell&hostCapabilities=old&work=PZ-1';
const builtUrl = buildMobileShellUrl(rawUrl, {
platform: 'android',
hostVersion: '0.1.0',
capabilities: ['host.getRuntime', 'share.open'],
});
const url = new URL(builtUrl);
expect(url.searchParams.get('work')).toBe('PZ-1');
expect(url.searchParams.get('clientRuntime')).toBe('native_app');
expect(url.searchParams.get('hostShell')).toBe('expo_mobile');
expect(url.searchParams.get('hostCapabilities')).toBe(
'host.getRuntime,share.open',
);
expect(builtUrl.match(/clientRuntime=/g)).toHaveLength(1);
expect(builtUrl.match(/hostShell=/g)).toHaveLength(1);
expect(builtUrl.match(/hostCapabilities=/g)).toHaveLength(1);
expect(builtUrl).not.toContain('clientRuntime=browser');
expect(builtUrl).not.toContain('hostShell=old_shell');
expect(builtUrl).not.toContain('hostCapabilities=old');
});
test('支持按平台注入不同能力清单', () => {
const iosUrl = new URL(
buildMobileShellUrl('https://www.genarrative.world/', {
platform: 'ios',
hostVersion: '0.1.0',
capabilities: ['host.getRuntime', 'app.setBadgeCount'],
}),
);
const androidUrl = new URL(
buildMobileShellUrl('https://www.genarrative.world/', {
platform: 'android',
hostVersion: '0.1.0',
capabilities: ['host.getRuntime'],
}),
);
expect(iosUrl.searchParams.get('hostCapabilities')).toBe(
'host.getRuntime,app.setBadgeCount',
);
expect(androidUrl.searchParams.get('hostCapabilities')).toBe(
'host.getRuntime',
);
});
test('移动壳基准 URL 默认只接受生产主站', () => {
expect(
resolveMobileShellBaseWebUrl('https://www.genarrative.world/path'),
).toBe(
'https://www.genarrative.world/path',
);
expect(resolveMobileShellBaseWebUrl('http://127.0.0.1:3000/')).toBe(
DEFAULT_MOBILE_SHELL_WEB_URL,
);
expect(resolveMobileShellBaseWebUrl('http://localhost:3000/')).toBe(
DEFAULT_MOBILE_SHELL_WEB_URL,
);
expect(resolveMobileShellBaseWebUrl('https://example.com/path')).toBe(
DEFAULT_MOBILE_SHELL_WEB_URL,
);
expect(resolveMobileShellBaseWebUrl('http://192.168.1.2:3000/')).toBe(
DEFAULT_MOBILE_SHELL_WEB_URL,
);
expect(resolveMobileShellBaseWebUrl('javascript:alert(1)')).toBe(
DEFAULT_MOBILE_SHELL_WEB_URL,
);
expect(resolveMobileShellBaseWebUrl('/relative/path')).toBe(
DEFAULT_MOBILE_SHELL_WEB_URL,
);
expect(resolveMobileShellBaseWebUrl('')).toBe(DEFAULT_MOBILE_SHELL_WEB_URL);
expect(resolveMobileShellBaseWebUrl(null)).toBe(
DEFAULT_MOBILE_SHELL_WEB_URL,
);
});
test('移动壳基准 URL 只在开发模式接受本机入口', () => {
const options = {
allowLocalDevelopment: true,
};
expect(
resolveMobileShellBaseWebUrl(' http://127.0.0.1:3000/ ', options),
).toBe('http://127.0.0.1:3000/');
expect(resolveMobileShellBaseWebUrl('http://localhost:3000/', options)).toBe(
'http://localhost:3000/',
);
expect(resolveMobileShellBaseWebUrl('http://[::1]:3000/', options)).toBe(
'http://[::1]:3000/',
);
});
test('移动壳 URL 构建默认不会接受本机入口', () => {
const url = new URL(
buildMobileShellUrl('http://127.0.0.1:3000/works/detail?work=PZ-1', {
platform: 'android',
hostVersion: '0.1.0',
capabilities: ['host.getRuntime'],
}),
);
expect(url.origin).toBe(HOST_BRIDGE_PUBLIC_WEB_ORIGIN);
expect(url.pathname).toBe('/');
expect(url.searchParams.get('clientRuntime')).toBe('native_app');
});
test('移动壳 URL 构建只在显式开发模式接受本机入口', () => {
const url = new URL(
buildMobileShellUrl(
'http://127.0.0.1:3000/works/detail?work=PZ-1',
{
platform: 'android',
hostVersion: '0.1.0',
capabilities: ['host.getRuntime'],
},
{
allowLocalDevelopment: true,
},
),
);
expect(url.origin).toBe('http://127.0.0.1:3000');
expect(url.pathname).toBe('/works/detail');
expect(url.searchParams.get('work')).toBe('PZ-1');
expect(url.searchParams.get('clientRuntime')).toBe('native_app');
});
test('非法启动 URL 回退到默认 H5 地址并继续附加宿主上下文', () => {
const url = new URL(
buildMobileShellUrl('javascript:alert(1)', {
platform: 'android',
hostVersion: '0.1.0',
capabilities: ['host.getRuntime'],
}),
);
expect(url.toString().startsWith(DEFAULT_MOBILE_SHELL_WEB_URL)).toBe(true);
expect(url.searchParams.get('clientRuntime')).toBe('native_app');
expect(url.searchParams.get('hostShell')).toBe('expo_mobile');
});
});