Files
Genarrative/apps/ai-game-creator-shell/tests/unhandledRejectionBridge.ts
T
k88936 06a4f9346f unhandledrejection 测试桥只吞被 window 侧消费的拒绝
- 合成事件的 reason 改为访问器:有监听方读过就算有人接手;没人读则交回安装前的监听器(含 vitest 失败上报)
- 新增 unhandledRejectionBridge.test.ts 钉住两条路径:无人接手会交回、window 读过则不再交回
- 避免桥把无关的未处理拒绝静默吞掉
2026-10-01 21:15:03 +08:00

43 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 把 Node 的 `unhandledRejection` 桥接成 jsdom `window` 的 `unhandledrejection` 事件。
*
* 生产 WebView 里,`void` 掉的 async handler(`onSubmit` / `onClick`)抛出的拒绝会派发
* `unhandledrejection`,被 `AuthenticatedClient` 的全局 handler 交给错误池。jsdom 既不派发
* 该事件,vitest 又会把未处理的拒绝当成用例失败;需要观察这条链路的用例用它接管 Node
* 监听器,再合成一个带 `reason` 的 window 事件。
*
* 只抑制**被 window 侧消费**的拒绝:合成事件的 `reason` 是访问器,任何监听方读过它就算
* 有人接手;没人读说明这条拒绝是意外的,交回安装前的监听器(含 vitest 的失败上报),
* 不会被静默吞掉。
*
* 用法:用例内 `const uninstall = installUnhandledRejectionBridge()`,在 `finally` 或
* `afterEach` 里 `uninstall()`。
*/
export function installUnhandledRejectionBridge() {
const originalListeners = process.listeners('unhandledRejection');
process.removeAllListeners('unhandledRejection');
const handleRejection = (reason: unknown, promise: Promise<unknown>) => {
let consumed = false;
const event = new Event('unhandledrejection');
Object.defineProperty(event, 'reason', {
get() {
consumed = true;
return reason;
},
});
window.dispatchEvent(event);
if (!consumed) {
for (const listener of originalListeners) {
listener(reason, promise);
}
}
};
process.on('unhandledRejection', handleRejection);
return () => {
process.removeListener('unhandledRejection', handleRejection);
for (const listener of originalListeners) {
process.on('unhandledRejection', listener);
}
};
}