@quake2ts/test-utils 0.0.1 → 0.0.739
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +486 -556
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -38
- package/dist/index.d.ts +30 -38
- package/dist/index.js +482 -551
- package/dist/index.js.map +1 -1
- package/package.json +32 -9
- package/src/e2e/visual.ts +72 -139
- package/src/game/factories.ts +17 -0
- package/src/game/helpers.ts +35 -5
- package/src/index.ts +1 -1
- package/src/server/mocks/state.ts +21 -13
- package/src/game/mocks.ts +0 -105
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quake2ts/test-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.739",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -26,25 +26,43 @@
|
|
|
26
26
|
"dist",
|
|
27
27
|
"src"
|
|
28
28
|
],
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
"
|
|
33
|
-
"@quake2ts/engine": "^0.0.1",
|
|
34
|
-
"@quake2ts/shared": "0.0.1"
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/jburnhams/quake2.git",
|
|
32
|
+
"directory": "quake2ts/packages/test-utils"
|
|
35
33
|
},
|
|
36
34
|
"peerDependencies": {
|
|
37
35
|
"@webgpu/types": "^0.1.68",
|
|
36
|
+
"gl-matrix": "^3.4.4",
|
|
38
37
|
"pixelmatch": "^7.1.0",
|
|
39
38
|
"playwright": "^1.57.0",
|
|
40
39
|
"pngjs": "^7.0.0",
|
|
41
40
|
"vitest": "^1.6.0",
|
|
42
|
-
"webgpu": "^0.3.8"
|
|
41
|
+
"webgpu": "^0.3.8",
|
|
42
|
+
"@quake2ts/engine": "^0.0.739",
|
|
43
|
+
"@quake2ts/game": "0.0.739",
|
|
44
|
+
"@quake2ts/server": "0.0.739",
|
|
45
|
+
"@quake2ts/shared": "0.0.739"
|
|
43
46
|
},
|
|
44
47
|
"peerDependenciesMeta": {
|
|
48
|
+
"@quake2ts/engine": {
|
|
49
|
+
"optional": true
|
|
50
|
+
},
|
|
51
|
+
"@quake2ts/game": {
|
|
52
|
+
"optional": true
|
|
53
|
+
},
|
|
54
|
+
"@quake2ts/server": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"@quake2ts/shared": {
|
|
58
|
+
"optional": true
|
|
59
|
+
},
|
|
45
60
|
"@webgpu/types": {
|
|
46
61
|
"optional": true
|
|
47
62
|
},
|
|
63
|
+
"gl-matrix": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
48
66
|
"pixelmatch": {
|
|
49
67
|
"optional": true
|
|
50
68
|
},
|
|
@@ -59,6 +77,7 @@
|
|
|
59
77
|
}
|
|
60
78
|
},
|
|
61
79
|
"devDependencies": {
|
|
80
|
+
"gl-matrix": "^3.4.4",
|
|
62
81
|
"@napi-rs/canvas": "^0.1.84",
|
|
63
82
|
"@types/jsdom": "^27.0.0",
|
|
64
83
|
"@types/pixelmatch": "^5.2.6",
|
|
@@ -72,7 +91,11 @@
|
|
|
72
91
|
"tsup": "^8.1.0",
|
|
73
92
|
"typescript": "^5.4.5",
|
|
74
93
|
"vitest": "^1.6.0",
|
|
75
|
-
"webgpu": "^0.3.8"
|
|
94
|
+
"webgpu": "^0.3.8",
|
|
95
|
+
"@quake2ts/engine": "^0.0.739",
|
|
96
|
+
"@quake2ts/game": "0.0.739",
|
|
97
|
+
"@quake2ts/server": "0.0.739",
|
|
98
|
+
"@quake2ts/shared": "0.0.739"
|
|
76
99
|
},
|
|
77
100
|
"scripts": {
|
|
78
101
|
"build": "tsup src/index.ts --format esm,cjs --dts",
|
package/src/e2e/visual.ts
CHANGED
|
@@ -1,172 +1,105 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Page } from 'playwright';
|
|
2
|
+
import { PNG } from 'pngjs';
|
|
3
|
+
import pixelmatch from 'pixelmatch';
|
|
4
|
+
import { Canvas } from '@napi-rs/canvas';
|
|
2
5
|
import fs from 'fs/promises';
|
|
3
6
|
import path from 'path';
|
|
4
7
|
|
|
5
8
|
export interface VisualDiff {
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
diffPixels: number;
|
|
10
|
+
diffPercentage: number;
|
|
11
|
+
isMatch: boolean;
|
|
12
|
+
diffImage?: Buffer;
|
|
8
13
|
}
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
* @param name Output filename (without extension)
|
|
14
|
-
*/
|
|
15
|
-
export async function captureGameScreenshot(page: any, name: string): Promise<Buffer> {
|
|
16
|
-
return await page.screenshot({ path: `${name}.png` });
|
|
15
|
+
export interface VisualScenario {
|
|
16
|
+
sceneName: string;
|
|
17
|
+
setup?: (page: Page) => Promise<void>;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
/**
|
|
20
|
-
*
|
|
21
|
-
* Compatible with both JSDOM/HTMLCanvasElement and @napi-rs/canvas.
|
|
21
|
+
* Captures a screenshot of the game canvas.
|
|
22
22
|
*/
|
|
23
|
-
export async function
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
// Handle JSDOM HTMLCanvasElement (if backed by node-canvas or similar)
|
|
31
|
-
else if ('toDataURL' in canvas) {
|
|
32
|
-
const dataUrl = canvas.toDataURL('image/png');
|
|
33
|
-
const base64 = dataUrl.replace(/^data:image\/png;base64,/, '');
|
|
34
|
-
buffer = Buffer.from(base64, 'base64');
|
|
35
|
-
} else {
|
|
36
|
-
throw new Error('Unsupported canvas type for screenshot');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
|
40
|
-
await fs.writeFile(filepath, buffer);
|
|
23
|
+
export async function captureGameScreenshot(page: Page, name: string): Promise<Buffer> {
|
|
24
|
+
const canvasElement = page.locator('canvas');
|
|
25
|
+
if (await canvasElement.count() > 0) {
|
|
26
|
+
return await canvasElement.screenshot({ path: `${name}.png` });
|
|
27
|
+
}
|
|
28
|
+
return await page.screenshot({ path: `${name}.png` });
|
|
41
29
|
}
|
|
42
30
|
|
|
43
31
|
/**
|
|
44
|
-
*
|
|
45
|
-
* Returns true if they match (within optional threshold logic, currently strict pixel match).
|
|
46
|
-
* If baseline does not exist, it saves the current state as baseline and returns true.
|
|
32
|
+
* Saves a canvas to a PNG file.
|
|
47
33
|
*/
|
|
48
|
-
export async function
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
// Get current image data
|
|
68
|
-
// We need to draw both to canvases to get pixel data easily with @napi-rs/canvas
|
|
69
|
-
// If input is already a canvas, we can use it.
|
|
70
|
-
|
|
71
|
-
// Helper to get buffer from input canvas
|
|
72
|
-
let currentBuffer: Buffer;
|
|
73
|
-
if ('toBuffer' in canvas && typeof canvas.toBuffer === 'function') {
|
|
74
|
-
currentBuffer = canvas.toBuffer('image/png');
|
|
75
|
-
} else if ('toDataURL' in canvas) {
|
|
76
|
-
const dataUrl = canvas.toDataURL('image/png');
|
|
77
|
-
currentBuffer = Buffer.from(dataUrl.replace(/^data:image\/png;base64,/, ''), 'base64');
|
|
78
|
-
} else {
|
|
79
|
-
throw new Error('Unsupported canvas type');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Simple Buffer comparison first (fastest)
|
|
83
|
-
if (baselineBuffer.equals(currentBuffer)) {
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// If buffers differ, it could be metadata or compression. Do pixel check.
|
|
88
|
-
// Note: Creating a new canvas to draw the image onto for pixel access
|
|
89
|
-
// This requires the 'Canvas' constructor which we imported.
|
|
90
|
-
const baselineCanvas = new Canvas(width, height);
|
|
91
|
-
const ctx = baselineCanvas.getContext('2d');
|
|
92
|
-
ctx.drawImage(baselineImage, 0, 0);
|
|
93
|
-
const baselineData = ctx.getImageData(0, 0, width, height).data;
|
|
94
|
-
|
|
95
|
-
// Load current buffer to image to draw (handles JSDOM/napi differences uniformally)
|
|
96
|
-
const currentImage = new Image();
|
|
97
|
-
currentImage.src = currentBuffer;
|
|
98
|
-
|
|
99
|
-
if (currentImage.width !== width || currentImage.height !== height) {
|
|
100
|
-
console.error(`Dimension mismatch: Baseline ${width}x${height} vs Current ${currentImage.width}x${currentImage.height}`);
|
|
101
|
-
return false;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const currentCanvas = new Canvas(width, height);
|
|
105
|
-
const ctx2 = currentCanvas.getContext('2d');
|
|
106
|
-
ctx2.drawImage(currentImage, 0, 0);
|
|
107
|
-
const currentData = ctx2.getImageData(0, 0, width, height).data;
|
|
108
|
-
|
|
109
|
-
let diffPixels = 0;
|
|
110
|
-
const totalPixels = width * height;
|
|
111
|
-
|
|
112
|
-
// Simple pixel diff
|
|
113
|
-
for (let i = 0; i < baselineData.length; i += 4) {
|
|
114
|
-
if (baselineData[i] !== currentData[i] || // R
|
|
115
|
-
baselineData[i+1] !== currentData[i+1] || // G
|
|
116
|
-
baselineData[i+2] !== currentData[i+2] || // B
|
|
117
|
-
baselineData[i+3] !== currentData[i+3]) { // A
|
|
118
|
-
diffPixels++;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (diffPixels > 0) {
|
|
123
|
-
console.error(`Visual regression: ${diffPixels} pixels differ (${(diffPixels/totalPixels*100).toFixed(2)}%)`);
|
|
124
|
-
// Save diff image? (Optional, skipping for now)
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return true;
|
|
34
|
+
export async function takeScreenshot(canvas: Canvas | HTMLCanvasElement, filepath: string): Promise<void> {
|
|
35
|
+
let buffer: Buffer;
|
|
36
|
+
|
|
37
|
+
// Handle @napi-rs/canvas Canvas object
|
|
38
|
+
if ('toBuffer' in canvas && typeof canvas.toBuffer === 'function') {
|
|
39
|
+
buffer = canvas.toBuffer('image/png');
|
|
40
|
+
}
|
|
41
|
+
// Handle JSDOM HTMLCanvasElement
|
|
42
|
+
else if ('toDataURL' in canvas) {
|
|
43
|
+
const dataUrl = canvas.toDataURL('image/png');
|
|
44
|
+
const base64 = dataUrl.replace(/^data:image\/png;base64,/, '');
|
|
45
|
+
buffer = Buffer.from(base64, 'base64');
|
|
46
|
+
} else {
|
|
47
|
+
throw new Error('Unsupported canvas type for screenshot');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
await fs.mkdir(path.dirname(filepath), { recursive: true });
|
|
51
|
+
await fs.writeFile(filepath, buffer);
|
|
129
52
|
}
|
|
130
53
|
|
|
131
54
|
/**
|
|
132
|
-
* Compares two
|
|
133
|
-
* Uses simple buffer check. Kept for backward compatibility or direct buffer comparison.
|
|
55
|
+
* Compares two image buffers using pixelmatch.
|
|
134
56
|
*/
|
|
135
57
|
export function compareScreenshots(baseline: Buffer, current: Buffer, threshold: number = 0.01): VisualDiff {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
58
|
+
try {
|
|
59
|
+
const img1 = PNG.sync.read(baseline);
|
|
60
|
+
const img2 = PNG.sync.read(current);
|
|
61
|
+
|
|
62
|
+
if (img1.width !== img2.width || img1.height !== img2.height) {
|
|
63
|
+
throw new Error(`Image dimensions do not match: ${img1.width}x${img1.height} vs ${img2.width}x${img2.height}`);
|
|
139
64
|
}
|
|
140
65
|
|
|
141
|
-
|
|
142
|
-
const
|
|
66
|
+
const { width, height } = img1;
|
|
67
|
+
const diff = new PNG({ width, height });
|
|
143
68
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
69
|
+
const diffPixels = pixelmatch(
|
|
70
|
+
img1.data,
|
|
71
|
+
img2.data,
|
|
72
|
+
diff.data,
|
|
73
|
+
width,
|
|
74
|
+
height,
|
|
75
|
+
{ threshold }
|
|
76
|
+
);
|
|
149
77
|
|
|
150
|
-
const diffPercentage = diffPixels /
|
|
78
|
+
const diffPercentage = diffPixels / (width * height);
|
|
79
|
+
const isMatch = diffPixels === 0;
|
|
151
80
|
|
|
152
81
|
return {
|
|
153
|
-
|
|
82
|
+
diffPixels,
|
|
83
|
+
diffPercentage,
|
|
84
|
+
isMatch,
|
|
85
|
+
diffImage: PNG.sync.write(diff)
|
|
154
86
|
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// Fallback or error handling
|
|
89
|
+
console.error('Error comparing screenshots:', error);
|
|
90
|
+
return {
|
|
91
|
+
diffPixels: -1,
|
|
92
|
+
diffPercentage: 1.0,
|
|
93
|
+
isMatch: false
|
|
94
|
+
};
|
|
95
|
+
}
|
|
160
96
|
}
|
|
161
97
|
|
|
162
98
|
/**
|
|
163
99
|
* Creates a visual test scenario.
|
|
164
100
|
*/
|
|
165
101
|
export function createVisualTestScenario(sceneName: string): VisualScenario {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// Setup scene logic
|
|
170
|
-
}
|
|
171
|
-
};
|
|
102
|
+
return {
|
|
103
|
+
sceneName
|
|
104
|
+
};
|
|
172
105
|
}
|
package/src/game/factories.ts
CHANGED
|
@@ -157,6 +157,23 @@ export function createPlayerEntityFactory(overrides: Partial<Entity> = {}): Part
|
|
|
157
157
|
movetype: MoveType.Walk,
|
|
158
158
|
svflags: ServerFlags.Player,
|
|
159
159
|
viewheight: 22,
|
|
160
|
+
client: {
|
|
161
|
+
inventory: {
|
|
162
|
+
ammo: { counts: [], caps: [] },
|
|
163
|
+
ownedWeapons: new Set(),
|
|
164
|
+
powerups: new Map(),
|
|
165
|
+
keys: new Set(),
|
|
166
|
+
items: new Set()
|
|
167
|
+
},
|
|
168
|
+
weaponStates: {
|
|
169
|
+
currentWeapon: null,
|
|
170
|
+
lastFireTime: 0,
|
|
171
|
+
weaponFrame: 0,
|
|
172
|
+
weaponIdleTime: 0,
|
|
173
|
+
states: new Map(), // Initialize states map correctly
|
|
174
|
+
activeWeaponId: null
|
|
175
|
+
}
|
|
176
|
+
} as any,
|
|
160
177
|
...overrides
|
|
161
178
|
});
|
|
162
179
|
}
|
package/src/game/helpers.ts
CHANGED
|
@@ -228,12 +228,8 @@ export function createPhysicsTestContext(bspModel?: BspModel): TestContext {
|
|
|
228
228
|
// For now, we'll just store the model on the context if we extended TestContext,
|
|
229
229
|
// but the task specifically asks to "Include collision world, traces".
|
|
230
230
|
|
|
231
|
-
// In a real scenario, we might want to hook up a real BSP
|
|
231
|
+
// In a real scenario, we might want to hook up a real BSP physics engine mock here
|
|
232
232
|
// or a mock that uses the BSP data.
|
|
233
|
-
// Since we don't have a full BSP physics engine mock ready to drop in,
|
|
234
|
-
// we will stick with the default trace mock which is already set up in createTestContext,
|
|
235
|
-
// but we acknowledge the bspModel parameter for future expansion where we might
|
|
236
|
-
// use it to seed the trace results.
|
|
237
233
|
}
|
|
238
234
|
|
|
239
235
|
return context;
|
|
@@ -308,3 +304,37 @@ export function createGameImportsAndEngine(overrides?: {
|
|
|
308
304
|
|
|
309
305
|
return { imports, engine };
|
|
310
306
|
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Creates a mock GameExports object with mocked properties.
|
|
310
|
+
* This is useful for testing game logic that consumes the game object.
|
|
311
|
+
*
|
|
312
|
+
* @param overrides Optional overrides for the game object properties
|
|
313
|
+
*/
|
|
314
|
+
export function createMockGameExports(overrides: Partial<any> = {}): any {
|
|
315
|
+
return {
|
|
316
|
+
init: vi.fn(),
|
|
317
|
+
shutdown: vi.fn(),
|
|
318
|
+
frame: vi.fn().mockReturnValue({ state: {} }),
|
|
319
|
+
clientThink: vi.fn(),
|
|
320
|
+
time: 0,
|
|
321
|
+
spawnWorld: vi.fn(),
|
|
322
|
+
deathmatch: false,
|
|
323
|
+
coop: false,
|
|
324
|
+
gameImports: {},
|
|
325
|
+
gameEngine: {},
|
|
326
|
+
entities: {
|
|
327
|
+
spawn: vi.fn(),
|
|
328
|
+
free: vi.fn(),
|
|
329
|
+
find: vi.fn(),
|
|
330
|
+
findByClassname: vi.fn(),
|
|
331
|
+
findByRadius: vi.fn(() => []),
|
|
332
|
+
forEachEntity: vi.fn(),
|
|
333
|
+
timeSeconds: 0,
|
|
334
|
+
...overrides.entities,
|
|
335
|
+
},
|
|
336
|
+
multicast: vi.fn(),
|
|
337
|
+
unicast: vi.fn(),
|
|
338
|
+
...overrides
|
|
339
|
+
};
|
|
340
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ export * from './game/helpers/save.js';
|
|
|
12
12
|
export * from './game/mocks/ai.js';
|
|
13
13
|
export * from './game/mocks/combat.js';
|
|
14
14
|
export * from './game/mocks/items.js';
|
|
15
|
-
export * from './game/mocks.js';
|
|
15
|
+
// Removed export * from './game/mocks.js';
|
|
16
16
|
export * from './server/mocks/transport.js';
|
|
17
17
|
export * from './server/mockTransport.js';
|
|
18
18
|
export * from './server/mockNetDriver.js';
|
|
@@ -4,11 +4,27 @@ import { Entity } from '@quake2ts/game';
|
|
|
4
4
|
import { vi } from 'vitest';
|
|
5
5
|
import { createMockNetDriver } from './transport.js';
|
|
6
6
|
|
|
7
|
-
// Define GameState interface locally
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
// Define GameState interface locally as we removed game/mocks.ts
|
|
8
|
+
export interface GameState {
|
|
9
|
+
levelName: string;
|
|
10
|
+
time: number;
|
|
11
|
+
entities: Entity[];
|
|
12
|
+
clients: any[]; // Mock client objects
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Creates a mock game state object.
|
|
17
|
+
* @param overrides Optional overrides for the game state.
|
|
18
|
+
*/
|
|
19
|
+
export function createMockGameState(overrides?: Partial<GameState>): GameState {
|
|
20
|
+
return {
|
|
21
|
+
levelName: 'test_level',
|
|
22
|
+
time: 0,
|
|
23
|
+
entities: [],
|
|
24
|
+
clients: [],
|
|
25
|
+
...overrides
|
|
26
|
+
};
|
|
27
|
+
}
|
|
12
28
|
|
|
13
29
|
/**
|
|
14
30
|
* Creates a mock server state object.
|
|
@@ -152,11 +168,3 @@ export function createMockServer(overrides?: Partial<MockServer>): MockServer {
|
|
|
152
168
|
...overrides
|
|
153
169
|
};
|
|
154
170
|
}
|
|
155
|
-
|
|
156
|
-
// Re-export GameState from game mocks if needed, or use the one from game/mocks
|
|
157
|
-
// Since we have a createMockGameState in game/mocks.ts, we should probably use that or alias it.
|
|
158
|
-
// The task says "Add createMockGameState() factory".
|
|
159
|
-
// If it is already in game/mocks.ts, we can just export it from there or re-export it here.
|
|
160
|
-
// But to avoid duplication, I will just re-export the one from game mocks if the intention is to have it available under server utils.
|
|
161
|
-
|
|
162
|
-
export { createMockGameState, type GameState } from '../../game/mocks.js';
|
package/src/game/mocks.ts
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { Entity, GameExports, GameImports } from '@quake2ts/game';
|
|
2
|
-
import { vi } from 'vitest';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Interface for mock GameState.
|
|
6
|
-
*/
|
|
7
|
-
export interface GameState {
|
|
8
|
-
levelName: string;
|
|
9
|
-
time: number;
|
|
10
|
-
entities: Entity[];
|
|
11
|
-
clients: any[]; // Mock client objects
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Creates a mock game state object.
|
|
16
|
-
* @param overrides Optional overrides for the game state.
|
|
17
|
-
*/
|
|
18
|
-
export function createMockGameState(overrides?: Partial<GameState>): GameState {
|
|
19
|
-
return {
|
|
20
|
-
levelName: 'test_level',
|
|
21
|
-
time: 0,
|
|
22
|
-
entities: [],
|
|
23
|
-
clients: [],
|
|
24
|
-
...overrides
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Creates a mock GameExports object.
|
|
30
|
-
*/
|
|
31
|
-
export function createMockGameExports(overrides?: Partial<GameExports>): GameExports {
|
|
32
|
-
return {
|
|
33
|
-
init: vi.fn(),
|
|
34
|
-
shutdown: vi.fn(),
|
|
35
|
-
spawnWorld: vi.fn(),
|
|
36
|
-
frame: vi.fn().mockReturnValue({ state: {} }),
|
|
37
|
-
clientConnect: vi.fn().mockReturnValue(true),
|
|
38
|
-
clientBegin: vi.fn().mockReturnValue({ index: 1, origin: { x: 0, y: 0, z: 0 } }),
|
|
39
|
-
clientDisconnect: vi.fn(),
|
|
40
|
-
clientThink: vi.fn(),
|
|
41
|
-
respawn: vi.fn(),
|
|
42
|
-
entities: {
|
|
43
|
-
getByIndex: vi.fn(),
|
|
44
|
-
forEachEntity: vi.fn(),
|
|
45
|
-
findByRadius: vi.fn(),
|
|
46
|
-
find: vi.fn(),
|
|
47
|
-
checkAnyCollision: vi.fn(),
|
|
48
|
-
trace: vi.fn(),
|
|
49
|
-
pointcontents: vi.fn(),
|
|
50
|
-
link: vi.fn(),
|
|
51
|
-
unlink: vi.fn(),
|
|
52
|
-
spawn: vi.fn(),
|
|
53
|
-
free: vi.fn(),
|
|
54
|
-
activeCount: 0,
|
|
55
|
-
world: { classname: 'worldspawn' } as any,
|
|
56
|
-
} as any,
|
|
57
|
-
multicast: vi.fn(),
|
|
58
|
-
unicast: vi.fn(),
|
|
59
|
-
configstring: vi.fn(),
|
|
60
|
-
serverCommand: vi.fn(),
|
|
61
|
-
sound: vi.fn(),
|
|
62
|
-
soundIndex: vi.fn(),
|
|
63
|
-
centerprintf: vi.fn(),
|
|
64
|
-
trace: vi.fn(),
|
|
65
|
-
time: 0,
|
|
66
|
-
deathmatch: false,
|
|
67
|
-
skill: 1,
|
|
68
|
-
rogue: false,
|
|
69
|
-
xatrix: false,
|
|
70
|
-
coop: false,
|
|
71
|
-
friendlyFire: false,
|
|
72
|
-
random: {
|
|
73
|
-
next: vi.fn(),
|
|
74
|
-
nextFloat: vi.fn(),
|
|
75
|
-
range: vi.fn(),
|
|
76
|
-
crandom: vi.fn(),
|
|
77
|
-
getState: vi.fn(),
|
|
78
|
-
setState: vi.fn()
|
|
79
|
-
} as any,
|
|
80
|
-
createSave: vi.fn(),
|
|
81
|
-
loadSave: vi.fn(),
|
|
82
|
-
serialize: vi.fn(),
|
|
83
|
-
loadState: vi.fn(),
|
|
84
|
-
setGodMode: vi.fn(),
|
|
85
|
-
setNoclip: vi.fn(),
|
|
86
|
-
setNotarget: vi.fn(),
|
|
87
|
-
giveItem: vi.fn(),
|
|
88
|
-
damage: vi.fn(),
|
|
89
|
-
teleport: vi.fn(),
|
|
90
|
-
registerHooks: vi.fn(),
|
|
91
|
-
hooks: {
|
|
92
|
-
onMapLoad: vi.fn(),
|
|
93
|
-
onMapUnload: vi.fn(),
|
|
94
|
-
onPlayerSpawn: vi.fn(),
|
|
95
|
-
onPlayerDeath: vi.fn(),
|
|
96
|
-
register: vi.fn(),
|
|
97
|
-
onPickup: vi.fn(), // Added onPickup mock
|
|
98
|
-
} as any,
|
|
99
|
-
setSpectator: vi.fn(),
|
|
100
|
-
registerEntitySpawn: vi.fn(),
|
|
101
|
-
unregisterEntitySpawn: vi.fn(),
|
|
102
|
-
getCustomEntities: vi.fn(),
|
|
103
|
-
...overrides
|
|
104
|
-
};
|
|
105
|
-
}
|