quake2ts 0.0.556 → 0.0.561
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/package.json +3 -1
- package/packages/client/dist/browser/index.global.js +17 -17
- package/packages/client/dist/browser/index.global.js.map +1 -1
- package/packages/client/dist/cjs/index.cjs +5369 -3577
- package/packages/client/dist/cjs/index.cjs.map +1 -1
- package/packages/client/dist/esm/index.js +5369 -3577
- package/packages/client/dist/esm/index.js.map +1 -1
- package/packages/client/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/client/dist/types/index.d.ts.map +1 -1
- package/packages/engine/dist/cjs/index.cjs +2256 -534
- package/packages/engine/dist/cjs/index.cjs.map +1 -1
- package/packages/engine/dist/esm/index.js +2266 -538
- package/packages/engine/dist/esm/index.js.map +1 -1
- package/packages/engine/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/engine/dist/types/assets/visibilityAnalyzer.d.ts +7 -2
- package/packages/engine/dist/types/assets/visibilityAnalyzer.d.ts.map +1 -1
- package/packages/engine/dist/types/render/bloom.d.ts +19 -0
- package/packages/engine/dist/types/render/bloom.d.ts.map +1 -0
- package/packages/engine/dist/types/render/frame.d.ts +2 -0
- package/packages/engine/dist/types/render/frame.d.ts.map +1 -1
- package/packages/engine/dist/types/render/renderer.d.ts +2 -0
- package/packages/engine/dist/types/render/renderer.d.ts.map +1 -1
- package/packages/game/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/shared/dist/tsconfig.tsbuildinfo +1 -1
- package/packages/test-utils/dist/index.cjs +1178 -580
- package/packages/test-utils/dist/index.cjs.map +1 -1
- package/packages/test-utils/dist/index.d.cts +198 -47
- package/packages/test-utils/dist/index.d.ts +198 -47
- package/packages/test-utils/dist/index.js +1149 -582
- package/packages/test-utils/dist/index.js.map +1 -1
- package/packages/tools/dist/tsconfig.tsbuildinfo +1 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,6 +17,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
|
|
20
30
|
// src/index.ts
|
|
@@ -22,17 +32,33 @@ var index_exports = {};
|
|
|
22
32
|
__export(index_exports, {
|
|
23
33
|
InputInjector: () => InputInjector,
|
|
24
34
|
MockPointerLock: () => MockPointerLock,
|
|
35
|
+
captureAudioEvents: () => captureAudioEvents,
|
|
36
|
+
captureCanvasDrawCalls: () => captureCanvasDrawCalls,
|
|
37
|
+
captureGameState: () => captureGameState,
|
|
25
38
|
createBinaryStreamMock: () => createBinaryStreamMock,
|
|
26
39
|
createBinaryWriterMock: () => createBinaryWriterMock,
|
|
40
|
+
createControlledTimer: () => createControlledTimer,
|
|
27
41
|
createEntity: () => createEntity,
|
|
28
42
|
createEntityStateFactory: () => createEntityStateFactory,
|
|
29
43
|
createGameStateSnapshotFactory: () => createGameStateSnapshotFactory,
|
|
44
|
+
createMockAudioContext: () => createMockAudioContext,
|
|
45
|
+
createMockCanvas: () => createMockCanvas,
|
|
46
|
+
createMockCanvasContext2D: () => createMockCanvasContext2D,
|
|
30
47
|
createMockEngine: () => createMockEngine,
|
|
31
48
|
createMockGame: () => createMockGame,
|
|
49
|
+
createMockImage: () => createMockImage,
|
|
50
|
+
createMockImageData: () => createMockImageData,
|
|
51
|
+
createMockIndexedDB: () => createMockIndexedDB,
|
|
52
|
+
createMockLocalStorage: () => createMockLocalStorage,
|
|
53
|
+
createMockPerformance: () => createMockPerformance,
|
|
54
|
+
createMockRAF: () => createMockRAF,
|
|
55
|
+
createMockSessionStorage: () => createMockSessionStorage,
|
|
32
56
|
createMockWebGL2Context: () => createMockWebGL2Context,
|
|
33
57
|
createNetChanMock: () => createNetChanMock,
|
|
34
58
|
createPlayerStateFactory: () => createPlayerStateFactory,
|
|
59
|
+
createPlaywrightTestClient: () => createPlaywrightTestClient,
|
|
35
60
|
createSpawnContext: () => createSpawnContext,
|
|
61
|
+
createStorageTestScenario: () => createStorageTestScenario,
|
|
36
62
|
createTestContext: () => createTestContext,
|
|
37
63
|
intersects: () => import_shared3.intersects,
|
|
38
64
|
ladderTrace: () => import_shared3.ladderTrace,
|
|
@@ -44,417 +70,271 @@ __export(index_exports, {
|
|
|
44
70
|
makeNode: () => makeNode,
|
|
45
71
|
makePlane: () => makePlane,
|
|
46
72
|
setupBrowserEnvironment: () => setupBrowserEnvironment,
|
|
73
|
+
setupMockAudioContext: () => setupMockAudioContext,
|
|
47
74
|
setupNodeEnvironment: () => setupNodeEnvironment,
|
|
75
|
+
simulateFrames: () => simulateFrames,
|
|
76
|
+
simulateFramesWithMock: () => simulateFramesWithMock,
|
|
48
77
|
stairTrace: () => import_shared3.stairTrace,
|
|
49
|
-
teardownBrowserEnvironment: () => teardownBrowserEnvironment
|
|
78
|
+
teardownBrowserEnvironment: () => teardownBrowserEnvironment,
|
|
79
|
+
teardownMockAudioContext: () => teardownMockAudioContext,
|
|
80
|
+
waitForGameReady: () => waitForGameReady
|
|
50
81
|
});
|
|
51
82
|
module.exports = __toCommonJS(index_exports);
|
|
52
83
|
|
|
53
|
-
// src/
|
|
54
|
-
var
|
|
55
|
-
var
|
|
56
|
-
|
|
57
|
-
writeShort: import_vitest.vi.fn(),
|
|
58
|
-
writeLong: import_vitest.vi.fn(),
|
|
59
|
-
writeString: import_vitest.vi.fn(),
|
|
60
|
-
writeBytes: import_vitest.vi.fn(),
|
|
61
|
-
getBuffer: import_vitest.vi.fn(() => new Uint8Array(0)),
|
|
62
|
-
reset: import_vitest.vi.fn(),
|
|
63
|
-
// Legacy methods (if any)
|
|
64
|
-
writeInt8: import_vitest.vi.fn(),
|
|
65
|
-
writeUint8: import_vitest.vi.fn(),
|
|
66
|
-
writeInt16: import_vitest.vi.fn(),
|
|
67
|
-
writeUint16: import_vitest.vi.fn(),
|
|
68
|
-
writeInt32: import_vitest.vi.fn(),
|
|
69
|
-
writeUint32: import_vitest.vi.fn(),
|
|
70
|
-
writeFloat: import_vitest.vi.fn(),
|
|
71
|
-
getData: import_vitest.vi.fn(() => new Uint8Array(0))
|
|
72
|
-
});
|
|
73
|
-
var createNetChanMock = () => ({
|
|
74
|
-
qport: 1234,
|
|
75
|
-
// Sequencing
|
|
76
|
-
incomingSequence: 0,
|
|
77
|
-
outgoingSequence: 0,
|
|
78
|
-
incomingAcknowledged: 0,
|
|
79
|
-
// Reliable messaging
|
|
80
|
-
incomingReliableAcknowledged: false,
|
|
81
|
-
incomingReliableSequence: 0,
|
|
82
|
-
outgoingReliableSequence: 0,
|
|
83
|
-
reliableMessage: createBinaryWriterMock(),
|
|
84
|
-
reliableLength: 0,
|
|
85
|
-
// Fragmentation
|
|
86
|
-
fragmentSendOffset: 0,
|
|
87
|
-
fragmentBuffer: null,
|
|
88
|
-
fragmentLength: 0,
|
|
89
|
-
fragmentReceived: 0,
|
|
90
|
-
// Timing
|
|
91
|
-
lastReceived: 0,
|
|
92
|
-
lastSent: 0,
|
|
93
|
-
remoteAddress: { type: "IP", port: 1234 },
|
|
94
|
-
// Methods
|
|
95
|
-
setup: import_vitest.vi.fn(),
|
|
96
|
-
reset: import_vitest.vi.fn(),
|
|
97
|
-
transmit: import_vitest.vi.fn(),
|
|
98
|
-
process: import_vitest.vi.fn(),
|
|
99
|
-
canSendReliable: import_vitest.vi.fn(() => true),
|
|
100
|
-
writeReliableByte: import_vitest.vi.fn(),
|
|
101
|
-
writeReliableShort: import_vitest.vi.fn(),
|
|
102
|
-
writeReliableLong: import_vitest.vi.fn(),
|
|
103
|
-
writeReliableString: import_vitest.vi.fn(),
|
|
104
|
-
getReliableData: import_vitest.vi.fn(() => new Uint8Array(0)),
|
|
105
|
-
needsKeepalive: import_vitest.vi.fn(() => false),
|
|
106
|
-
isTimedOut: import_vitest.vi.fn(() => false)
|
|
107
|
-
});
|
|
108
|
-
var createBinaryStreamMock = () => ({
|
|
109
|
-
getPosition: import_vitest.vi.fn(() => 0),
|
|
110
|
-
getReadPosition: import_vitest.vi.fn(() => 0),
|
|
111
|
-
getLength: import_vitest.vi.fn(() => 0),
|
|
112
|
-
getRemaining: import_vitest.vi.fn(() => 0),
|
|
113
|
-
seek: import_vitest.vi.fn(),
|
|
114
|
-
setReadPosition: import_vitest.vi.fn(),
|
|
115
|
-
hasMore: import_vitest.vi.fn(() => true),
|
|
116
|
-
hasBytes: import_vitest.vi.fn((amount) => true),
|
|
117
|
-
readChar: import_vitest.vi.fn(() => 0),
|
|
118
|
-
readByte: import_vitest.vi.fn(() => 0),
|
|
119
|
-
readShort: import_vitest.vi.fn(() => 0),
|
|
120
|
-
readUShort: import_vitest.vi.fn(() => 0),
|
|
121
|
-
readLong: import_vitest.vi.fn(() => 0),
|
|
122
|
-
readULong: import_vitest.vi.fn(() => 0),
|
|
123
|
-
readFloat: import_vitest.vi.fn(() => 0),
|
|
124
|
-
readString: import_vitest.vi.fn(() => ""),
|
|
125
|
-
readStringLine: import_vitest.vi.fn(() => ""),
|
|
126
|
-
readCoord: import_vitest.vi.fn(() => 0),
|
|
127
|
-
readAngle: import_vitest.vi.fn(() => 0),
|
|
128
|
-
readAngle16: import_vitest.vi.fn(() => 0),
|
|
129
|
-
readData: import_vitest.vi.fn((length) => new Uint8Array(length)),
|
|
130
|
-
readPos: import_vitest.vi.fn(),
|
|
131
|
-
readDir: import_vitest.vi.fn()
|
|
132
|
-
});
|
|
84
|
+
// src/setup/browser.ts
|
|
85
|
+
var import_jsdom = require("jsdom");
|
|
86
|
+
var import_canvas = require("@napi-rs/canvas");
|
|
87
|
+
var import_auto = require("fake-indexeddb/auto");
|
|
133
88
|
|
|
134
|
-
// src/
|
|
135
|
-
var
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
89
|
+
// src/e2e/input.ts
|
|
90
|
+
var MockPointerLock = class {
|
|
91
|
+
static setup(doc) {
|
|
92
|
+
let _pointerLockElement = null;
|
|
93
|
+
Object.defineProperty(doc, "pointerLockElement", {
|
|
94
|
+
get: () => _pointerLockElement,
|
|
95
|
+
configurable: true
|
|
96
|
+
});
|
|
97
|
+
doc.exitPointerLock = () => {
|
|
98
|
+
if (_pointerLockElement) {
|
|
99
|
+
_pointerLockElement = null;
|
|
100
|
+
doc.dispatchEvent(new Event("pointerlockchange"));
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
global.HTMLElement.prototype.requestPointerLock = function() {
|
|
104
|
+
_pointerLockElement = this;
|
|
105
|
+
doc.dispatchEvent(new Event("pointerlockchange"));
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
var InputInjector = class {
|
|
110
|
+
constructor(doc, win) {
|
|
111
|
+
this.doc = doc;
|
|
112
|
+
this.win = win;
|
|
113
|
+
}
|
|
114
|
+
keyDown(key, code) {
|
|
115
|
+
const event = new this.win.KeyboardEvent("keydown", {
|
|
116
|
+
key,
|
|
117
|
+
code: code || key,
|
|
118
|
+
bubbles: true,
|
|
119
|
+
cancelable: true,
|
|
120
|
+
view: this.win
|
|
121
|
+
});
|
|
122
|
+
this.doc.dispatchEvent(event);
|
|
123
|
+
}
|
|
124
|
+
keyUp(key, code) {
|
|
125
|
+
const event = new this.win.KeyboardEvent("keyup", {
|
|
126
|
+
key,
|
|
127
|
+
code: code || key,
|
|
128
|
+
bubbles: true,
|
|
129
|
+
cancelable: true,
|
|
130
|
+
view: this.win
|
|
131
|
+
});
|
|
132
|
+
this.doc.dispatchEvent(event);
|
|
133
|
+
}
|
|
134
|
+
mouseMove(movementX, movementY, clientX = 0, clientY = 0) {
|
|
135
|
+
const event = new this.win.MouseEvent("mousemove", {
|
|
136
|
+
bubbles: true,
|
|
137
|
+
cancelable: true,
|
|
138
|
+
view: this.win,
|
|
139
|
+
clientX,
|
|
140
|
+
clientY,
|
|
141
|
+
movementX,
|
|
142
|
+
// Note: JSDOM might not support this standard property fully on event init
|
|
143
|
+
movementY
|
|
144
|
+
});
|
|
145
|
+
Object.defineProperty(event, "movementX", { value: movementX });
|
|
146
|
+
Object.defineProperty(event, "movementY", { value: movementY });
|
|
147
|
+
const target = this.doc.pointerLockElement || this.doc;
|
|
148
|
+
target.dispatchEvent(event);
|
|
149
|
+
}
|
|
150
|
+
mouseDown(button = 0) {
|
|
151
|
+
const event = new this.win.MouseEvent("mousedown", {
|
|
152
|
+
button,
|
|
153
|
+
bubbles: true,
|
|
154
|
+
cancelable: true,
|
|
155
|
+
view: this.win
|
|
156
|
+
});
|
|
157
|
+
const target = this.doc.pointerLockElement || this.doc;
|
|
158
|
+
target.dispatchEvent(event);
|
|
159
|
+
}
|
|
160
|
+
mouseUp(button = 0) {
|
|
161
|
+
const event = new this.win.MouseEvent("mouseup", {
|
|
162
|
+
button,
|
|
163
|
+
bubbles: true,
|
|
164
|
+
cancelable: true,
|
|
165
|
+
view: this.win
|
|
166
|
+
});
|
|
167
|
+
const target = this.doc.pointerLockElement || this.doc;
|
|
168
|
+
target.dispatchEvent(event);
|
|
169
|
+
}
|
|
170
|
+
wheel(deltaY) {
|
|
171
|
+
const event = new this.win.WheelEvent("wheel", {
|
|
172
|
+
deltaY,
|
|
173
|
+
bubbles: true,
|
|
174
|
+
cancelable: true,
|
|
175
|
+
view: this.win
|
|
176
|
+
});
|
|
177
|
+
const target = this.doc.pointerLockElement || this.doc;
|
|
178
|
+
target.dispatchEvent(event);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
200
181
|
|
|
201
|
-
// src/
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
angles: { x: 0, y: 0, z: 0 },
|
|
233
|
-
oldOrigin: { x: 0, y: 0, z: 0 },
|
|
234
|
-
modelIndex: 0,
|
|
235
|
-
modelIndex2: 0,
|
|
236
|
-
modelIndex3: 0,
|
|
237
|
-
modelIndex4: 0,
|
|
238
|
-
frame: 0,
|
|
239
|
-
skinNum: 0,
|
|
240
|
-
effects: 0,
|
|
241
|
-
renderfx: 0,
|
|
242
|
-
solid: 0,
|
|
243
|
-
sound: 0,
|
|
244
|
-
event: 0,
|
|
245
|
-
...overrides
|
|
246
|
-
});
|
|
247
|
-
var createGameStateSnapshotFactory = (overrides) => ({
|
|
248
|
-
gravity: { x: 0, y: 0, z: -800 },
|
|
249
|
-
origin: { x: 0, y: 0, z: 0 },
|
|
250
|
-
velocity: { x: 0, y: 0, z: 0 },
|
|
251
|
-
viewangles: { x: 0, y: 0, z: 0 },
|
|
252
|
-
level: { timeSeconds: 0, frameNumber: 0, previousTimeSeconds: 0, deltaSeconds: 0.1 },
|
|
253
|
-
entities: {
|
|
254
|
-
activeCount: 0,
|
|
255
|
-
worldClassname: "worldspawn"
|
|
256
|
-
},
|
|
257
|
-
packetEntities: [],
|
|
258
|
-
pmFlags: 0,
|
|
259
|
-
pmType: 0,
|
|
260
|
-
waterlevel: 0,
|
|
261
|
-
watertype: 0,
|
|
262
|
-
deltaAngles: { x: 0, y: 0, z: 0 },
|
|
263
|
-
health: 100,
|
|
264
|
-
armor: 0,
|
|
265
|
-
ammo: 0,
|
|
266
|
-
blend: [0, 0, 0, 0],
|
|
267
|
-
damageAlpha: 0,
|
|
268
|
-
damageIndicators: [],
|
|
269
|
-
stats: [],
|
|
270
|
-
kick_angles: { x: 0, y: 0, z: 0 },
|
|
271
|
-
kick_origin: { x: 0, y: 0, z: 0 },
|
|
272
|
-
gunoffset: { x: 0, y: 0, z: 0 },
|
|
273
|
-
gunangles: { x: 0, y: 0, z: 0 },
|
|
274
|
-
gunindex: 0,
|
|
275
|
-
pm_time: 0,
|
|
276
|
-
gun_frame: 0,
|
|
277
|
-
rdflags: 0,
|
|
278
|
-
fov: 90,
|
|
279
|
-
renderfx: 0,
|
|
280
|
-
pm_flags: 0,
|
|
281
|
-
pm_type: 0,
|
|
282
|
-
...overrides
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
// src/game/helpers.ts
|
|
286
|
-
var import_vitest2 = require("vitest");
|
|
287
|
-
var import_game = require("@quake2ts/game");
|
|
288
|
-
var import_shared2 = require("@quake2ts/shared");
|
|
289
|
-
var import_shared3 = require("@quake2ts/shared");
|
|
290
|
-
var createMockEngine = () => ({
|
|
291
|
-
sound: import_vitest2.vi.fn(),
|
|
292
|
-
soundIndex: import_vitest2.vi.fn((sound) => 0),
|
|
293
|
-
modelIndex: import_vitest2.vi.fn((model) => 0),
|
|
294
|
-
centerprintf: import_vitest2.vi.fn()
|
|
295
|
-
});
|
|
296
|
-
var createMockGame = (seed = 12345) => {
|
|
297
|
-
const spawnRegistry = new import_game.SpawnRegistry();
|
|
298
|
-
const hooks = new import_game.ScriptHookRegistry();
|
|
299
|
-
const game = {
|
|
300
|
-
random: (0, import_shared2.createRandomGenerator)({ seed }),
|
|
301
|
-
registerEntitySpawn: import_vitest2.vi.fn((classname, spawnFunc) => {
|
|
302
|
-
spawnRegistry.register(classname, (entity) => spawnFunc(entity));
|
|
303
|
-
}),
|
|
304
|
-
unregisterEntitySpawn: import_vitest2.vi.fn((classname) => {
|
|
305
|
-
spawnRegistry.unregister(classname);
|
|
306
|
-
}),
|
|
307
|
-
getCustomEntities: import_vitest2.vi.fn(() => Array.from(spawnRegistry.keys())),
|
|
308
|
-
hooks,
|
|
309
|
-
registerHooks: import_vitest2.vi.fn((newHooks) => hooks.register(newHooks)),
|
|
310
|
-
spawnWorld: import_vitest2.vi.fn(() => {
|
|
311
|
-
hooks.onMapLoad("q2dm1");
|
|
312
|
-
}),
|
|
313
|
-
clientBegin: import_vitest2.vi.fn((client) => {
|
|
314
|
-
hooks.onPlayerSpawn({});
|
|
315
|
-
}),
|
|
316
|
-
damage: import_vitest2.vi.fn((amount) => {
|
|
317
|
-
hooks.onDamage({}, null, null, amount, 0, 0);
|
|
318
|
-
})
|
|
319
|
-
};
|
|
320
|
-
return { game, spawnRegistry };
|
|
321
|
-
};
|
|
322
|
-
function createTestContext(options) {
|
|
323
|
-
const engine = createMockEngine();
|
|
324
|
-
const seed = options?.seed ?? 12345;
|
|
325
|
-
const { game, spawnRegistry } = createMockGame(seed);
|
|
326
|
-
const traceFn = import_vitest2.vi.fn((start, end, mins, maxs) => ({
|
|
327
|
-
fraction: 1,
|
|
328
|
-
ent: null,
|
|
329
|
-
allsolid: false,
|
|
330
|
-
startsolid: false,
|
|
331
|
-
endpos: end,
|
|
332
|
-
plane: { normal: { x: 0, y: 0, z: 1 }, dist: 0 },
|
|
333
|
-
surfaceFlags: 0,
|
|
334
|
-
contents: 0
|
|
335
|
-
}));
|
|
336
|
-
const entityList = options?.initialEntities ? [...options.initialEntities] : [];
|
|
337
|
-
const hooks = game.hooks;
|
|
338
|
-
const entities = {
|
|
339
|
-
spawn: import_vitest2.vi.fn(() => {
|
|
340
|
-
const ent = new import_game.Entity(entityList.length + 1);
|
|
341
|
-
entityList.push(ent);
|
|
342
|
-
hooks.onEntitySpawn(ent);
|
|
343
|
-
return ent;
|
|
344
|
-
}),
|
|
345
|
-
free: import_vitest2.vi.fn((ent) => {
|
|
346
|
-
const idx = entityList.indexOf(ent);
|
|
347
|
-
if (idx !== -1) {
|
|
348
|
-
entityList.splice(idx, 1);
|
|
349
|
-
}
|
|
350
|
-
hooks.onEntityRemove(ent);
|
|
351
|
-
}),
|
|
352
|
-
finalizeSpawn: import_vitest2.vi.fn(),
|
|
353
|
-
freeImmediate: import_vitest2.vi.fn((ent) => {
|
|
354
|
-
const idx = entityList.indexOf(ent);
|
|
355
|
-
if (idx !== -1) {
|
|
356
|
-
entityList.splice(idx, 1);
|
|
357
|
-
}
|
|
358
|
-
}),
|
|
359
|
-
setSpawnRegistry: import_vitest2.vi.fn(),
|
|
360
|
-
timeSeconds: 10,
|
|
361
|
-
deltaSeconds: 0.1,
|
|
362
|
-
modelIndex: import_vitest2.vi.fn(() => 0),
|
|
363
|
-
scheduleThink: import_vitest2.vi.fn((entity, time) => {
|
|
364
|
-
entity.nextthink = time;
|
|
365
|
-
}),
|
|
366
|
-
linkentity: import_vitest2.vi.fn(),
|
|
367
|
-
trace: traceFn,
|
|
368
|
-
pointcontents: import_vitest2.vi.fn(() => 0),
|
|
369
|
-
multicast: import_vitest2.vi.fn(),
|
|
370
|
-
unicast: import_vitest2.vi.fn(),
|
|
371
|
-
engine,
|
|
372
|
-
game,
|
|
373
|
-
sound: import_vitest2.vi.fn((ent, chan, sound, vol, attn, timeofs) => {
|
|
374
|
-
engine.sound(ent, chan, sound, vol, attn, timeofs);
|
|
375
|
-
}),
|
|
376
|
-
soundIndex: import_vitest2.vi.fn((sound) => engine.soundIndex(sound)),
|
|
377
|
-
useTargets: import_vitest2.vi.fn((entity, activator) => {
|
|
378
|
-
}),
|
|
379
|
-
findByTargetName: import_vitest2.vi.fn(() => []),
|
|
380
|
-
pickTarget: import_vitest2.vi.fn(() => null),
|
|
381
|
-
killBox: import_vitest2.vi.fn(),
|
|
382
|
-
rng: (0, import_shared2.createRandomGenerator)({ seed }),
|
|
383
|
-
imports: {
|
|
384
|
-
configstring: import_vitest2.vi.fn(),
|
|
385
|
-
trace: traceFn,
|
|
386
|
-
pointcontents: import_vitest2.vi.fn(() => 0)
|
|
182
|
+
// src/setup/webgl.ts
|
|
183
|
+
function createMockWebGL2Context(canvas) {
|
|
184
|
+
const gl = {
|
|
185
|
+
canvas,
|
|
186
|
+
drawingBufferWidth: canvas.width,
|
|
187
|
+
drawingBufferHeight: canvas.height,
|
|
188
|
+
// Constants
|
|
189
|
+
VERTEX_SHADER: 35633,
|
|
190
|
+
FRAGMENT_SHADER: 35632,
|
|
191
|
+
COMPILE_STATUS: 35713,
|
|
192
|
+
LINK_STATUS: 35714,
|
|
193
|
+
ARRAY_BUFFER: 34962,
|
|
194
|
+
ELEMENT_ARRAY_BUFFER: 34963,
|
|
195
|
+
STATIC_DRAW: 35044,
|
|
196
|
+
DYNAMIC_DRAW: 35048,
|
|
197
|
+
FLOAT: 5126,
|
|
198
|
+
DEPTH_TEST: 2929,
|
|
199
|
+
BLEND: 3042,
|
|
200
|
+
SRC_ALPHA: 770,
|
|
201
|
+
ONE_MINUS_SRC_ALPHA: 771,
|
|
202
|
+
TEXTURE_2D: 3553,
|
|
203
|
+
RGBA: 6408,
|
|
204
|
+
UNSIGNED_BYTE: 5121,
|
|
205
|
+
COLOR_BUFFER_BIT: 16384,
|
|
206
|
+
DEPTH_BUFFER_BIT: 256,
|
|
207
|
+
TRIANGLES: 4,
|
|
208
|
+
TRIANGLE_STRIP: 5,
|
|
209
|
+
TRIANGLE_FAN: 6,
|
|
210
|
+
// Methods
|
|
211
|
+
createShader: () => ({}),
|
|
212
|
+
shaderSource: () => {
|
|
387
213
|
},
|
|
388
|
-
|
|
389
|
-
intermission_angle: { x: 0, y: 0, z: 0 },
|
|
390
|
-
intermission_origin: { x: 0, y: 0, z: 0 },
|
|
391
|
-
next_auto_save: 0,
|
|
392
|
-
health_bar_entities: null
|
|
214
|
+
compileShader: () => {
|
|
393
215
|
},
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
}),
|
|
398
|
-
find: import_vitest2.vi.fn((predicate) => {
|
|
399
|
-
return entityList.find(predicate);
|
|
400
|
-
}),
|
|
401
|
-
findByClassname: import_vitest2.vi.fn((classname) => {
|
|
402
|
-
return entityList.find((e) => e.classname === classname);
|
|
403
|
-
}),
|
|
404
|
-
beginFrame: import_vitest2.vi.fn((timeSeconds) => {
|
|
405
|
-
entities.timeSeconds = timeSeconds;
|
|
406
|
-
}),
|
|
407
|
-
targetAwareness: {
|
|
408
|
-
timeSeconds: 10,
|
|
409
|
-
frameNumber: 1,
|
|
410
|
-
sightEntity: null,
|
|
411
|
-
soundEntity: null
|
|
216
|
+
getShaderParameter: (_, param) => {
|
|
217
|
+
if (param === 35713) return true;
|
|
218
|
+
return true;
|
|
412
219
|
},
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
|
|
220
|
+
getShaderInfoLog: () => "",
|
|
221
|
+
createProgram: () => ({}),
|
|
222
|
+
attachShader: () => {
|
|
223
|
+
},
|
|
224
|
+
linkProgram: () => {
|
|
225
|
+
},
|
|
226
|
+
getProgramParameter: (_, param) => {
|
|
227
|
+
if (param === 35714) return true;
|
|
228
|
+
return true;
|
|
229
|
+
},
|
|
230
|
+
getProgramInfoLog: () => "",
|
|
231
|
+
useProgram: () => {
|
|
232
|
+
},
|
|
233
|
+
createBuffer: () => ({}),
|
|
234
|
+
bindBuffer: () => {
|
|
235
|
+
},
|
|
236
|
+
bufferData: () => {
|
|
237
|
+
},
|
|
238
|
+
enableVertexAttribArray: () => {
|
|
239
|
+
},
|
|
240
|
+
vertexAttribPointer: () => {
|
|
241
|
+
},
|
|
242
|
+
enable: () => {
|
|
243
|
+
},
|
|
244
|
+
disable: () => {
|
|
245
|
+
},
|
|
246
|
+
depthMask: () => {
|
|
247
|
+
},
|
|
248
|
+
blendFunc: () => {
|
|
249
|
+
},
|
|
250
|
+
viewport: () => {
|
|
251
|
+
},
|
|
252
|
+
clearColor: () => {
|
|
253
|
+
},
|
|
254
|
+
clear: () => {
|
|
255
|
+
},
|
|
256
|
+
createTexture: () => ({}),
|
|
257
|
+
bindTexture: () => {
|
|
258
|
+
},
|
|
259
|
+
texImage2D: () => {
|
|
260
|
+
},
|
|
261
|
+
texParameteri: () => {
|
|
262
|
+
},
|
|
263
|
+
activeTexture: () => {
|
|
264
|
+
},
|
|
265
|
+
uniform1i: () => {
|
|
266
|
+
},
|
|
267
|
+
uniform1f: () => {
|
|
268
|
+
},
|
|
269
|
+
uniform2f: () => {
|
|
270
|
+
},
|
|
271
|
+
uniform3f: () => {
|
|
272
|
+
},
|
|
273
|
+
uniform4f: () => {
|
|
274
|
+
},
|
|
275
|
+
uniformMatrix4fv: () => {
|
|
276
|
+
},
|
|
277
|
+
getUniformLocation: () => ({}),
|
|
278
|
+
getAttribLocation: () => 0,
|
|
279
|
+
drawArrays: () => {
|
|
280
|
+
},
|
|
281
|
+
drawElements: () => {
|
|
282
|
+
},
|
|
283
|
+
createVertexArray: () => ({}),
|
|
284
|
+
bindVertexArray: () => {
|
|
285
|
+
},
|
|
286
|
+
deleteShader: () => {
|
|
287
|
+
},
|
|
288
|
+
deleteProgram: () => {
|
|
289
|
+
},
|
|
290
|
+
deleteBuffer: () => {
|
|
291
|
+
},
|
|
292
|
+
deleteTexture: () => {
|
|
293
|
+
},
|
|
294
|
+
deleteVertexArray: () => {
|
|
295
|
+
},
|
|
296
|
+
// WebGL2 specific
|
|
297
|
+
texImage3D: () => {
|
|
298
|
+
},
|
|
299
|
+
uniformBlockBinding: () => {
|
|
300
|
+
},
|
|
301
|
+
getExtension: () => null
|
|
302
|
+
};
|
|
303
|
+
return gl;
|
|
441
304
|
}
|
|
442
305
|
|
|
443
306
|
// src/setup/browser.ts
|
|
444
|
-
var import_jsdom = require("jsdom");
|
|
445
|
-
var import_canvas = require("@napi-rs/canvas");
|
|
446
|
-
var import_auto = require("fake-indexeddb/auto");
|
|
447
307
|
function setupBrowserEnvironment(options = {}) {
|
|
448
|
-
const {
|
|
308
|
+
const {
|
|
309
|
+
url = "http://localhost",
|
|
310
|
+
pretendToBeVisual = true,
|
|
311
|
+
resources = void 0,
|
|
312
|
+
enableWebGL2 = false,
|
|
313
|
+
enablePointerLock = false
|
|
314
|
+
} = options;
|
|
449
315
|
const dom = new import_jsdom.JSDOM("<!DOCTYPE html><html><head></head><body></body></html>", {
|
|
450
316
|
url,
|
|
451
|
-
pretendToBeVisual
|
|
317
|
+
pretendToBeVisual,
|
|
318
|
+
resources
|
|
452
319
|
});
|
|
453
320
|
global.window = dom.window;
|
|
454
321
|
global.document = dom.window.document;
|
|
455
|
-
|
|
322
|
+
try {
|
|
323
|
+
global.navigator = dom.window.navigator;
|
|
324
|
+
} catch (e) {
|
|
325
|
+
try {
|
|
326
|
+
Object.defineProperty(global, "navigator", {
|
|
327
|
+
value: dom.window.navigator,
|
|
328
|
+
writable: true,
|
|
329
|
+
configurable: true
|
|
330
|
+
});
|
|
331
|
+
} catch (e2) {
|
|
332
|
+
console.warn("Could not assign global.navigator, skipping.");
|
|
333
|
+
}
|
|
334
|
+
}
|
|
456
335
|
global.location = dom.window.location;
|
|
457
336
|
global.HTMLElement = dom.window.HTMLElement;
|
|
337
|
+
global.HTMLCanvasElement = dom.window.HTMLCanvasElement;
|
|
458
338
|
global.Event = dom.window.Event;
|
|
459
339
|
global.CustomEvent = dom.window.CustomEvent;
|
|
460
340
|
global.DragEvent = dom.window.DragEvent;
|
|
@@ -507,6 +387,9 @@ function setupBrowserEnvironment(options = {}) {
|
|
|
507
387
|
if (contextId === "2d") {
|
|
508
388
|
return napiCanvas.getContext("2d", options3);
|
|
509
389
|
}
|
|
390
|
+
if (enableWebGL2 && contextId === "webgl2") {
|
|
391
|
+
return createMockWebGL2Context(domCanvas);
|
|
392
|
+
}
|
|
510
393
|
if (contextId === "webgl" || contextId === "webgl2") {
|
|
511
394
|
return originalGetContext(contextId, options3);
|
|
512
395
|
}
|
|
@@ -517,6 +400,15 @@ function setupBrowserEnvironment(options = {}) {
|
|
|
517
400
|
}
|
|
518
401
|
return originalCreateElement(tagName, options2);
|
|
519
402
|
};
|
|
403
|
+
if (enableWebGL2) {
|
|
404
|
+
const originalProtoGetContext = global.HTMLCanvasElement.prototype.getContext;
|
|
405
|
+
global.HTMLCanvasElement.prototype.getContext = function(contextId, options2) {
|
|
406
|
+
if (contextId === "webgl2") {
|
|
407
|
+
return createMockWebGL2Context(this);
|
|
408
|
+
}
|
|
409
|
+
return originalProtoGetContext.call(this, contextId, options2);
|
|
410
|
+
};
|
|
411
|
+
}
|
|
520
412
|
global.Image = import_canvas.Image;
|
|
521
413
|
global.ImageData = import_canvas.ImageData;
|
|
522
414
|
if (typeof global.createImageBitmap === "undefined") {
|
|
@@ -543,6 +435,24 @@ function setupBrowserEnvironment(options = {}) {
|
|
|
543
435
|
return Buffer.from(str, "base64").toString("binary");
|
|
544
436
|
};
|
|
545
437
|
}
|
|
438
|
+
if (enablePointerLock) {
|
|
439
|
+
MockPointerLock.setup(global.document);
|
|
440
|
+
}
|
|
441
|
+
if (typeof global.requestAnimationFrame === "undefined") {
|
|
442
|
+
let lastTime = 0;
|
|
443
|
+
global.requestAnimationFrame = (callback) => {
|
|
444
|
+
const currTime = Date.now();
|
|
445
|
+
const timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
|
446
|
+
const id = setTimeout(() => {
|
|
447
|
+
callback(currTime + timeToCall);
|
|
448
|
+
}, timeToCall);
|
|
449
|
+
lastTime = currTime + timeToCall;
|
|
450
|
+
return id;
|
|
451
|
+
};
|
|
452
|
+
global.cancelAnimationFrame = (id) => {
|
|
453
|
+
clearTimeout(id);
|
|
454
|
+
};
|
|
455
|
+
}
|
|
546
456
|
}
|
|
547
457
|
function teardownBrowserEnvironment() {
|
|
548
458
|
delete global.window;
|
|
@@ -551,9 +461,100 @@ function teardownBrowserEnvironment() {
|
|
|
551
461
|
delete global.localStorage;
|
|
552
462
|
delete global.location;
|
|
553
463
|
delete global.HTMLElement;
|
|
464
|
+
delete global.HTMLCanvasElement;
|
|
554
465
|
delete global.Image;
|
|
555
466
|
delete global.ImageData;
|
|
556
467
|
delete global.createImageBitmap;
|
|
468
|
+
delete global.Event;
|
|
469
|
+
delete global.CustomEvent;
|
|
470
|
+
delete global.DragEvent;
|
|
471
|
+
delete global.MouseEvent;
|
|
472
|
+
delete global.KeyboardEvent;
|
|
473
|
+
delete global.FocusEvent;
|
|
474
|
+
delete global.WheelEvent;
|
|
475
|
+
delete global.InputEvent;
|
|
476
|
+
delete global.UIEvent;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// src/setup/canvas.ts
|
|
480
|
+
var import_canvas2 = require("@napi-rs/canvas");
|
|
481
|
+
function createMockCanvas(width = 300, height = 150) {
|
|
482
|
+
if (typeof document !== "undefined" && document.createElement) {
|
|
483
|
+
const canvas2 = document.createElement("canvas");
|
|
484
|
+
canvas2.width = width;
|
|
485
|
+
canvas2.height = height;
|
|
486
|
+
return canvas2;
|
|
487
|
+
}
|
|
488
|
+
const canvas = new import_canvas2.Canvas(width, height);
|
|
489
|
+
const originalGetContext = canvas.getContext.bind(canvas);
|
|
490
|
+
canvas.getContext = function(contextId, options) {
|
|
491
|
+
if (contextId === "webgl2") {
|
|
492
|
+
return createMockWebGL2Context(canvas);
|
|
493
|
+
}
|
|
494
|
+
if (contextId === "2d") {
|
|
495
|
+
return originalGetContext("2d", options);
|
|
496
|
+
}
|
|
497
|
+
return originalGetContext(contextId, options);
|
|
498
|
+
};
|
|
499
|
+
return canvas;
|
|
500
|
+
}
|
|
501
|
+
function createMockCanvasContext2D(canvas) {
|
|
502
|
+
if (!canvas) {
|
|
503
|
+
canvas = createMockCanvas();
|
|
504
|
+
}
|
|
505
|
+
return canvas.getContext("2d");
|
|
506
|
+
}
|
|
507
|
+
function captureCanvasDrawCalls(context) {
|
|
508
|
+
const drawCalls = [];
|
|
509
|
+
const methodsToSpy = [
|
|
510
|
+
"fillRect",
|
|
511
|
+
"strokeRect",
|
|
512
|
+
"clearRect",
|
|
513
|
+
"fillText",
|
|
514
|
+
"strokeText",
|
|
515
|
+
"drawImage",
|
|
516
|
+
"beginPath",
|
|
517
|
+
"closePath",
|
|
518
|
+
"moveTo",
|
|
519
|
+
"lineTo",
|
|
520
|
+
"arc",
|
|
521
|
+
"arcTo",
|
|
522
|
+
"bezierCurveTo",
|
|
523
|
+
"quadraticCurveTo",
|
|
524
|
+
"stroke",
|
|
525
|
+
"fill",
|
|
526
|
+
"putImageData"
|
|
527
|
+
];
|
|
528
|
+
methodsToSpy.forEach((method) => {
|
|
529
|
+
const original = context[method];
|
|
530
|
+
if (typeof original === "function") {
|
|
531
|
+
context[method] = function(...args) {
|
|
532
|
+
drawCalls.push({ method, args });
|
|
533
|
+
return original.apply(this, args);
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
return drawCalls;
|
|
538
|
+
}
|
|
539
|
+
function createMockImageData(width, height, fillColor) {
|
|
540
|
+
const imageData = new import_canvas2.ImageData(width, height);
|
|
541
|
+
if (fillColor) {
|
|
542
|
+
const [r, g, b, a] = fillColor;
|
|
543
|
+
for (let i = 0; i < imageData.data.length; i += 4) {
|
|
544
|
+
imageData.data[i] = r;
|
|
545
|
+
imageData.data[i + 1] = g;
|
|
546
|
+
imageData.data[i + 2] = b;
|
|
547
|
+
imageData.data[i + 3] = a;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return imageData;
|
|
551
|
+
}
|
|
552
|
+
function createMockImage(width, height, src) {
|
|
553
|
+
const img = new import_canvas2.Image();
|
|
554
|
+
if (width) img.width = width;
|
|
555
|
+
if (height) img.height = height;
|
|
556
|
+
if (src) img.src = src;
|
|
557
|
+
return img;
|
|
557
558
|
}
|
|
558
559
|
|
|
559
560
|
// src/setup/node.ts
|
|
@@ -562,237 +563,829 @@ function setupNodeEnvironment() {
|
|
|
562
563
|
}
|
|
563
564
|
}
|
|
564
565
|
|
|
565
|
-
// src/setup/
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
shaderSource: () => {
|
|
596
|
-
},
|
|
597
|
-
compileShader: () => {
|
|
566
|
+
// src/setup/storage.ts
|
|
567
|
+
var import_auto2 = require("fake-indexeddb/auto");
|
|
568
|
+
function createMockLocalStorage(initialData = {}) {
|
|
569
|
+
const storage = new Map(Object.entries(initialData));
|
|
570
|
+
return {
|
|
571
|
+
getItem: (key) => storage.get(key) || null,
|
|
572
|
+
setItem: (key, value) => storage.set(key, value),
|
|
573
|
+
removeItem: (key) => storage.delete(key),
|
|
574
|
+
clear: () => storage.clear(),
|
|
575
|
+
key: (index) => Array.from(storage.keys())[index] || null,
|
|
576
|
+
get length() {
|
|
577
|
+
return storage.size;
|
|
578
|
+
}
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
function createMockSessionStorage(initialData = {}) {
|
|
582
|
+
return createMockLocalStorage(initialData);
|
|
583
|
+
}
|
|
584
|
+
function createMockIndexedDB() {
|
|
585
|
+
if (typeof indexedDB === "undefined") {
|
|
586
|
+
throw new Error("IndexedDB mock not found. Ensure fake-indexeddb is loaded.");
|
|
587
|
+
}
|
|
588
|
+
return indexedDB;
|
|
589
|
+
}
|
|
590
|
+
function createStorageTestScenario(storageType = "local") {
|
|
591
|
+
const storage = storageType === "local" ? createMockLocalStorage() : createMockSessionStorage();
|
|
592
|
+
return {
|
|
593
|
+
storage,
|
|
594
|
+
populate(data) {
|
|
595
|
+
Object.entries(data).forEach(([k, v]) => storage.setItem(k, v));
|
|
598
596
|
},
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
597
|
+
verify(key, value) {
|
|
598
|
+
return storage.getItem(key) === value;
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/setup/audio.ts
|
|
604
|
+
function createMockAudioContext() {
|
|
605
|
+
return {
|
|
606
|
+
createGain: () => ({
|
|
607
|
+
connect: () => {
|
|
608
|
+
},
|
|
609
|
+
gain: { value: 1, setValueAtTime: () => {
|
|
610
|
+
} }
|
|
611
|
+
}),
|
|
612
|
+
createOscillator: () => ({
|
|
613
|
+
connect: () => {
|
|
614
|
+
},
|
|
615
|
+
start: () => {
|
|
616
|
+
},
|
|
617
|
+
stop: () => {
|
|
618
|
+
},
|
|
619
|
+
frequency: { value: 440 }
|
|
620
|
+
}),
|
|
621
|
+
createBufferSource: () => ({
|
|
622
|
+
connect: () => {
|
|
623
|
+
},
|
|
624
|
+
start: () => {
|
|
625
|
+
},
|
|
626
|
+
stop: () => {
|
|
627
|
+
},
|
|
628
|
+
buffer: null,
|
|
629
|
+
playbackRate: { value: 1 },
|
|
630
|
+
loop: false
|
|
631
|
+
}),
|
|
632
|
+
destination: {},
|
|
633
|
+
currentTime: 0,
|
|
634
|
+
state: "running",
|
|
635
|
+
resume: async () => {
|
|
602
636
|
},
|
|
603
|
-
|
|
604
|
-
createProgram: () => ({}),
|
|
605
|
-
attachShader: () => {
|
|
637
|
+
suspend: async () => {
|
|
606
638
|
},
|
|
607
|
-
|
|
639
|
+
close: async () => {
|
|
608
640
|
},
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
641
|
+
decodeAudioData: async (buffer) => ({
|
|
642
|
+
duration: 1,
|
|
643
|
+
length: 44100,
|
|
644
|
+
sampleRate: 44100,
|
|
645
|
+
numberOfChannels: 2,
|
|
646
|
+
getChannelData: () => new Float32Array(44100)
|
|
647
|
+
}),
|
|
648
|
+
createBuffer: (channels, length, sampleRate) => ({
|
|
649
|
+
duration: length / sampleRate,
|
|
650
|
+
length,
|
|
651
|
+
sampleRate,
|
|
652
|
+
numberOfChannels: channels,
|
|
653
|
+
getChannelData: () => new Float32Array(length)
|
|
654
|
+
})
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
function setupMockAudioContext() {
|
|
658
|
+
if (typeof global.AudioContext === "undefined" && typeof global.window !== "undefined") {
|
|
659
|
+
global.AudioContext = class {
|
|
660
|
+
constructor() {
|
|
661
|
+
return createMockAudioContext();
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
global.window.AudioContext = global.AudioContext;
|
|
665
|
+
global.window.webkitAudioContext = global.AudioContext;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
function teardownMockAudioContext() {
|
|
669
|
+
if (global.AudioContext && global.AudioContext.toString().includes("class")) {
|
|
670
|
+
delete global.AudioContext;
|
|
671
|
+
delete global.window.AudioContext;
|
|
672
|
+
delete global.window.webkitAudioContext;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
function captureAudioEvents(context) {
|
|
676
|
+
return [];
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// src/setup/timing.ts
|
|
680
|
+
var activeMockRAF;
|
|
681
|
+
function createMockRAF() {
|
|
682
|
+
let callbacks = [];
|
|
683
|
+
let nextId = 1;
|
|
684
|
+
let currentTime = 0;
|
|
685
|
+
const originalRAF = global.requestAnimationFrame;
|
|
686
|
+
const originalCancelRAF = global.cancelAnimationFrame;
|
|
687
|
+
const raf = (callback) => {
|
|
688
|
+
const id = nextId++;
|
|
689
|
+
callbacks.push({ id, callback });
|
|
690
|
+
return id;
|
|
691
|
+
};
|
|
692
|
+
const cancel = (id) => {
|
|
693
|
+
callbacks = callbacks.filter((cb) => cb.id !== id);
|
|
694
|
+
};
|
|
695
|
+
const mock = {
|
|
696
|
+
tick(timestamp) {
|
|
697
|
+
if (typeof timestamp !== "number") {
|
|
698
|
+
currentTime += 16.6;
|
|
699
|
+
} else {
|
|
700
|
+
currentTime = timestamp;
|
|
701
|
+
}
|
|
702
|
+
const currentCallbacks = [...callbacks];
|
|
703
|
+
callbacks = [];
|
|
704
|
+
currentCallbacks.forEach(({ callback }) => {
|
|
705
|
+
callback(currentTime);
|
|
706
|
+
});
|
|
612
707
|
},
|
|
613
|
-
|
|
614
|
-
|
|
708
|
+
advance(deltaMs = 16.6) {
|
|
709
|
+
this.tick(currentTime + deltaMs);
|
|
615
710
|
},
|
|
616
|
-
|
|
617
|
-
|
|
711
|
+
getCallbacks() {
|
|
712
|
+
return callbacks.map((c) => c.callback);
|
|
618
713
|
},
|
|
619
|
-
|
|
714
|
+
reset() {
|
|
715
|
+
callbacks = [];
|
|
716
|
+
nextId = 1;
|
|
717
|
+
currentTime = 0;
|
|
620
718
|
},
|
|
621
|
-
|
|
719
|
+
enable() {
|
|
720
|
+
activeMockRAF = this;
|
|
721
|
+
global.requestAnimationFrame = raf;
|
|
722
|
+
global.cancelAnimationFrame = cancel;
|
|
622
723
|
},
|
|
623
|
-
|
|
724
|
+
disable() {
|
|
725
|
+
if (activeMockRAF === this) {
|
|
726
|
+
activeMockRAF = void 0;
|
|
727
|
+
}
|
|
728
|
+
if (originalRAF) {
|
|
729
|
+
global.requestAnimationFrame = originalRAF;
|
|
730
|
+
} else {
|
|
731
|
+
delete global.requestAnimationFrame;
|
|
732
|
+
}
|
|
733
|
+
if (originalCancelRAF) {
|
|
734
|
+
global.cancelAnimationFrame = originalCancelRAF;
|
|
735
|
+
} else {
|
|
736
|
+
delete global.cancelAnimationFrame;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
};
|
|
740
|
+
return mock;
|
|
741
|
+
}
|
|
742
|
+
function createMockPerformance(startTime = 0) {
|
|
743
|
+
let currentTime = startTime;
|
|
744
|
+
const mockPerf = {
|
|
745
|
+
now: () => currentTime,
|
|
746
|
+
timeOrigin: startTime,
|
|
747
|
+
timing: {
|
|
748
|
+
navigationStart: startTime
|
|
624
749
|
},
|
|
625
|
-
|
|
750
|
+
clearMarks: () => {
|
|
626
751
|
},
|
|
627
|
-
|
|
752
|
+
clearMeasures: () => {
|
|
628
753
|
},
|
|
629
|
-
|
|
754
|
+
clearResourceTimings: () => {
|
|
630
755
|
},
|
|
631
|
-
|
|
756
|
+
getEntries: () => [],
|
|
757
|
+
getEntriesByName: () => [],
|
|
758
|
+
getEntriesByType: () => [],
|
|
759
|
+
mark: () => {
|
|
632
760
|
},
|
|
633
|
-
|
|
761
|
+
measure: () => {
|
|
634
762
|
},
|
|
635
|
-
|
|
763
|
+
setResourceTimingBufferSize: () => {
|
|
636
764
|
},
|
|
637
|
-
|
|
765
|
+
toJSON: () => ({}),
|
|
766
|
+
addEventListener: () => {
|
|
638
767
|
},
|
|
639
|
-
|
|
640
|
-
bindTexture: () => {
|
|
768
|
+
removeEventListener: () => {
|
|
641
769
|
},
|
|
642
|
-
|
|
770
|
+
dispatchEvent: () => true
|
|
771
|
+
};
|
|
772
|
+
mockPerf.advance = (deltaMs) => {
|
|
773
|
+
currentTime += deltaMs;
|
|
774
|
+
};
|
|
775
|
+
mockPerf.setTime = (time) => {
|
|
776
|
+
currentTime = time;
|
|
777
|
+
};
|
|
778
|
+
return mockPerf;
|
|
779
|
+
}
|
|
780
|
+
function createControlledTimer() {
|
|
781
|
+
let currentTime = 0;
|
|
782
|
+
let timers = [];
|
|
783
|
+
let nextId = 1;
|
|
784
|
+
const originalSetTimeout = global.setTimeout;
|
|
785
|
+
const originalClearTimeout = global.clearTimeout;
|
|
786
|
+
const originalSetInterval = global.setInterval;
|
|
787
|
+
const originalClearInterval = global.clearInterval;
|
|
788
|
+
const mockSetTimeout = (callback, delay = 0, ...args) => {
|
|
789
|
+
const id = nextId++;
|
|
790
|
+
timers.push({ id, callback, dueTime: currentTime + delay, args });
|
|
791
|
+
return id;
|
|
792
|
+
};
|
|
793
|
+
const mockClearTimeout = (id) => {
|
|
794
|
+
timers = timers.filter((t) => t.id !== id);
|
|
795
|
+
};
|
|
796
|
+
const mockSetInterval = (callback, delay = 0, ...args) => {
|
|
797
|
+
const id = nextId++;
|
|
798
|
+
timers.push({ id, callback, dueTime: currentTime + delay, interval: delay, args });
|
|
799
|
+
return id;
|
|
800
|
+
};
|
|
801
|
+
const mockClearInterval = (id) => {
|
|
802
|
+
timers = timers.filter((t) => t.id !== id);
|
|
803
|
+
};
|
|
804
|
+
global.setTimeout = mockSetTimeout;
|
|
805
|
+
global.clearTimeout = mockClearTimeout;
|
|
806
|
+
global.setInterval = mockSetInterval;
|
|
807
|
+
global.clearInterval = mockClearInterval;
|
|
808
|
+
return {
|
|
809
|
+
tick() {
|
|
810
|
+
this.advanceBy(0);
|
|
643
811
|
},
|
|
644
|
-
|
|
812
|
+
advanceBy(ms) {
|
|
813
|
+
const targetTime = currentTime + ms;
|
|
814
|
+
while (true) {
|
|
815
|
+
let earliest = null;
|
|
816
|
+
for (const t of timers) {
|
|
817
|
+
if (!earliest || t.dueTime < earliest.dueTime) {
|
|
818
|
+
earliest = t;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
if (!earliest || earliest.dueTime > targetTime) {
|
|
822
|
+
break;
|
|
823
|
+
}
|
|
824
|
+
currentTime = earliest.dueTime;
|
|
825
|
+
const { callback, args, interval, id } = earliest;
|
|
826
|
+
if (interval !== void 0) {
|
|
827
|
+
earliest.dueTime += interval;
|
|
828
|
+
if (interval === 0) earliest.dueTime += 1;
|
|
829
|
+
} else {
|
|
830
|
+
timers = timers.filter((t) => t.id !== id);
|
|
831
|
+
}
|
|
832
|
+
callback(...args);
|
|
833
|
+
}
|
|
834
|
+
currentTime = targetTime;
|
|
645
835
|
},
|
|
646
|
-
|
|
836
|
+
clear() {
|
|
837
|
+
timers = [];
|
|
647
838
|
},
|
|
648
|
-
|
|
839
|
+
restore() {
|
|
840
|
+
global.setTimeout = originalSetTimeout;
|
|
841
|
+
global.clearTimeout = originalClearTimeout;
|
|
842
|
+
global.setInterval = originalSetInterval;
|
|
843
|
+
global.clearInterval = originalClearInterval;
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
function simulateFrames(count, frameTimeMs = 16.6, callback) {
|
|
848
|
+
if (!activeMockRAF) {
|
|
849
|
+
throw new Error("simulateFrames requires an active MockRAF. Ensure createMockRAF().enable() is called.");
|
|
850
|
+
}
|
|
851
|
+
for (let i = 0; i < count; i++) {
|
|
852
|
+
if (callback) callback(i);
|
|
853
|
+
activeMockRAF.advance(frameTimeMs);
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
function simulateFramesWithMock(mock, count, frameTimeMs = 16.6, callback) {
|
|
857
|
+
for (let i = 0; i < count; i++) {
|
|
858
|
+
if (callback) callback(i);
|
|
859
|
+
mock.advance(frameTimeMs);
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// src/e2e/playwright.ts
|
|
864
|
+
var import_playwright = require("playwright");
|
|
865
|
+
var import_http = require("http");
|
|
866
|
+
var import_serve_handler = __toESM(require("serve-handler"), 1);
|
|
867
|
+
async function createPlaywrightTestClient(options = {}) {
|
|
868
|
+
let staticServer;
|
|
869
|
+
let clientUrl = options.clientUrl;
|
|
870
|
+
const rootPath = options.rootPath || process.cwd();
|
|
871
|
+
if (!clientUrl) {
|
|
872
|
+
staticServer = (0, import_http.createServer)((request, response) => {
|
|
873
|
+
return (0, import_serve_handler.default)(request, response, {
|
|
874
|
+
public: rootPath,
|
|
875
|
+
cleanUrls: false,
|
|
876
|
+
headers: [
|
|
877
|
+
{
|
|
878
|
+
source: "**/*",
|
|
879
|
+
headers: [
|
|
880
|
+
{ key: "Cache-Control", value: "no-cache" },
|
|
881
|
+
{ key: "Access-Control-Allow-Origin", value: "*" },
|
|
882
|
+
{ key: "Cross-Origin-Opener-Policy", value: "same-origin" },
|
|
883
|
+
{ key: "Cross-Origin-Embedder-Policy", value: "require-corp" }
|
|
884
|
+
]
|
|
885
|
+
}
|
|
886
|
+
]
|
|
887
|
+
});
|
|
888
|
+
});
|
|
889
|
+
await new Promise((resolve) => {
|
|
890
|
+
if (!staticServer) return;
|
|
891
|
+
staticServer.listen(0, () => {
|
|
892
|
+
const addr = staticServer?.address();
|
|
893
|
+
const port = typeof addr === "object" ? addr?.port : 0;
|
|
894
|
+
clientUrl = `http://localhost:${port}`;
|
|
895
|
+
console.log(`Test client serving from ${rootPath} at ${clientUrl}`);
|
|
896
|
+
resolve();
|
|
897
|
+
});
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
const browser = await import_playwright.chromium.launch({
|
|
901
|
+
headless: options.headless ?? true,
|
|
902
|
+
args: [
|
|
903
|
+
"--use-gl=egl",
|
|
904
|
+
"--ignore-gpu-blocklist",
|
|
905
|
+
...options.launchOptions?.args || []
|
|
906
|
+
],
|
|
907
|
+
...options.launchOptions
|
|
908
|
+
});
|
|
909
|
+
const width = options.width || 1280;
|
|
910
|
+
const height = options.height || 720;
|
|
911
|
+
const context = await browser.newContext({
|
|
912
|
+
viewport: { width, height },
|
|
913
|
+
deviceScaleFactor: 1,
|
|
914
|
+
...options.contextOptions
|
|
915
|
+
});
|
|
916
|
+
const page = await context.newPage();
|
|
917
|
+
const close = async () => {
|
|
918
|
+
await browser.close();
|
|
919
|
+
if (staticServer) {
|
|
920
|
+
staticServer.close();
|
|
921
|
+
}
|
|
922
|
+
};
|
|
923
|
+
const navigate = async (url) => {
|
|
924
|
+
const targetUrl = url || clientUrl;
|
|
925
|
+
if (!targetUrl) throw new Error("No URL to navigate to");
|
|
926
|
+
let finalUrl = targetUrl;
|
|
927
|
+
if (options.serverUrl && !targetUrl.includes("connect=")) {
|
|
928
|
+
const separator = targetUrl.includes("?") ? "&" : "?";
|
|
929
|
+
finalUrl = `${targetUrl}${separator}connect=${encodeURIComponent(options.serverUrl)}`;
|
|
930
|
+
}
|
|
931
|
+
console.log(`Navigating to: ${finalUrl}`);
|
|
932
|
+
await page.goto(finalUrl, { waitUntil: "domcontentloaded" });
|
|
933
|
+
};
|
|
934
|
+
return {
|
|
935
|
+
browser,
|
|
936
|
+
context,
|
|
937
|
+
page,
|
|
938
|
+
server: staticServer,
|
|
939
|
+
close,
|
|
940
|
+
navigate,
|
|
941
|
+
waitForGame: async (timeout = 1e4) => {
|
|
942
|
+
await waitForGameReady(page, timeout);
|
|
649
943
|
},
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
944
|
+
injectInput: async (type, data) => {
|
|
945
|
+
await page.evaluate(({ type: type2, data: data2 }) => {
|
|
946
|
+
if (window.injectGameInput) window.injectGameInput(type2, data2);
|
|
947
|
+
}, { type, data });
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
async function waitForGameReady(page, timeout = 1e4) {
|
|
952
|
+
try {
|
|
953
|
+
await page.waitForFunction(() => {
|
|
954
|
+
return window.gameInstance && window.gameInstance.isReady;
|
|
955
|
+
}, null, { timeout });
|
|
956
|
+
} catch (e) {
|
|
957
|
+
await page.waitForSelector("canvas", { timeout });
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
async function captureGameState(page) {
|
|
961
|
+
return await page.evaluate(() => {
|
|
962
|
+
if (window.gameInstance && window.gameInstance.getState) {
|
|
963
|
+
return window.gameInstance.getState();
|
|
964
|
+
}
|
|
965
|
+
return {};
|
|
966
|
+
});
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// src/shared/bsp.ts
|
|
970
|
+
var import_shared = require("@quake2ts/shared");
|
|
971
|
+
function makePlane(normal, dist) {
|
|
972
|
+
return {
|
|
973
|
+
normal,
|
|
974
|
+
dist,
|
|
975
|
+
type: Math.abs(normal.x) === 1 ? 0 : Math.abs(normal.y) === 1 ? 1 : Math.abs(normal.z) === 1 ? 2 : 3,
|
|
976
|
+
signbits: (0, import_shared.computePlaneSignBits)(normal)
|
|
977
|
+
};
|
|
978
|
+
}
|
|
979
|
+
function makeAxisBrush(size, contents = import_shared.CONTENTS_SOLID) {
|
|
980
|
+
const half = size / 2;
|
|
981
|
+
const planes = [
|
|
982
|
+
makePlane({ x: 1, y: 0, z: 0 }, half),
|
|
983
|
+
makePlane({ x: -1, y: 0, z: 0 }, half),
|
|
984
|
+
makePlane({ x: 0, y: 1, z: 0 }, half),
|
|
985
|
+
makePlane({ x: 0, y: -1, z: 0 }, half),
|
|
986
|
+
makePlane({ x: 0, y: 0, z: 1 }, half),
|
|
987
|
+
makePlane({ x: 0, y: 0, z: -1 }, half)
|
|
988
|
+
];
|
|
989
|
+
return {
|
|
990
|
+
contents,
|
|
991
|
+
sides: planes.map((plane) => ({ plane, surfaceFlags: 0 }))
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
function makeNode(plane, children) {
|
|
995
|
+
return { plane, children };
|
|
996
|
+
}
|
|
997
|
+
function makeBspModel(planes, nodes, leaves, brushes, leafBrushes) {
|
|
998
|
+
return {
|
|
999
|
+
planes,
|
|
1000
|
+
nodes,
|
|
1001
|
+
leaves,
|
|
1002
|
+
brushes,
|
|
1003
|
+
leafBrushes,
|
|
1004
|
+
bmodels: []
|
|
1005
|
+
};
|
|
1006
|
+
}
|
|
1007
|
+
function makeLeaf(contents, firstLeafBrush, numLeafBrushes) {
|
|
1008
|
+
return { contents, cluster: 0, area: 0, firstLeafBrush, numLeafBrushes };
|
|
1009
|
+
}
|
|
1010
|
+
function makeLeafModel(brushes) {
|
|
1011
|
+
const planes = brushes.flatMap((brush) => brush.sides.map((side) => side.plane));
|
|
1012
|
+
return {
|
|
1013
|
+
planes,
|
|
1014
|
+
nodes: [],
|
|
1015
|
+
leaves: [makeLeaf(0, 0, brushes.length)],
|
|
1016
|
+
brushes,
|
|
1017
|
+
leafBrushes: brushes.map((_, i) => i),
|
|
1018
|
+
bmodels: []
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
function makeBrushFromMinsMaxs(mins, maxs, contents = import_shared.CONTENTS_SOLID) {
|
|
1022
|
+
const planes = [
|
|
1023
|
+
makePlane({ x: 1, y: 0, z: 0 }, maxs.x),
|
|
1024
|
+
makePlane({ x: -1, y: 0, z: 0 }, -mins.x),
|
|
1025
|
+
makePlane({ x: 0, y: 1, z: 0 }, maxs.y),
|
|
1026
|
+
makePlane({ x: 0, y: -1, z: 0 }, -mins.y),
|
|
1027
|
+
makePlane({ x: 0, y: 0, z: 1 }, maxs.z),
|
|
1028
|
+
makePlane({ x: 0, y: 0, z: -1 }, -mins.z)
|
|
1029
|
+
];
|
|
1030
|
+
return {
|
|
1031
|
+
contents,
|
|
1032
|
+
sides: planes.map((plane) => ({ plane, surfaceFlags: 0 }))
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// src/shared/mocks.ts
|
|
1037
|
+
var import_vitest = require("vitest");
|
|
1038
|
+
var createBinaryWriterMock = () => ({
|
|
1039
|
+
writeByte: import_vitest.vi.fn(),
|
|
1040
|
+
writeShort: import_vitest.vi.fn(),
|
|
1041
|
+
writeLong: import_vitest.vi.fn(),
|
|
1042
|
+
writeString: import_vitest.vi.fn(),
|
|
1043
|
+
writeBytes: import_vitest.vi.fn(),
|
|
1044
|
+
getBuffer: import_vitest.vi.fn(() => new Uint8Array(0)),
|
|
1045
|
+
reset: import_vitest.vi.fn(),
|
|
1046
|
+
// Legacy methods (if any)
|
|
1047
|
+
writeInt8: import_vitest.vi.fn(),
|
|
1048
|
+
writeUint8: import_vitest.vi.fn(),
|
|
1049
|
+
writeInt16: import_vitest.vi.fn(),
|
|
1050
|
+
writeUint16: import_vitest.vi.fn(),
|
|
1051
|
+
writeInt32: import_vitest.vi.fn(),
|
|
1052
|
+
writeUint32: import_vitest.vi.fn(),
|
|
1053
|
+
writeFloat: import_vitest.vi.fn(),
|
|
1054
|
+
getData: import_vitest.vi.fn(() => new Uint8Array(0))
|
|
1055
|
+
});
|
|
1056
|
+
var createNetChanMock = () => ({
|
|
1057
|
+
qport: 1234,
|
|
1058
|
+
// Sequencing
|
|
1059
|
+
incomingSequence: 0,
|
|
1060
|
+
outgoingSequence: 0,
|
|
1061
|
+
incomingAcknowledged: 0,
|
|
1062
|
+
// Reliable messaging
|
|
1063
|
+
incomingReliableAcknowledged: false,
|
|
1064
|
+
incomingReliableSequence: 0,
|
|
1065
|
+
outgoingReliableSequence: 0,
|
|
1066
|
+
reliableMessage: createBinaryWriterMock(),
|
|
1067
|
+
reliableLength: 0,
|
|
1068
|
+
// Fragmentation
|
|
1069
|
+
fragmentSendOffset: 0,
|
|
1070
|
+
fragmentBuffer: null,
|
|
1071
|
+
fragmentLength: 0,
|
|
1072
|
+
fragmentReceived: 0,
|
|
1073
|
+
// Timing
|
|
1074
|
+
lastReceived: 0,
|
|
1075
|
+
lastSent: 0,
|
|
1076
|
+
remoteAddress: { type: "IP", port: 1234 },
|
|
1077
|
+
// Methods
|
|
1078
|
+
setup: import_vitest.vi.fn(),
|
|
1079
|
+
reset: import_vitest.vi.fn(),
|
|
1080
|
+
transmit: import_vitest.vi.fn(),
|
|
1081
|
+
process: import_vitest.vi.fn(),
|
|
1082
|
+
canSendReliable: import_vitest.vi.fn(() => true),
|
|
1083
|
+
writeReliableByte: import_vitest.vi.fn(),
|
|
1084
|
+
writeReliableShort: import_vitest.vi.fn(),
|
|
1085
|
+
writeReliableLong: import_vitest.vi.fn(),
|
|
1086
|
+
writeReliableString: import_vitest.vi.fn(),
|
|
1087
|
+
getReliableData: import_vitest.vi.fn(() => new Uint8Array(0)),
|
|
1088
|
+
needsKeepalive: import_vitest.vi.fn(() => false),
|
|
1089
|
+
isTimedOut: import_vitest.vi.fn(() => false)
|
|
1090
|
+
});
|
|
1091
|
+
var createBinaryStreamMock = () => ({
|
|
1092
|
+
getPosition: import_vitest.vi.fn(() => 0),
|
|
1093
|
+
getReadPosition: import_vitest.vi.fn(() => 0),
|
|
1094
|
+
getLength: import_vitest.vi.fn(() => 0),
|
|
1095
|
+
getRemaining: import_vitest.vi.fn(() => 0),
|
|
1096
|
+
seek: import_vitest.vi.fn(),
|
|
1097
|
+
setReadPosition: import_vitest.vi.fn(),
|
|
1098
|
+
hasMore: import_vitest.vi.fn(() => true),
|
|
1099
|
+
hasBytes: import_vitest.vi.fn((amount) => true),
|
|
1100
|
+
readChar: import_vitest.vi.fn(() => 0),
|
|
1101
|
+
readByte: import_vitest.vi.fn(() => 0),
|
|
1102
|
+
readShort: import_vitest.vi.fn(() => 0),
|
|
1103
|
+
readUShort: import_vitest.vi.fn(() => 0),
|
|
1104
|
+
readLong: import_vitest.vi.fn(() => 0),
|
|
1105
|
+
readULong: import_vitest.vi.fn(() => 0),
|
|
1106
|
+
readFloat: import_vitest.vi.fn(() => 0),
|
|
1107
|
+
readString: import_vitest.vi.fn(() => ""),
|
|
1108
|
+
readStringLine: import_vitest.vi.fn(() => ""),
|
|
1109
|
+
readCoord: import_vitest.vi.fn(() => 0),
|
|
1110
|
+
readAngle: import_vitest.vi.fn(() => 0),
|
|
1111
|
+
readAngle16: import_vitest.vi.fn(() => 0),
|
|
1112
|
+
readData: import_vitest.vi.fn((length) => new Uint8Array(length)),
|
|
1113
|
+
readPos: import_vitest.vi.fn(),
|
|
1114
|
+
readDir: import_vitest.vi.fn()
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
// src/game/factories.ts
|
|
1118
|
+
var createPlayerStateFactory = (overrides) => ({
|
|
1119
|
+
pm_type: 0,
|
|
1120
|
+
pm_time: 0,
|
|
1121
|
+
pm_flags: 0,
|
|
1122
|
+
origin: { x: 0, y: 0, z: 0 },
|
|
1123
|
+
velocity: { x: 0, y: 0, z: 0 },
|
|
1124
|
+
viewAngles: { x: 0, y: 0, z: 0 },
|
|
1125
|
+
onGround: false,
|
|
1126
|
+
waterLevel: 0,
|
|
1127
|
+
watertype: 0,
|
|
1128
|
+
mins: { x: 0, y: 0, z: 0 },
|
|
1129
|
+
maxs: { x: 0, y: 0, z: 0 },
|
|
1130
|
+
damageAlpha: 0,
|
|
1131
|
+
damageIndicators: [],
|
|
1132
|
+
blend: [0, 0, 0, 0],
|
|
1133
|
+
stats: [],
|
|
1134
|
+
kick_angles: { x: 0, y: 0, z: 0 },
|
|
1135
|
+
kick_origin: { x: 0, y: 0, z: 0 },
|
|
1136
|
+
gunoffset: { x: 0, y: 0, z: 0 },
|
|
1137
|
+
gunangles: { x: 0, y: 0, z: 0 },
|
|
1138
|
+
gunindex: 0,
|
|
1139
|
+
gun_frame: 0,
|
|
1140
|
+
rdflags: 0,
|
|
1141
|
+
fov: 90,
|
|
1142
|
+
renderfx: 0,
|
|
1143
|
+
...overrides
|
|
1144
|
+
});
|
|
1145
|
+
var createEntityStateFactory = (overrides) => ({
|
|
1146
|
+
number: 0,
|
|
1147
|
+
origin: { x: 0, y: 0, z: 0 },
|
|
1148
|
+
angles: { x: 0, y: 0, z: 0 },
|
|
1149
|
+
oldOrigin: { x: 0, y: 0, z: 0 },
|
|
1150
|
+
modelIndex: 0,
|
|
1151
|
+
modelIndex2: 0,
|
|
1152
|
+
modelIndex3: 0,
|
|
1153
|
+
modelIndex4: 0,
|
|
1154
|
+
frame: 0,
|
|
1155
|
+
skinNum: 0,
|
|
1156
|
+
effects: 0,
|
|
1157
|
+
renderfx: 0,
|
|
1158
|
+
solid: 0,
|
|
1159
|
+
sound: 0,
|
|
1160
|
+
event: 0,
|
|
1161
|
+
...overrides
|
|
1162
|
+
});
|
|
1163
|
+
var createGameStateSnapshotFactory = (overrides) => ({
|
|
1164
|
+
gravity: { x: 0, y: 0, z: -800 },
|
|
1165
|
+
origin: { x: 0, y: 0, z: 0 },
|
|
1166
|
+
velocity: { x: 0, y: 0, z: 0 },
|
|
1167
|
+
viewangles: { x: 0, y: 0, z: 0 },
|
|
1168
|
+
level: { timeSeconds: 0, frameNumber: 0, previousTimeSeconds: 0, deltaSeconds: 0.1 },
|
|
1169
|
+
entities: {
|
|
1170
|
+
activeCount: 0,
|
|
1171
|
+
worldClassname: "worldspawn"
|
|
1172
|
+
},
|
|
1173
|
+
packetEntities: [],
|
|
1174
|
+
pmFlags: 0,
|
|
1175
|
+
pmType: 0,
|
|
1176
|
+
waterlevel: 0,
|
|
1177
|
+
watertype: 0,
|
|
1178
|
+
deltaAngles: { x: 0, y: 0, z: 0 },
|
|
1179
|
+
health: 100,
|
|
1180
|
+
armor: 0,
|
|
1181
|
+
ammo: 0,
|
|
1182
|
+
blend: [0, 0, 0, 0],
|
|
1183
|
+
damageAlpha: 0,
|
|
1184
|
+
damageIndicators: [],
|
|
1185
|
+
stats: [],
|
|
1186
|
+
kick_angles: { x: 0, y: 0, z: 0 },
|
|
1187
|
+
kick_origin: { x: 0, y: 0, z: 0 },
|
|
1188
|
+
gunoffset: { x: 0, y: 0, z: 0 },
|
|
1189
|
+
gunangles: { x: 0, y: 0, z: 0 },
|
|
1190
|
+
gunindex: 0,
|
|
1191
|
+
pm_time: 0,
|
|
1192
|
+
gun_frame: 0,
|
|
1193
|
+
rdflags: 0,
|
|
1194
|
+
fov: 90,
|
|
1195
|
+
renderfx: 0,
|
|
1196
|
+
pm_flags: 0,
|
|
1197
|
+
pm_type: 0,
|
|
1198
|
+
...overrides
|
|
1199
|
+
});
|
|
1200
|
+
|
|
1201
|
+
// src/game/helpers.ts
|
|
1202
|
+
var import_vitest2 = require("vitest");
|
|
1203
|
+
var import_game = require("@quake2ts/game");
|
|
1204
|
+
var import_shared2 = require("@quake2ts/shared");
|
|
1205
|
+
var import_shared3 = require("@quake2ts/shared");
|
|
1206
|
+
var createMockEngine = () => ({
|
|
1207
|
+
sound: import_vitest2.vi.fn(),
|
|
1208
|
+
soundIndex: import_vitest2.vi.fn((sound) => 0),
|
|
1209
|
+
modelIndex: import_vitest2.vi.fn((model) => 0),
|
|
1210
|
+
centerprintf: import_vitest2.vi.fn()
|
|
1211
|
+
});
|
|
1212
|
+
var createMockGame = (seed = 12345) => {
|
|
1213
|
+
const spawnRegistry = new import_game.SpawnRegistry();
|
|
1214
|
+
const hooks = new import_game.ScriptHookRegistry();
|
|
1215
|
+
const game = {
|
|
1216
|
+
random: (0, import_shared2.createRandomGenerator)({ seed }),
|
|
1217
|
+
registerEntitySpawn: import_vitest2.vi.fn((classname, spawnFunc) => {
|
|
1218
|
+
spawnRegistry.register(classname, (entity) => spawnFunc(entity));
|
|
1219
|
+
}),
|
|
1220
|
+
unregisterEntitySpawn: import_vitest2.vi.fn((classname) => {
|
|
1221
|
+
spawnRegistry.unregister(classname);
|
|
1222
|
+
}),
|
|
1223
|
+
getCustomEntities: import_vitest2.vi.fn(() => Array.from(spawnRegistry.keys())),
|
|
1224
|
+
hooks,
|
|
1225
|
+
registerHooks: import_vitest2.vi.fn((newHooks) => hooks.register(newHooks)),
|
|
1226
|
+
spawnWorld: import_vitest2.vi.fn(() => {
|
|
1227
|
+
hooks.onMapLoad("q2dm1");
|
|
1228
|
+
}),
|
|
1229
|
+
clientBegin: import_vitest2.vi.fn((client) => {
|
|
1230
|
+
hooks.onPlayerSpawn({});
|
|
1231
|
+
}),
|
|
1232
|
+
damage: import_vitest2.vi.fn((amount) => {
|
|
1233
|
+
hooks.onDamage({}, null, null, amount, 0, 0);
|
|
1234
|
+
})
|
|
1235
|
+
};
|
|
1236
|
+
return { game, spawnRegistry };
|
|
1237
|
+
};
|
|
1238
|
+
function createTestContext(options) {
|
|
1239
|
+
const engine = createMockEngine();
|
|
1240
|
+
const seed = options?.seed ?? 12345;
|
|
1241
|
+
const { game, spawnRegistry } = createMockGame(seed);
|
|
1242
|
+
const traceFn = import_vitest2.vi.fn((start, end, mins, maxs) => ({
|
|
1243
|
+
fraction: 1,
|
|
1244
|
+
ent: null,
|
|
1245
|
+
allsolid: false,
|
|
1246
|
+
startsolid: false,
|
|
1247
|
+
endpos: end,
|
|
1248
|
+
plane: { normal: { x: 0, y: 0, z: 1 }, dist: 0 },
|
|
1249
|
+
surfaceFlags: 0,
|
|
1250
|
+
contents: 0
|
|
1251
|
+
}));
|
|
1252
|
+
const entityList = options?.initialEntities ? [...options.initialEntities] : [];
|
|
1253
|
+
const hooks = game.hooks;
|
|
1254
|
+
const entities = {
|
|
1255
|
+
spawn: import_vitest2.vi.fn(() => {
|
|
1256
|
+
const ent = new import_game.Entity(entityList.length + 1);
|
|
1257
|
+
entityList.push(ent);
|
|
1258
|
+
hooks.onEntitySpawn(ent);
|
|
1259
|
+
return ent;
|
|
1260
|
+
}),
|
|
1261
|
+
free: import_vitest2.vi.fn((ent) => {
|
|
1262
|
+
const idx = entityList.indexOf(ent);
|
|
1263
|
+
if (idx !== -1) {
|
|
1264
|
+
entityList.splice(idx, 1);
|
|
1265
|
+
}
|
|
1266
|
+
hooks.onEntityRemove(ent);
|
|
1267
|
+
}),
|
|
1268
|
+
finalizeSpawn: import_vitest2.vi.fn(),
|
|
1269
|
+
freeImmediate: import_vitest2.vi.fn((ent) => {
|
|
1270
|
+
const idx = entityList.indexOf(ent);
|
|
1271
|
+
if (idx !== -1) {
|
|
1272
|
+
entityList.splice(idx, 1);
|
|
1273
|
+
}
|
|
1274
|
+
}),
|
|
1275
|
+
setSpawnRegistry: import_vitest2.vi.fn(),
|
|
1276
|
+
timeSeconds: 10,
|
|
1277
|
+
deltaSeconds: 0.1,
|
|
1278
|
+
modelIndex: import_vitest2.vi.fn(() => 0),
|
|
1279
|
+
scheduleThink: import_vitest2.vi.fn((entity, time) => {
|
|
1280
|
+
entity.nextthink = time;
|
|
1281
|
+
}),
|
|
1282
|
+
linkentity: import_vitest2.vi.fn(),
|
|
1283
|
+
trace: traceFn,
|
|
1284
|
+
pointcontents: import_vitest2.vi.fn(() => 0),
|
|
1285
|
+
multicast: import_vitest2.vi.fn(),
|
|
1286
|
+
unicast: import_vitest2.vi.fn(),
|
|
1287
|
+
engine,
|
|
1288
|
+
game,
|
|
1289
|
+
sound: import_vitest2.vi.fn((ent, chan, sound, vol, attn, timeofs) => {
|
|
1290
|
+
engine.sound(ent, chan, sound, vol, attn, timeofs);
|
|
1291
|
+
}),
|
|
1292
|
+
soundIndex: import_vitest2.vi.fn((sound) => engine.soundIndex(sound)),
|
|
1293
|
+
useTargets: import_vitest2.vi.fn((entity, activator) => {
|
|
1294
|
+
}),
|
|
1295
|
+
findByTargetName: import_vitest2.vi.fn(() => []),
|
|
1296
|
+
pickTarget: import_vitest2.vi.fn(() => null),
|
|
1297
|
+
killBox: import_vitest2.vi.fn(),
|
|
1298
|
+
rng: (0, import_shared2.createRandomGenerator)({ seed }),
|
|
1299
|
+
imports: {
|
|
1300
|
+
configstring: import_vitest2.vi.fn(),
|
|
1301
|
+
trace: traceFn,
|
|
1302
|
+
pointcontents: import_vitest2.vi.fn(() => 0)
|
|
678
1303
|
},
|
|
679
|
-
|
|
680
|
-
|
|
1304
|
+
level: {
|
|
1305
|
+
intermission_angle: { x: 0, y: 0, z: 0 },
|
|
1306
|
+
intermission_origin: { x: 0, y: 0, z: 0 },
|
|
1307
|
+
next_auto_save: 0,
|
|
1308
|
+
health_bar_entities: null
|
|
681
1309
|
},
|
|
682
|
-
|
|
1310
|
+
targetNameIndex: /* @__PURE__ */ new Map(),
|
|
1311
|
+
forEachEntity: import_vitest2.vi.fn((callback) => {
|
|
1312
|
+
entityList.forEach(callback);
|
|
1313
|
+
}),
|
|
1314
|
+
find: import_vitest2.vi.fn((predicate) => {
|
|
1315
|
+
return entityList.find(predicate);
|
|
1316
|
+
}),
|
|
1317
|
+
findByClassname: import_vitest2.vi.fn((classname) => {
|
|
1318
|
+
return entityList.find((e) => e.classname === classname);
|
|
1319
|
+
}),
|
|
1320
|
+
beginFrame: import_vitest2.vi.fn((timeSeconds) => {
|
|
1321
|
+
entities.timeSeconds = timeSeconds;
|
|
1322
|
+
}),
|
|
1323
|
+
targetAwareness: {
|
|
1324
|
+
timeSeconds: 10,
|
|
1325
|
+
frameNumber: 1,
|
|
1326
|
+
sightEntity: null,
|
|
1327
|
+
soundEntity: null
|
|
683
1328
|
},
|
|
684
|
-
|
|
1329
|
+
// Adding missing properties to satisfy EntitySystem interface partially or fully
|
|
1330
|
+
// We cast to unknown first anyway, but filling these in makes it safer for consumers
|
|
1331
|
+
skill: 1,
|
|
1332
|
+
deathmatch: false,
|
|
1333
|
+
coop: false,
|
|
1334
|
+
activeCount: entityList.length,
|
|
1335
|
+
world: entityList.find((e) => e.classname === "worldspawn") || new import_game.Entity(0)
|
|
1336
|
+
// ... other EntitySystem properties would go here
|
|
1337
|
+
};
|
|
1338
|
+
return {
|
|
1339
|
+
keyValues: {},
|
|
1340
|
+
entities,
|
|
1341
|
+
game,
|
|
1342
|
+
engine,
|
|
1343
|
+
health_multiplier: 1,
|
|
1344
|
+
warn: import_vitest2.vi.fn(),
|
|
1345
|
+
free: import_vitest2.vi.fn(),
|
|
1346
|
+
// Mock precache functions if they are part of SpawnContext in future or TestContext extensions
|
|
1347
|
+
precacheModel: import_vitest2.vi.fn(),
|
|
1348
|
+
precacheSound: import_vitest2.vi.fn(),
|
|
1349
|
+
precacheImage: import_vitest2.vi.fn()
|
|
685
1350
|
};
|
|
686
|
-
return gl;
|
|
687
1351
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
get: () => _pointerLockElement,
|
|
695
|
-
configurable: true
|
|
696
|
-
});
|
|
697
|
-
doc.exitPointerLock = () => {
|
|
698
|
-
if (_pointerLockElement) {
|
|
699
|
-
_pointerLockElement = null;
|
|
700
|
-
doc.dispatchEvent(new Event("pointerlockchange"));
|
|
701
|
-
}
|
|
702
|
-
};
|
|
703
|
-
global.HTMLElement.prototype.requestPointerLock = function() {
|
|
704
|
-
_pointerLockElement = this;
|
|
705
|
-
doc.dispatchEvent(new Event("pointerlockchange"));
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
};
|
|
709
|
-
var InputInjector = class {
|
|
710
|
-
constructor(doc, win) {
|
|
711
|
-
this.doc = doc;
|
|
712
|
-
this.win = win;
|
|
713
|
-
}
|
|
714
|
-
keyDown(key, code) {
|
|
715
|
-
const event = new this.win.KeyboardEvent("keydown", {
|
|
716
|
-
key,
|
|
717
|
-
code: code || key,
|
|
718
|
-
bubbles: true,
|
|
719
|
-
cancelable: true,
|
|
720
|
-
view: this.win
|
|
721
|
-
});
|
|
722
|
-
this.doc.dispatchEvent(event);
|
|
723
|
-
}
|
|
724
|
-
keyUp(key, code) {
|
|
725
|
-
const event = new this.win.KeyboardEvent("keyup", {
|
|
726
|
-
key,
|
|
727
|
-
code: code || key,
|
|
728
|
-
bubbles: true,
|
|
729
|
-
cancelable: true,
|
|
730
|
-
view: this.win
|
|
731
|
-
});
|
|
732
|
-
this.doc.dispatchEvent(event);
|
|
733
|
-
}
|
|
734
|
-
mouseMove(movementX, movementY, clientX = 0, clientY = 0) {
|
|
735
|
-
const event = new this.win.MouseEvent("mousemove", {
|
|
736
|
-
bubbles: true,
|
|
737
|
-
cancelable: true,
|
|
738
|
-
view: this.win,
|
|
739
|
-
clientX,
|
|
740
|
-
clientY,
|
|
741
|
-
movementX,
|
|
742
|
-
// Note: JSDOM might not support this standard property fully on event init
|
|
743
|
-
movementY
|
|
744
|
-
});
|
|
745
|
-
Object.defineProperty(event, "movementX", { value: movementX });
|
|
746
|
-
Object.defineProperty(event, "movementY", { value: movementY });
|
|
747
|
-
const target = this.doc.pointerLockElement || this.doc;
|
|
748
|
-
target.dispatchEvent(event);
|
|
749
|
-
}
|
|
750
|
-
mouseDown(button = 0) {
|
|
751
|
-
const event = new this.win.MouseEvent("mousedown", {
|
|
752
|
-
button,
|
|
753
|
-
bubbles: true,
|
|
754
|
-
cancelable: true,
|
|
755
|
-
view: this.win
|
|
756
|
-
});
|
|
757
|
-
const target = this.doc.pointerLockElement || this.doc;
|
|
758
|
-
target.dispatchEvent(event);
|
|
759
|
-
}
|
|
760
|
-
mouseUp(button = 0) {
|
|
761
|
-
const event = new this.win.MouseEvent("mouseup", {
|
|
762
|
-
button,
|
|
763
|
-
bubbles: true,
|
|
764
|
-
cancelable: true,
|
|
765
|
-
view: this.win
|
|
766
|
-
});
|
|
767
|
-
const target = this.doc.pointerLockElement || this.doc;
|
|
768
|
-
target.dispatchEvent(event);
|
|
769
|
-
}
|
|
770
|
-
wheel(deltaY) {
|
|
771
|
-
const event = new this.win.WheelEvent("wheel", {
|
|
772
|
-
deltaY,
|
|
773
|
-
bubbles: true,
|
|
774
|
-
cancelable: true,
|
|
775
|
-
view: this.win
|
|
776
|
-
});
|
|
777
|
-
const target = this.doc.pointerLockElement || this.doc;
|
|
778
|
-
target.dispatchEvent(event);
|
|
779
|
-
}
|
|
780
|
-
};
|
|
1352
|
+
function createSpawnContext() {
|
|
1353
|
+
return createTestContext();
|
|
1354
|
+
}
|
|
1355
|
+
function createEntity() {
|
|
1356
|
+
return new import_game.Entity(1);
|
|
1357
|
+
}
|
|
781
1358
|
// Annotate the CommonJS export names for ESM import in node:
|
|
782
1359
|
0 && (module.exports = {
|
|
783
1360
|
InputInjector,
|
|
784
1361
|
MockPointerLock,
|
|
1362
|
+
captureAudioEvents,
|
|
1363
|
+
captureCanvasDrawCalls,
|
|
1364
|
+
captureGameState,
|
|
785
1365
|
createBinaryStreamMock,
|
|
786
1366
|
createBinaryWriterMock,
|
|
1367
|
+
createControlledTimer,
|
|
787
1368
|
createEntity,
|
|
788
1369
|
createEntityStateFactory,
|
|
789
1370
|
createGameStateSnapshotFactory,
|
|
1371
|
+
createMockAudioContext,
|
|
1372
|
+
createMockCanvas,
|
|
1373
|
+
createMockCanvasContext2D,
|
|
790
1374
|
createMockEngine,
|
|
791
1375
|
createMockGame,
|
|
1376
|
+
createMockImage,
|
|
1377
|
+
createMockImageData,
|
|
1378
|
+
createMockIndexedDB,
|
|
1379
|
+
createMockLocalStorage,
|
|
1380
|
+
createMockPerformance,
|
|
1381
|
+
createMockRAF,
|
|
1382
|
+
createMockSessionStorage,
|
|
792
1383
|
createMockWebGL2Context,
|
|
793
1384
|
createNetChanMock,
|
|
794
1385
|
createPlayerStateFactory,
|
|
1386
|
+
createPlaywrightTestClient,
|
|
795
1387
|
createSpawnContext,
|
|
1388
|
+
createStorageTestScenario,
|
|
796
1389
|
createTestContext,
|
|
797
1390
|
intersects,
|
|
798
1391
|
ladderTrace,
|
|
@@ -804,8 +1397,13 @@ var InputInjector = class {
|
|
|
804
1397
|
makeNode,
|
|
805
1398
|
makePlane,
|
|
806
1399
|
setupBrowserEnvironment,
|
|
1400
|
+
setupMockAudioContext,
|
|
807
1401
|
setupNodeEnvironment,
|
|
1402
|
+
simulateFrames,
|
|
1403
|
+
simulateFramesWithMock,
|
|
808
1404
|
stairTrace,
|
|
809
|
-
teardownBrowserEnvironment
|
|
1405
|
+
teardownBrowserEnvironment,
|
|
1406
|
+
teardownMockAudioContext,
|
|
1407
|
+
waitForGameReady
|
|
810
1408
|
});
|
|
811
1409
|
//# sourceMappingURL=index.cjs.map
|