启动一个临时 WebSocket 服务器,返回 URL 和清理函数
()
| 23 | |
| 24 | /** 启动一个临时 WebSocket 服务器,返回 URL 和清理函数 */ |
| 25 | function createMockWSServer(): Promise<{ |
| 26 | url: string; |
| 27 | connections: WebSocket[]; |
| 28 | close: () => Promise<void>; |
| 29 | /** 向所有已连接客户端发送消息 */ |
| 30 | broadcast: (data: unknown) => void; |
| 31 | /** 等待收到指定 action 的消息 */ |
| 32 | waitForAction: (action: string, timeout?: number) => Promise<unknown>; |
| 33 | }> { |
| 34 | return new Promise((resolve, reject) => { |
| 35 | const connections: WebSocket[] = []; |
| 36 | const messageListeners: Array<(msg: unknown) => void> = []; |
| 37 | |
| 38 | const wss = new WebSocketServer({ host: "127.0.0.1", port: 0 }, () => { |
| 39 | const addr = wss.address(); |
| 40 | if (typeof addr === "string") { |
| 41 | reject(new Error("Unexpected address type")); |
| 42 | return; |
| 43 | } |
| 44 | const url = `ws://127.0.0.1:${addr.port}`; |
| 45 | |
| 46 | wss.on("connection", (ws) => { |
| 47 | connections.push(ws); |
| 48 | ws.on("message", (raw) => { |
| 49 | try { |
| 50 | const msg = JSON.parse(raw.toString()); |
| 51 | for (const listener of messageListeners) { |
| 52 | listener(msg); |
| 53 | } |
| 54 | } catch { |
| 55 | // 忽略非 JSON 消息 |
| 56 | } |
| 57 | }); |
| 58 | }); |
| 59 | |
| 60 | resolve({ |
| 61 | url, |
| 62 | connections, |
| 63 | close: () => |
| 64 | new Promise<void>((res) => { |
| 65 | for (const ws of connections) ws.close(); |
| 66 | wss.close(() => res()); |
| 67 | }), |
| 68 | broadcast: (data: unknown) => { |
| 69 | const payload = JSON.stringify(data); |
| 70 | for (const ws of connections) { |
| 71 | if (ws.readyState === ws.OPEN) { |
| 72 | ws.send(payload); |
| 73 | } |
| 74 | } |
| 75 | }, |
| 76 | waitForAction: (action: string, timeout = 10_000) => |
| 77 | new Promise<unknown>((resolve, reject) => { |
| 78 | const timer = setTimeout(() => { |
| 79 | const idx = messageListeners.indexOf(handler); |
| 80 | if (idx >= 0) messageListeners.splice(idx, 1); |
| 81 | reject(new Error(`Timeout waiting for action: ${action}`)); |
| 82 | }, timeout); |