@smoregg/sdk 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/controller.cjs +193 -115
- package/dist/cjs/controller.cjs.map +1 -1
- package/dist/cjs/errors.cjs +1 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/events.cjs +19 -2
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/index.cjs +2 -7
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/screen.cjs +185 -130
- package/dist/cjs/screen.cjs.map +1 -1
- package/dist/cjs/shared.cjs +34 -0
- package/dist/cjs/shared.cjs.map +1 -0
- package/dist/cjs/testing.cjs +125 -74
- package/dist/cjs/testing.cjs.map +1 -1
- package/dist/cjs/transport/PostMessageTransport.cjs +12 -0
- package/dist/cjs/transport/PostMessageTransport.cjs.map +1 -1
- package/dist/cjs/transport/protocol.cjs +2 -0
- package/dist/cjs/transport/protocol.cjs.map +1 -1
- package/dist/cjs/types.cjs +16 -0
- package/dist/cjs/types.cjs.map +1 -0
- package/dist/esm/controller.js +195 -117
- package/dist/esm/controller.js.map +1 -1
- package/dist/esm/errors.js +1 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/events.js +18 -3
- package/dist/esm/events.js.map +1 -1
- package/dist/esm/index.js +1 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/screen.js +187 -132
- package/dist/esm/screen.js.map +1 -1
- package/dist/esm/shared.js +30 -0
- package/dist/esm/shared.js.map +1 -0
- package/dist/esm/testing.js +125 -74
- package/dist/esm/testing.js.map +1 -1
- package/dist/esm/transport/PostMessageTransport.js +12 -0
- package/dist/esm/transport/PostMessageTransport.js.map +1 -1
- package/dist/esm/transport/protocol.js +2 -1
- package/dist/esm/transport/protocol.js.map +1 -1
- package/dist/esm/types.js +14 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/types/controller.d.ts +1 -1
- package/dist/types/controller.d.ts.map +1 -1
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/events.d.ts +10 -1
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/screen.d.ts +3 -3
- package/dist/types/screen.d.ts.map +1 -1
- package/dist/types/shared.d.ts +21 -0
- package/dist/types/shared.d.ts.map +1 -0
- package/dist/types/testing.d.ts +63 -4
- package/dist/types/testing.d.ts.map +1 -1
- package/dist/types/transport/PostMessageTransport.d.ts +1 -0
- package/dist/types/transport/PostMessageTransport.d.ts.map +1 -1
- package/dist/types/transport/protocol.d.ts +4 -0
- package/dist/types/transport/protocol.d.ts.map +1 -1
- package/dist/types/types.d.ts +215 -347
- package/dist/types/types.d.ts.map +1 -1
- package/dist/umd/smore-sdk.umd.js +442 -787
- package/dist/umd/smore-sdk.umd.js.map +1 -1
- package/dist/umd/smore-sdk.umd.min.js +1 -1
- package/dist/umd/smore-sdk.umd.min.js.map +1 -1
- package/package.json +7 -1
- package/dist/cjs/config.cjs +0 -13
- package/dist/cjs/config.cjs.map +0 -1
- package/dist/esm/config.js +0 -10
- package/dist/esm/config.js.map +0 -1
- package/dist/types/config.d.ts +0 -35
- package/dist/types/config.d.ts.map +0 -1
package/dist/esm/testing.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { validateEventName } from './events.js';
|
|
2
|
+
import { SmoreSDKError } from './errors.js';
|
|
2
3
|
|
|
3
4
|
function createMockScreen(options = {}) {
|
|
4
5
|
const {
|
|
@@ -8,6 +9,7 @@ function createMockScreen(options = {}) {
|
|
|
8
9
|
} = options;
|
|
9
10
|
let _controllers = [...initialControllers];
|
|
10
11
|
let _isReady = false;
|
|
12
|
+
let _isConnected = false;
|
|
11
13
|
let _isDestroyed = false;
|
|
12
14
|
const listeners = /* @__PURE__ */ new Map();
|
|
13
15
|
const _onAllReadyCallbacks = /* @__PURE__ */ new Set();
|
|
@@ -16,8 +18,8 @@ function createMockScreen(options = {}) {
|
|
|
16
18
|
const _onControllerDisconnectCallbacks = /* @__PURE__ */ new Set();
|
|
17
19
|
const _onControllerReconnectCallbacks = /* @__PURE__ */ new Set();
|
|
18
20
|
const _onCharacterUpdatedCallbacks = /* @__PURE__ */ new Set();
|
|
19
|
-
const _onRateLimitedCallbacks = /* @__PURE__ */ new Set();
|
|
20
21
|
const _onErrorCallbacks = /* @__PURE__ */ new Set();
|
|
22
|
+
const _onConnectionChangeCallbacks = /* @__PURE__ */ new Set();
|
|
21
23
|
let _allReadyFired = false;
|
|
22
24
|
let _readyResolve;
|
|
23
25
|
const _readyPromise = new Promise((resolve) => {
|
|
@@ -39,6 +41,12 @@ function createMockScreen(options = {}) {
|
|
|
39
41
|
get isDestroyed() {
|
|
40
42
|
return _isDestroyed;
|
|
41
43
|
},
|
|
44
|
+
get isConnected() {
|
|
45
|
+
return _isConnected;
|
|
46
|
+
},
|
|
47
|
+
get protocolVersion() {
|
|
48
|
+
return 1;
|
|
49
|
+
},
|
|
42
50
|
get ready() {
|
|
43
51
|
return _readyPromise;
|
|
44
52
|
},
|
|
@@ -82,87 +90,82 @@ function createMockScreen(options = {}) {
|
|
|
82
90
|
_onCharacterUpdatedCallbacks.delete(callback);
|
|
83
91
|
};
|
|
84
92
|
},
|
|
85
|
-
onRateLimited(callback) {
|
|
86
|
-
_onRateLimitedCallbacks.add(callback);
|
|
87
|
-
return () => {
|
|
88
|
-
_onRateLimitedCallbacks.delete(callback);
|
|
89
|
-
};
|
|
90
|
-
},
|
|
91
93
|
onError(callback) {
|
|
92
94
|
_onErrorCallbacks.add(callback);
|
|
93
95
|
return () => {
|
|
94
96
|
_onErrorCallbacks.delete(callback);
|
|
95
97
|
};
|
|
96
98
|
},
|
|
99
|
+
onConnectionChange(callback) {
|
|
100
|
+
_onConnectionChangeCallbacks.add(callback);
|
|
101
|
+
return () => {
|
|
102
|
+
_onConnectionChangeCallbacks.delete(callback);
|
|
103
|
+
};
|
|
104
|
+
},
|
|
97
105
|
// === Communication Methods ===
|
|
98
106
|
broadcast(event, data) {
|
|
99
107
|
if (_isDestroyed) {
|
|
100
|
-
throw new
|
|
108
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call broadcast() after destroy()");
|
|
101
109
|
}
|
|
102
110
|
if (!_isReady) {
|
|
103
|
-
throw new
|
|
104
|
-
}
|
|
105
|
-
validateEventName(event);
|
|
106
|
-
broadcasts.push({ event, data });
|
|
107
|
-
},
|
|
108
|
-
broadcastRaw(event, data) {
|
|
109
|
-
if (_isDestroyed) {
|
|
110
|
-
throw new Error("Cannot broadcast: screen is destroyed");
|
|
111
|
-
}
|
|
112
|
-
if (!_isReady) {
|
|
113
|
-
throw new Error("Cannot broadcast: screen is not ready");
|
|
111
|
+
throw new SmoreSDKError("NOT_READY", "Cannot call broadcast() before screen is ready");
|
|
114
112
|
}
|
|
115
113
|
validateEventName(event);
|
|
116
114
|
broadcasts.push({ event, data });
|
|
117
115
|
},
|
|
118
116
|
sendToController(playerIndex, event, data) {
|
|
119
117
|
if (_isDestroyed) {
|
|
120
|
-
throw new
|
|
118
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call sendToController() after destroy()");
|
|
121
119
|
}
|
|
122
120
|
if (!_isReady) {
|
|
123
|
-
throw new
|
|
121
|
+
throw new SmoreSDKError("NOT_READY", "Cannot call sendToController() before screen is ready");
|
|
124
122
|
}
|
|
125
123
|
validateEventName(event);
|
|
126
124
|
if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
|
|
127
|
-
throw new
|
|
128
|
-
}
|
|
129
|
-
sends.push({ playerIndex, event, data });
|
|
130
|
-
},
|
|
131
|
-
sendToControllerRaw(playerIndex, event, data) {
|
|
132
|
-
if (_isDestroyed) {
|
|
133
|
-
throw new Error("Cannot send: screen is destroyed");
|
|
134
|
-
}
|
|
135
|
-
if (!_isReady) {
|
|
136
|
-
throw new Error("Cannot send: screen is not ready");
|
|
137
|
-
}
|
|
138
|
-
validateEventName(event);
|
|
139
|
-
if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
|
|
140
|
-
throw new Error(`Invalid player index: ${playerIndex}`);
|
|
125
|
+
throw new SmoreSDKError("INVALID_PLAYER", `No controller found with player index ${playerIndex}`);
|
|
141
126
|
}
|
|
142
127
|
sends.push({ playerIndex, event, data });
|
|
143
128
|
},
|
|
144
129
|
// === Game Lifecycle ===
|
|
145
130
|
gameOver(results) {
|
|
146
131
|
if (_isDestroyed) {
|
|
147
|
-
throw new
|
|
132
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call gameOver() after destroy()");
|
|
148
133
|
}
|
|
149
134
|
if (!_isReady) {
|
|
150
|
-
throw new
|
|
135
|
+
throw new SmoreSDKError("NOT_READY", "Cannot call gameOver() before screen is ready");
|
|
151
136
|
}
|
|
152
137
|
broadcasts.push({ event: "smore:game-over", data: { results } });
|
|
153
138
|
},
|
|
154
139
|
signalReady() {
|
|
155
140
|
if (_isDestroyed) {
|
|
156
|
-
throw new
|
|
141
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call signalReady() after destroy()");
|
|
157
142
|
}
|
|
158
143
|
if (!_isReady) {
|
|
159
|
-
throw new
|
|
144
|
+
throw new SmoreSDKError("NOT_READY", "Cannot call signalReady() before screen is ready");
|
|
160
145
|
}
|
|
161
146
|
},
|
|
162
147
|
// === Event Subscription ===
|
|
163
148
|
on(event, handler) {
|
|
164
|
-
validateEventName(event);
|
|
165
149
|
const eventStr = event;
|
|
150
|
+
if (eventStr.startsWith("$")) {
|
|
151
|
+
switch (eventStr) {
|
|
152
|
+
case "$controller-join":
|
|
153
|
+
return screen.onControllerJoin(handler);
|
|
154
|
+
case "$controller-leave":
|
|
155
|
+
return screen.onControllerLeave(handler);
|
|
156
|
+
case "$controller-disconnect":
|
|
157
|
+
return screen.onControllerDisconnect(handler);
|
|
158
|
+
case "$controller-reconnect":
|
|
159
|
+
return screen.onControllerReconnect(handler);
|
|
160
|
+
case "$character-updated":
|
|
161
|
+
return screen.onCharacterUpdated(handler);
|
|
162
|
+
case "$all-ready":
|
|
163
|
+
return screen.onAllReady(handler);
|
|
164
|
+
case "$error":
|
|
165
|
+
return screen.onError(handler);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
validateEventName(event);
|
|
166
169
|
if (!listeners.has(eventStr)) {
|
|
167
170
|
listeners.set(eventStr, /* @__PURE__ */ new Set());
|
|
168
171
|
}
|
|
@@ -188,6 +191,13 @@ function createMockScreen(options = {}) {
|
|
|
188
191
|
listeners.get(eventStr)?.delete(handler);
|
|
189
192
|
}
|
|
190
193
|
},
|
|
194
|
+
removeAllListeners(event) {
|
|
195
|
+
if (event) {
|
|
196
|
+
listeners.delete(event);
|
|
197
|
+
} else {
|
|
198
|
+
listeners.clear();
|
|
199
|
+
}
|
|
200
|
+
},
|
|
191
201
|
// === Utilities ===
|
|
192
202
|
getController(playerIndex) {
|
|
193
203
|
return _controllers.find((c) => c.playerIndex === playerIndex);
|
|
@@ -195,9 +205,6 @@ function createMockScreen(options = {}) {
|
|
|
195
205
|
getControllerCount() {
|
|
196
206
|
return _controllers.filter((c) => c.connected).length;
|
|
197
207
|
},
|
|
198
|
-
hasAnyConnectedControllers() {
|
|
199
|
-
return _controllers.some((c) => c.connected);
|
|
200
|
-
},
|
|
201
208
|
// === Cleanup ===
|
|
202
209
|
/**
|
|
203
210
|
* Note: destroy() clears recorded broadcast/event arrays. Call getBroadcasts() before destroy() if assertions are needed.
|
|
@@ -233,7 +240,7 @@ function createMockScreen(options = {}) {
|
|
|
233
240
|
simulateControllerDisconnect(playerIndex) {
|
|
234
241
|
const controller = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
235
242
|
if (!controller) {
|
|
236
|
-
throw new
|
|
243
|
+
throw new SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
237
244
|
}
|
|
238
245
|
_controllers = _controllers.map(
|
|
239
246
|
(c) => c.playerIndex === playerIndex ? { ...c, connected: false } : c
|
|
@@ -246,7 +253,7 @@ function createMockScreen(options = {}) {
|
|
|
246
253
|
simulateControllerReconnect(playerIndex) {
|
|
247
254
|
const controller = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
248
255
|
if (!controller) {
|
|
249
|
-
throw new
|
|
256
|
+
throw new SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
250
257
|
}
|
|
251
258
|
const reconnectedController = { ...controller, connected: true };
|
|
252
259
|
_controllers = _controllers.map(
|
|
@@ -257,16 +264,13 @@ function createMockScreen(options = {}) {
|
|
|
257
264
|
simulateCharacterUpdate(playerIndex, appearance) {
|
|
258
265
|
const controller = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
259
266
|
if (!controller) {
|
|
260
|
-
throw new
|
|
267
|
+
throw new SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
261
268
|
}
|
|
262
269
|
_controllers = _controllers.map(
|
|
263
270
|
(c) => c.playerIndex === playerIndex ? { ...c, appearance } : c
|
|
264
271
|
);
|
|
265
272
|
_onCharacterUpdatedCallbacks.forEach((cb) => cb(playerIndex, appearance));
|
|
266
273
|
},
|
|
267
|
-
simulateRateLimited(event) {
|
|
268
|
-
_onRateLimitedCallbacks.forEach((cb) => cb(event));
|
|
269
|
-
},
|
|
270
274
|
simulateAllReady() {
|
|
271
275
|
_allReadyFired = true;
|
|
272
276
|
_onAllReadyCallbacks.forEach((cb) => cb());
|
|
@@ -274,6 +278,10 @@ function createMockScreen(options = {}) {
|
|
|
274
278
|
simulateError(error) {
|
|
275
279
|
_onErrorCallbacks.forEach((cb) => cb(error));
|
|
276
280
|
},
|
|
281
|
+
simulateConnectionChange(connected) {
|
|
282
|
+
_isConnected = connected;
|
|
283
|
+
_onConnectionChangeCallbacks.forEach((cb) => cb(connected));
|
|
284
|
+
},
|
|
277
285
|
getBroadcasts() {
|
|
278
286
|
return [...broadcasts];
|
|
279
287
|
},
|
|
@@ -286,6 +294,7 @@ function createMockScreen(options = {}) {
|
|
|
286
294
|
},
|
|
287
295
|
triggerReady() {
|
|
288
296
|
_isReady = true;
|
|
297
|
+
_isConnected = true;
|
|
289
298
|
_readyResolve();
|
|
290
299
|
}
|
|
291
300
|
};
|
|
@@ -297,10 +306,11 @@ function createMockScreen(options = {}) {
|
|
|
297
306
|
function createMockController(options = {}) {
|
|
298
307
|
const {
|
|
299
308
|
roomCode = "TEST",
|
|
300
|
-
|
|
309
|
+
myPlayerIndex = 0,
|
|
301
310
|
autoReady = true
|
|
302
311
|
} = options;
|
|
303
312
|
let _isReady = false;
|
|
313
|
+
let _isConnected = false;
|
|
304
314
|
let _isDestroyed = false;
|
|
305
315
|
let _controllers = options.controllers ?? [];
|
|
306
316
|
const listeners = /* @__PURE__ */ new Map();
|
|
@@ -310,8 +320,9 @@ function createMockController(options = {}) {
|
|
|
310
320
|
const _onControllerDisconnectCallbacks = /* @__PURE__ */ new Set();
|
|
311
321
|
const _onControllerReconnectCallbacks = /* @__PURE__ */ new Set();
|
|
312
322
|
const _onCharacterUpdatedCallbacks = /* @__PURE__ */ new Set();
|
|
313
|
-
const _onRateLimitedCallbacks = /* @__PURE__ */ new Set();
|
|
314
323
|
const _onErrorCallbacks = /* @__PURE__ */ new Set();
|
|
324
|
+
const _onGameOverCallbacks = /* @__PURE__ */ new Set();
|
|
325
|
+
const _onConnectionChangeCallbacks = /* @__PURE__ */ new Set();
|
|
315
326
|
let _allReadyFired = false;
|
|
316
327
|
let _readyResolve;
|
|
317
328
|
const _readyPromise = new Promise((resolve) => {
|
|
@@ -320,8 +331,8 @@ function createMockController(options = {}) {
|
|
|
320
331
|
const sentEvents = [];
|
|
321
332
|
const controller = {
|
|
322
333
|
// === Properties ===
|
|
323
|
-
get
|
|
324
|
-
return
|
|
334
|
+
get myPlayerIndex() {
|
|
335
|
+
return myPlayerIndex;
|
|
325
336
|
},
|
|
326
337
|
get roomCode() {
|
|
327
338
|
return roomCode;
|
|
@@ -332,6 +343,12 @@ function createMockController(options = {}) {
|
|
|
332
343
|
get isDestroyed() {
|
|
333
344
|
return _isDestroyed;
|
|
334
345
|
},
|
|
346
|
+
get isConnected() {
|
|
347
|
+
return _isConnected;
|
|
348
|
+
},
|
|
349
|
+
get protocolVersion() {
|
|
350
|
+
return 1;
|
|
351
|
+
},
|
|
335
352
|
get controllers() {
|
|
336
353
|
return [..._controllers];
|
|
337
354
|
},
|
|
@@ -378,48 +395,70 @@ function createMockController(options = {}) {
|
|
|
378
395
|
_onCharacterUpdatedCallbacks.delete(callback);
|
|
379
396
|
};
|
|
380
397
|
},
|
|
381
|
-
onRateLimited(callback) {
|
|
382
|
-
_onRateLimitedCallbacks.add(callback);
|
|
383
|
-
return () => {
|
|
384
|
-
_onRateLimitedCallbacks.delete(callback);
|
|
385
|
-
};
|
|
386
|
-
},
|
|
387
398
|
onError(callback) {
|
|
388
399
|
_onErrorCallbacks.add(callback);
|
|
389
400
|
return () => {
|
|
390
401
|
_onErrorCallbacks.delete(callback);
|
|
391
402
|
};
|
|
392
403
|
},
|
|
404
|
+
onConnectionChange(callback) {
|
|
405
|
+
_onConnectionChangeCallbacks.add(callback);
|
|
406
|
+
return () => {
|
|
407
|
+
_onConnectionChangeCallbacks.delete(callback);
|
|
408
|
+
};
|
|
409
|
+
},
|
|
410
|
+
onGameOver(callback) {
|
|
411
|
+
_onGameOverCallbacks.add(callback);
|
|
412
|
+
return () => {
|
|
413
|
+
_onGameOverCallbacks.delete(callback);
|
|
414
|
+
};
|
|
415
|
+
},
|
|
393
416
|
getControllerCount() {
|
|
394
417
|
return _controllers.filter((c) => c.connected).length;
|
|
395
418
|
},
|
|
419
|
+
getController(playerIndex) {
|
|
420
|
+
return _controllers.find((c) => c.playerIndex === playerIndex);
|
|
421
|
+
},
|
|
396
422
|
// === Communication Methods ===
|
|
397
423
|
send(event, data) {
|
|
398
424
|
if (_isDestroyed) {
|
|
399
|
-
throw new
|
|
400
|
-
}
|
|
401
|
-
validateEventName(event);
|
|
402
|
-
sentEvents.push({ event, data });
|
|
403
|
-
},
|
|
404
|
-
sendRaw(event, data) {
|
|
405
|
-
if (_isDestroyed) {
|
|
406
|
-
throw new Error("Cannot send: controller is destroyed");
|
|
425
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call send() after destroy()");
|
|
407
426
|
}
|
|
408
427
|
validateEventName(event);
|
|
409
428
|
sentEvents.push({ event, data });
|
|
410
429
|
},
|
|
411
430
|
signalReady() {
|
|
412
431
|
if (_isDestroyed) {
|
|
413
|
-
throw new
|
|
432
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call signalReady() after destroy()");
|
|
414
433
|
}
|
|
415
434
|
if (!_isReady) {
|
|
416
|
-
throw new
|
|
435
|
+
throw new SmoreSDKError("NOT_READY", "Cannot call signalReady() before controller is ready");
|
|
417
436
|
}
|
|
418
437
|
},
|
|
419
438
|
// === Event Subscription ===
|
|
420
439
|
on(event, handler) {
|
|
421
|
-
validateEventName(event);
|
|
422
440
|
const eventStr = event;
|
|
441
|
+
if (eventStr.startsWith("$")) {
|
|
442
|
+
switch (eventStr) {
|
|
443
|
+
case "$controller-join":
|
|
444
|
+
return controller.onControllerJoin(handler);
|
|
445
|
+
case "$controller-leave":
|
|
446
|
+
return controller.onControllerLeave(handler);
|
|
447
|
+
case "$controller-disconnect":
|
|
448
|
+
return controller.onControllerDisconnect(handler);
|
|
449
|
+
case "$controller-reconnect":
|
|
450
|
+
return controller.onControllerReconnect(handler);
|
|
451
|
+
case "$character-updated":
|
|
452
|
+
return controller.onCharacterUpdated(handler);
|
|
453
|
+
case "$all-ready":
|
|
454
|
+
return controller.onAllReady(handler);
|
|
455
|
+
case "$error":
|
|
456
|
+
return controller.onError(handler);
|
|
457
|
+
case "$game-over":
|
|
458
|
+
return controller.onGameOver(handler);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
validateEventName(event);
|
|
423
462
|
if (!listeners.has(eventStr)) {
|
|
424
463
|
listeners.set(eventStr, /* @__PURE__ */ new Set());
|
|
425
464
|
}
|
|
@@ -445,6 +484,13 @@ function createMockController(options = {}) {
|
|
|
445
484
|
listeners.get(eventStr)?.delete(handler);
|
|
446
485
|
}
|
|
447
486
|
},
|
|
487
|
+
removeAllListeners(event) {
|
|
488
|
+
if (event) {
|
|
489
|
+
listeners.delete(event);
|
|
490
|
+
} else {
|
|
491
|
+
listeners.clear();
|
|
492
|
+
}
|
|
493
|
+
},
|
|
448
494
|
// === Cleanup ===
|
|
449
495
|
destroy() {
|
|
450
496
|
_isDestroyed = true;
|
|
@@ -470,6 +516,7 @@ function createMockController(options = {}) {
|
|
|
470
516
|
},
|
|
471
517
|
triggerReady() {
|
|
472
518
|
_isReady = true;
|
|
519
|
+
_isConnected = true;
|
|
473
520
|
_readyResolve();
|
|
474
521
|
},
|
|
475
522
|
/**
|
|
@@ -510,22 +557,26 @@ function createMockController(options = {}) {
|
|
|
510
557
|
simulateCharacterUpdate(playerIndex, appearance) {
|
|
511
558
|
const ctrl = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
512
559
|
if (!ctrl) {
|
|
513
|
-
throw new
|
|
560
|
+
throw new SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
514
561
|
}
|
|
515
562
|
_controllers = _controllers.map(
|
|
516
563
|
(c) => c.playerIndex === playerIndex ? { ...c, appearance } : c
|
|
517
564
|
);
|
|
518
565
|
_onCharacterUpdatedCallbacks.forEach((cb) => cb(playerIndex, appearance));
|
|
519
566
|
},
|
|
520
|
-
simulateRateLimited(event) {
|
|
521
|
-
_onRateLimitedCallbacks.forEach((cb) => cb(event));
|
|
522
|
-
},
|
|
523
567
|
simulateAllReady() {
|
|
524
568
|
_allReadyFired = true;
|
|
525
569
|
_onAllReadyCallbacks.forEach((cb) => cb());
|
|
526
570
|
},
|
|
527
571
|
simulateError(error) {
|
|
528
572
|
_onErrorCallbacks.forEach((cb) => cb(error));
|
|
573
|
+
},
|
|
574
|
+
simulateGameOver(results) {
|
|
575
|
+
_onGameOverCallbacks.forEach((cb) => cb(results));
|
|
576
|
+
},
|
|
577
|
+
simulateConnectionChange(connected) {
|
|
578
|
+
_isConnected = connected;
|
|
579
|
+
_onConnectionChangeCallbacks.forEach((cb) => cb(connected));
|
|
529
580
|
}
|
|
530
581
|
};
|
|
531
582
|
if (autoReady) {
|
package/dist/esm/testing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sources":["../../src/testing.ts"],"sourcesContent":["/**\n * @smoregg/sdk - Testing Utilities\n *\n * Mock implementations of Screen and Controller for unit testing.\n * All methods work synchronously for predictable test execution.\n *\n * Mocks use the same event emitter pattern as the real implementations:\n * lifecycle callbacks are registered via methods (onAllReady, onControllerJoin, etc.)\n * on the returned instance rather than via config options.\n *\n * @packageDocumentation\n */\n\nimport type {\n EventMap,\n EventNames,\n EventData,\n CharacterAppearance,\n ControllerInfo,\n PlayerIndex,\n GameResults,\n ScreenEventHandler,\n ControllerEventHandler,\n MockScreen,\n MockController,\n MockOptions,\n SmoreError,\n} from './types';\nimport { validateEventName } from './events';\n\n// =============================================================================\n// MOCK SCREEN IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedBroadcast {\n event: string;\n data: unknown;\n}\n\ninterface RecordedSend {\n playerIndex: PlayerIndex;\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Screen for testing game logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * Uses the same event emitter pattern as real Screen: register lifecycle\n * callbacks via methods on the returned instance.\n *\n * @example\n * ```ts\n * const screen = createMockScreen<MyEvents>({\n * controllers: [\n * { playerIndex: 0, nickname: 'Player 1', connected: true },\n * { playerIndex: 1, nickname: 'Player 2', connected: true },\n * ],\n * });\n *\n * screen.on('tap', (pi, data) => handleTap(pi, data));\n * screen.onAllReady(() => startGame());\n *\n * // Simulate player input\n * screen.simulateEvent(0, 'tap', { x: 100, y: 200 });\n *\n * // Check what was broadcast\n * expect(screen.getBroadcasts()).toContainEqual({\n * event: 'score-update',\n * data: { scores: { 0: 10 } },\n * });\n * ```\n */\nexport function createMockScreen<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockScreen<TEvents> {\n const {\n roomCode = 'TEST',\n controllers: initialControllers = [],\n autoReady = true,\n } = options;\n\n // Internal state\n let _controllers: ControllerInfo[] = [...initialControllers];\n let _isReady = false;\n let _isDestroyed = false;\n\n // Event listeners\n const listeners = new Map<string, Set<ScreenEventHandler>>();\n\n // Lifecycle callback sets\n const _onAllReadyCallbacks = new Set<() => void>();\n const _onControllerJoinCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onControllerLeaveCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerDisconnectCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerReconnectCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onCharacterUpdatedCallbacks = new Set<(index: PlayerIndex, appearance: CharacterAppearance | null) => void>();\n const _onRateLimitedCallbacks = new Set<(event: string) => void>();\n const _onErrorCallbacks = new Set<(error: SmoreError) => void>();\n\n // Whether all-ready has fired\n let _allReadyFired = false;\n\n // Ready promise\n let _readyResolve: () => void;\n const _readyPromise = new Promise<void>((resolve) => {\n _readyResolve = resolve;\n });\n\n // Recorded events for testing\n const broadcasts: RecordedBroadcast[] = [];\n const sends: RecordedSend[] = [];\n\n // Screen implementation\n const screen: MockScreen<TEvents> = {\n // === Properties ===\n get controllers() {\n return [..._controllers];\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n get ready() {\n return _readyPromise;\n },\n\n // === Lifecycle Methods ===\n onAllReady(callback: () => void): () => void {\n if (_allReadyFired) {\n callback();\n }\n _onAllReadyCallbacks.add(callback);\n return () => { _onAllReadyCallbacks.delete(callback); };\n },\n\n onControllerJoin(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerJoinCallbacks.add(callback);\n return () => { _onControllerJoinCallbacks.delete(callback); };\n },\n\n onControllerLeave(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerLeaveCallbacks.add(callback);\n return () => { _onControllerLeaveCallbacks.delete(callback); };\n },\n\n onControllerDisconnect(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerDisconnectCallbacks.add(callback);\n return () => { _onControllerDisconnectCallbacks.delete(callback); };\n },\n\n onControllerReconnect(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerReconnectCallbacks.add(callback);\n return () => { _onControllerReconnectCallbacks.delete(callback); };\n },\n\n onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void {\n _onCharacterUpdatedCallbacks.add(callback);\n return () => { _onCharacterUpdatedCallbacks.delete(callback); };\n },\n\n onRateLimited(callback: (event: string) => void): () => void {\n _onRateLimitedCallbacks.add(callback);\n return () => { _onRateLimitedCallbacks.delete(callback); };\n },\n\n onError(callback: (error: SmoreError) => void): () => void {\n _onErrorCallbacks.add(callback);\n return () => { _onErrorCallbacks.delete(callback); };\n },\n\n // === Communication Methods ===\n broadcast<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot broadcast: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot broadcast: screen is not ready');\n }\n validateEventName(event as string);\n broadcasts.push({ event: event as string, data });\n },\n\n broadcastRaw(event: string, data?: unknown): void {\n if (_isDestroyed) {\n throw new Error('Cannot broadcast: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot broadcast: screen is not ready');\n }\n validateEventName(event);\n broadcasts.push({ event, data });\n },\n\n sendToController<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot send: screen is not ready');\n }\n validateEventName(event as string);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new Error(`Invalid player index: ${playerIndex}`);\n }\n sends.push({ playerIndex, event: event as string, data });\n },\n\n sendToControllerRaw(\n playerIndex: PlayerIndex,\n event: string,\n data?: unknown,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot send: screen is not ready');\n }\n validateEventName(event);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new Error(`Invalid player index: ${playerIndex}`);\n }\n sends.push({ playerIndex, event, data });\n },\n\n // === Game Lifecycle ===\n gameOver(results?: GameResults): void {\n if (_isDestroyed) {\n throw new Error('Cannot call gameOver: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call gameOver: screen is not ready');\n }\n broadcasts.push({ event: 'smore:game-over', data: { results } });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new Error('Cannot call signalReady: screen is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call signalReady: screen is not ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ScreenEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ScreenEventHandler<EventData<TEvents, K>> = (playerIndex, data) => {\n handler(playerIndex, data);\n screen.off(event, wrapper);\n };\n return screen.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ScreenEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n }\n },\n\n // === Utilities ===\n getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return _controllers.find((c) => c.playerIndex === playerIndex);\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n hasAnyConnectedControllers(): boolean {\n return _controllers.some(c => c.connected);\n },\n\n // === Cleanup ===\n /**\n * Note: destroy() clears recorded broadcast/event arrays. Call getBroadcasts() before destroy() if assertions are needed.\n */\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(playerIndex, data);\n });\n }\n },\n\n simulateControllerJoin(info: ControllerInfo): void {\n _controllers.push(info);\n _onControllerJoinCallbacks.forEach(cb => cb(info.playerIndex, info));\n },\n\n simulateControllerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);\n _onControllerLeaveCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a controller network disconnect (player still in room but unreachable).\n */\n simulateControllerDisconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n // Mark as disconnected (need to create new object since ControllerInfo is readonly)\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex\n ? { ...c, connected: false }\n : c\n );\n _onControllerDisconnectCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a controller network reconnect after disconnect.\n */\n simulateControllerReconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n // Mark as connected (need to create new object since ControllerInfo is readonly)\n const reconnectedController = { ...controller, connected: true };\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? reconnectedController : c\n );\n _onControllerReconnectCallbacks.forEach(cb => cb(playerIndex, reconnectedController));\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n _onCharacterUpdatedCallbacks.forEach(cb => cb(playerIndex, appearance));\n },\n\n simulateRateLimited(event: string): void {\n _onRateLimitedCallbacks.forEach(cb => cb(event));\n },\n\n simulateAllReady(): void {\n _allReadyFired = true;\n _onAllReadyCallbacks.forEach(cb => cb());\n },\n\n simulateError(error: any): void {\n _onErrorCallbacks.forEach(cb => cb(error));\n },\n\n getBroadcasts(): Array<{ event: string; data: unknown }> {\n return [...broadcasts];\n },\n\n getSentToController(\n playerIndex: PlayerIndex,\n ): Array<{ event: string; data: unknown }> {\n return sends\n .filter((s) => s.playerIndex === playerIndex)\n .map((s) => ({ event: s.event, data: s.data }));\n },\n\n clearRecordedEvents(): void {\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n _readyResolve();\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => screen.triggerReady(), 0);\n }\n\n return screen;\n}\n\n// =============================================================================\n// MOCK CONTROLLER IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedEvent {\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Controller for testing player input logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * Uses the same event emitter pattern as real Controller: register lifecycle\n * callbacks via methods on the returned instance.\n *\n * @example\n * ```ts\n * const controller = createMockController<MyEvents>({\n * myIndex: 0,\n * });\n *\n * controller.on('your-turn', (data) => handleTurn(data));\n * controller.onAllReady(() => console.log('Ready!'));\n *\n * // Simulate receiving from screen\n * controller.simulateEvent('your-turn', { timeLimit: 30 });\n *\n * // Check what was sent\n * expect(controller.getSentEvents()).toContainEqual({\n * event: 'answer',\n * data: { choice: 2 },\n * });\n * ```\n */\nexport function createMockController<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockController<TEvents> {\n const {\n roomCode = 'TEST',\n myIndex = 0,\n autoReady = true,\n } = options;\n\n // Internal state -- uses a full ControllerInfo[] array (matching MockScreen pattern)\n // to preserve nickname/appearance data from simulatePlayerJoin()\n let _isReady = false;\n let _isDestroyed = false;\n let _controllers: ControllerInfo[] = options.controllers ?? [];\n\n // Event listeners\n const listeners = new Map<string, Set<ControllerEventHandler>>();\n\n // Lifecycle callback sets\n const _onAllReadyCallbacks = new Set<() => void>();\n const _onControllerJoinCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onControllerLeaveCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerDisconnectCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerReconnectCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onCharacterUpdatedCallbacks = new Set<(index: PlayerIndex, appearance: CharacterAppearance | null) => void>();\n const _onRateLimitedCallbacks = new Set<(event: string) => void>();\n const _onErrorCallbacks = new Set<(error: SmoreError) => void>();\n\n // Whether all-ready has fired\n let _allReadyFired = false;\n\n // Ready promise\n let _readyResolve: () => void;\n const _readyPromise = new Promise<void>((resolve) => {\n _readyResolve = resolve;\n });\n\n // Recorded events for testing\n const sentEvents: RecordedEvent[] = [];\n\n // Controller implementation\n const controller: MockController<TEvents> = {\n // === Properties ===\n get myIndex() {\n return myIndex;\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n\n get controllers(): readonly ControllerInfo[] {\n return [..._controllers];\n },\n\n get ready() {\n return _readyPromise;\n },\n\n // === Lifecycle Methods ===\n onAllReady(callback: () => void): () => void {\n if (_allReadyFired) {\n callback();\n }\n _onAllReadyCallbacks.add(callback);\n return () => { _onAllReadyCallbacks.delete(callback); };\n },\n\n onControllerJoin(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerJoinCallbacks.add(callback);\n return () => { _onControllerJoinCallbacks.delete(callback); };\n },\n\n onControllerLeave(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerLeaveCallbacks.add(callback);\n return () => { _onControllerLeaveCallbacks.delete(callback); };\n },\n\n onControllerDisconnect(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerDisconnectCallbacks.add(callback);\n return () => { _onControllerDisconnectCallbacks.delete(callback); };\n },\n\n onControllerReconnect(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerReconnectCallbacks.add(callback);\n return () => { _onControllerReconnectCallbacks.delete(callback); };\n },\n\n onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void {\n _onCharacterUpdatedCallbacks.add(callback);\n return () => { _onCharacterUpdatedCallbacks.delete(callback); };\n },\n\n onRateLimited(callback: (event: string) => void): () => void {\n _onRateLimitedCallbacks.add(callback);\n return () => { _onRateLimitedCallbacks.delete(callback); };\n },\n\n onError(callback: (error: SmoreError) => void): () => void {\n _onErrorCallbacks.add(callback);\n return () => { _onErrorCallbacks.delete(callback); };\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n // === Communication Methods ===\n send<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: controller is destroyed');\n }\n validateEventName(event as string);\n sentEvents.push({ event: event as string, data });\n },\n\n sendRaw(event: string, data?: unknown): void {\n if (_isDestroyed) {\n throw new Error('Cannot send: controller is destroyed');\n }\n validateEventName(event);\n sentEvents.push({ event, data });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new Error('Cannot call signalReady: controller is destroyed');\n }\n if (!_isReady) {\n throw new Error('Cannot call signalReady: controller is not ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ControllerEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ControllerEventHandler<EventData<TEvents, K>> = (data) => {\n handler(data);\n controller.off(event, wrapper);\n };\n return controller.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ControllerEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n }\n },\n\n // === Cleanup ===\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n sentEvents.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(data);\n });\n }\n },\n\n getSentEvents(): Array<{ event: string; data: unknown }> {\n return [...sentEvents];\n },\n\n clearRecordedEvents(): void {\n sentEvents.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n _readyResolve();\n },\n\n /**\n * Simulate a new player joining the room.\n * Stores full ControllerInfo (nickname, appearance, etc.) for later retrieval.\n */\n simulatePlayerJoin(playerIndex: PlayerIndex, info: ControllerInfo): void {\n if (!_controllers.some(c => c.playerIndex === playerIndex)) {\n _controllers = [..._controllers, { ...info, connected: info.connected ?? true }];\n }\n _onControllerJoinCallbacks.forEach(cb => cb(playerIndex, info));\n },\n\n /**\n * Simulate a player leaving the room (fully removed).\n */\n simulatePlayerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter(c => c.playerIndex !== playerIndex);\n _onControllerLeaveCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a player network disconnect (player still in room but unreachable).\n */\n simulatePlayerDisconnect(playerIndex: PlayerIndex): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...c, connected: false } : c\n );\n _onControllerDisconnectCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a player network reconnect after disconnect.\n */\n simulatePlayerReconnect(playerIndex: PlayerIndex, info: ControllerInfo): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...info, connected: true } : c\n );\n _onControllerReconnectCallbacks.forEach(cb => cb(playerIndex, info));\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const ctrl = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!ctrl) {\n throw new Error(`Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n _onCharacterUpdatedCallbacks.forEach(cb => cb(playerIndex, appearance));\n },\n\n simulateRateLimited(event: string): void {\n _onRateLimitedCallbacks.forEach(cb => cb(event));\n },\n\n simulateAllReady(): void {\n _allReadyFired = true;\n _onAllReadyCallbacks.forEach(cb => cb());\n },\n\n simulateError(error: any): void {\n _onErrorCallbacks.forEach(cb => cb(error));\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => controller.triggerReady(), 0);\n }\n\n return controller;\n}\n\n// =============================================================================\n// EXPORTS\n// =============================================================================\n\nexport type { MockScreen, MockController, MockOptions };\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"testing.js","sources":["../../src/testing.ts"],"sourcesContent":["/**\n * @smoregg/sdk - Testing Utilities\n *\n * Mock implementations of Screen and Controller for unit testing.\n * All methods work synchronously for predictable test execution.\n *\n * Mocks use the same event emitter pattern as the real implementations:\n * lifecycle callbacks are registered via methods (onAllReady, onControllerJoin, etc.)\n * on the returned instance rather than via config options.\n *\n * @packageDocumentation\n */\n\nimport type {\n EventMap,\n EventNames,\n EventData,\n CharacterAppearance,\n ControllerInfo,\n PlayerIndex,\n GameResults,\n ScreenEventHandler,\n ControllerEventHandler,\n SmoreError,\n Screen,\n Controller,\n RoomCode,\n} from './types';\nimport { validateEventName } from './events';\nimport { SmoreSDKError } from './errors';\n\n// =============================================================================\n// MOCK TYPES (internal — not part of public API)\n// =============================================================================\n\n/**\n * Options for creating mock instances.\n */\nexport interface MockOptions {\n /** Initial room code */\n roomCode?: RoomCode;\n /** Initial controllers for Screen mock */\n controllers?: ControllerInfo[];\n /** My index for Controller mock */\n myPlayerIndex?: PlayerIndex;\n /** Auto-trigger ready state */\n autoReady?: boolean;\n}\n\n/**\n * Mock Screen for testing.\n * Extends Screen with additional test utilities.\n */\nexport interface MockScreen<TEvents extends EventMap = EventMap>\n extends Screen<TEvents> {\n simulateEvent<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void;\n simulateControllerJoin(info: ControllerInfo): void;\n simulateControllerLeave(playerIndex: PlayerIndex): void;\n simulateControllerDisconnect(playerIndex: PlayerIndex): void;\n simulateControllerReconnect(playerIndex: PlayerIndex): void;\n getBroadcasts(): Array<{ event: string; data: unknown }>;\n getSentToController(playerIndex: PlayerIndex): Array<{ event: string; data: unknown }>;\n clearRecordedEvents(): void;\n triggerReady(): void;\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void;\n simulateAllReady(): void;\n simulateError(error: any): void;\n simulateConnectionChange(connected: boolean): void;\n}\n\n/**\n * Mock Controller for testing.\n * Extends Controller with additional test utilities.\n */\nexport interface MockController<TEvents extends EventMap = EventMap>\n extends Controller<TEvents> {\n simulateEvent<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void;\n getSentEvents(): Array<{ event: string; data: unknown }>;\n clearRecordedEvents(): void;\n triggerReady(): void;\n simulatePlayerJoin(playerIndex: PlayerIndex, info: ControllerInfo): void;\n simulatePlayerLeave(playerIndex: PlayerIndex): void;\n simulatePlayerDisconnect(playerIndex: PlayerIndex): void;\n simulatePlayerReconnect(playerIndex: PlayerIndex, info: ControllerInfo): void;\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void;\n simulateAllReady(): void;\n simulateError(error: any): void;\n simulateGameOver(results?: GameResults): void;\n simulateConnectionChange(connected: boolean): void;\n}\n\n// =============================================================================\n// MOCK SCREEN IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedBroadcast {\n event: string;\n data: unknown;\n}\n\ninterface RecordedSend {\n playerIndex: PlayerIndex;\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Screen for testing game logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * Uses the same event emitter pattern as real Screen: register lifecycle\n * callbacks via methods on the returned instance.\n *\n * @example\n * ```ts\n * const screen = createMockScreen<MyEvents>({\n * controllers: [\n * { playerIndex: 0, nickname: 'Player 1', connected: true },\n * { playerIndex: 1, nickname: 'Player 2', connected: true },\n * ],\n * });\n *\n * screen.on('tap', (playerIndex, data) => handleTap(playerIndex, data));\n * screen.onAllReady(() => startGame());\n *\n * // Simulate player input\n * screen.simulateEvent(0, 'tap', { x: 100, y: 200 });\n *\n * // Check what was broadcast\n * expect(screen.getBroadcasts()).toContainEqual({\n * event: 'score-update',\n * data: { scores: { 0: 10 } },\n * });\n * ```\n */\nexport function createMockScreen<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockScreen<TEvents> {\n const {\n roomCode = 'TEST',\n controllers: initialControllers = [],\n autoReady = true,\n } = options;\n\n // Internal state\n let _controllers: ControllerInfo[] = [...initialControllers];\n let _isReady = false;\n let _isConnected = false;\n let _isDestroyed = false;\n\n // Event listeners\n const listeners = new Map<string, Set<ScreenEventHandler>>();\n\n // Lifecycle callback sets\n const _onAllReadyCallbacks = new Set<() => void>();\n const _onControllerJoinCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onControllerLeaveCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerDisconnectCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerReconnectCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onCharacterUpdatedCallbacks = new Set<(index: PlayerIndex, appearance: CharacterAppearance | null) => void>();\n const _onErrorCallbacks = new Set<(error: SmoreError) => void>();\n const _onConnectionChangeCallbacks = new Set<(connected: boolean) => void>();\n\n // Whether all-ready has fired\n let _allReadyFired = false;\n\n // Ready promise\n let _readyResolve: () => void;\n const _readyPromise = new Promise<void>((resolve) => {\n _readyResolve = resolve;\n });\n\n // Recorded events for testing\n const broadcasts: RecordedBroadcast[] = [];\n const sends: RecordedSend[] = [];\n\n // Screen implementation\n const screen: MockScreen<TEvents> = {\n // === Properties ===\n get controllers() {\n return [..._controllers];\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n get isConnected() {\n return _isConnected;\n },\n get protocolVersion() {\n return 1;\n },\n get ready() {\n return _readyPromise;\n },\n\n // === Lifecycle Methods ===\n onAllReady(callback: () => void): () => void {\n if (_allReadyFired) {\n callback();\n }\n _onAllReadyCallbacks.add(callback);\n return () => { _onAllReadyCallbacks.delete(callback); };\n },\n\n onControllerJoin(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerJoinCallbacks.add(callback);\n return () => { _onControllerJoinCallbacks.delete(callback); };\n },\n\n onControllerLeave(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerLeaveCallbacks.add(callback);\n return () => { _onControllerLeaveCallbacks.delete(callback); };\n },\n\n onControllerDisconnect(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerDisconnectCallbacks.add(callback);\n return () => { _onControllerDisconnectCallbacks.delete(callback); };\n },\n\n onControllerReconnect(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerReconnectCallbacks.add(callback);\n return () => { _onControllerReconnectCallbacks.delete(callback); };\n },\n\n onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void {\n _onCharacterUpdatedCallbacks.add(callback);\n return () => { _onCharacterUpdatedCallbacks.delete(callback); };\n },\n\n onError(callback: (error: SmoreError) => void): () => void {\n _onErrorCallbacks.add(callback);\n return () => { _onErrorCallbacks.delete(callback); };\n },\n\n onConnectionChange(callback: (connected: boolean) => void): () => void {\n _onConnectionChangeCallbacks.add(callback);\n return () => { _onConnectionChangeCallbacks.delete(callback); };\n },\n\n // === Communication Methods ===\n broadcast<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call broadcast() after destroy()');\n }\n if (!_isReady) {\n throw new SmoreSDKError('NOT_READY', 'Cannot call broadcast() before screen is ready');\n }\n validateEventName(event as string);\n broadcasts.push({ event: event as string, data });\n },\n\n sendToController<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call sendToController() after destroy()');\n }\n if (!_isReady) {\n throw new SmoreSDKError('NOT_READY', 'Cannot call sendToController() before screen is ready');\n }\n validateEventName(event as string);\n if (!_controllers.some((c) => c.playerIndex === playerIndex)) {\n throw new SmoreSDKError('INVALID_PLAYER', `No controller found with player index ${playerIndex}`);\n }\n sends.push({ playerIndex, event: event as string, data });\n },\n\n // === Game Lifecycle ===\n gameOver(results?: GameResults): void {\n if (_isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call gameOver() after destroy()');\n }\n if (!_isReady) {\n throw new SmoreSDKError('NOT_READY', 'Cannot call gameOver() before screen is ready');\n }\n broadcasts.push({ event: 'smore:game-over', data: { results } });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call signalReady() after destroy()');\n }\n if (!_isReady) {\n throw new SmoreSDKError('NOT_READY', 'Cannot call signalReady() before screen is ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n const eventStr = event as string;\n\n // Handle $-prefixed lifecycle events by delegating to appropriate lifecycle method\n if (eventStr.startsWith('$')) {\n switch (eventStr) {\n case '$controller-join':\n return screen.onControllerJoin(handler as any);\n case '$controller-leave':\n return screen.onControllerLeave(handler as any);\n case '$controller-disconnect':\n return screen.onControllerDisconnect(handler as any);\n case '$controller-reconnect':\n return screen.onControllerReconnect(handler as any);\n case '$character-updated':\n return screen.onCharacterUpdated(handler as any);\n case '$all-ready':\n return screen.onAllReady(handler as any);\n case '$error':\n return screen.onError(handler as any);\n default:\n // Unknown lifecycle event, just treat as regular event\n break;\n }\n }\n\n validateEventName(event as string);\n\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ScreenEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ScreenEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ScreenEventHandler<EventData<TEvents, K>> = (playerIndex, data) => {\n handler(playerIndex, data);\n screen.off(event, wrapper);\n };\n return screen.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ScreenEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ScreenEventHandler);\n }\n },\n\n removeAllListeners(event?: string): void {\n if (event) {\n listeners.delete(event);\n } else {\n listeners.clear();\n }\n },\n\n // === Utilities ===\n getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return _controllers.find((c) => c.playerIndex === playerIndex);\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n // === Cleanup ===\n /**\n * Note: destroy() clears recorded broadcast/event arrays. Call getBroadcasts() before destroy() if assertions are needed.\n */\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n playerIndex: PlayerIndex,\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(playerIndex, data);\n });\n }\n },\n\n simulateControllerJoin(info: ControllerInfo): void {\n _controllers.push(info);\n _onControllerJoinCallbacks.forEach(cb => cb(info.playerIndex, info));\n },\n\n simulateControllerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);\n _onControllerLeaveCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a controller network disconnect (player still in room but unreachable).\n */\n simulateControllerDisconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new SmoreSDKError('INVALID_PLAYER', `Controller ${playerIndex} not found`);\n }\n // Mark as disconnected (need to create new object since ControllerInfo is readonly)\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex\n ? { ...c, connected: false }\n : c\n );\n _onControllerDisconnectCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a controller network reconnect after disconnect.\n */\n simulateControllerReconnect(playerIndex: PlayerIndex): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new SmoreSDKError('INVALID_PLAYER', `Controller ${playerIndex} not found`);\n }\n // Mark as connected (need to create new object since ControllerInfo is readonly)\n const reconnectedController = { ...controller, connected: true };\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? reconnectedController : c\n );\n _onControllerReconnectCallbacks.forEach(cb => cb(playerIndex, reconnectedController));\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const controller = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!controller) {\n throw new SmoreSDKError('INVALID_PLAYER', `Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n _onCharacterUpdatedCallbacks.forEach(cb => cb(playerIndex, appearance));\n },\n\n simulateAllReady(): void {\n _allReadyFired = true;\n _onAllReadyCallbacks.forEach(cb => cb());\n },\n\n simulateError(error: any): void {\n _onErrorCallbacks.forEach(cb => cb(error));\n },\n\n simulateConnectionChange(connected: boolean): void {\n _isConnected = connected;\n _onConnectionChangeCallbacks.forEach(cb => cb(connected));\n },\n\n getBroadcasts(): Array<{ event: string; data: unknown }> {\n return [...broadcasts];\n },\n\n getSentToController(\n playerIndex: PlayerIndex,\n ): Array<{ event: string; data: unknown }> {\n return sends\n .filter((s) => s.playerIndex === playerIndex)\n .map((s) => ({ event: s.event, data: s.data }));\n },\n\n clearRecordedEvents(): void {\n broadcasts.length = 0;\n sends.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n _isConnected = true;\n _readyResolve();\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => screen.triggerReady(), 0);\n }\n\n return screen;\n}\n\n// =============================================================================\n// MOCK CONTROLLER IMPLEMENTATION\n// =============================================================================\n\ninterface RecordedEvent {\n event: string;\n data: unknown;\n}\n\n/**\n * Create a mock Controller for testing player input logic.\n *\n * All methods work synchronously. Events can be simulated and recorded\n * for assertions in unit tests.\n *\n * Uses the same event emitter pattern as real Controller: register lifecycle\n * callbacks via methods on the returned instance.\n *\n * @example\n * ```ts\n * const controller = createMockController<MyEvents>({\n * myPlayerIndex: 0,\n * });\n *\n * controller.on('your-turn', (data) => handleTurn(data));\n * controller.onAllReady(() => console.log('Ready!'));\n *\n * // Simulate receiving from screen\n * controller.simulateEvent('your-turn', { timeLimit: 30 });\n *\n * // Check what was sent\n * expect(controller.getSentEvents()).toContainEqual({\n * event: 'answer',\n * data: { choice: 2 },\n * });\n * ```\n */\nexport function createMockController<TEvents extends EventMap = EventMap>(\n options: MockOptions = {},\n): MockController<TEvents> {\n const {\n roomCode = 'TEST',\n myPlayerIndex = 0,\n autoReady = true,\n } = options;\n\n // Internal state -- uses a full ControllerInfo[] array (matching MockScreen pattern)\n // to preserve nickname/appearance data from simulatePlayerJoin()\n let _isReady = false;\n let _isConnected = false;\n let _isDestroyed = false;\n let _controllers: ControllerInfo[] = options.controllers ?? [];\n\n // Event listeners\n const listeners = new Map<string, Set<ControllerEventHandler>>();\n\n // Lifecycle callback sets\n const _onAllReadyCallbacks = new Set<() => void>();\n const _onControllerJoinCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onControllerLeaveCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerDisconnectCallbacks = new Set<(index: PlayerIndex) => void>();\n const _onControllerReconnectCallbacks = new Set<(index: PlayerIndex, info: ControllerInfo) => void>();\n const _onCharacterUpdatedCallbacks = new Set<(index: PlayerIndex, appearance: CharacterAppearance | null) => void>();\n const _onErrorCallbacks = new Set<(error: SmoreError) => void>();\n const _onGameOverCallbacks = new Set<(results?: GameResults) => void>();\n const _onConnectionChangeCallbacks = new Set<(connected: boolean) => void>();\n\n // Whether all-ready has fired\n let _allReadyFired = false;\n\n // Ready promise\n let _readyResolve: () => void;\n const _readyPromise = new Promise<void>((resolve) => {\n _readyResolve = resolve;\n });\n\n // Recorded events for testing\n const sentEvents: RecordedEvent[] = [];\n\n // Controller implementation\n const controller: MockController<TEvents> = {\n // === Properties ===\n get myPlayerIndex() {\n return myPlayerIndex;\n },\n get roomCode() {\n return roomCode;\n },\n get isReady() {\n return _isReady;\n },\n get isDestroyed() {\n return _isDestroyed;\n },\n\n get isConnected() {\n return _isConnected;\n },\n\n get protocolVersion() {\n return 1;\n },\n\n get controllers(): readonly ControllerInfo[] {\n return [..._controllers];\n },\n\n get ready() {\n return _readyPromise;\n },\n\n // === Lifecycle Methods ===\n onAllReady(callback: () => void): () => void {\n if (_allReadyFired) {\n callback();\n }\n _onAllReadyCallbacks.add(callback);\n return () => { _onAllReadyCallbacks.delete(callback); };\n },\n\n onControllerJoin(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerJoinCallbacks.add(callback);\n return () => { _onControllerJoinCallbacks.delete(callback); };\n },\n\n onControllerLeave(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerLeaveCallbacks.add(callback);\n return () => { _onControllerLeaveCallbacks.delete(callback); };\n },\n\n onControllerDisconnect(callback: (playerIndex: PlayerIndex) => void): () => void {\n _onControllerDisconnectCallbacks.add(callback);\n return () => { _onControllerDisconnectCallbacks.delete(callback); };\n },\n\n onControllerReconnect(callback: (playerIndex: PlayerIndex, info: ControllerInfo) => void): () => void {\n _onControllerReconnectCallbacks.add(callback);\n return () => { _onControllerReconnectCallbacks.delete(callback); };\n },\n\n onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void {\n _onCharacterUpdatedCallbacks.add(callback);\n return () => { _onCharacterUpdatedCallbacks.delete(callback); };\n },\n\n onError(callback: (error: SmoreError) => void): () => void {\n _onErrorCallbacks.add(callback);\n return () => { _onErrorCallbacks.delete(callback); };\n },\n\n onConnectionChange(callback: (connected: boolean) => void): () => void {\n _onConnectionChangeCallbacks.add(callback);\n return () => { _onConnectionChangeCallbacks.delete(callback); };\n },\n\n onGameOver(callback: (results?: GameResults) => void): () => void {\n _onGameOverCallbacks.add(callback);\n return () => { _onGameOverCallbacks.delete(callback); };\n },\n\n getControllerCount(): number {\n return _controllers.filter(c => c.connected).length;\n },\n\n getController(playerIndex: PlayerIndex): ControllerInfo | undefined {\n return _controllers.find((c) => c.playerIndex === playerIndex);\n },\n\n // === Communication Methods ===\n send<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n if (_isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call send() after destroy()');\n }\n validateEventName(event as string);\n sentEvents.push({ event: event as string, data });\n },\n\n signalReady(): void {\n if (_isDestroyed) {\n throw new SmoreSDKError('DESTROYED', 'Cannot call signalReady() after destroy()');\n }\n if (!_isReady) {\n throw new SmoreSDKError('NOT_READY', 'Cannot call signalReady() before controller is ready');\n }\n // No-op in mock (real implementation emits to server)\n },\n\n // === Event Subscription ===\n on<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n const eventStr = event as string;\n\n // Handle $-prefixed lifecycle events by delegating to appropriate lifecycle method\n if (eventStr.startsWith('$')) {\n switch (eventStr) {\n case '$controller-join':\n return controller.onControllerJoin(handler as any);\n case '$controller-leave':\n return controller.onControllerLeave(handler as any);\n case '$controller-disconnect':\n return controller.onControllerDisconnect(handler as any);\n case '$controller-reconnect':\n return controller.onControllerReconnect(handler as any);\n case '$character-updated':\n return controller.onCharacterUpdated(handler as any);\n case '$all-ready':\n return controller.onAllReady(handler as any);\n case '$error':\n return controller.onError(handler as any);\n case '$game-over':\n return controller.onGameOver(handler as any);\n default:\n // Unknown lifecycle event, just treat as regular event\n break;\n }\n }\n\n validateEventName(event as string);\n\n if (!listeners.has(eventStr)) {\n listeners.set(eventStr, new Set());\n }\n listeners.get(eventStr)!.add(handler as ControllerEventHandler);\n\n return () => {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n };\n },\n\n once<K extends EventNames<TEvents>>(\n event: K,\n handler: ControllerEventHandler<EventData<TEvents, K>>,\n ): () => void {\n validateEventName(event as string);\n const wrapper: ControllerEventHandler<EventData<TEvents, K>> = (data) => {\n handler(data);\n controller.off(event, wrapper);\n };\n return controller.on(event, wrapper);\n },\n\n off<K extends EventNames<TEvents>>(\n event: K,\n handler?: ControllerEventHandler<EventData<TEvents, K>>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n if (!handler) {\n listeners.delete(eventStr);\n } else {\n listeners.get(eventStr)?.delete(handler as ControllerEventHandler);\n }\n },\n\n removeAllListeners(event?: string): void {\n if (event) {\n listeners.delete(event);\n } else {\n listeners.clear();\n }\n },\n\n // === Cleanup ===\n destroy(): void {\n _isDestroyed = true;\n listeners.clear();\n sentEvents.length = 0;\n },\n\n // === Mock-specific methods ===\n simulateEvent<K extends EventNames<TEvents>>(\n event: K,\n data: EventData<TEvents, K>,\n ): void {\n validateEventName(event as string);\n const eventStr = event as string;\n const handlers = listeners.get(eventStr);\n if (handlers) {\n handlers.forEach((handler) => {\n handler(data);\n });\n }\n },\n\n getSentEvents(): Array<{ event: string; data: unknown }> {\n return [...sentEvents];\n },\n\n clearRecordedEvents(): void {\n sentEvents.length = 0;\n },\n\n triggerReady(): void {\n _isReady = true;\n _isConnected = true;\n _readyResolve();\n },\n\n /**\n * Simulate a new player joining the room.\n * Stores full ControllerInfo (nickname, appearance, etc.) for later retrieval.\n */\n simulatePlayerJoin(playerIndex: PlayerIndex, info: ControllerInfo): void {\n if (!_controllers.some(c => c.playerIndex === playerIndex)) {\n _controllers = [..._controllers, { ...info, connected: info.connected ?? true }];\n }\n _onControllerJoinCallbacks.forEach(cb => cb(playerIndex, info));\n },\n\n /**\n * Simulate a player leaving the room (fully removed).\n */\n simulatePlayerLeave(playerIndex: PlayerIndex): void {\n _controllers = _controllers.filter(c => c.playerIndex !== playerIndex);\n _onControllerLeaveCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a player network disconnect (player still in room but unreachable).\n */\n simulatePlayerDisconnect(playerIndex: PlayerIndex): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...c, connected: false } : c\n );\n _onControllerDisconnectCallbacks.forEach(cb => cb(playerIndex));\n },\n\n /**\n * Simulate a player network reconnect after disconnect.\n */\n simulatePlayerReconnect(playerIndex: PlayerIndex, info: ControllerInfo): void {\n _controllers = _controllers.map(c =>\n c.playerIndex === playerIndex ? { ...info, connected: true } : c\n );\n _onControllerReconnectCallbacks.forEach(cb => cb(playerIndex, info));\n },\n\n simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void {\n const ctrl = _controllers.find((c) => c.playerIndex === playerIndex);\n if (!ctrl) {\n throw new SmoreSDKError('INVALID_PLAYER', `Controller ${playerIndex} not found`);\n }\n _controllers = _controllers.map((c) =>\n c.playerIndex === playerIndex ? { ...c, appearance } : c\n );\n _onCharacterUpdatedCallbacks.forEach(cb => cb(playerIndex, appearance));\n },\n\n simulateAllReady(): void {\n _allReadyFired = true;\n _onAllReadyCallbacks.forEach(cb => cb());\n },\n\n simulateError(error: any): void {\n _onErrorCallbacks.forEach(cb => cb(error));\n },\n\n simulateGameOver(results?: GameResults): void {\n _onGameOverCallbacks.forEach(cb => cb(results));\n },\n\n simulateConnectionChange(connected: boolean): void {\n _isConnected = connected;\n _onConnectionChangeCallbacks.forEach(cb => cb(connected));\n },\n };\n\n /**\n * Auto-trigger ready if enabled.\n *\n * **Note:** `autoReady` uses `setTimeout(0)` which fires asynchronously (next tick).\n * For synchronous test control, use `autoReady: false` and call `triggerReady()` manually.\n */\n if (autoReady) {\n setTimeout(() => controller.triggerReady(), 0);\n }\n\n return controller;\n}\n\n"],"names":[],"mappings":"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"}
|