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
|
@@ -1912,7 +1912,7 @@ function teardownBrowserEnvironment() {
|
|
|
1912
1912
|
}
|
|
1913
1913
|
|
|
1914
1914
|
// src/setup/canvas.ts
|
|
1915
|
-
import { Canvas as Canvas2,
|
|
1915
|
+
import { Canvas as Canvas2, ImageData as ImageData2 } from "@napi-rs/canvas";
|
|
1916
1916
|
function createMockCanvas(width = 300, height = 150) {
|
|
1917
1917
|
if (typeof document !== "undefined" && document.createElement) {
|
|
1918
1918
|
const canvas2 = document.createElement("canvas");
|
|
@@ -1920,75 +1920,92 @@ function createMockCanvas(width = 300, height = 150) {
|
|
|
1920
1920
|
canvas2.height = height;
|
|
1921
1921
|
return canvas2;
|
|
1922
1922
|
}
|
|
1923
|
-
const
|
|
1924
|
-
const
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1923
|
+
const napiCanvas = new Canvas2(width, height);
|
|
1924
|
+
const canvas = {
|
|
1925
|
+
width,
|
|
1926
|
+
height,
|
|
1927
|
+
getContext: (contextId, options) => {
|
|
1928
|
+
if (contextId === "2d") {
|
|
1929
|
+
return napiCanvas.getContext("2d", options);
|
|
1930
|
+
}
|
|
1931
|
+
if (contextId === "webgl2") {
|
|
1932
|
+
return createMockWebGL2Context(canvas);
|
|
1933
|
+
}
|
|
1934
|
+
return null;
|
|
1935
|
+
},
|
|
1936
|
+
toDataURL: () => napiCanvas.toDataURL(),
|
|
1937
|
+
toBuffer: (mime) => napiCanvas.toBuffer(mime)
|
|
1938
|
+
// Add other properties as needed
|
|
1933
1939
|
};
|
|
1934
1940
|
return canvas;
|
|
1935
1941
|
}
|
|
1936
1942
|
function createMockCanvasContext2D(canvas) {
|
|
1937
|
-
|
|
1938
|
-
|
|
1943
|
+
const c = canvas || createMockCanvas();
|
|
1944
|
+
const ctx = c.getContext("2d");
|
|
1945
|
+
if (!ctx) {
|
|
1946
|
+
throw new Error("Failed to create 2D context");
|
|
1939
1947
|
}
|
|
1940
|
-
return
|
|
1948
|
+
return ctx;
|
|
1941
1949
|
}
|
|
1942
1950
|
function captureCanvasDrawCalls(context) {
|
|
1943
|
-
const
|
|
1944
|
-
const
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
"
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
"beginPath",
|
|
1952
|
-
"closePath",
|
|
1953
|
-
"moveTo",
|
|
1954
|
-
"lineTo",
|
|
1955
|
-
"arc",
|
|
1956
|
-
"arcTo",
|
|
1957
|
-
"bezierCurveTo",
|
|
1958
|
-
"quadraticCurveTo",
|
|
1959
|
-
"stroke",
|
|
1960
|
-
"fill",
|
|
1961
|
-
"putImageData"
|
|
1962
|
-
];
|
|
1963
|
-
methodsToSpy.forEach((method) => {
|
|
1964
|
-
const original = context[method];
|
|
1965
|
-
if (typeof original === "function") {
|
|
1966
|
-
context[method] = function(...args) {
|
|
1967
|
-
drawCalls.push({ method, args });
|
|
1968
|
-
return original.apply(this, args);
|
|
1951
|
+
const calls = [];
|
|
1952
|
+
const proto = Object.getPrototypeOf(context);
|
|
1953
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
1954
|
+
const value = context[key];
|
|
1955
|
+
if (typeof value === "function") {
|
|
1956
|
+
context[key] = function(...args) {
|
|
1957
|
+
calls.push({ method: key, args });
|
|
1958
|
+
return value.apply(context, args);
|
|
1969
1959
|
};
|
|
1970
1960
|
}
|
|
1971
|
-
}
|
|
1972
|
-
return
|
|
1961
|
+
}
|
|
1962
|
+
return calls;
|
|
1973
1963
|
}
|
|
1974
1964
|
function createMockImageData(width, height, fillColor) {
|
|
1975
|
-
|
|
1965
|
+
if (typeof global.ImageData !== "undefined") {
|
|
1966
|
+
const data2 = new Uint8ClampedArray(width * height * 4);
|
|
1967
|
+
if (fillColor) {
|
|
1968
|
+
for (let i = 0; i < data2.length; i += 4) {
|
|
1969
|
+
data2[i] = fillColor[0];
|
|
1970
|
+
data2[i + 1] = fillColor[1];
|
|
1971
|
+
data2[i + 2] = fillColor[2];
|
|
1972
|
+
data2[i + 3] = fillColor[3];
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1975
|
+
return new global.ImageData(data2, width, height);
|
|
1976
|
+
}
|
|
1977
|
+
const data = new Uint8ClampedArray(width * height * 4);
|
|
1976
1978
|
if (fillColor) {
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
imageData.data[i + 3] = a;
|
|
1979
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
1980
|
+
data[i] = fillColor[0];
|
|
1981
|
+
data[i + 1] = fillColor[1];
|
|
1982
|
+
data[i + 2] = fillColor[2];
|
|
1983
|
+
data[i + 3] = fillColor[3];
|
|
1983
1984
|
}
|
|
1984
1985
|
}
|
|
1985
|
-
return
|
|
1986
|
+
return new ImageData2(data, width, height);
|
|
1986
1987
|
}
|
|
1987
|
-
function createMockImage(width, height, src) {
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1988
|
+
function createMockImage(width = 100, height = 100, src = "") {
|
|
1989
|
+
if (typeof document !== "undefined" && document.createElement) {
|
|
1990
|
+
const img2 = document.createElement("img");
|
|
1991
|
+
img2.width = width;
|
|
1992
|
+
img2.height = height;
|
|
1993
|
+
if (src) img2.src = src;
|
|
1994
|
+
return img2;
|
|
1995
|
+
}
|
|
1996
|
+
const img = {
|
|
1997
|
+
width,
|
|
1998
|
+
height,
|
|
1999
|
+
src,
|
|
2000
|
+
complete: true,
|
|
2001
|
+
onload: null,
|
|
2002
|
+
onerror: null
|
|
2003
|
+
};
|
|
2004
|
+
if (src) {
|
|
2005
|
+
setTimeout(() => {
|
|
2006
|
+
if (img.onload) img.onload();
|
|
2007
|
+
}, 0);
|
|
2008
|
+
}
|
|
1992
2009
|
return img;
|
|
1993
2010
|
}
|
|
1994
2011
|
|
|
@@ -2056,376 +2073,285 @@ function setupWebGPUMocks() {
|
|
|
2056
2073
|
}
|
|
2057
2074
|
|
|
2058
2075
|
// src/setup/timing.ts
|
|
2059
|
-
var activeMockRAF;
|
|
2060
2076
|
function createMockRAF() {
|
|
2061
2077
|
let callbacks = [];
|
|
2062
|
-
let
|
|
2078
|
+
let lastId = 0;
|
|
2063
2079
|
let currentTime = 0;
|
|
2064
2080
|
const originalRAF = global.requestAnimationFrame;
|
|
2065
2081
|
const originalCancelRAF = global.cancelAnimationFrame;
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
callbacks.push({ id, callback });
|
|
2069
|
-
return
|
|
2082
|
+
global.requestAnimationFrame = (callback) => {
|
|
2083
|
+
lastId++;
|
|
2084
|
+
callbacks.push({ id: lastId, callback });
|
|
2085
|
+
return lastId;
|
|
2070
2086
|
};
|
|
2071
|
-
|
|
2087
|
+
global.cancelAnimationFrame = (id) => {
|
|
2072
2088
|
callbacks = callbacks.filter((cb) => cb.id !== id);
|
|
2073
2089
|
};
|
|
2074
|
-
|
|
2075
|
-
tick(
|
|
2076
|
-
if (
|
|
2077
|
-
|
|
2078
|
-
} else {
|
|
2079
|
-
currentTime = timestamp;
|
|
2080
|
-
}
|
|
2090
|
+
return {
|
|
2091
|
+
tick(time) {
|
|
2092
|
+
if (time) currentTime = time;
|
|
2093
|
+
else currentTime += 16.66;
|
|
2081
2094
|
const currentCallbacks = [...callbacks];
|
|
2082
2095
|
callbacks = [];
|
|
2083
|
-
currentCallbacks.forEach((
|
|
2084
|
-
callback(currentTime);
|
|
2085
|
-
});
|
|
2096
|
+
currentCallbacks.forEach((cb) => cb.callback(currentTime));
|
|
2086
2097
|
},
|
|
2087
|
-
advance(
|
|
2088
|
-
|
|
2098
|
+
advance(ms) {
|
|
2099
|
+
currentTime += ms;
|
|
2100
|
+
this.tick(currentTime);
|
|
2089
2101
|
},
|
|
2090
2102
|
getCallbacks() {
|
|
2091
|
-
return callbacks
|
|
2092
|
-
},
|
|
2093
|
-
reset() {
|
|
2094
|
-
callbacks = [];
|
|
2095
|
-
nextId = 1;
|
|
2096
|
-
currentTime = 0;
|
|
2097
|
-
},
|
|
2098
|
-
enable() {
|
|
2099
|
-
activeMockRAF = this;
|
|
2100
|
-
global.requestAnimationFrame = raf;
|
|
2101
|
-
global.cancelAnimationFrame = cancel;
|
|
2102
|
-
},
|
|
2103
|
-
disable() {
|
|
2104
|
-
if (activeMockRAF === this) {
|
|
2105
|
-
activeMockRAF = void 0;
|
|
2106
|
-
}
|
|
2107
|
-
if (originalRAF) {
|
|
2108
|
-
global.requestAnimationFrame = originalRAF;
|
|
2109
|
-
} else {
|
|
2110
|
-
delete global.requestAnimationFrame;
|
|
2111
|
-
}
|
|
2112
|
-
if (originalCancelRAF) {
|
|
2113
|
-
global.cancelAnimationFrame = originalCancelRAF;
|
|
2114
|
-
} else {
|
|
2115
|
-
delete global.cancelAnimationFrame;
|
|
2116
|
-
}
|
|
2103
|
+
return callbacks;
|
|
2117
2104
|
}
|
|
2118
2105
|
};
|
|
2119
|
-
return mock;
|
|
2120
2106
|
}
|
|
2121
2107
|
function createMockPerformance(startTime = 0) {
|
|
2122
|
-
let
|
|
2123
|
-
const
|
|
2124
|
-
now: () =>
|
|
2108
|
+
let now = startTime;
|
|
2109
|
+
const mockPerformance = {
|
|
2110
|
+
now: () => now,
|
|
2125
2111
|
timeOrigin: startTime,
|
|
2126
2112
|
timing: {
|
|
2127
2113
|
navigationStart: startTime
|
|
2128
2114
|
},
|
|
2129
|
-
|
|
2130
|
-
},
|
|
2131
|
-
clearMeasures: () => {
|
|
2115
|
+
mark: (_name) => {
|
|
2132
2116
|
},
|
|
2133
|
-
|
|
2117
|
+
measure: (_name, _start, _end) => {
|
|
2134
2118
|
},
|
|
2135
2119
|
getEntries: () => [],
|
|
2136
|
-
getEntriesByName: () => [],
|
|
2137
|
-
getEntriesByType: () => [],
|
|
2138
|
-
|
|
2139
|
-
},
|
|
2140
|
-
measure: () => {
|
|
2120
|
+
getEntriesByName: (_name) => [],
|
|
2121
|
+
getEntriesByType: (_type) => [],
|
|
2122
|
+
clearMarks: (_name) => {
|
|
2141
2123
|
},
|
|
2142
|
-
|
|
2124
|
+
clearMeasures: (_name) => {
|
|
2143
2125
|
},
|
|
2144
|
-
|
|
2145
|
-
addEventListener: () => {
|
|
2126
|
+
clearResourceTimings: () => {
|
|
2146
2127
|
},
|
|
2147
|
-
|
|
2128
|
+
setResourceTimingBufferSize: (_maxSize) => {
|
|
2148
2129
|
},
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
mockPerf.advance = (deltaMs) => {
|
|
2152
|
-
currentTime += deltaMs;
|
|
2153
|
-
};
|
|
2154
|
-
mockPerf.setTime = (time) => {
|
|
2155
|
-
currentTime = time;
|
|
2130
|
+
onresourcetimingbufferfull: null,
|
|
2131
|
+
toJSON: () => ({})
|
|
2156
2132
|
};
|
|
2157
|
-
|
|
2133
|
+
if (typeof global.performance === "undefined") {
|
|
2134
|
+
global.performance = mockPerformance;
|
|
2135
|
+
}
|
|
2136
|
+
return mockPerformance;
|
|
2158
2137
|
}
|
|
2159
2138
|
function createControlledTimer() {
|
|
2160
|
-
|
|
2161
|
-
let timers = [];
|
|
2162
|
-
let nextId = 1;
|
|
2163
|
-
const originalSetTimeout = global.setTimeout;
|
|
2164
|
-
const originalClearTimeout = global.clearTimeout;
|
|
2165
|
-
const originalSetInterval = global.setInterval;
|
|
2166
|
-
const originalClearInterval = global.clearInterval;
|
|
2167
|
-
const mockSetTimeout = (callback, delay = 0, ...args) => {
|
|
2168
|
-
const id = nextId++;
|
|
2169
|
-
timers.push({ id, callback, dueTime: currentTime + delay, args });
|
|
2170
|
-
return id;
|
|
2171
|
-
};
|
|
2172
|
-
const mockClearTimeout = (id) => {
|
|
2173
|
-
timers = timers.filter((t) => t.id !== id);
|
|
2174
|
-
};
|
|
2175
|
-
const mockSetInterval = (callback, delay = 0, ...args) => {
|
|
2176
|
-
const id = nextId++;
|
|
2177
|
-
timers.push({ id, callback, dueTime: currentTime + delay, interval: delay, args });
|
|
2178
|
-
return id;
|
|
2179
|
-
};
|
|
2180
|
-
const mockClearInterval = (id) => {
|
|
2181
|
-
timers = timers.filter((t) => t.id !== id);
|
|
2182
|
-
};
|
|
2183
|
-
global.setTimeout = mockSetTimeout;
|
|
2184
|
-
global.clearTimeout = mockClearTimeout;
|
|
2185
|
-
global.setInterval = mockSetInterval;
|
|
2186
|
-
global.clearInterval = mockClearInterval;
|
|
2139
|
+
console.warn("createControlledTimer: Recommend using vi.useFakeTimers() instead.");
|
|
2187
2140
|
return {
|
|
2188
|
-
|
|
2189
|
-
this.advanceBy(0);
|
|
2190
|
-
},
|
|
2191
|
-
advanceBy(ms) {
|
|
2192
|
-
const targetTime = currentTime + ms;
|
|
2193
|
-
while (true) {
|
|
2194
|
-
let earliest = null;
|
|
2195
|
-
for (const t of timers) {
|
|
2196
|
-
if (!earliest || t.dueTime < earliest.dueTime) {
|
|
2197
|
-
earliest = t;
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2200
|
-
if (!earliest || earliest.dueTime > targetTime) {
|
|
2201
|
-
break;
|
|
2202
|
-
}
|
|
2203
|
-
currentTime = earliest.dueTime;
|
|
2204
|
-
const { callback, args, interval, id } = earliest;
|
|
2205
|
-
if (interval !== void 0) {
|
|
2206
|
-
earliest.dueTime += interval;
|
|
2207
|
-
if (interval === 0) earliest.dueTime += 1;
|
|
2208
|
-
} else {
|
|
2209
|
-
timers = timers.filter((t) => t.id !== id);
|
|
2210
|
-
}
|
|
2211
|
-
callback(...args);
|
|
2212
|
-
}
|
|
2213
|
-
currentTime = targetTime;
|
|
2141
|
+
advanceBy: (ms) => {
|
|
2214
2142
|
},
|
|
2215
|
-
|
|
2216
|
-
timers = [];
|
|
2143
|
+
runAll: () => {
|
|
2217
2144
|
},
|
|
2218
|
-
|
|
2219
|
-
global.setTimeout = originalSetTimeout;
|
|
2220
|
-
global.clearTimeout = originalClearTimeout;
|
|
2221
|
-
global.setInterval = originalSetInterval;
|
|
2222
|
-
global.clearInterval = originalClearInterval;
|
|
2145
|
+
clear: () => {
|
|
2223
2146
|
}
|
|
2224
2147
|
};
|
|
2225
2148
|
}
|
|
2226
|
-
function simulateFrames(count,
|
|
2227
|
-
if (!activeMockRAF) {
|
|
2228
|
-
throw new Error("simulateFrames requires an active MockRAF. Ensure createMockRAF().enable() is called.");
|
|
2229
|
-
}
|
|
2230
|
-
for (let i = 0; i < count; i++) {
|
|
2231
|
-
if (callback) callback(i);
|
|
2232
|
-
activeMockRAF.advance(frameTimeMs);
|
|
2233
|
-
}
|
|
2234
|
-
}
|
|
2235
|
-
function simulateFramesWithMock(mock, count, frameTimeMs = 16.6, callback) {
|
|
2149
|
+
function simulateFrames(count, frameTime = 16, callback) {
|
|
2236
2150
|
for (let i = 0; i < count; i++) {
|
|
2237
2151
|
if (callback) callback(i);
|
|
2238
|
-
mock.advance(frameTimeMs);
|
|
2239
2152
|
}
|
|
2240
2153
|
}
|
|
2241
2154
|
|
|
2242
2155
|
// src/setup/node.ts
|
|
2243
2156
|
function setupNodeEnvironment(options = {}) {
|
|
2244
|
-
|
|
2245
|
-
|
|
2157
|
+
}
|
|
2158
|
+
function teardownNodeEnvironment() {
|
|
2159
|
+
}
|
|
2160
|
+
|
|
2161
|
+
// src/engine/rendering.ts
|
|
2162
|
+
import { vi as vi12 } from "vitest";
|
|
2163
|
+
function createMockRenderingContext() {
|
|
2164
|
+
const gl = createMockWebGL2Context();
|
|
2165
|
+
const camera = {
|
|
2166
|
+
update: vi12.fn(),
|
|
2167
|
+
getViewMatrix: vi12.fn().mockReturnValue(new Float32Array(16)),
|
|
2168
|
+
getProjectionMatrix: vi12.fn().mockReturnValue(new Float32Array(16)),
|
|
2169
|
+
getViewProjectionMatrix: vi12.fn().mockReturnValue(new Float32Array(16)),
|
|
2170
|
+
getPosition: vi12.fn().mockReturnValue([0, 0, 0]),
|
|
2171
|
+
getForward: vi12.fn().mockReturnValue([0, 0, -1]),
|
|
2172
|
+
getRight: vi12.fn().mockReturnValue([1, 0, 0]),
|
|
2173
|
+
getUp: vi12.fn().mockReturnValue([0, 1, 0]),
|
|
2174
|
+
extractFrustumPlanes: vi12.fn(),
|
|
2175
|
+
transform: {
|
|
2176
|
+
origin: [0, 0, 0],
|
|
2177
|
+
angles: [0, 0, 0],
|
|
2178
|
+
fov: 90
|
|
2179
|
+
}
|
|
2180
|
+
};
|
|
2181
|
+
const md2 = {
|
|
2182
|
+
render: vi12.fn(),
|
|
2183
|
+
init: vi12.fn(),
|
|
2184
|
+
resize: vi12.fn()
|
|
2185
|
+
};
|
|
2186
|
+
const bsp = {
|
|
2187
|
+
render: vi12.fn(),
|
|
2188
|
+
init: vi12.fn(),
|
|
2189
|
+
resize: vi12.fn()
|
|
2190
|
+
};
|
|
2191
|
+
const sprite = {
|
|
2192
|
+
render: vi12.fn(),
|
|
2193
|
+
init: vi12.fn(),
|
|
2194
|
+
resize: vi12.fn()
|
|
2195
|
+
};
|
|
2196
|
+
const poly = {
|
|
2197
|
+
render: vi12.fn(),
|
|
2198
|
+
init: vi12.fn(),
|
|
2199
|
+
resize: vi12.fn()
|
|
2200
|
+
};
|
|
2201
|
+
const particle = {
|
|
2202
|
+
render: vi12.fn(),
|
|
2203
|
+
init: vi12.fn(),
|
|
2204
|
+
resize: vi12.fn()
|
|
2205
|
+
};
|
|
2206
|
+
return {
|
|
2207
|
+
gl,
|
|
2208
|
+
camera,
|
|
2209
|
+
pipelines: {
|
|
2210
|
+
md2,
|
|
2211
|
+
bsp,
|
|
2212
|
+
sprite,
|
|
2213
|
+
poly,
|
|
2214
|
+
particle
|
|
2215
|
+
}
|
|
2216
|
+
};
|
|
2246
2217
|
}
|
|
2247
2218
|
|
|
2248
2219
|
// src/setup/storage.ts
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
const storage = new Map(Object.entries(initialData));
|
|
2220
|
+
function createStorageMock(initialData = {}) {
|
|
2221
|
+
const store = new Map(Object.entries(initialData));
|
|
2252
2222
|
return {
|
|
2253
|
-
getItem: (key) =>
|
|
2254
|
-
setItem: (key, value) =>
|
|
2255
|
-
removeItem: (key) =>
|
|
2256
|
-
clear: () =>
|
|
2257
|
-
key: (index) => Array.from(
|
|
2223
|
+
getItem: (key) => store.get(key) || null,
|
|
2224
|
+
setItem: (key, value) => store.set(key, value.toString()),
|
|
2225
|
+
removeItem: (key) => store.delete(key),
|
|
2226
|
+
clear: () => store.clear(),
|
|
2227
|
+
key: (index) => Array.from(store.keys())[index] || null,
|
|
2258
2228
|
get length() {
|
|
2259
|
-
return
|
|
2229
|
+
return store.size;
|
|
2260
2230
|
}
|
|
2261
2231
|
};
|
|
2262
2232
|
}
|
|
2233
|
+
function createMockLocalStorage(initialData = {}) {
|
|
2234
|
+
return createStorageMock(initialData);
|
|
2235
|
+
}
|
|
2263
2236
|
function createMockSessionStorage(initialData = {}) {
|
|
2264
|
-
return
|
|
2237
|
+
return createStorageMock(initialData);
|
|
2265
2238
|
}
|
|
2266
|
-
function createMockIndexedDB() {
|
|
2267
|
-
|
|
2268
|
-
throw new Error("IndexedDB mock not found. Ensure fake-indexeddb is loaded.");
|
|
2269
|
-
}
|
|
2270
|
-
return indexedDB;
|
|
2239
|
+
function createMockIndexedDB(databases) {
|
|
2240
|
+
return global.indexedDB;
|
|
2271
2241
|
}
|
|
2272
2242
|
function createStorageTestScenario(storageType = "local") {
|
|
2273
|
-
if (storageType === "indexed") {
|
|
2274
|
-
const dbName = `test-db-${Math.random().toString(36).substring(7)}`;
|
|
2275
|
-
const storeName = "test-store";
|
|
2276
|
-
const storage2 = createMockIndexedDB();
|
|
2277
|
-
return {
|
|
2278
|
-
storage: storage2,
|
|
2279
|
-
populate: async (data) => {
|
|
2280
|
-
return new Promise((resolve, reject) => {
|
|
2281
|
-
const req = storage2.open(dbName, 1);
|
|
2282
|
-
req.onupgradeneeded = (e) => {
|
|
2283
|
-
const db = e.target.result;
|
|
2284
|
-
db.createObjectStore(storeName);
|
|
2285
|
-
};
|
|
2286
|
-
req.onsuccess = (e) => {
|
|
2287
|
-
const db = e.target.result;
|
|
2288
|
-
const tx = db.transaction(storeName, "readwrite");
|
|
2289
|
-
const store = tx.objectStore(storeName);
|
|
2290
|
-
Object.entries(data).forEach(([k, v]) => store.put(v, k));
|
|
2291
|
-
tx.oncomplete = () => {
|
|
2292
|
-
db.close();
|
|
2293
|
-
resolve();
|
|
2294
|
-
};
|
|
2295
|
-
tx.onerror = () => reject(tx.error);
|
|
2296
|
-
};
|
|
2297
|
-
req.onerror = () => reject(req.error);
|
|
2298
|
-
});
|
|
2299
|
-
},
|
|
2300
|
-
verify: async (key, value) => {
|
|
2301
|
-
return new Promise((resolve, reject) => {
|
|
2302
|
-
const req = storage2.open(dbName, 1);
|
|
2303
|
-
req.onsuccess = (e) => {
|
|
2304
|
-
const db = e.target.result;
|
|
2305
|
-
if (!db.objectStoreNames.contains(storeName)) {
|
|
2306
|
-
db.close();
|
|
2307
|
-
resolve(false);
|
|
2308
|
-
return;
|
|
2309
|
-
}
|
|
2310
|
-
const tx = db.transaction(storeName, "readonly");
|
|
2311
|
-
const store = tx.objectStore(storeName);
|
|
2312
|
-
const getReq = store.get(key);
|
|
2313
|
-
getReq.onsuccess = () => {
|
|
2314
|
-
const result = getReq.result === value;
|
|
2315
|
-
db.close();
|
|
2316
|
-
resolve(result);
|
|
2317
|
-
};
|
|
2318
|
-
getReq.onerror = () => {
|
|
2319
|
-
db.close();
|
|
2320
|
-
resolve(false);
|
|
2321
|
-
};
|
|
2322
|
-
};
|
|
2323
|
-
req.onerror = () => reject(req.error);
|
|
2324
|
-
});
|
|
2325
|
-
}
|
|
2326
|
-
};
|
|
2327
|
-
}
|
|
2328
|
-
const storage = storageType === "local" ? createMockLocalStorage() : createMockSessionStorage();
|
|
2329
2243
|
return {
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
},
|
|
2334
|
-
verify(key, value) {
|
|
2335
|
-
return storage.getItem(key) === value;
|
|
2336
|
-
}
|
|
2244
|
+
localStorage: createMockLocalStorage(),
|
|
2245
|
+
sessionStorage: createMockSessionStorage(),
|
|
2246
|
+
indexedDB: createMockIndexedDB()
|
|
2337
2247
|
};
|
|
2338
2248
|
}
|
|
2339
2249
|
|
|
2340
2250
|
// src/setup/audio.ts
|
|
2341
|
-
function createMockAudioContext() {
|
|
2342
|
-
const context = {
|
|
2343
|
-
createGain: () => ({
|
|
2344
|
-
connect: () => {
|
|
2345
|
-
},
|
|
2346
|
-
gain: { value: 1, setValueAtTime: () => {
|
|
2347
|
-
} }
|
|
2348
|
-
}),
|
|
2349
|
-
createOscillator: () => ({
|
|
2350
|
-
connect: () => {
|
|
2351
|
-
},
|
|
2352
|
-
start: () => {
|
|
2353
|
-
},
|
|
2354
|
-
stop: () => {
|
|
2355
|
-
},
|
|
2356
|
-
frequency: { value: 440 }
|
|
2357
|
-
}),
|
|
2358
|
-
createBufferSource: () => ({
|
|
2359
|
-
connect: () => {
|
|
2360
|
-
},
|
|
2361
|
-
start: () => {
|
|
2362
|
-
},
|
|
2363
|
-
stop: () => {
|
|
2364
|
-
},
|
|
2365
|
-
buffer: null,
|
|
2366
|
-
playbackRate: { value: 1 },
|
|
2367
|
-
loop: false
|
|
2368
|
-
}),
|
|
2369
|
-
destination: {},
|
|
2370
|
-
currentTime: 0,
|
|
2371
|
-
state: "running",
|
|
2372
|
-
resume: async () => {
|
|
2373
|
-
},
|
|
2374
|
-
suspend: async () => {
|
|
2375
|
-
},
|
|
2376
|
-
close: async () => {
|
|
2377
|
-
},
|
|
2378
|
-
decodeAudioData: async (buffer) => ({
|
|
2379
|
-
duration: 1,
|
|
2380
|
-
length: 44100,
|
|
2381
|
-
sampleRate: 44100,
|
|
2382
|
-
numberOfChannels: 2,
|
|
2383
|
-
getChannelData: () => new Float32Array(44100)
|
|
2384
|
-
}),
|
|
2385
|
-
createBuffer: (channels, length2, sampleRate) => ({
|
|
2386
|
-
duration: length2 / sampleRate,
|
|
2387
|
-
length: length2,
|
|
2388
|
-
sampleRate,
|
|
2389
|
-
numberOfChannels: channels,
|
|
2390
|
-
getChannelData: () => new Float32Array(length2)
|
|
2391
|
-
}),
|
|
2392
|
-
// Helper to track events if needed
|
|
2393
|
-
_events: []
|
|
2394
|
-
};
|
|
2395
|
-
return new Proxy(context, {
|
|
2396
|
-
get(target, prop, receiver) {
|
|
2397
|
-
if (prop === "_events") return target._events;
|
|
2398
|
-
const value = Reflect.get(target, prop, receiver);
|
|
2399
|
-
if (typeof value === "function") {
|
|
2400
|
-
return (...args) => {
|
|
2401
|
-
target._events.push({ type: String(prop), args });
|
|
2402
|
-
return Reflect.apply(value, target, args);
|
|
2403
|
-
};
|
|
2404
|
-
}
|
|
2405
|
-
return value;
|
|
2406
|
-
}
|
|
2407
|
-
});
|
|
2408
|
-
}
|
|
2409
2251
|
function setupMockAudioContext() {
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2252
|
+
class MockAudioContext {
|
|
2253
|
+
constructor() {
|
|
2254
|
+
this.state = "suspended";
|
|
2255
|
+
this.destination = {};
|
|
2256
|
+
this.currentTime = 0;
|
|
2257
|
+
this.listener = {
|
|
2258
|
+
positionX: { value: 0 },
|
|
2259
|
+
positionY: { value: 0 },
|
|
2260
|
+
positionZ: { value: 0 },
|
|
2261
|
+
forwardX: { value: 0 },
|
|
2262
|
+
forwardY: { value: 0 },
|
|
2263
|
+
forwardZ: { value: 0 },
|
|
2264
|
+
upX: { value: 0 },
|
|
2265
|
+
upY: { value: 0 },
|
|
2266
|
+
upZ: { value: 0 },
|
|
2267
|
+
setOrientation: () => {
|
|
2268
|
+
},
|
|
2269
|
+
setPosition: () => {
|
|
2270
|
+
}
|
|
2271
|
+
};
|
|
2272
|
+
}
|
|
2273
|
+
createGain() {
|
|
2274
|
+
return {
|
|
2275
|
+
gain: { value: 1, linearRampToValueAtTime: () => {
|
|
2276
|
+
} },
|
|
2277
|
+
connect: () => {
|
|
2278
|
+
},
|
|
2279
|
+
disconnect: () => {
|
|
2280
|
+
}
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2283
|
+
createBufferSource() {
|
|
2284
|
+
return {
|
|
2285
|
+
buffer: null,
|
|
2286
|
+
loop: false,
|
|
2287
|
+
playbackRate: { value: 1 },
|
|
2288
|
+
connect: () => {
|
|
2289
|
+
},
|
|
2290
|
+
start: () => {
|
|
2291
|
+
},
|
|
2292
|
+
stop: () => {
|
|
2293
|
+
},
|
|
2294
|
+
disconnect: () => {
|
|
2295
|
+
},
|
|
2296
|
+
onended: null
|
|
2297
|
+
};
|
|
2298
|
+
}
|
|
2299
|
+
createPanner() {
|
|
2300
|
+
return {
|
|
2301
|
+
panningModel: "equalpower",
|
|
2302
|
+
distanceModel: "inverse",
|
|
2303
|
+
positionX: { value: 0 },
|
|
2304
|
+
positionY: { value: 0 },
|
|
2305
|
+
positionZ: { value: 0 },
|
|
2306
|
+
orientationX: { value: 0 },
|
|
2307
|
+
orientationY: { value: 0 },
|
|
2308
|
+
orientationZ: { value: 0 },
|
|
2309
|
+
coneInnerAngle: 360,
|
|
2310
|
+
coneOuterAngle: 360,
|
|
2311
|
+
coneOuterGain: 0,
|
|
2312
|
+
connect: () => {
|
|
2313
|
+
},
|
|
2314
|
+
disconnect: () => {
|
|
2315
|
+
},
|
|
2316
|
+
setPosition: () => {
|
|
2317
|
+
},
|
|
2318
|
+
setOrientation: () => {
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
createBuffer(numOfChannels, length2, sampleRate) {
|
|
2323
|
+
return {
|
|
2324
|
+
duration: length2 / sampleRate,
|
|
2325
|
+
length: length2,
|
|
2326
|
+
sampleRate,
|
|
2327
|
+
numberOfChannels: numOfChannels,
|
|
2328
|
+
getChannelData: () => new Float32Array(length2)
|
|
2329
|
+
};
|
|
2330
|
+
}
|
|
2331
|
+
decodeAudioData(data, success) {
|
|
2332
|
+
const buffer = this.createBuffer(2, 100, 44100);
|
|
2333
|
+
if (success) success(buffer);
|
|
2334
|
+
return Promise.resolve(buffer);
|
|
2335
|
+
}
|
|
2336
|
+
resume() {
|
|
2337
|
+
return Promise.resolve();
|
|
2338
|
+
}
|
|
2339
|
+
suspend() {
|
|
2340
|
+
return Promise.resolve();
|
|
2341
|
+
}
|
|
2342
|
+
close() {
|
|
2343
|
+
return Promise.resolve();
|
|
2344
|
+
}
|
|
2418
2345
|
}
|
|
2346
|
+
global.AudioContext = MockAudioContext;
|
|
2347
|
+
global.webkitAudioContext = MockAudioContext;
|
|
2419
2348
|
}
|
|
2420
2349
|
function teardownMockAudioContext() {
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
delete global.window.AudioContext;
|
|
2424
|
-
delete global.window.webkitAudioContext;
|
|
2425
|
-
}
|
|
2350
|
+
delete global.AudioContext;
|
|
2351
|
+
delete global.webkitAudioContext;
|
|
2426
2352
|
}
|
|
2427
2353
|
function captureAudioEvents(context) {
|
|
2428
|
-
return
|
|
2354
|
+
return [];
|
|
2429
2355
|
}
|
|
2430
2356
|
|
|
2431
2357
|
// ../../node_modules/.pnpm/gl-matrix@3.4.4/node_modules/gl-matrix/esm/common.js
|
|
@@ -2915,217 +2841,110 @@ function simulateCameraMovement(camera, input, deltaTime) {
|
|
|
2915
2841
|
}
|
|
2916
2842
|
|
|
2917
2843
|
// src/e2e/playwright.ts
|
|
2918
|
-
import { chromium } from "playwright";
|
|
2919
|
-
import { createServer } from "http";
|
|
2920
|
-
import handler from "serve-handler";
|
|
2921
2844
|
async function createPlaywrightTestClient(options = {}) {
|
|
2922
|
-
let
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
return handler(request, response, {
|
|
2928
|
-
public: rootPath,
|
|
2929
|
-
cleanUrls: false,
|
|
2930
|
-
headers: [
|
|
2931
|
-
{
|
|
2932
|
-
source: "**/*",
|
|
2933
|
-
headers: [
|
|
2934
|
-
{ key: "Cache-Control", value: "no-cache" },
|
|
2935
|
-
{ key: "Access-Control-Allow-Origin", value: "*" },
|
|
2936
|
-
{ key: "Cross-Origin-Opener-Policy", value: "same-origin" },
|
|
2937
|
-
{ key: "Cross-Origin-Embedder-Policy", value: "require-corp" }
|
|
2938
|
-
]
|
|
2939
|
-
}
|
|
2940
|
-
]
|
|
2941
|
-
});
|
|
2942
|
-
});
|
|
2943
|
-
await new Promise((resolve) => {
|
|
2944
|
-
if (!staticServer) return;
|
|
2945
|
-
staticServer.listen(0, () => {
|
|
2946
|
-
const addr = staticServer?.address();
|
|
2947
|
-
const port = typeof addr === "object" ? addr?.port : 0;
|
|
2948
|
-
clientUrl = `http://localhost:${port}`;
|
|
2949
|
-
console.log(`Test client serving from ${rootPath} at ${clientUrl}`);
|
|
2950
|
-
resolve();
|
|
2951
|
-
});
|
|
2952
|
-
});
|
|
2845
|
+
let playwright;
|
|
2846
|
+
try {
|
|
2847
|
+
playwright = await import("playwright");
|
|
2848
|
+
} catch (e) {
|
|
2849
|
+
throw new Error("Playwright is not installed. Please install it to use this utility.");
|
|
2953
2850
|
}
|
|
2954
|
-
const browser = await chromium.launch({
|
|
2955
|
-
headless: options.headless ?? true
|
|
2956
|
-
args: [
|
|
2957
|
-
"--use-gl=egl",
|
|
2958
|
-
"--ignore-gpu-blocklist",
|
|
2959
|
-
...options.launchOptions?.args || []
|
|
2960
|
-
],
|
|
2961
|
-
...options.launchOptions
|
|
2851
|
+
const browser = await playwright.chromium.launch({
|
|
2852
|
+
headless: options.headless ?? true
|
|
2962
2853
|
});
|
|
2963
|
-
const width = options.width || 1280;
|
|
2964
|
-
const height = options.height || 720;
|
|
2965
2854
|
const context = await browser.newContext({
|
|
2966
|
-
viewport: { width, height }
|
|
2967
|
-
deviceScaleFactor: 1,
|
|
2968
|
-
...options.contextOptions
|
|
2855
|
+
viewport: options.viewport || { width: 1280, height: 720 }
|
|
2969
2856
|
});
|
|
2970
2857
|
const page = await context.newPage();
|
|
2971
|
-
const close = async () => {
|
|
2972
|
-
await browser.close();
|
|
2973
|
-
if (staticServer) {
|
|
2974
|
-
staticServer.close();
|
|
2975
|
-
}
|
|
2976
|
-
};
|
|
2977
|
-
const navigate = async (url) => {
|
|
2978
|
-
const targetUrl = url || clientUrl;
|
|
2979
|
-
if (!targetUrl) throw new Error("No URL to navigate to");
|
|
2980
|
-
let finalUrl = targetUrl;
|
|
2981
|
-
if (options.serverUrl && !targetUrl.includes("connect=")) {
|
|
2982
|
-
const separator = targetUrl.includes("?") ? "&" : "?";
|
|
2983
|
-
finalUrl = `${targetUrl}${separator}connect=${encodeURIComponent(options.serverUrl)}`;
|
|
2984
|
-
}
|
|
2985
|
-
console.log(`Navigating to: ${finalUrl}`);
|
|
2986
|
-
await page.goto(finalUrl, { waitUntil: "domcontentloaded" });
|
|
2987
|
-
};
|
|
2988
2858
|
return {
|
|
2989
|
-
browser,
|
|
2990
|
-
context,
|
|
2991
2859
|
page,
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2860
|
+
browser,
|
|
2861
|
+
async navigate(url) {
|
|
2862
|
+
await page.goto(url);
|
|
2863
|
+
},
|
|
2864
|
+
async waitForGame() {
|
|
2865
|
+
await waitForGameReady(page);
|
|
2997
2866
|
},
|
|
2998
|
-
|
|
2867
|
+
async injectInput(type, data) {
|
|
2999
2868
|
await page.evaluate(({ type: type2, data: data2 }) => {
|
|
3000
|
-
|
|
2869
|
+
console.log("Injecting input", type2, data2);
|
|
3001
2870
|
}, { type, data });
|
|
2871
|
+
},
|
|
2872
|
+
async screenshot(name) {
|
|
2873
|
+
return await page.screenshot({ path: `${name}.png` });
|
|
2874
|
+
},
|
|
2875
|
+
async close() {
|
|
2876
|
+
await browser.close();
|
|
3002
2877
|
}
|
|
3003
2878
|
};
|
|
3004
2879
|
}
|
|
3005
2880
|
async function waitForGameReady(page, timeout = 1e4) {
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
}, null, { timeout });
|
|
3010
|
-
} catch (e) {
|
|
3011
|
-
await page.waitForSelector("canvas", { timeout });
|
|
3012
|
-
}
|
|
2881
|
+
await page.waitForFunction(() => {
|
|
2882
|
+
return window.game && window.game.isRunning;
|
|
2883
|
+
}, { timeout });
|
|
3013
2884
|
}
|
|
3014
2885
|
async function captureGameState(page) {
|
|
3015
2886
|
return await page.evaluate(() => {
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
2887
|
+
const game = window.game;
|
|
2888
|
+
return {
|
|
2889
|
+
time: game ? game.time : 0,
|
|
2890
|
+
entities: game && game.entities ? game.entities.length : 0
|
|
2891
|
+
};
|
|
3020
2892
|
});
|
|
3021
2893
|
}
|
|
3022
2894
|
|
|
3023
2895
|
// src/e2e/network.ts
|
|
3024
|
-
var CONDITIONS = {
|
|
3025
|
-
"good": {
|
|
3026
|
-
offline: false,
|
|
3027
|
-
downloadThroughput: 10 * 1024 * 1024,
|
|
3028
|
-
// 10 Mbps
|
|
3029
|
-
uploadThroughput: 5 * 1024 * 1024,
|
|
3030
|
-
// 5 Mbps
|
|
3031
|
-
latency: 20
|
|
3032
|
-
},
|
|
3033
|
-
"slow": {
|
|
3034
|
-
offline: false,
|
|
3035
|
-
downloadThroughput: 500 * 1024,
|
|
3036
|
-
// 500 Kbps
|
|
3037
|
-
uploadThroughput: 500 * 1024,
|
|
3038
|
-
latency: 400
|
|
3039
|
-
},
|
|
3040
|
-
"unstable": {
|
|
3041
|
-
offline: false,
|
|
3042
|
-
downloadThroughput: 1 * 1024 * 1024,
|
|
3043
|
-
uploadThroughput: 1 * 1024 * 1024,
|
|
3044
|
-
latency: 100
|
|
3045
|
-
},
|
|
3046
|
-
"offline": {
|
|
3047
|
-
offline: true,
|
|
3048
|
-
downloadThroughput: 0,
|
|
3049
|
-
uploadThroughput: 0,
|
|
3050
|
-
latency: 0
|
|
3051
|
-
}
|
|
3052
|
-
};
|
|
3053
2896
|
function simulateNetworkCondition(condition) {
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
}
|
|
3057
|
-
|
|
2897
|
+
switch (condition) {
|
|
2898
|
+
case "good":
|
|
2899
|
+
return { latency: 20, jitter: 5, packetLoss: 0, bandwidth: 10 * 1024 * 1024 };
|
|
2900
|
+
case "slow":
|
|
2901
|
+
return { latency: 150, jitter: 20, packetLoss: 0.01, bandwidth: 1 * 1024 * 1024 };
|
|
2902
|
+
case "unstable":
|
|
2903
|
+
return { latency: 100, jitter: 100, packetLoss: 0.05, bandwidth: 512 * 1024 };
|
|
2904
|
+
case "offline":
|
|
2905
|
+
return { latency: 0, jitter: 0, packetLoss: 1, bandwidth: 0 };
|
|
2906
|
+
case "custom":
|
|
2907
|
+
default:
|
|
2908
|
+
return { latency: 0, jitter: 0, packetLoss: 0, bandwidth: Infinity };
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
function createCustomNetworkCondition(latency, jitter, packetLoss) {
|
|
3058
2912
|
return {
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
latency: latency + Math.random() * jitter,
|
|
3065
|
-
downloadThroughput: baseConfig?.downloadThroughput || -1,
|
|
3066
|
-
uploadThroughput: baseConfig?.uploadThroughput || -1
|
|
3067
|
-
});
|
|
3068
|
-
},
|
|
3069
|
-
async clear(page) {
|
|
3070
|
-
const client = await page.context().newCDPSession(page);
|
|
3071
|
-
await client.send("Network.emulateNetworkConditions", {
|
|
3072
|
-
offline: false,
|
|
3073
|
-
latency: 0,
|
|
3074
|
-
downloadThroughput: -1,
|
|
3075
|
-
uploadThroughput: -1
|
|
3076
|
-
});
|
|
3077
|
-
}
|
|
2913
|
+
latency,
|
|
2914
|
+
jitter,
|
|
2915
|
+
packetLoss,
|
|
2916
|
+
bandwidth: Infinity
|
|
2917
|
+
// Default to unlimited unless specified
|
|
3078
2918
|
};
|
|
3079
2919
|
}
|
|
3080
|
-
|
|
3081
|
-
const simulator = createCustomNetworkCondition(0, 0, 0, {
|
|
3082
|
-
offline: false,
|
|
3083
|
-
latency: 0,
|
|
3084
|
-
downloadThroughput: bytesPerSecond,
|
|
3085
|
-
uploadThroughput: bytesPerSecond
|
|
3086
|
-
});
|
|
3087
|
-
await simulator.apply(page);
|
|
2920
|
+
function throttleBandwidth(bytesPerSecond) {
|
|
3088
2921
|
}
|
|
3089
2922
|
|
|
3090
2923
|
// src/e2e/visual.ts
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
}
|
|
3104
|
-
async function compareScreenshots(baseline, current, threshold = 0.1) {
|
|
3105
|
-
if (baseline.equals(current)) {
|
|
3106
|
-
return { pixelDiff: 0, matched: true };
|
|
2924
|
+
async function captureGameScreenshot(page, name) {
|
|
2925
|
+
return await page.screenshot({ path: `${name}.png` });
|
|
2926
|
+
}
|
|
2927
|
+
function compareScreenshots(baseline, current, threshold = 0.01) {
|
|
2928
|
+
if (baseline.length !== current.length) {
|
|
2929
|
+
return { diffPercentage: 1 };
|
|
2930
|
+
}
|
|
2931
|
+
let diffPixels = 0;
|
|
2932
|
+
const totalPixels = baseline.length;
|
|
2933
|
+
for (let i = 0; i < baseline.length; i++) {
|
|
2934
|
+
if (baseline[i] !== current[i]) {
|
|
2935
|
+
diffPixels++;
|
|
2936
|
+
}
|
|
3107
2937
|
}
|
|
2938
|
+
const diffPercentage = diffPixels / totalPixels;
|
|
3108
2939
|
return {
|
|
3109
|
-
|
|
3110
|
-
//
|
|
3111
|
-
matched: false
|
|
2940
|
+
diffPercentage
|
|
2941
|
+
// Generating a diff image buffer would require a library like pixelmatch
|
|
3112
2942
|
};
|
|
3113
2943
|
}
|
|
3114
|
-
function createVisualTestScenario(
|
|
2944
|
+
function createVisualTestScenario(sceneName) {
|
|
3115
2945
|
return {
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
},
|
|
3119
|
-
async compare(snapshotName, baselineDir) {
|
|
3120
|
-
const name = `${sceneName}-${snapshotName}`;
|
|
3121
|
-
const current = await captureGameScreenshot(page, name, { dir: "__screenshots__/current" });
|
|
3122
|
-
try {
|
|
3123
|
-
const baselinePath = path.join(baselineDir, `${name}.png`);
|
|
3124
|
-
const baseline = await fs.readFile(baselinePath);
|
|
3125
|
-
return await compareScreenshots(baseline, current);
|
|
3126
|
-
} catch (e) {
|
|
3127
|
-
return { pixelDiff: -1, matched: false };
|
|
3128
|
-
}
|
|
2946
|
+
sceneName,
|
|
2947
|
+
setup: async () => {
|
|
3129
2948
|
}
|
|
3130
2949
|
};
|
|
3131
2950
|
}
|
|
@@ -3156,7 +2975,6 @@ export {
|
|
|
3156
2975
|
createMockAI,
|
|
3157
2976
|
createMockAmmoItem,
|
|
3158
2977
|
createMockArmorItem,
|
|
3159
|
-
createMockAudioContext,
|
|
3160
2978
|
createMockCamera,
|
|
3161
2979
|
createMockCanvas,
|
|
3162
2980
|
createMockCanvasContext2D,
|
|
@@ -3192,6 +3010,7 @@ export {
|
|
|
3192
3010
|
createMockRConClient,
|
|
3193
3011
|
createMockRateLimiter,
|
|
3194
3012
|
createMockRefDef,
|
|
3013
|
+
createMockRenderingContext,
|
|
3195
3014
|
createMockServer,
|
|
3196
3015
|
createMockServerClient,
|
|
3197
3016
|
createMockServerConsole,
|
|
@@ -3241,7 +3060,6 @@ export {
|
|
|
3241
3060
|
simulateBandwidthLimit,
|
|
3242
3061
|
simulateCameraMovement,
|
|
3243
3062
|
simulateFrames,
|
|
3244
|
-
simulateFramesWithMock,
|
|
3245
3063
|
simulateHandshake,
|
|
3246
3064
|
simulateNetworkCondition,
|
|
3247
3065
|
simulatePlayerInput,
|
|
@@ -3254,6 +3072,7 @@ export {
|
|
|
3254
3072
|
stairTrace,
|
|
3255
3073
|
teardownBrowserEnvironment,
|
|
3256
3074
|
teardownMockAudioContext,
|
|
3075
|
+
teardownNodeEnvironment,
|
|
3257
3076
|
throttleBandwidth,
|
|
3258
3077
|
verifySnapshotConsistency,
|
|
3259
3078
|
waitForGameReady
|