// Execute the actual Emscripten library against SocketTask and browser test doubles. const fs = require('fs'); const vm = require('vm'); const assert = require('assert/strict'); const source = fs.readFileSync('AIClient/Assets/Plugins/WebGL/GameWebSocket.jslib', 'utf8'); let checks = 0; function environment(kind) { let sockets = [], memory = {}, next = 1; const make = () => { const s = { callbacks: {}, sent: [], closes: 0, onOpen(fn) { this.callbacks.open = fn; }, onMessage(fn) { this.callbacks.message = fn; }, onError(fn) { this.callbacks.error = fn; }, onClose(fn) { this.callbacks.close = fn; }, close() { this.closes++; }, send(arg) { if (this.throwSend) throw Error('send rejected'); this.sent.push(arg); }, emit(type, data = {}) { (this.callbacks[type] || this['on' + type])(data); } }; sockets.push(s); return s; }; const context = { LibraryManager: { library: {} }, mergeInto: Object.assign, UTF8ToString: x => typeof x === 'string' ? x : memory[x], lengthBytesUTF8: x => Buffer.byteLength(x), _malloc: () => next++, stringToUTF8: (x, p) => memory[p] = x }; const wx = { connectSocket(options) { const s = make(); s.options = options; return s; } }; if (kind === 'wechat') context.wx = wx; else if (kind === 'global') context.GameGlobal = { wx }; else if (kind === 'browser') context.WebSocket = function (url) { const s = make(); s.url = url; return s; }; vm.createContext(context); vm.runInContext(source, context); context.AIGameSockets = context.LibraryManager.library.$AIGameSockets; const api = context.LibraryManager.library; return { api, sockets, context, poll(id) { const p = api.AIGame_WsPoll(id); const text = memory[p]; delete memory[p]; return text; } }; } for (const kind of ['wechat', 'global', 'browser']) { const e = environment(kind), id = e.api.AIGame_WsCreate('wss://game.test/ws'); const s = e.sockets[0]; s.emit('open'); assert.equal(e.poll(id), 'O'); s.emit('message', { data: '中文消息🙂' }); assert.equal(e.poll(id), 'M中文消息🙂'); e.api.AIGame_WsSend(id, 'hello'); assert.equal(kind === 'browser' ? s.sent[0] : s.sent[0].data, 'hello'); s.emit('error', { errMsg: 'offline' }); s.emit('close', { code: 1006 }); assert.ok(e.poll(id).startsWith('E')); assert.equal(e.poll(id), undefined); e.api.AIGame_WsDispose(id); assert.equal(s.closes, 1); s.emit('message', { data: 'stale' }); assert.equal(e.poll(id), undefined); assert.equal(Object.keys(e.context.AIGameSockets.sockets).length, 0); checks++; } { const e = environment('wechat'), id = e.api.AIGame_WsCreate('wss://test'); e.api.AIGame_WsDispose(id); e.sockets[0].emit('open'); assert.equal(e.sockets[0].closes, 2); assert.equal(e.poll(id), undefined); checks++; } for (const mode of ['throw', 'callback']) { const e = environment('wechat'), id = e.api.AIGame_WsCreate('wss://test'), s = e.sockets[0]; s.emit('open'); e.poll(id); if (mode === 'throw') s.throwSend = true; e.api.AIGame_WsSend(id, 'hello'); if (mode === 'callback') s.sent[0].fail({ errMsg: 'send denied' }); assert.ok(e.poll(id).startsWith('E')); e.api.AIGame_WsDispose(id); checks++; } { const e = environment('wechat'), id = e.api.AIGame_WsCreate('wss://test'); e.sockets[0].options.fail({ errMsg: 'domain rejected' }); assert.equal(e.poll(id), 'Edomain rejected'); e.api.AIGame_WsDispose(id); checks++; } { const e = environment('missing'), id = e.api.AIGame_WsCreate('wss://test'); assert.ok(e.poll(id).startsWith('E')); e.api.AIGame_WsDispose(id); checks++; } console.log(`PASS: ${checks} WebSocket bridge scenarios (WeChat, GameGlobal, browser, UTF-8, error, send failure, disposal).`);