@quake2ts/test-utils 0.0.828 → 0.0.830

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quake2ts/test-utils",
3
- "version": "0.0.828",
3
+ "version": "0.0.830",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.js",
@@ -55,10 +55,10 @@
55
55
  "serve-handler": "^6.1.6",
56
56
  "vitest": "^1.6.0",
57
57
  "webgpu": "^0.3.8",
58
- "@quake2ts/server": "0.0.828",
59
- "@quake2ts/engine": "^0.0.828",
60
- "@quake2ts/shared": "0.0.828",
61
- "@quake2ts/game": "0.0.828"
58
+ "@quake2ts/engine": "^0.0.830",
59
+ "@quake2ts/game": "0.0.830",
60
+ "@quake2ts/shared": "0.0.830",
61
+ "@quake2ts/server": "0.0.830"
62
62
  },
63
63
  "peerDependenciesMeta": {
64
64
  "@quake2ts/engine": {
@@ -114,10 +114,10 @@
114
114
  "typescript": "^5.9.3",
115
115
  "vitest": "^4.0.16",
116
116
  "webgpu": "^0.3.8",
117
- "@quake2ts/engine": "^0.0.828",
118
- "@quake2ts/game": "0.0.828",
119
- "@quake2ts/shared": "0.0.828",
120
- "@quake2ts/server": "0.0.828"
117
+ "@quake2ts/engine": "^0.0.830",
118
+ "@quake2ts/game": "0.0.830",
119
+ "@quake2ts/shared": "0.0.830",
120
+ "@quake2ts/server": "0.0.830"
121
121
  },
122
122
  "dependencies": {
123
123
  "upng-js": "^2.1.0"
@@ -0,0 +1,68 @@
1
+ import {
2
+ SpawnRegistry,
3
+ createDefaultSpawnRegistry,
4
+ type SpawnFunction,
5
+ type SpawnContext,
6
+ type SpawnOptions,
7
+ spawnEntityFromDictionary,
8
+ Entity
9
+ } from '@quake2ts/game';
10
+ import type { TestContext } from '../helpers.js';
11
+
12
+ /**
13
+ * Creates a configured SpawnRegistry for testing.
14
+ * Uses the default spawn registry but handles the game dependency if not provided.
15
+ */
16
+ export function createSpawnRegistry(game?: any): SpawnRegistry {
17
+ // If game is not provided, we can pass a mock or empty object
18
+ // The default registry primarily uses game for registering items
19
+ const gameMock = game || {};
20
+ return createDefaultSpawnRegistry(gameMock);
21
+ }
22
+
23
+ /**
24
+ * Registers a custom spawn function for testing.
25
+ */
26
+ export function registerTestSpawn(
27
+ registry: SpawnRegistry,
28
+ classname: string,
29
+ spawnFunc: SpawnFunction
30
+ ): void {
31
+ registry.register(classname, spawnFunc);
32
+ }
33
+
34
+ export interface SpawnTestEntityOptions {
35
+ classname: string;
36
+ keyValues?: Record<string, string>;
37
+ registry?: SpawnRegistry;
38
+ onWarning?: (message: string) => void;
39
+ }
40
+
41
+ /**
42
+ * Convenience function to spawn an entity using the spawn system.
43
+ */
44
+ export function spawnTestEntity(
45
+ context: TestContext,
46
+ options: SpawnTestEntityOptions
47
+ ): Entity | null {
48
+ const registry = options.registry || context.game?.entities?.spawnRegistry;
49
+
50
+ if (!registry) {
51
+ throw new Error('No spawn registry provided and none found on context.game.entities');
52
+ }
53
+
54
+ const keyValues = {
55
+ classname: options.classname,
56
+ ...(options.keyValues || {})
57
+ };
58
+
59
+ const spawnOptions: SpawnOptions = {
60
+ registry,
61
+ entities: context.entities,
62
+ onWarning: options.onWarning
63
+ };
64
+
65
+ // The type of spawnEntityFromDictionary might need validation based on exports
66
+ // but usually it accepts Record<string,string> which we provide.
67
+ return spawnEntityFromDictionary(keyValues, spawnOptions);
68
+ }
@@ -27,6 +27,7 @@ export interface MockGame {
27
27
  spawnWorld: LegacyMock<[], void>;
28
28
  clientBegin: LegacyMock<[any], void>;
29
29
  damage: LegacyMock<[number], void>;
30
+ entities: any;
30
31
  }
31
32
 
32
33
  export interface TestContext extends SpawnContext {
@@ -67,7 +68,10 @@ export const createMockGame = (seed: number = 12345): { game: MockGame, spawnReg
67
68
  }),
68
69
  damage: vi.fn((amount: number) => {
69
70
  hooks.onDamage({} as any, null, null, amount, 0, 0);
70
- })
71
+ }),
72
+ entities: {
73
+ spawnRegistry
74
+ }
71
75
  };
72
76
 
73
77
  return { game, spawnRegistry };
@@ -91,7 +95,11 @@ export function createTestContext(options?: { seed?: number, initialEntities?: E
91
95
  const hooks = game.hooks;
92
96
 
93
97
  // We need to store the registry reference to implement registerEntityClass/getSpawnFunction
94
- let currentSpawnRegistry: SpawnRegistry | undefined;
98
+ let currentSpawnRegistry: SpawnRegistry | undefined = spawnRegistry;
99
+
100
+ const findByTargetName = (targetname: string) => {
101
+ return entityList.filter(e => e.targetname === targetname && e.inUse);
102
+ };
95
103
 
96
104
  const entities = {
97
105
  spawn: vi.fn(() => {
@@ -147,9 +155,20 @@ export function createTestContext(options?: { seed?: number, initialEntities?: E
147
155
  }),
148
156
  soundIndex: vi.fn((sound: string) => engine.soundIndex(sound)),
149
157
  useTargets: vi.fn((entity: Entity, activator: Entity | null) => {
158
+ if (entity.target) {
159
+ const targets = findByTargetName(entity.target);
160
+ for (const t of targets) {
161
+ t.use?.(t, entity, activator);
162
+ }
163
+ }
164
+ }),
165
+ findByTargetName: vi.fn(findByTargetName),
166
+ pickTarget: vi.fn((targetname: string | undefined) => {
167
+ if (!targetname) return null;
168
+ const matches = findByTargetName(targetname);
169
+ if (matches.length === 0) return null;
170
+ return matches[0];
150
171
  }),
151
- findByTargetName: vi.fn(() => []),
152
- pickTarget: vi.fn(() => null),
153
172
  killBox: vi.fn(),
154
173
  rng: createRandomGenerator({ seed }),
155
174
  imports: {
@@ -171,7 +190,7 @@ export function createTestContext(options?: { seed?: number, initialEntities?: E
171
190
  return entityList.find(predicate);
172
191
  }),
173
192
  findByClassname: vi.fn((classname: string) => {
174
- return entityList.find(e => e.classname === classname);
193
+ return entityList.filter(e => e.classname === classname);
175
194
  }),
176
195
  beginFrame: vi.fn((timeSeconds: number) => {
177
196
  (entities as any).timeSeconds = timeSeconds;
@@ -193,6 +212,9 @@ export function createTestContext(options?: { seed?: number, initialEntities?: E
193
212
  world: entityList.find(e => e.classname === 'worldspawn') || new Entity(0),
194
213
  } as unknown as EntitySystem;
195
214
 
215
+ // Fix circular reference
216
+ game.entities = entities;
217
+
196
218
  return {
197
219
  keyValues: {},
198
220
  entities,
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export * from './shared/collision.js';
7
7
  export * from './shared/factories.js';
8
8
  export * from './game/factories.js';
9
9
  export * from './game/helpers.js';
10
+ export * from './game/helpers/spawn.js';
10
11
  export * from './game/helpers/physics.js';
11
12
  export * from './game/helpers/save.js';
12
13
  export * from './game/mocks/ai.js';