quake2ts 0.0.581 → 0.0.584
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 +1 -1
- package/packages/test-utils/dist/index.cjs +357 -538
- package/packages/test-utils/dist/index.cjs.map +1 -1
- package/packages/test-utils/dist/index.d.cts +153 -108
- package/packages/test-utils/dist/index.d.ts +153 -108
- package/packages/test-utils/dist/index.js +356 -537
- package/packages/test-utils/dist/index.js.map +1 -1
|
@@ -56,7 +56,6 @@ __export(index_exports, {
|
|
|
56
56
|
createMockAI: () => createMockAI,
|
|
57
57
|
createMockAmmoItem: () => createMockAmmoItem,
|
|
58
58
|
createMockArmorItem: () => createMockArmorItem,
|
|
59
|
-
createMockAudioContext: () => createMockAudioContext,
|
|
60
59
|
createMockCamera: () => createMockCamera,
|
|
61
60
|
createMockCanvas: () => createMockCanvas,
|
|
62
61
|
createMockCanvasContext2D: () => createMockCanvasContext2D,
|
|
@@ -92,6 +91,7 @@ __export(index_exports, {
|
|
|
92
91
|
createMockRConClient: () => createMockRConClient,
|
|
93
92
|
createMockRateLimiter: () => createMockRateLimiter,
|
|
94
93
|
createMockRefDef: () => createMockRefDef,
|
|
94
|
+
createMockRenderingContext: () => createMockRenderingContext,
|
|
95
95
|
createMockServer: () => createMockServer,
|
|
96
96
|
createMockServerClient: () => createMockServerClient,
|
|
97
97
|
createMockServerConsole: () => createMockServerConsole,
|
|
@@ -141,7 +141,6 @@ __export(index_exports, {
|
|
|
141
141
|
simulateBandwidthLimit: () => simulateBandwidthLimit,
|
|
142
142
|
simulateCameraMovement: () => simulateCameraMovement,
|
|
143
143
|
simulateFrames: () => simulateFrames,
|
|
144
|
-
simulateFramesWithMock: () => simulateFramesWithMock,
|
|
145
144
|
simulateHandshake: () => simulateHandshake,
|
|
146
145
|
simulateNetworkCondition: () => simulateNetworkCondition,
|
|
147
146
|
simulatePlayerInput: () => simulatePlayerInput,
|
|
@@ -154,6 +153,7 @@ __export(index_exports, {
|
|
|
154
153
|
stairTrace: () => import_shared3.stairTrace,
|
|
155
154
|
teardownBrowserEnvironment: () => teardownBrowserEnvironment,
|
|
156
155
|
teardownMockAudioContext: () => teardownMockAudioContext,
|
|
156
|
+
teardownNodeEnvironment: () => teardownNodeEnvironment,
|
|
157
157
|
throttleBandwidth: () => throttleBandwidth,
|
|
158
158
|
verifySnapshotConsistency: () => verifySnapshotConsistency,
|
|
159
159
|
waitForGameReady: () => waitForGameReady
|
|
@@ -2056,75 +2056,92 @@ function createMockCanvas(width = 300, height = 150) {
|
|
|
2056
2056
|
canvas2.height = height;
|
|
2057
2057
|
return canvas2;
|
|
2058
2058
|
}
|
|
2059
|
-
const
|
|
2060
|
-
const
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2059
|
+
const napiCanvas = new import_canvas2.Canvas(width, height);
|
|
2060
|
+
const canvas = {
|
|
2061
|
+
width,
|
|
2062
|
+
height,
|
|
2063
|
+
getContext: (contextId, options) => {
|
|
2064
|
+
if (contextId === "2d") {
|
|
2065
|
+
return napiCanvas.getContext("2d", options);
|
|
2066
|
+
}
|
|
2067
|
+
if (contextId === "webgl2") {
|
|
2068
|
+
return createMockWebGL2Context(canvas);
|
|
2069
|
+
}
|
|
2070
|
+
return null;
|
|
2071
|
+
},
|
|
2072
|
+
toDataURL: () => napiCanvas.toDataURL(),
|
|
2073
|
+
toBuffer: (mime) => napiCanvas.toBuffer(mime)
|
|
2074
|
+
// Add other properties as needed
|
|
2069
2075
|
};
|
|
2070
2076
|
return canvas;
|
|
2071
2077
|
}
|
|
2072
2078
|
function createMockCanvasContext2D(canvas) {
|
|
2073
|
-
|
|
2074
|
-
|
|
2079
|
+
const c = canvas || createMockCanvas();
|
|
2080
|
+
const ctx = c.getContext("2d");
|
|
2081
|
+
if (!ctx) {
|
|
2082
|
+
throw new Error("Failed to create 2D context");
|
|
2075
2083
|
}
|
|
2076
|
-
return
|
|
2084
|
+
return ctx;
|
|
2077
2085
|
}
|
|
2078
2086
|
function captureCanvasDrawCalls(context) {
|
|
2079
|
-
const
|
|
2080
|
-
const
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
"
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
"beginPath",
|
|
2088
|
-
"closePath",
|
|
2089
|
-
"moveTo",
|
|
2090
|
-
"lineTo",
|
|
2091
|
-
"arc",
|
|
2092
|
-
"arcTo",
|
|
2093
|
-
"bezierCurveTo",
|
|
2094
|
-
"quadraticCurveTo",
|
|
2095
|
-
"stroke",
|
|
2096
|
-
"fill",
|
|
2097
|
-
"putImageData"
|
|
2098
|
-
];
|
|
2099
|
-
methodsToSpy.forEach((method) => {
|
|
2100
|
-
const original = context[method];
|
|
2101
|
-
if (typeof original === "function") {
|
|
2102
|
-
context[method] = function(...args) {
|
|
2103
|
-
drawCalls.push({ method, args });
|
|
2104
|
-
return original.apply(this, args);
|
|
2087
|
+
const calls = [];
|
|
2088
|
+
const proto = Object.getPrototypeOf(context);
|
|
2089
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
2090
|
+
const value = context[key];
|
|
2091
|
+
if (typeof value === "function") {
|
|
2092
|
+
context[key] = function(...args) {
|
|
2093
|
+
calls.push({ method: key, args });
|
|
2094
|
+
return value.apply(context, args);
|
|
2105
2095
|
};
|
|
2106
2096
|
}
|
|
2107
|
-
}
|
|
2108
|
-
return
|
|
2097
|
+
}
|
|
2098
|
+
return calls;
|
|
2109
2099
|
}
|
|
2110
2100
|
function createMockImageData(width, height, fillColor) {
|
|
2111
|
-
|
|
2101
|
+
if (typeof global.ImageData !== "undefined") {
|
|
2102
|
+
const data2 = new Uint8ClampedArray(width * height * 4);
|
|
2103
|
+
if (fillColor) {
|
|
2104
|
+
for (let i = 0; i < data2.length; i += 4) {
|
|
2105
|
+
data2[i] = fillColor[0];
|
|
2106
|
+
data2[i + 1] = fillColor[1];
|
|
2107
|
+
data2[i + 2] = fillColor[2];
|
|
2108
|
+
data2[i + 3] = fillColor[3];
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
return new global.ImageData(data2, width, height);
|
|
2112
|
+
}
|
|
2113
|
+
const data = new Uint8ClampedArray(width * height * 4);
|
|
2112
2114
|
if (fillColor) {
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
imageData.data[i + 3] = a;
|
|
2115
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
2116
|
+
data[i] = fillColor[0];
|
|
2117
|
+
data[i + 1] = fillColor[1];
|
|
2118
|
+
data[i + 2] = fillColor[2];
|
|
2119
|
+
data[i + 3] = fillColor[3];
|
|
2119
2120
|
}
|
|
2120
2121
|
}
|
|
2121
|
-
return
|
|
2122
|
+
return new import_canvas2.ImageData(data, width, height);
|
|
2122
2123
|
}
|
|
2123
|
-
function createMockImage(width, height, src) {
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2124
|
+
function createMockImage(width = 100, height = 100, src = "") {
|
|
2125
|
+
if (typeof document !== "undefined" && document.createElement) {
|
|
2126
|
+
const img2 = document.createElement("img");
|
|
2127
|
+
img2.width = width;
|
|
2128
|
+
img2.height = height;
|
|
2129
|
+
if (src) img2.src = src;
|
|
2130
|
+
return img2;
|
|
2131
|
+
}
|
|
2132
|
+
const img = {
|
|
2133
|
+
width,
|
|
2134
|
+
height,
|
|
2135
|
+
src,
|
|
2136
|
+
complete: true,
|
|
2137
|
+
onload: null,
|
|
2138
|
+
onerror: null
|
|
2139
|
+
};
|
|
2140
|
+
if (src) {
|
|
2141
|
+
setTimeout(() => {
|
|
2142
|
+
if (img.onload) img.onload();
|
|
2143
|
+
}, 0);
|
|
2144
|
+
}
|
|
2128
2145
|
return img;
|
|
2129
2146
|
}
|
|
2130
2147
|
|
|
@@ -2192,376 +2209,285 @@ function setupWebGPUMocks() {
|
|
|
2192
2209
|
}
|
|
2193
2210
|
|
|
2194
2211
|
// src/setup/timing.ts
|
|
2195
|
-
var activeMockRAF;
|
|
2196
2212
|
function createMockRAF() {
|
|
2197
2213
|
let callbacks = [];
|
|
2198
|
-
let
|
|
2214
|
+
let lastId = 0;
|
|
2199
2215
|
let currentTime = 0;
|
|
2200
2216
|
const originalRAF = global.requestAnimationFrame;
|
|
2201
2217
|
const originalCancelRAF = global.cancelAnimationFrame;
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
callbacks.push({ id, callback });
|
|
2205
|
-
return
|
|
2218
|
+
global.requestAnimationFrame = (callback) => {
|
|
2219
|
+
lastId++;
|
|
2220
|
+
callbacks.push({ id: lastId, callback });
|
|
2221
|
+
return lastId;
|
|
2206
2222
|
};
|
|
2207
|
-
|
|
2223
|
+
global.cancelAnimationFrame = (id) => {
|
|
2208
2224
|
callbacks = callbacks.filter((cb) => cb.id !== id);
|
|
2209
2225
|
};
|
|
2210
|
-
|
|
2211
|
-
tick(
|
|
2212
|
-
if (
|
|
2213
|
-
|
|
2214
|
-
} else {
|
|
2215
|
-
currentTime = timestamp;
|
|
2216
|
-
}
|
|
2226
|
+
return {
|
|
2227
|
+
tick(time) {
|
|
2228
|
+
if (time) currentTime = time;
|
|
2229
|
+
else currentTime += 16.66;
|
|
2217
2230
|
const currentCallbacks = [...callbacks];
|
|
2218
2231
|
callbacks = [];
|
|
2219
|
-
currentCallbacks.forEach((
|
|
2220
|
-
callback(currentTime);
|
|
2221
|
-
});
|
|
2232
|
+
currentCallbacks.forEach((cb) => cb.callback(currentTime));
|
|
2222
2233
|
},
|
|
2223
|
-
advance(
|
|
2224
|
-
|
|
2234
|
+
advance(ms) {
|
|
2235
|
+
currentTime += ms;
|
|
2236
|
+
this.tick(currentTime);
|
|
2225
2237
|
},
|
|
2226
2238
|
getCallbacks() {
|
|
2227
|
-
return callbacks
|
|
2228
|
-
},
|
|
2229
|
-
reset() {
|
|
2230
|
-
callbacks = [];
|
|
2231
|
-
nextId = 1;
|
|
2232
|
-
currentTime = 0;
|
|
2233
|
-
},
|
|
2234
|
-
enable() {
|
|
2235
|
-
activeMockRAF = this;
|
|
2236
|
-
global.requestAnimationFrame = raf;
|
|
2237
|
-
global.cancelAnimationFrame = cancel;
|
|
2238
|
-
},
|
|
2239
|
-
disable() {
|
|
2240
|
-
if (activeMockRAF === this) {
|
|
2241
|
-
activeMockRAF = void 0;
|
|
2242
|
-
}
|
|
2243
|
-
if (originalRAF) {
|
|
2244
|
-
global.requestAnimationFrame = originalRAF;
|
|
2245
|
-
} else {
|
|
2246
|
-
delete global.requestAnimationFrame;
|
|
2247
|
-
}
|
|
2248
|
-
if (originalCancelRAF) {
|
|
2249
|
-
global.cancelAnimationFrame = originalCancelRAF;
|
|
2250
|
-
} else {
|
|
2251
|
-
delete global.cancelAnimationFrame;
|
|
2252
|
-
}
|
|
2239
|
+
return callbacks;
|
|
2253
2240
|
}
|
|
2254
2241
|
};
|
|
2255
|
-
return mock;
|
|
2256
2242
|
}
|
|
2257
2243
|
function createMockPerformance(startTime = 0) {
|
|
2258
|
-
let
|
|
2259
|
-
const
|
|
2260
|
-
now: () =>
|
|
2244
|
+
let now = startTime;
|
|
2245
|
+
const mockPerformance = {
|
|
2246
|
+
now: () => now,
|
|
2261
2247
|
timeOrigin: startTime,
|
|
2262
2248
|
timing: {
|
|
2263
2249
|
navigationStart: startTime
|
|
2264
2250
|
},
|
|
2265
|
-
|
|
2266
|
-
},
|
|
2267
|
-
clearMeasures: () => {
|
|
2251
|
+
mark: (_name) => {
|
|
2268
2252
|
},
|
|
2269
|
-
|
|
2253
|
+
measure: (_name, _start, _end) => {
|
|
2270
2254
|
},
|
|
2271
2255
|
getEntries: () => [],
|
|
2272
|
-
getEntriesByName: () => [],
|
|
2273
|
-
getEntriesByType: () => [],
|
|
2274
|
-
|
|
2275
|
-
},
|
|
2276
|
-
measure: () => {
|
|
2256
|
+
getEntriesByName: (_name) => [],
|
|
2257
|
+
getEntriesByType: (_type) => [],
|
|
2258
|
+
clearMarks: (_name) => {
|
|
2277
2259
|
},
|
|
2278
|
-
|
|
2260
|
+
clearMeasures: (_name) => {
|
|
2279
2261
|
},
|
|
2280
|
-
|
|
2281
|
-
addEventListener: () => {
|
|
2262
|
+
clearResourceTimings: () => {
|
|
2282
2263
|
},
|
|
2283
|
-
|
|
2264
|
+
setResourceTimingBufferSize: (_maxSize) => {
|
|
2284
2265
|
},
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
mockPerf.advance = (deltaMs) => {
|
|
2288
|
-
currentTime += deltaMs;
|
|
2289
|
-
};
|
|
2290
|
-
mockPerf.setTime = (time) => {
|
|
2291
|
-
currentTime = time;
|
|
2266
|
+
onresourcetimingbufferfull: null,
|
|
2267
|
+
toJSON: () => ({})
|
|
2292
2268
|
};
|
|
2293
|
-
|
|
2269
|
+
if (typeof global.performance === "undefined") {
|
|
2270
|
+
global.performance = mockPerformance;
|
|
2271
|
+
}
|
|
2272
|
+
return mockPerformance;
|
|
2294
2273
|
}
|
|
2295
2274
|
function createControlledTimer() {
|
|
2296
|
-
|
|
2297
|
-
let timers = [];
|
|
2298
|
-
let nextId = 1;
|
|
2299
|
-
const originalSetTimeout = global.setTimeout;
|
|
2300
|
-
const originalClearTimeout = global.clearTimeout;
|
|
2301
|
-
const originalSetInterval = global.setInterval;
|
|
2302
|
-
const originalClearInterval = global.clearInterval;
|
|
2303
|
-
const mockSetTimeout = (callback, delay = 0, ...args) => {
|
|
2304
|
-
const id = nextId++;
|
|
2305
|
-
timers.push({ id, callback, dueTime: currentTime + delay, args });
|
|
2306
|
-
return id;
|
|
2307
|
-
};
|
|
2308
|
-
const mockClearTimeout = (id) => {
|
|
2309
|
-
timers = timers.filter((t) => t.id !== id);
|
|
2310
|
-
};
|
|
2311
|
-
const mockSetInterval = (callback, delay = 0, ...args) => {
|
|
2312
|
-
const id = nextId++;
|
|
2313
|
-
timers.push({ id, callback, dueTime: currentTime + delay, interval: delay, args });
|
|
2314
|
-
return id;
|
|
2315
|
-
};
|
|
2316
|
-
const mockClearInterval = (id) => {
|
|
2317
|
-
timers = timers.filter((t) => t.id !== id);
|
|
2318
|
-
};
|
|
2319
|
-
global.setTimeout = mockSetTimeout;
|
|
2320
|
-
global.clearTimeout = mockClearTimeout;
|
|
2321
|
-
global.setInterval = mockSetInterval;
|
|
2322
|
-
global.clearInterval = mockClearInterval;
|
|
2275
|
+
console.warn("createControlledTimer: Recommend using vi.useFakeTimers() instead.");
|
|
2323
2276
|
return {
|
|
2324
|
-
|
|
2325
|
-
this.advanceBy(0);
|
|
2326
|
-
},
|
|
2327
|
-
advanceBy(ms) {
|
|
2328
|
-
const targetTime = currentTime + ms;
|
|
2329
|
-
while (true) {
|
|
2330
|
-
let earliest = null;
|
|
2331
|
-
for (const t of timers) {
|
|
2332
|
-
if (!earliest || t.dueTime < earliest.dueTime) {
|
|
2333
|
-
earliest = t;
|
|
2334
|
-
}
|
|
2335
|
-
}
|
|
2336
|
-
if (!earliest || earliest.dueTime > targetTime) {
|
|
2337
|
-
break;
|
|
2338
|
-
}
|
|
2339
|
-
currentTime = earliest.dueTime;
|
|
2340
|
-
const { callback, args, interval, id } = earliest;
|
|
2341
|
-
if (interval !== void 0) {
|
|
2342
|
-
earliest.dueTime += interval;
|
|
2343
|
-
if (interval === 0) earliest.dueTime += 1;
|
|
2344
|
-
} else {
|
|
2345
|
-
timers = timers.filter((t) => t.id !== id);
|
|
2346
|
-
}
|
|
2347
|
-
callback(...args);
|
|
2348
|
-
}
|
|
2349
|
-
currentTime = targetTime;
|
|
2277
|
+
advanceBy: (ms) => {
|
|
2350
2278
|
},
|
|
2351
|
-
|
|
2352
|
-
timers = [];
|
|
2279
|
+
runAll: () => {
|
|
2353
2280
|
},
|
|
2354
|
-
|
|
2355
|
-
global.setTimeout = originalSetTimeout;
|
|
2356
|
-
global.clearTimeout = originalClearTimeout;
|
|
2357
|
-
global.setInterval = originalSetInterval;
|
|
2358
|
-
global.clearInterval = originalClearInterval;
|
|
2281
|
+
clear: () => {
|
|
2359
2282
|
}
|
|
2360
2283
|
};
|
|
2361
2284
|
}
|
|
2362
|
-
function simulateFrames(count,
|
|
2363
|
-
if (!activeMockRAF) {
|
|
2364
|
-
throw new Error("simulateFrames requires an active MockRAF. Ensure createMockRAF().enable() is called.");
|
|
2365
|
-
}
|
|
2366
|
-
for (let i = 0; i < count; i++) {
|
|
2367
|
-
if (callback) callback(i);
|
|
2368
|
-
activeMockRAF.advance(frameTimeMs);
|
|
2369
|
-
}
|
|
2370
|
-
}
|
|
2371
|
-
function simulateFramesWithMock(mock, count, frameTimeMs = 16.6, callback) {
|
|
2285
|
+
function simulateFrames(count, frameTime = 16, callback) {
|
|
2372
2286
|
for (let i = 0; i < count; i++) {
|
|
2373
2287
|
if (callback) callback(i);
|
|
2374
|
-
mock.advance(frameTimeMs);
|
|
2375
2288
|
}
|
|
2376
2289
|
}
|
|
2377
2290
|
|
|
2378
2291
|
// src/setup/node.ts
|
|
2379
2292
|
function setupNodeEnvironment(options = {}) {
|
|
2380
|
-
|
|
2381
|
-
|
|
2293
|
+
}
|
|
2294
|
+
function teardownNodeEnvironment() {
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
// src/engine/rendering.ts
|
|
2298
|
+
var import_vitest12 = require("vitest");
|
|
2299
|
+
function createMockRenderingContext() {
|
|
2300
|
+
const gl = createMockWebGL2Context();
|
|
2301
|
+
const camera = {
|
|
2302
|
+
update: import_vitest12.vi.fn(),
|
|
2303
|
+
getViewMatrix: import_vitest12.vi.fn().mockReturnValue(new Float32Array(16)),
|
|
2304
|
+
getProjectionMatrix: import_vitest12.vi.fn().mockReturnValue(new Float32Array(16)),
|
|
2305
|
+
getViewProjectionMatrix: import_vitest12.vi.fn().mockReturnValue(new Float32Array(16)),
|
|
2306
|
+
getPosition: import_vitest12.vi.fn().mockReturnValue([0, 0, 0]),
|
|
2307
|
+
getForward: import_vitest12.vi.fn().mockReturnValue([0, 0, -1]),
|
|
2308
|
+
getRight: import_vitest12.vi.fn().mockReturnValue([1, 0, 0]),
|
|
2309
|
+
getUp: import_vitest12.vi.fn().mockReturnValue([0, 1, 0]),
|
|
2310
|
+
extractFrustumPlanes: import_vitest12.vi.fn(),
|
|
2311
|
+
transform: {
|
|
2312
|
+
origin: [0, 0, 0],
|
|
2313
|
+
angles: [0, 0, 0],
|
|
2314
|
+
fov: 90
|
|
2315
|
+
}
|
|
2316
|
+
};
|
|
2317
|
+
const md2 = {
|
|
2318
|
+
render: import_vitest12.vi.fn(),
|
|
2319
|
+
init: import_vitest12.vi.fn(),
|
|
2320
|
+
resize: import_vitest12.vi.fn()
|
|
2321
|
+
};
|
|
2322
|
+
const bsp = {
|
|
2323
|
+
render: import_vitest12.vi.fn(),
|
|
2324
|
+
init: import_vitest12.vi.fn(),
|
|
2325
|
+
resize: import_vitest12.vi.fn()
|
|
2326
|
+
};
|
|
2327
|
+
const sprite = {
|
|
2328
|
+
render: import_vitest12.vi.fn(),
|
|
2329
|
+
init: import_vitest12.vi.fn(),
|
|
2330
|
+
resize: import_vitest12.vi.fn()
|
|
2331
|
+
};
|
|
2332
|
+
const poly = {
|
|
2333
|
+
render: import_vitest12.vi.fn(),
|
|
2334
|
+
init: import_vitest12.vi.fn(),
|
|
2335
|
+
resize: import_vitest12.vi.fn()
|
|
2336
|
+
};
|
|
2337
|
+
const particle = {
|
|
2338
|
+
render: import_vitest12.vi.fn(),
|
|
2339
|
+
init: import_vitest12.vi.fn(),
|
|
2340
|
+
resize: import_vitest12.vi.fn()
|
|
2341
|
+
};
|
|
2342
|
+
return {
|
|
2343
|
+
gl,
|
|
2344
|
+
camera,
|
|
2345
|
+
pipelines: {
|
|
2346
|
+
md2,
|
|
2347
|
+
bsp,
|
|
2348
|
+
sprite,
|
|
2349
|
+
poly,
|
|
2350
|
+
particle
|
|
2351
|
+
}
|
|
2352
|
+
};
|
|
2382
2353
|
}
|
|
2383
2354
|
|
|
2384
2355
|
// src/setup/storage.ts
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
const storage = new Map(Object.entries(initialData));
|
|
2356
|
+
function createStorageMock(initialData = {}) {
|
|
2357
|
+
const store = new Map(Object.entries(initialData));
|
|
2388
2358
|
return {
|
|
2389
|
-
getItem: (key) =>
|
|
2390
|
-
setItem: (key, value) =>
|
|
2391
|
-
removeItem: (key) =>
|
|
2392
|
-
clear: () =>
|
|
2393
|
-
key: (index) => Array.from(
|
|
2359
|
+
getItem: (key) => store.get(key) || null,
|
|
2360
|
+
setItem: (key, value) => store.set(key, value.toString()),
|
|
2361
|
+
removeItem: (key) => store.delete(key),
|
|
2362
|
+
clear: () => store.clear(),
|
|
2363
|
+
key: (index) => Array.from(store.keys())[index] || null,
|
|
2394
2364
|
get length() {
|
|
2395
|
-
return
|
|
2365
|
+
return store.size;
|
|
2396
2366
|
}
|
|
2397
2367
|
};
|
|
2398
2368
|
}
|
|
2369
|
+
function createMockLocalStorage(initialData = {}) {
|
|
2370
|
+
return createStorageMock(initialData);
|
|
2371
|
+
}
|
|
2399
2372
|
function createMockSessionStorage(initialData = {}) {
|
|
2400
|
-
return
|
|
2373
|
+
return createStorageMock(initialData);
|
|
2401
2374
|
}
|
|
2402
|
-
function createMockIndexedDB() {
|
|
2403
|
-
|
|
2404
|
-
throw new Error("IndexedDB mock not found. Ensure fake-indexeddb is loaded.");
|
|
2405
|
-
}
|
|
2406
|
-
return indexedDB;
|
|
2375
|
+
function createMockIndexedDB(databases) {
|
|
2376
|
+
return global.indexedDB;
|
|
2407
2377
|
}
|
|
2408
2378
|
function createStorageTestScenario(storageType = "local") {
|
|
2409
|
-
if (storageType === "indexed") {
|
|
2410
|
-
const dbName = `test-db-${Math.random().toString(36).substring(7)}`;
|
|
2411
|
-
const storeName = "test-store";
|
|
2412
|
-
const storage2 = createMockIndexedDB();
|
|
2413
|
-
return {
|
|
2414
|
-
storage: storage2,
|
|
2415
|
-
populate: async (data) => {
|
|
2416
|
-
return new Promise((resolve, reject) => {
|
|
2417
|
-
const req = storage2.open(dbName, 1);
|
|
2418
|
-
req.onupgradeneeded = (e) => {
|
|
2419
|
-
const db = e.target.result;
|
|
2420
|
-
db.createObjectStore(storeName);
|
|
2421
|
-
};
|
|
2422
|
-
req.onsuccess = (e) => {
|
|
2423
|
-
const db = e.target.result;
|
|
2424
|
-
const tx = db.transaction(storeName, "readwrite");
|
|
2425
|
-
const store = tx.objectStore(storeName);
|
|
2426
|
-
Object.entries(data).forEach(([k, v]) => store.put(v, k));
|
|
2427
|
-
tx.oncomplete = () => {
|
|
2428
|
-
db.close();
|
|
2429
|
-
resolve();
|
|
2430
|
-
};
|
|
2431
|
-
tx.onerror = () => reject(tx.error);
|
|
2432
|
-
};
|
|
2433
|
-
req.onerror = () => reject(req.error);
|
|
2434
|
-
});
|
|
2435
|
-
},
|
|
2436
|
-
verify: async (key, value) => {
|
|
2437
|
-
return new Promise((resolve, reject) => {
|
|
2438
|
-
const req = storage2.open(dbName, 1);
|
|
2439
|
-
req.onsuccess = (e) => {
|
|
2440
|
-
const db = e.target.result;
|
|
2441
|
-
if (!db.objectStoreNames.contains(storeName)) {
|
|
2442
|
-
db.close();
|
|
2443
|
-
resolve(false);
|
|
2444
|
-
return;
|
|
2445
|
-
}
|
|
2446
|
-
const tx = db.transaction(storeName, "readonly");
|
|
2447
|
-
const store = tx.objectStore(storeName);
|
|
2448
|
-
const getReq = store.get(key);
|
|
2449
|
-
getReq.onsuccess = () => {
|
|
2450
|
-
const result = getReq.result === value;
|
|
2451
|
-
db.close();
|
|
2452
|
-
resolve(result);
|
|
2453
|
-
};
|
|
2454
|
-
getReq.onerror = () => {
|
|
2455
|
-
db.close();
|
|
2456
|
-
resolve(false);
|
|
2457
|
-
};
|
|
2458
|
-
};
|
|
2459
|
-
req.onerror = () => reject(req.error);
|
|
2460
|
-
});
|
|
2461
|
-
}
|
|
2462
|
-
};
|
|
2463
|
-
}
|
|
2464
|
-
const storage = storageType === "local" ? createMockLocalStorage() : createMockSessionStorage();
|
|
2465
2379
|
return {
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
},
|
|
2470
|
-
verify(key, value) {
|
|
2471
|
-
return storage.getItem(key) === value;
|
|
2472
|
-
}
|
|
2380
|
+
localStorage: createMockLocalStorage(),
|
|
2381
|
+
sessionStorage: createMockSessionStorage(),
|
|
2382
|
+
indexedDB: createMockIndexedDB()
|
|
2473
2383
|
};
|
|
2474
2384
|
}
|
|
2475
2385
|
|
|
2476
2386
|
// src/setup/audio.ts
|
|
2477
|
-
function createMockAudioContext() {
|
|
2478
|
-
const context = {
|
|
2479
|
-
createGain: () => ({
|
|
2480
|
-
connect: () => {
|
|
2481
|
-
},
|
|
2482
|
-
gain: { value: 1, setValueAtTime: () => {
|
|
2483
|
-
} }
|
|
2484
|
-
}),
|
|
2485
|
-
createOscillator: () => ({
|
|
2486
|
-
connect: () => {
|
|
2487
|
-
},
|
|
2488
|
-
start: () => {
|
|
2489
|
-
},
|
|
2490
|
-
stop: () => {
|
|
2491
|
-
},
|
|
2492
|
-
frequency: { value: 440 }
|
|
2493
|
-
}),
|
|
2494
|
-
createBufferSource: () => ({
|
|
2495
|
-
connect: () => {
|
|
2496
|
-
},
|
|
2497
|
-
start: () => {
|
|
2498
|
-
},
|
|
2499
|
-
stop: () => {
|
|
2500
|
-
},
|
|
2501
|
-
buffer: null,
|
|
2502
|
-
playbackRate: { value: 1 },
|
|
2503
|
-
loop: false
|
|
2504
|
-
}),
|
|
2505
|
-
destination: {},
|
|
2506
|
-
currentTime: 0,
|
|
2507
|
-
state: "running",
|
|
2508
|
-
resume: async () => {
|
|
2509
|
-
},
|
|
2510
|
-
suspend: async () => {
|
|
2511
|
-
},
|
|
2512
|
-
close: async () => {
|
|
2513
|
-
},
|
|
2514
|
-
decodeAudioData: async (buffer) => ({
|
|
2515
|
-
duration: 1,
|
|
2516
|
-
length: 44100,
|
|
2517
|
-
sampleRate: 44100,
|
|
2518
|
-
numberOfChannels: 2,
|
|
2519
|
-
getChannelData: () => new Float32Array(44100)
|
|
2520
|
-
}),
|
|
2521
|
-
createBuffer: (channels, length2, sampleRate) => ({
|
|
2522
|
-
duration: length2 / sampleRate,
|
|
2523
|
-
length: length2,
|
|
2524
|
-
sampleRate,
|
|
2525
|
-
numberOfChannels: channels,
|
|
2526
|
-
getChannelData: () => new Float32Array(length2)
|
|
2527
|
-
}),
|
|
2528
|
-
// Helper to track events if needed
|
|
2529
|
-
_events: []
|
|
2530
|
-
};
|
|
2531
|
-
return new Proxy(context, {
|
|
2532
|
-
get(target, prop, receiver) {
|
|
2533
|
-
if (prop === "_events") return target._events;
|
|
2534
|
-
const value = Reflect.get(target, prop, receiver);
|
|
2535
|
-
if (typeof value === "function") {
|
|
2536
|
-
return (...args) => {
|
|
2537
|
-
target._events.push({ type: String(prop), args });
|
|
2538
|
-
return Reflect.apply(value, target, args);
|
|
2539
|
-
};
|
|
2540
|
-
}
|
|
2541
|
-
return value;
|
|
2542
|
-
}
|
|
2543
|
-
});
|
|
2544
|
-
}
|
|
2545
2387
|
function setupMockAudioContext() {
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2388
|
+
class MockAudioContext {
|
|
2389
|
+
constructor() {
|
|
2390
|
+
this.state = "suspended";
|
|
2391
|
+
this.destination = {};
|
|
2392
|
+
this.currentTime = 0;
|
|
2393
|
+
this.listener = {
|
|
2394
|
+
positionX: { value: 0 },
|
|
2395
|
+
positionY: { value: 0 },
|
|
2396
|
+
positionZ: { value: 0 },
|
|
2397
|
+
forwardX: { value: 0 },
|
|
2398
|
+
forwardY: { value: 0 },
|
|
2399
|
+
forwardZ: { value: 0 },
|
|
2400
|
+
upX: { value: 0 },
|
|
2401
|
+
upY: { value: 0 },
|
|
2402
|
+
upZ: { value: 0 },
|
|
2403
|
+
setOrientation: () => {
|
|
2404
|
+
},
|
|
2405
|
+
setPosition: () => {
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
}
|
|
2409
|
+
createGain() {
|
|
2410
|
+
return {
|
|
2411
|
+
gain: { value: 1, linearRampToValueAtTime: () => {
|
|
2412
|
+
} },
|
|
2413
|
+
connect: () => {
|
|
2414
|
+
},
|
|
2415
|
+
disconnect: () => {
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2418
|
+
}
|
|
2419
|
+
createBufferSource() {
|
|
2420
|
+
return {
|
|
2421
|
+
buffer: null,
|
|
2422
|
+
loop: false,
|
|
2423
|
+
playbackRate: { value: 1 },
|
|
2424
|
+
connect: () => {
|
|
2425
|
+
},
|
|
2426
|
+
start: () => {
|
|
2427
|
+
},
|
|
2428
|
+
stop: () => {
|
|
2429
|
+
},
|
|
2430
|
+
disconnect: () => {
|
|
2431
|
+
},
|
|
2432
|
+
onended: null
|
|
2433
|
+
};
|
|
2434
|
+
}
|
|
2435
|
+
createPanner() {
|
|
2436
|
+
return {
|
|
2437
|
+
panningModel: "equalpower",
|
|
2438
|
+
distanceModel: "inverse",
|
|
2439
|
+
positionX: { value: 0 },
|
|
2440
|
+
positionY: { value: 0 },
|
|
2441
|
+
positionZ: { value: 0 },
|
|
2442
|
+
orientationX: { value: 0 },
|
|
2443
|
+
orientationY: { value: 0 },
|
|
2444
|
+
orientationZ: { value: 0 },
|
|
2445
|
+
coneInnerAngle: 360,
|
|
2446
|
+
coneOuterAngle: 360,
|
|
2447
|
+
coneOuterGain: 0,
|
|
2448
|
+
connect: () => {
|
|
2449
|
+
},
|
|
2450
|
+
disconnect: () => {
|
|
2451
|
+
},
|
|
2452
|
+
setPosition: () => {
|
|
2453
|
+
},
|
|
2454
|
+
setOrientation: () => {
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
createBuffer(numOfChannels, length2, sampleRate) {
|
|
2459
|
+
return {
|
|
2460
|
+
duration: length2 / sampleRate,
|
|
2461
|
+
length: length2,
|
|
2462
|
+
sampleRate,
|
|
2463
|
+
numberOfChannels: numOfChannels,
|
|
2464
|
+
getChannelData: () => new Float32Array(length2)
|
|
2465
|
+
};
|
|
2466
|
+
}
|
|
2467
|
+
decodeAudioData(data, success) {
|
|
2468
|
+
const buffer = this.createBuffer(2, 100, 44100);
|
|
2469
|
+
if (success) success(buffer);
|
|
2470
|
+
return Promise.resolve(buffer);
|
|
2471
|
+
}
|
|
2472
|
+
resume() {
|
|
2473
|
+
return Promise.resolve();
|
|
2474
|
+
}
|
|
2475
|
+
suspend() {
|
|
2476
|
+
return Promise.resolve();
|
|
2477
|
+
}
|
|
2478
|
+
close() {
|
|
2479
|
+
return Promise.resolve();
|
|
2480
|
+
}
|
|
2554
2481
|
}
|
|
2482
|
+
global.AudioContext = MockAudioContext;
|
|
2483
|
+
global.webkitAudioContext = MockAudioContext;
|
|
2555
2484
|
}
|
|
2556
2485
|
function teardownMockAudioContext() {
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
delete global.window.AudioContext;
|
|
2560
|
-
delete global.window.webkitAudioContext;
|
|
2561
|
-
}
|
|
2486
|
+
delete global.AudioContext;
|
|
2487
|
+
delete global.webkitAudioContext;
|
|
2562
2488
|
}
|
|
2563
2489
|
function captureAudioEvents(context) {
|
|
2564
|
-
return
|
|
2490
|
+
return [];
|
|
2565
2491
|
}
|
|
2566
2492
|
|
|
2567
2493
|
// ../../node_modules/.pnpm/gl-matrix@3.4.4/node_modules/gl-matrix/esm/common.js
|
|
@@ -3051,217 +2977,110 @@ function simulateCameraMovement(camera, input, deltaTime) {
|
|
|
3051
2977
|
}
|
|
3052
2978
|
|
|
3053
2979
|
// src/e2e/playwright.ts
|
|
3054
|
-
var import_playwright = require("playwright");
|
|
3055
|
-
var import_http = require("http");
|
|
3056
|
-
var import_serve_handler = __toESM(require("serve-handler"), 1);
|
|
3057
2980
|
async function createPlaywrightTestClient(options = {}) {
|
|
3058
|
-
let
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
return (0, import_serve_handler.default)(request, response, {
|
|
3064
|
-
public: rootPath,
|
|
3065
|
-
cleanUrls: false,
|
|
3066
|
-
headers: [
|
|
3067
|
-
{
|
|
3068
|
-
source: "**/*",
|
|
3069
|
-
headers: [
|
|
3070
|
-
{ key: "Cache-Control", value: "no-cache" },
|
|
3071
|
-
{ key: "Access-Control-Allow-Origin", value: "*" },
|
|
3072
|
-
{ key: "Cross-Origin-Opener-Policy", value: "same-origin" },
|
|
3073
|
-
{ key: "Cross-Origin-Embedder-Policy", value: "require-corp" }
|
|
3074
|
-
]
|
|
3075
|
-
}
|
|
3076
|
-
]
|
|
3077
|
-
});
|
|
3078
|
-
});
|
|
3079
|
-
await new Promise((resolve) => {
|
|
3080
|
-
if (!staticServer) return;
|
|
3081
|
-
staticServer.listen(0, () => {
|
|
3082
|
-
const addr = staticServer?.address();
|
|
3083
|
-
const port = typeof addr === "object" ? addr?.port : 0;
|
|
3084
|
-
clientUrl = `http://localhost:${port}`;
|
|
3085
|
-
console.log(`Test client serving from ${rootPath} at ${clientUrl}`);
|
|
3086
|
-
resolve();
|
|
3087
|
-
});
|
|
3088
|
-
});
|
|
2981
|
+
let playwright;
|
|
2982
|
+
try {
|
|
2983
|
+
playwright = await import("playwright");
|
|
2984
|
+
} catch (e) {
|
|
2985
|
+
throw new Error("Playwright is not installed. Please install it to use this utility.");
|
|
3089
2986
|
}
|
|
3090
|
-
const browser = await
|
|
3091
|
-
headless: options.headless ?? true
|
|
3092
|
-
args: [
|
|
3093
|
-
"--use-gl=egl",
|
|
3094
|
-
"--ignore-gpu-blocklist",
|
|
3095
|
-
...options.launchOptions?.args || []
|
|
3096
|
-
],
|
|
3097
|
-
...options.launchOptions
|
|
2987
|
+
const browser = await playwright.chromium.launch({
|
|
2988
|
+
headless: options.headless ?? true
|
|
3098
2989
|
});
|
|
3099
|
-
const width = options.width || 1280;
|
|
3100
|
-
const height = options.height || 720;
|
|
3101
2990
|
const context = await browser.newContext({
|
|
3102
|
-
viewport: { width, height }
|
|
3103
|
-
deviceScaleFactor: 1,
|
|
3104
|
-
...options.contextOptions
|
|
2991
|
+
viewport: options.viewport || { width: 1280, height: 720 }
|
|
3105
2992
|
});
|
|
3106
2993
|
const page = await context.newPage();
|
|
3107
|
-
const close = async () => {
|
|
3108
|
-
await browser.close();
|
|
3109
|
-
if (staticServer) {
|
|
3110
|
-
staticServer.close();
|
|
3111
|
-
}
|
|
3112
|
-
};
|
|
3113
|
-
const navigate = async (url) => {
|
|
3114
|
-
const targetUrl = url || clientUrl;
|
|
3115
|
-
if (!targetUrl) throw new Error("No URL to navigate to");
|
|
3116
|
-
let finalUrl = targetUrl;
|
|
3117
|
-
if (options.serverUrl && !targetUrl.includes("connect=")) {
|
|
3118
|
-
const separator = targetUrl.includes("?") ? "&" : "?";
|
|
3119
|
-
finalUrl = `${targetUrl}${separator}connect=${encodeURIComponent(options.serverUrl)}`;
|
|
3120
|
-
}
|
|
3121
|
-
console.log(`Navigating to: ${finalUrl}`);
|
|
3122
|
-
await page.goto(finalUrl, { waitUntil: "domcontentloaded" });
|
|
3123
|
-
};
|
|
3124
2994
|
return {
|
|
3125
|
-
browser,
|
|
3126
|
-
context,
|
|
3127
2995
|
page,
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
2996
|
+
browser,
|
|
2997
|
+
async navigate(url) {
|
|
2998
|
+
await page.goto(url);
|
|
2999
|
+
},
|
|
3000
|
+
async waitForGame() {
|
|
3001
|
+
await waitForGameReady(page);
|
|
3133
3002
|
},
|
|
3134
|
-
|
|
3003
|
+
async injectInput(type, data) {
|
|
3135
3004
|
await page.evaluate(({ type: type2, data: data2 }) => {
|
|
3136
|
-
|
|
3005
|
+
console.log("Injecting input", type2, data2);
|
|
3137
3006
|
}, { type, data });
|
|
3007
|
+
},
|
|
3008
|
+
async screenshot(name) {
|
|
3009
|
+
return await page.screenshot({ path: `${name}.png` });
|
|
3010
|
+
},
|
|
3011
|
+
async close() {
|
|
3012
|
+
await browser.close();
|
|
3138
3013
|
}
|
|
3139
3014
|
};
|
|
3140
3015
|
}
|
|
3141
3016
|
async function waitForGameReady(page, timeout = 1e4) {
|
|
3142
|
-
|
|
3143
|
-
|
|
3144
|
-
|
|
3145
|
-
}, null, { timeout });
|
|
3146
|
-
} catch (e) {
|
|
3147
|
-
await page.waitForSelector("canvas", { timeout });
|
|
3148
|
-
}
|
|
3017
|
+
await page.waitForFunction(() => {
|
|
3018
|
+
return window.game && window.game.isRunning;
|
|
3019
|
+
}, { timeout });
|
|
3149
3020
|
}
|
|
3150
3021
|
async function captureGameState(page) {
|
|
3151
3022
|
return await page.evaluate(() => {
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3023
|
+
const game = window.game;
|
|
3024
|
+
return {
|
|
3025
|
+
time: game ? game.time : 0,
|
|
3026
|
+
entities: game && game.entities ? game.entities.length : 0
|
|
3027
|
+
};
|
|
3156
3028
|
});
|
|
3157
3029
|
}
|
|
3158
3030
|
|
|
3159
3031
|
// src/e2e/network.ts
|
|
3160
|
-
var CONDITIONS = {
|
|
3161
|
-
"good": {
|
|
3162
|
-
offline: false,
|
|
3163
|
-
downloadThroughput: 10 * 1024 * 1024,
|
|
3164
|
-
// 10 Mbps
|
|
3165
|
-
uploadThroughput: 5 * 1024 * 1024,
|
|
3166
|
-
// 5 Mbps
|
|
3167
|
-
latency: 20
|
|
3168
|
-
},
|
|
3169
|
-
"slow": {
|
|
3170
|
-
offline: false,
|
|
3171
|
-
downloadThroughput: 500 * 1024,
|
|
3172
|
-
// 500 Kbps
|
|
3173
|
-
uploadThroughput: 500 * 1024,
|
|
3174
|
-
latency: 400
|
|
3175
|
-
},
|
|
3176
|
-
"unstable": {
|
|
3177
|
-
offline: false,
|
|
3178
|
-
downloadThroughput: 1 * 1024 * 1024,
|
|
3179
|
-
uploadThroughput: 1 * 1024 * 1024,
|
|
3180
|
-
latency: 100
|
|
3181
|
-
},
|
|
3182
|
-
"offline": {
|
|
3183
|
-
offline: true,
|
|
3184
|
-
downloadThroughput: 0,
|
|
3185
|
-
uploadThroughput: 0,
|
|
3186
|
-
latency: 0
|
|
3187
|
-
}
|
|
3188
|
-
};
|
|
3189
3032
|
function simulateNetworkCondition(condition) {
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
}
|
|
3193
|
-
|
|
3033
|
+
switch (condition) {
|
|
3034
|
+
case "good":
|
|
3035
|
+
return { latency: 20, jitter: 5, packetLoss: 0, bandwidth: 10 * 1024 * 1024 };
|
|
3036
|
+
case "slow":
|
|
3037
|
+
return { latency: 150, jitter: 20, packetLoss: 0.01, bandwidth: 1 * 1024 * 1024 };
|
|
3038
|
+
case "unstable":
|
|
3039
|
+
return { latency: 100, jitter: 100, packetLoss: 0.05, bandwidth: 512 * 1024 };
|
|
3040
|
+
case "offline":
|
|
3041
|
+
return { latency: 0, jitter: 0, packetLoss: 1, bandwidth: 0 };
|
|
3042
|
+
case "custom":
|
|
3043
|
+
default:
|
|
3044
|
+
return { latency: 0, jitter: 0, packetLoss: 0, bandwidth: Infinity };
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
function createCustomNetworkCondition(latency, jitter, packetLoss) {
|
|
3194
3048
|
return {
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
latency: latency + Math.random() * jitter,
|
|
3201
|
-
downloadThroughput: baseConfig?.downloadThroughput || -1,
|
|
3202
|
-
uploadThroughput: baseConfig?.uploadThroughput || -1
|
|
3203
|
-
});
|
|
3204
|
-
},
|
|
3205
|
-
async clear(page) {
|
|
3206
|
-
const client = await page.context().newCDPSession(page);
|
|
3207
|
-
await client.send("Network.emulateNetworkConditions", {
|
|
3208
|
-
offline: false,
|
|
3209
|
-
latency: 0,
|
|
3210
|
-
downloadThroughput: -1,
|
|
3211
|
-
uploadThroughput: -1
|
|
3212
|
-
});
|
|
3213
|
-
}
|
|
3049
|
+
latency,
|
|
3050
|
+
jitter,
|
|
3051
|
+
packetLoss,
|
|
3052
|
+
bandwidth: Infinity
|
|
3053
|
+
// Default to unlimited unless specified
|
|
3214
3054
|
};
|
|
3215
3055
|
}
|
|
3216
|
-
|
|
3217
|
-
const simulator = createCustomNetworkCondition(0, 0, 0, {
|
|
3218
|
-
offline: false,
|
|
3219
|
-
latency: 0,
|
|
3220
|
-
downloadThroughput: bytesPerSecond,
|
|
3221
|
-
uploadThroughput: bytesPerSecond
|
|
3222
|
-
});
|
|
3223
|
-
await simulator.apply(page);
|
|
3056
|
+
function throttleBandwidth(bytesPerSecond) {
|
|
3224
3057
|
}
|
|
3225
3058
|
|
|
3226
3059
|
// src/e2e/visual.ts
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
}
|
|
3240
|
-
async function compareScreenshots(baseline, current, threshold = 0.1) {
|
|
3241
|
-
if (baseline.equals(current)) {
|
|
3242
|
-
return { pixelDiff: 0, matched: true };
|
|
3060
|
+
async function captureGameScreenshot(page, name) {
|
|
3061
|
+
return await page.screenshot({ path: `${name}.png` });
|
|
3062
|
+
}
|
|
3063
|
+
function compareScreenshots(baseline, current, threshold = 0.01) {
|
|
3064
|
+
if (baseline.length !== current.length) {
|
|
3065
|
+
return { diffPercentage: 1 };
|
|
3066
|
+
}
|
|
3067
|
+
let diffPixels = 0;
|
|
3068
|
+
const totalPixels = baseline.length;
|
|
3069
|
+
for (let i = 0; i < baseline.length; i++) {
|
|
3070
|
+
if (baseline[i] !== current[i]) {
|
|
3071
|
+
diffPixels++;
|
|
3072
|
+
}
|
|
3243
3073
|
}
|
|
3074
|
+
const diffPercentage = diffPixels / totalPixels;
|
|
3244
3075
|
return {
|
|
3245
|
-
|
|
3246
|
-
//
|
|
3247
|
-
matched: false
|
|
3076
|
+
diffPercentage
|
|
3077
|
+
// Generating a diff image buffer would require a library like pixelmatch
|
|
3248
3078
|
};
|
|
3249
3079
|
}
|
|
3250
|
-
function createVisualTestScenario(
|
|
3080
|
+
function createVisualTestScenario(sceneName) {
|
|
3251
3081
|
return {
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
},
|
|
3255
|
-
async compare(snapshotName, baselineDir) {
|
|
3256
|
-
const name = `${sceneName}-${snapshotName}`;
|
|
3257
|
-
const current = await captureGameScreenshot(page, name, { dir: "__screenshots__/current" });
|
|
3258
|
-
try {
|
|
3259
|
-
const baselinePath = import_path.default.join(baselineDir, `${name}.png`);
|
|
3260
|
-
const baseline = await import_promises.default.readFile(baselinePath);
|
|
3261
|
-
return await compareScreenshots(baseline, current);
|
|
3262
|
-
} catch (e) {
|
|
3263
|
-
return { pixelDiff: -1, matched: false };
|
|
3264
|
-
}
|
|
3082
|
+
sceneName,
|
|
3083
|
+
setup: async () => {
|
|
3265
3084
|
}
|
|
3266
3085
|
};
|
|
3267
3086
|
}
|
|
@@ -3293,7 +3112,6 @@ function createVisualTestScenario(page, sceneName) {
|
|
|
3293
3112
|
createMockAI,
|
|
3294
3113
|
createMockAmmoItem,
|
|
3295
3114
|
createMockArmorItem,
|
|
3296
|
-
createMockAudioContext,
|
|
3297
3115
|
createMockCamera,
|
|
3298
3116
|
createMockCanvas,
|
|
3299
3117
|
createMockCanvasContext2D,
|
|
@@ -3329,6 +3147,7 @@ function createVisualTestScenario(page, sceneName) {
|
|
|
3329
3147
|
createMockRConClient,
|
|
3330
3148
|
createMockRateLimiter,
|
|
3331
3149
|
createMockRefDef,
|
|
3150
|
+
createMockRenderingContext,
|
|
3332
3151
|
createMockServer,
|
|
3333
3152
|
createMockServerClient,
|
|
3334
3153
|
createMockServerConsole,
|
|
@@ -3378,7 +3197,6 @@ function createVisualTestScenario(page, sceneName) {
|
|
|
3378
3197
|
simulateBandwidthLimit,
|
|
3379
3198
|
simulateCameraMovement,
|
|
3380
3199
|
simulateFrames,
|
|
3381
|
-
simulateFramesWithMock,
|
|
3382
3200
|
simulateHandshake,
|
|
3383
3201
|
simulateNetworkCondition,
|
|
3384
3202
|
simulatePlayerInput,
|
|
@@ -3391,6 +3209,7 @@ function createVisualTestScenario(page, sceneName) {
|
|
|
3391
3209
|
stairTrace,
|
|
3392
3210
|
teardownBrowserEnvironment,
|
|
3393
3211
|
teardownMockAudioContext,
|
|
3212
|
+
teardownNodeEnvironment,
|
|
3394
3213
|
throttleBandwidth,
|
|
3395
3214
|
verifySnapshotConsistency,
|
|
3396
3215
|
waitForGameReady
|