/** * 原生 WebSocket 客户端测试工具 * * 用于替代 Socket.IO 客户端进行测试 */ import WebSocket from 'ws'; export interface WebSocketTestClient { connect(): Promise; disconnect(): void; send(event: string, data: any): void; on(event: string, callback: (data: any) => void): void; off(event: string, callback?: (data: any) => void): void; waitForEvent(event: string, timeout?: number): Promise; isConnected(): boolean; } export class WebSocketTestClientImpl implements WebSocketTestClient { private ws: WebSocket | null = null; private eventHandlers = new Map void>>(); private connected = false; constructor(private url: string) {} async connect(): Promise { return new Promise((resolve, reject) => { this.ws = new WebSocket(this.url); this.ws.on('open', () => { this.connected = true; resolve(); }); this.ws.on('error', (error) => { reject(error); }); this.ws.on('message', (data) => { try { const message = JSON.parse(data.toString()); const { event, data: eventData } = message; const handlers = this.eventHandlers.get(event); if (handlers) { handlers.forEach(handler => handler(eventData)); } } catch (error) { console.error('Failed to parse WebSocket message:', error); } }); this.ws.on('close', () => { this.connected = false; }); }); } disconnect(): void { if (this.ws) { this.ws.close(); this.ws = null; this.connected = false; } } send(event: string, data: any): void { if (this.ws && this.connected) { const message = JSON.stringify({ event, data }); this.ws.send(message); } else { throw new Error('WebSocket is not connected'); } } on(event: string, callback: (data: any) => void): void { if (!this.eventHandlers.has(event)) { this.eventHandlers.set(event, new Set()); } this.eventHandlers.get(event)!.add(callback); } off(event: string, callback?: (data: any) => void): void { const handlers = this.eventHandlers.get(event); if (handlers) { if (callback) { handlers.delete(callback); } else { handlers.clear(); } } } async waitForEvent(event: string, timeout: number = 5000): Promise { return new Promise((resolve, reject) => { const timer = setTimeout(() => { this.off(event, handler); reject(new Error(`Timeout waiting for event: ${event}`)); }, timeout); const handler = (data: any) => { clearTimeout(timer); this.off(event, handler); resolve(data); }; this.on(event, handler); }); } isConnected(): boolean { return this.connected; } } export function createWebSocketTestClient(url: string): WebSocketTestClient { return new WebSocketTestClientImpl(url); }