Files
Genarrative/src/services/runtimeAudioFeedback.test.ts
T
kdletters ba000111d7 收口原生能力可信边界
H5 原生能力只信任真实 host.getRuntime 回包

移除桌面壳未声明网络事件白名单

补齐原生壳能力门控测试和共享决策记录
2026-06-21 18:08:02 +08:00

141 lines
3.7 KiB
TypeScript

/* @vitest-environment jsdom */
import { afterEach, describe, expect, test, vi } from 'vitest';
import {
resetHostRuntimeCacheForTest,
setHostRuntimeCacheForTest,
} from './host-bridge/hostBridge';
import { resetNativeAppHostBridgeForTest } from './host-bridge/nativeAppHostBridge';
import {
triggerRuntimeClickFeedback,
triggerRuntimeImpactFeedback,
} from './runtimeAudioFeedback';
function waitForNextTask() {
return new Promise<void>((resolve) => {
window.setTimeout(resolve, 0);
});
}
function asTauriInvoke(
invoke: (command: string, args?: Record<string, unknown>) => Promise<unknown>,
) {
return async function tauriInvoke<Result = unknown>(
command: string,
args?: Record<string, unknown>,
) {
return (await invoke(command, args)) as Result;
};
}
afterEach(() => {
vi.restoreAllMocks();
window.history.replaceState(null, '', '/');
delete window.ReactNativeWebView;
delete window.__TAURI__;
resetNativeAppHostBridgeForTest();
resetHostRuntimeCacheForTest();
});
describe('runtimeAudioFeedback', () => {
test('普通浏览器点击反馈使用 vibration fallback', async () => {
const vibrate = vi.fn();
Object.defineProperty(navigator, 'vibrate', {
configurable: true,
value: vibrate,
});
triggerRuntimeImpactFeedback('light', 12);
await waitForNextTask();
expect(vibrate).toHaveBeenCalledWith([12]);
});
test('原生壳触觉反馈成功时不再触发浏览器 vibration fallback', async () => {
const vibrate = vi.fn();
const invoke = vi.fn(
async (_command: string, args?: Record<string, unknown>) => {
const request = (args as { request: { id: string } }).request;
return {
bridge: 'GenarrativeHostBridge',
version: 1,
id: request.id,
ok: true,
result: true,
};
},
);
Object.defineProperty(navigator, 'vibrate', {
configurable: true,
value: vibrate,
});
window.history.replaceState(
null,
'',
'/?clientRuntime=native_app&hostCapabilities=haptics.impact',
);
setHostRuntimeCacheForTest({
shell: 'tauri_desktop',
platform: 'linux',
hostVersion: '0.1.0',
bridgeVersion: 1,
capabilities: ['haptics.impact'],
});
window.__TAURI__ = {
core: {
invoke: asTauriInvoke(invoke),
},
};
triggerRuntimeClickFeedback(undefined, 0.6, {
fallbackVibrationPatternMs: 12,
hapticsStyle: 'medium',
});
await waitForNextTask();
expect(invoke).toHaveBeenCalledWith('host_bridge_request', {
request: expect.objectContaining({
method: 'haptics.impact',
payload: {
style: 'medium',
},
}),
});
expect(vibrate).not.toHaveBeenCalled();
});
test('原生壳不支持触觉反馈时回退到浏览器 vibration', async () => {
const vibrate = vi.fn();
Object.defineProperty(navigator, 'vibrate', {
configurable: true,
value: vibrate,
});
window.history.replaceState(null, '', '/?clientRuntime=native_app');
window.__TAURI__ = {
core: {
invoke: asTauriInvoke(
vi.fn(async (_command: string, args?: Record<string, unknown>) => {
const request = (args as { request: { id: string } }).request;
return {
bridge: 'GenarrativeHostBridge',
version: 1,
id: request.id,
ok: false,
error: {
code: 'unsupported_method',
message: 'unsupported_method',
},
};
}),
),
},
};
triggerRuntimeImpactFeedback('light', 12);
await waitForNextTask();
expect(vibrate).toHaveBeenCalledWith([12]);
});
});