@deck.gl/test-utils 9.3.0-alpha.2 → 9.3.0-alpha.5

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.
@@ -0,0 +1,465 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // dist/vitest.js
21
+ var vitest_exports = {};
22
+ __export(vitest_exports, {
23
+ device: () => device,
24
+ generateLayerTests: () => generateLayerTests,
25
+ getLayerUniforms: () => getLayerUniforms,
26
+ gl: () => gl,
27
+ testInitializeLayer: () => testInitializeLayer,
28
+ testInitializeLayerAsync: () => testInitializeLayerAsync,
29
+ testLayer: () => testLayer2,
30
+ testLayerAsync: () => testLayerAsync2,
31
+ toLowPrecision: () => toLowPrecision
32
+ });
33
+ module.exports = __toCommonJS(vitest_exports);
34
+ var import_vitest = require("vitest");
35
+
36
+ // dist/lifecycle-test.js
37
+ var import_core = require("@deck.gl/core");
38
+
39
+ // dist/utils/setup-gl.js
40
+ var import_test_utils = require("@luma.gl/test-utils");
41
+ var device = import_test_utils.webglDevice || new import_test_utils.NullDevice({});
42
+ var _a;
43
+ var gl = ((_a = import_test_utils.webglDevice) == null ? void 0 : _a.gl) || 1;
44
+ globalThis.glContext = globalThis.glContext || gl;
45
+
46
+ // dist/lifecycle-test.js
47
+ var testViewport = new import_core.MapView({}).makeViewport({
48
+ width: 100,
49
+ height: 100,
50
+ viewState: { longitude: 0, latitude: 0, zoom: 1 }
51
+ });
52
+ function defaultOnError(error, title) {
53
+ if (error) {
54
+ throw error;
55
+ }
56
+ }
57
+ function initializeLayerManager({ layer, viewport = testViewport, onError = defaultOnError }) {
58
+ const layerManager = new import_core.LayerManager(device, { viewport });
59
+ layerManager.setProps({
60
+ onError: (error) => onError(error, `initializing ${layer.id}`)
61
+ });
62
+ layerManager.setLayers([layer]);
63
+ return layerManager;
64
+ }
65
+ function testInitializeLayer(opts) {
66
+ const layerManager = initializeLayerManager(opts);
67
+ if (opts.finalize === false) {
68
+ return {
69
+ finalize: () => layerManager.finalize()
70
+ };
71
+ }
72
+ layerManager.finalize();
73
+ return null;
74
+ }
75
+ async function testInitializeLayerAsync(opts) {
76
+ const layerManager = initializeLayerManager(opts);
77
+ const deckRenderer = new import_core.DeckRenderer(device);
78
+ while (!opts.layer.isLoaded) {
79
+ await update({ layerManager, deckRenderer, oldResourceCounts: {} });
80
+ }
81
+ if (opts.finalize === false) {
82
+ return {
83
+ finalize: () => layerManager.finalize()
84
+ };
85
+ }
86
+ layerManager.finalize();
87
+ return null;
88
+ }
89
+ function testLayer(opts) {
90
+ const { Layer, testCases = [], spies = [], onError = defaultOnError, createSpy, resetSpy } = opts;
91
+ const resources = setupLayerTests(`testing ${Layer.layerName}`, opts);
92
+ let layer = new Layer();
93
+ for (const testCase of testCases) {
94
+ const oldState = { ...layer.state };
95
+ const { layer: newLayer, spyMap } = runLayerTestUpdate(testCase, resources, layer, spies, createSpy);
96
+ runLayerTestPostUpdateCheck(testCase, newLayer, oldState, spyMap);
97
+ Object.keys(spyMap).forEach((k) => resetSpy(spyMap[k]));
98
+ layer = newLayer;
99
+ }
100
+ const error = cleanupAfterLayerTests(resources);
101
+ if (error) {
102
+ onError(error, `${Layer.layerName} should delete all resources`);
103
+ }
104
+ }
105
+ async function testLayerAsync(opts) {
106
+ const { Layer, testCases = [], spies = [], onError = defaultOnError, createSpy, resetSpy } = opts;
107
+ const resources = setupLayerTests(`testing ${Layer.layerName}`, opts);
108
+ let layer = new Layer();
109
+ for (const testCase of testCases) {
110
+ const oldState = { ...layer.state };
111
+ const { layer: newLayer, spyMap } = runLayerTestUpdate(testCase, resources, layer, spies, createSpy);
112
+ runLayerTestPostUpdateCheck(testCase, newLayer, oldState, spyMap);
113
+ while (!newLayer.isLoaded) {
114
+ await update(resources);
115
+ runLayerTestPostUpdateCheck(testCase, newLayer, oldState, spyMap);
116
+ }
117
+ Object.keys(spyMap).forEach((k) => resetSpy(spyMap[k]));
118
+ layer = newLayer;
119
+ }
120
+ const error = await cleanupAfterLayerTestsAsync(resources);
121
+ if (error) {
122
+ onError(error, `${Layer.layerName} should delete all resources`);
123
+ }
124
+ }
125
+ function setupLayerTests(testTitle, { viewport = testViewport, timeline, onError = defaultOnError }) {
126
+ const oldResourceCounts = getResourceCounts();
127
+ const layerManager = new import_core.LayerManager(device, { viewport, timeline });
128
+ const deckRenderer = new import_core.DeckRenderer(device);
129
+ const props = {
130
+ layerFilter: null,
131
+ drawPickingColors: false,
132
+ onError: (error) => onError(error, testTitle)
133
+ };
134
+ layerManager.setProps(props);
135
+ deckRenderer.setProps(props);
136
+ return { layerManager, deckRenderer, oldResourceCounts };
137
+ }
138
+ function cleanupAfterLayerTests({ layerManager, deckRenderer, oldResourceCounts }) {
139
+ layerManager.setLayers([]);
140
+ layerManager.finalize();
141
+ deckRenderer.finalize();
142
+ return getResourceCountDelta(oldResourceCounts);
143
+ }
144
+ async function cleanupAfterLayerTestsAsync({ layerManager, deckRenderer, oldResourceCounts }) {
145
+ layerManager.setLayers([]);
146
+ await new Promise((resolve) => setTimeout(resolve, 0));
147
+ layerManager.finalize();
148
+ deckRenderer.finalize();
149
+ return getResourceCountDelta(oldResourceCounts);
150
+ }
151
+ function getResourceCounts() {
152
+ const resourceStats = luma.stats.get("Resource Counts");
153
+ return {
154
+ Texture2D: resourceStats.get("Texture2Ds Active").count,
155
+ Buffer: resourceStats.get("Buffers Active").count
156
+ };
157
+ }
158
+ function getResourceCountDelta(oldResourceCounts) {
159
+ const resourceCounts = getResourceCounts();
160
+ for (const resourceName in resourceCounts) {
161
+ if (resourceCounts[resourceName] !== oldResourceCounts[resourceName]) {
162
+ return new Error(`${resourceCounts[resourceName] - oldResourceCounts[resourceName]} ${resourceName}s`);
163
+ }
164
+ }
165
+ return null;
166
+ }
167
+ function injectSpies(layer, spies, spyFactory) {
168
+ const spyMap = {};
169
+ if (spies) {
170
+ for (const functionName of spies) {
171
+ spyMap[functionName] = spyFactory(Object.getPrototypeOf(layer), functionName);
172
+ }
173
+ }
174
+ return spyMap;
175
+ }
176
+ function runLayerTestPostUpdateCheck(testCase, newLayer, oldState, spyMap) {
177
+ if (testCase.onAfterUpdate) {
178
+ const subLayers = newLayer.isComposite ? newLayer.getSubLayers() : [];
179
+ const subLayer = subLayers.length ? subLayers[0] : null;
180
+ testCase.onAfterUpdate({
181
+ testCase,
182
+ layer: newLayer,
183
+ oldState,
184
+ subLayers,
185
+ subLayer,
186
+ spies: spyMap
187
+ });
188
+ }
189
+ }
190
+ function runLayerTestUpdate(testCase, { layerManager, deckRenderer }, layer, spies, spyFactory) {
191
+ const { props, updateProps, onBeforeUpdate, viewport = layerManager.context.viewport } = testCase;
192
+ if (onBeforeUpdate) {
193
+ onBeforeUpdate({ layer, testCase });
194
+ }
195
+ if (props) {
196
+ layer = new layer.constructor(props);
197
+ } else if (updateProps) {
198
+ layer = layer.clone(updateProps);
199
+ }
200
+ spies = testCase.spies || spies;
201
+ const spyMap = injectSpies(layer, spies, spyFactory);
202
+ const drawLayers = () => {
203
+ deckRenderer.renderLayers({
204
+ pass: "test",
205
+ views: {},
206
+ effects: [],
207
+ viewports: [viewport],
208
+ layers: layerManager.getLayers(),
209
+ onViewportActive: layerManager.activateViewport
210
+ });
211
+ };
212
+ layerManager.setLayers([layer]);
213
+ drawLayers();
214
+ if (layerManager.needsUpdate()) {
215
+ layerManager.updateLayers();
216
+ drawLayers();
217
+ }
218
+ return { layer, spyMap };
219
+ }
220
+ function update({ layerManager, deckRenderer }) {
221
+ return new Promise((resolve) => {
222
+ const onAnimationFrame = () => {
223
+ if (layerManager.needsUpdate()) {
224
+ layerManager.updateLayers();
225
+ deckRenderer.renderLayers({
226
+ pass: "test",
227
+ views: {},
228
+ effects: [],
229
+ viewports: [layerManager.context.viewport],
230
+ layers: layerManager.getLayers(),
231
+ onViewportActive: layerManager.activateViewport
232
+ });
233
+ resolve();
234
+ return;
235
+ }
236
+ setTimeout(onAnimationFrame, 50);
237
+ };
238
+ onAnimationFrame();
239
+ });
240
+ }
241
+
242
+ // dist/utils/layer.js
243
+ function getLayerUniforms(layer, blockName) {
244
+ const uniforms = {};
245
+ const uniformStore = layer.getModels()[0]._uniformStore;
246
+ const uniformBlocks = blockName ? [uniformStore.uniformBlocks.get(blockName)] : uniformStore.uniformBlocks.values();
247
+ for (const block of uniformBlocks) {
248
+ Object.assign(uniforms, block.uniforms);
249
+ }
250
+ return uniforms;
251
+ }
252
+
253
+ // dist/utils/precision.js
254
+ function toLowPrecision(input, precision = 11) {
255
+ if (typeof input === "number") {
256
+ return Number(input.toPrecision(precision));
257
+ }
258
+ if (Array.isArray(input)) {
259
+ return input.map((item) => toLowPrecision(item, precision));
260
+ }
261
+ if (typeof input === "object") {
262
+ for (const key in input) {
263
+ input[key] = toLowPrecision(input[key], precision);
264
+ }
265
+ }
266
+ return input;
267
+ }
268
+
269
+ // dist/generate-layer-tests.js
270
+ var import_core2 = require("@deck.gl/core");
271
+ function noop() {
272
+ }
273
+ function defaultAssert(condition, comment) {
274
+ if (!condition) {
275
+ throw new Error(comment);
276
+ }
277
+ }
278
+ function generateLayerTests({
279
+ // eslint-disable-next-line @typescript-eslint/no-shadow
280
+ Layer,
281
+ sampleProps = {},
282
+ assert = defaultAssert,
283
+ onBeforeUpdate = noop,
284
+ onAfterUpdate = noop,
285
+ runDefaultAsserts = true
286
+ }) {
287
+ assert(Layer.layerName, "Layer should have display name");
288
+ function wrapTestCaseTitle(title) {
289
+ return `${Layer.layerName}#${title}`;
290
+ }
291
+ const testCases = [
292
+ {
293
+ title: "Empty props",
294
+ props: {}
295
+ },
296
+ {
297
+ title: "Null data",
298
+ // @ts-expect-error null may not be an expected data type
299
+ updateProps: { data: null }
300
+ },
301
+ {
302
+ title: "Sample data",
303
+ updateProps: sampleProps
304
+ }
305
+ ];
306
+ try {
307
+ new Layer({});
308
+ } catch (error) {
309
+ assert(false, `Construct ${Layer.layerName} throws: ${error.message}`);
310
+ }
311
+ const { _propTypes: propTypes, _mergedDefaultProps: defaultProps } = Layer;
312
+ testCases.push(...makeAltDataTestCases(sampleProps, propTypes));
313
+ for (const propName in Layer.defaultProps) {
314
+ if (!(propName in sampleProps)) {
315
+ const newTestCase = makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, assert }) || [];
316
+ testCases.push(...newTestCase);
317
+ }
318
+ }
319
+ testCases.forEach((testCase) => {
320
+ testCase.title = wrapTestCaseTitle(testCase.title);
321
+ const beforeFunc = testCase.onBeforeUpdate || noop;
322
+ const afterFunc = testCase.onAfterUpdate || noop;
323
+ testCase.onBeforeUpdate = (params) => {
324
+ beforeFunc(params);
325
+ onBeforeUpdate(params);
326
+ };
327
+ testCase.onAfterUpdate = (params) => {
328
+ afterFunc(params);
329
+ onAfterUpdate(params);
330
+ if (runDefaultAsserts) {
331
+ if (params.layer.isComposite) {
332
+ const { data } = params.layer.props;
333
+ if (data && typeof data === "object" && (0, import_core2._count)(data)) {
334
+ assert(params.subLayers.length, "Layer should have sublayers");
335
+ }
336
+ } else {
337
+ assert(params.layer.getModels().length, "Layer should have models");
338
+ }
339
+ }
340
+ };
341
+ });
342
+ return testCases;
343
+ }
344
+ function makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, assert }) {
345
+ const newProps = { ...sampleProps };
346
+ const propDef = propTypes[propName];
347
+ if (!propDef) {
348
+ return null;
349
+ }
350
+ switch (propDef.type) {
351
+ case "boolean":
352
+ newProps[propName] = !defaultProps[propName];
353
+ return [
354
+ {
355
+ title: `${propName}: ${String(newProps[propName])}`,
356
+ props: newProps
357
+ }
358
+ ];
359
+ case "number":
360
+ if ("max" in propDef) {
361
+ newProps[propName] = propDef.max;
362
+ } else if ("min" in propDef) {
363
+ newProps[propName] = propDef.min;
364
+ } else {
365
+ newProps[propName] = defaultProps[propName] + 1;
366
+ }
367
+ return [
368
+ {
369
+ title: `${propName}: ${String(newProps[propName])}`,
370
+ props: newProps
371
+ }
372
+ ];
373
+ case "accessor": {
374
+ if (typeof defaultProps[propName] === "function") {
375
+ return null;
376
+ }
377
+ let callCount = 0;
378
+ newProps[propName] = () => {
379
+ callCount++;
380
+ return defaultProps[propName];
381
+ };
382
+ newProps.updateTriggers = {
383
+ [propName]: "function"
384
+ };
385
+ const onBeforeUpdate = () => callCount = 0;
386
+ const onAfterUpdate = () => assert(callCount > 0, "accessor function is called");
387
+ return [
388
+ {
389
+ title: `${propName}: () => ${defaultProps[propName]}`,
390
+ props: newProps,
391
+ onBeforeUpdate,
392
+ onAfterUpdate
393
+ },
394
+ {
395
+ title: `${propName}: updateTrigger`,
396
+ updateProps: {
397
+ updateTriggers: {
398
+ [propName]: "function+trigger"
399
+ }
400
+ },
401
+ onBeforeUpdate,
402
+ onAfterUpdate
403
+ }
404
+ ];
405
+ }
406
+ default:
407
+ return null;
408
+ }
409
+ }
410
+ function makeAltDataTestCases(props, propTypes) {
411
+ const originalData = props.data;
412
+ if (!Array.isArray(originalData)) {
413
+ return [];
414
+ }
415
+ const partialUpdateProps = {
416
+ data: originalData.slice(),
417
+ _dataDiff: () => [{ startRow: 0, endRow: 2 }]
418
+ };
419
+ const genIterableProps = {
420
+ data: new Set(originalData),
421
+ _dataDiff: null
422
+ };
423
+ const nonIterableProps = {
424
+ data: {
425
+ length: originalData.length
426
+ }
427
+ };
428
+ for (const propName in props) {
429
+ if (propTypes[propName].type === "accessor") {
430
+ nonIterableProps[propName] = (_, info) => props[propName](originalData[info.index], info);
431
+ }
432
+ }
433
+ return [
434
+ {
435
+ title: "Partial update",
436
+ updateProps: partialUpdateProps
437
+ },
438
+ {
439
+ title: "Generic iterable data",
440
+ updateProps: genIterableProps
441
+ },
442
+ {
443
+ title: "non-iterable data",
444
+ updateProps: nonIterableProps
445
+ }
446
+ ];
447
+ }
448
+
449
+ // dist/vitest.js
450
+ var defaultSpyFactory = (obj, method) => import_vitest.vi.spyOn(obj, method);
451
+ var defaultResetSpy = (spy) => {
452
+ var _a2;
453
+ return (_a2 = spy.mockRestore) == null ? void 0 : _a2.call(spy);
454
+ };
455
+ function testLayer2(opts) {
456
+ const createSpy = opts.createSpy || defaultSpyFactory;
457
+ const resetSpy = opts.resetSpy || defaultResetSpy;
458
+ return testLayer({ ...opts, createSpy, resetSpy });
459
+ }
460
+ function testLayerAsync2(opts) {
461
+ const createSpy = opts.createSpy || defaultSpyFactory;
462
+ const resetSpy = opts.resetSpy || defaultResetSpy;
463
+ return testLayerAsync({ ...opts, createSpy, resetSpy });
464
+ }
465
+ //# sourceMappingURL=vitest.cjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/vitest.ts", "../src/lifecycle-test.ts", "../src/utils/setup-gl.ts", "../src/utils/layer.ts", "../src/utils/precision.ts", "../src/generate-layer-tests.ts"],
4
+ "sourcesContent": ["// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\n// Vitest-specific entry point with vi.spyOn default\n// Use: import { testLayer } from '@deck.gl/test-utils/vitest'\n\nimport {vi} from 'vitest';\nimport {testLayer as testLayerCore, testLayerAsync as testLayerAsyncCore} from './lifecycle-test';\nimport type {Layer} from '@deck.gl/core';\nimport type {ResetSpy, SpyFactory, TestLayerOptions} from './lifecycle-test';\n\n/** Default spy factory using vi.spyOn */\nconst defaultSpyFactory: SpyFactory = (obj, method) => vi.spyOn(obj, method as never);\n\n/** Default reset for vitest spies - restores original implementation */\nconst defaultResetSpy: ResetSpy = spy => spy.mockRestore?.();\n\nexport function testLayer<LayerT extends Layer>(\n opts: Omit<TestLayerOptions<LayerT>, 'createSpy' | 'resetSpy'> & {\n createSpy?: SpyFactory;\n resetSpy?: ResetSpy;\n }\n) {\n const createSpy = opts.createSpy || defaultSpyFactory;\n const resetSpy = opts.resetSpy || defaultResetSpy;\n return testLayerCore({...opts, createSpy, resetSpy});\n}\n\nexport function testLayerAsync<LayerT extends Layer>(\n opts: Omit<TestLayerOptions<LayerT>, 'createSpy' | 'resetSpy'> & {\n createSpy?: SpyFactory;\n resetSpy?: ResetSpy;\n }\n) {\n const createSpy = opts.createSpy || defaultSpyFactory;\n const resetSpy = opts.resetSpy || defaultResetSpy;\n return testLayerAsyncCore({...opts, createSpy, resetSpy});\n}\n\n// Re-export non-spy utilities\nexport {testInitializeLayer, testInitializeLayerAsync} from './lifecycle-test';\nexport {getLayerUniforms} from './utils/layer';\nexport {toLowPrecision} from './utils/precision';\nexport {gl, device} from './utils/setup-gl';\nexport {generateLayerTests} from './generate-layer-tests';\n\n// Types\nexport type {LayerTestCase, ResetSpy, SpyFactory, TestLayerOptions} from './lifecycle-test';\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {LayerManager, MapView, DeckRenderer} from '@deck.gl/core';\n\nimport {device} from './utils/setup-gl';\n\nimport type {Layer, CompositeLayer, Viewport} from '@deck.gl/core';\nimport type {Timeline} from '@luma.gl/engine';\nimport type {StatsManager} from '@luma.gl/core';\n\nconst testViewport = new MapView({}).makeViewport({\n width: 100,\n height: 100,\n viewState: {longitude: 0, latitude: 0, zoom: 1}\n}) as Viewport;\n\nfunction defaultOnError(error: unknown, title: string): void {\n if (error) {\n throw error;\n }\n}\n\ntype InitializeLayerTestOptions = {\n /** The layer instance to test */\n layer: Layer;\n /** The initial viewport\n * @default WebMercatorViewport\n */\n viewport?: Viewport;\n /** Callback if any error is thrown */\n onError?: (error: unknown, title: string) => void;\n};\n\nfunction initializeLayerManager({\n layer,\n viewport = testViewport,\n onError = defaultOnError\n}: InitializeLayerTestOptions): LayerManager {\n const layerManager = new LayerManager(device, {viewport});\n layerManager.setProps({\n onError: error => onError(error, `initializing ${layer.id}`)\n });\n\n layerManager.setLayers([layer]);\n return layerManager;\n}\n\n/** Test that initializing a layer does not throw.\n * Use `testInitializeLayerAsync` if the layer's initialization flow contains async operations.\n */\nexport function testInitializeLayer(\n opts: InitializeLayerTestOptions & {\n /** Automatically finalize the layer and release all resources after the test */\n finalize?: true;\n }\n): null;\nexport function testInitializeLayer(\n opts: InitializeLayerTestOptions & {\n /** Automatically finalize the layer and release all resources after the test */\n finalize: false;\n }\n): {\n /** Finalize the layer and release all resources */\n finalize: () => void;\n};\n\nexport function testInitializeLayer(\n opts: InitializeLayerTestOptions & {\n /** Automatically finalize the layer and release all resources after the test */\n finalize?: boolean;\n }\n): {\n /** Finalize the layer and release all resources */\n finalize: () => void;\n} | null {\n const layerManager = initializeLayerManager(opts);\n if (opts.finalize === false) {\n return {\n finalize: () => layerManager.finalize()\n };\n }\n layerManager.finalize();\n return null;\n}\n\n/** Test that initializing a layer does not throw.\n * Resolves when the layer's isLoaded flag becomes true.\n */\nexport function testInitializeLayerAsync(\n opts: InitializeLayerTestOptions & {\n /** Automatically finalize the layer and release all resources after the test */\n finalize?: true;\n }\n): Promise<null>;\nexport function testInitializeLayerAsync(\n opts: InitializeLayerTestOptions & {\n /** Automatically finalize the layer and release all resources after the test */\n finalize: false;\n }\n): Promise<{\n /** Finalize the layer and release all resources */\n finalize: () => void;\n}>;\n\nexport async function testInitializeLayerAsync(\n opts: InitializeLayerTestOptions & {\n /** Automatically finalize the layer and release all resources after the test */\n finalize?: boolean;\n }\n): Promise<{\n /** Finalize the layer and release all resources */\n finalize: () => void;\n} | null> {\n const layerManager = initializeLayerManager(opts);\n const deckRenderer = new DeckRenderer(device);\n while (!opts.layer.isLoaded) {\n await update({layerManager, deckRenderer, oldResourceCounts: {}});\n }\n if (opts.finalize === false) {\n return {\n finalize: () => layerManager.finalize()\n };\n }\n layerManager.finalize();\n return null;\n}\n\n/** Spy object compatible with both vitest and probe.gl */\nexport type Spy = {\n /** Restore the original method (vitest) */\n mockRestore?: () => void;\n /** Restore the original method (probe.gl) */\n restore?: () => void;\n /** Call history (vitest) */\n mock?: {calls: unknown[][]};\n /** Call history (probe.gl) */\n calls?: unknown[][];\n /** Whether the spy was called (probe.gl) */\n called?: boolean;\n};\n\n/** Factory function to create a spy on an object method */\nexport type SpyFactory = (obj: object, method: string) => Spy;\n\n/** Function to reset/cleanup a spy after each test case */\nexport type ResetSpy = (spy: Spy) => void;\n\nexport type LayerClass<LayerT extends Layer> = {\n new (...args): LayerT;\n layerName: string;\n defaultProps: any;\n};\n\nexport type LayerTestCase<LayerT extends Layer> = {\n title: string;\n viewport?: Viewport;\n /** Reset the props of the test layer instance */\n props?: Partial<LayerT['props']>;\n /** Update the given props of the test layer instance */\n updateProps?: Partial<LayerT['props']>;\n /** List of layer method names to watch */\n spies?: string[];\n\n /** Called before layer updates */\n onBeforeUpdate?: (params: {layer: Layer; testCase: LayerTestCase<LayerT>}) => void;\n\n /** Called after layer is updated */\n onAfterUpdate?: (params: {\n testCase: LayerTestCase<LayerT>;\n layer: LayerT;\n oldState: any;\n subLayers: Layer[];\n subLayer: Layer | null;\n spies: Record<string, Spy>;\n }) => void;\n};\n\ntype TestResources = {\n layerManager: LayerManager;\n deckRenderer: DeckRenderer;\n oldResourceCounts: Record<string, number>;\n};\n\nexport type TestLayerOptions<LayerT extends Layer> = {\n /** The layer class to test against */\n Layer: LayerClass<LayerT>;\n /** The initial viewport\n * @default WebMercatorViewport\n */\n viewport?: Viewport;\n /**\n * If provided, used to controls time progression. Useful for testing transitions and animations.\n */\n timeline?: Timeline;\n testCases?: LayerTestCase<LayerT>[];\n /**\n * List of layer method names to watch\n */\n spies?: string[];\n /** Callback if any error is thrown */\n onError?: (error: Error, title: string) => void;\n /** Factory function to create spies */\n createSpy: SpyFactory;\n /** Function to reset/cleanup a spy after each test case */\n resetSpy: ResetSpy;\n};\n\n/**\n * Initialize and updates a layer over a sequence of scenarios (test cases).\n * Use `testLayerAsync` if the layer's update flow contains async operations.\n */\nexport function testLayer<LayerT extends Layer>(opts: TestLayerOptions<LayerT>): void {\n const {Layer, testCases = [], spies = [], onError = defaultOnError, createSpy, resetSpy} = opts;\n\n const resources = setupLayerTests(`testing ${Layer.layerName}`, opts);\n\n let layer = new Layer();\n // Run successive update tests\n for (const testCase of testCases) {\n // Save old state before update\n const oldState = {...layer.state};\n\n const {layer: newLayer, spyMap} = runLayerTestUpdate(\n testCase,\n resources,\n layer,\n spies,\n createSpy\n );\n\n runLayerTestPostUpdateCheck(testCase, newLayer, oldState, spyMap);\n\n // Reset spies between test cases\n Object.keys(spyMap).forEach(k => resetSpy(spyMap[k]));\n layer = newLayer;\n }\n\n const error = cleanupAfterLayerTests(resources);\n if (error) {\n onError(error, `${Layer.layerName} should delete all resources`);\n }\n}\n\n/**\n * Initialize and updates a layer over a sequence of scenarios (test cases).\n * Each test case is awaited until the layer's isLoaded flag is true.\n */\nexport async function testLayerAsync<LayerT extends Layer>(\n opts: TestLayerOptions<LayerT>\n): Promise<void> {\n const {Layer, testCases = [], spies = [], onError = defaultOnError, createSpy, resetSpy} = opts;\n\n const resources = setupLayerTests(`testing ${Layer.layerName}`, opts);\n\n let layer = new Layer();\n // Run successive update tests\n for (const testCase of testCases) {\n // Save old state before update\n const oldState = {...layer.state};\n\n const {layer: newLayer, spyMap} = runLayerTestUpdate(\n testCase,\n resources,\n layer,\n spies,\n createSpy\n );\n\n runLayerTestPostUpdateCheck(testCase, newLayer, oldState, spyMap);\n\n while (!newLayer.isLoaded) {\n await update(resources);\n runLayerTestPostUpdateCheck(testCase, newLayer, oldState, spyMap);\n }\n\n // Reset spies between test cases\n Object.keys(spyMap).forEach(k => resetSpy(spyMap[k]));\n layer = newLayer;\n }\n\n // Use async cleanup to allow pending luma.gl async operations to complete\n const error = await cleanupAfterLayerTestsAsync(resources);\n if (error) {\n onError(error, `${Layer.layerName} should delete all resources`);\n }\n}\n\nfunction setupLayerTests(\n testTitle: string,\n {\n viewport = testViewport,\n timeline,\n onError = defaultOnError\n }: {\n viewport?: Viewport;\n timeline?: Timeline;\n onError?: (error: Error, title: string) => void;\n }\n): TestResources {\n const oldResourceCounts = getResourceCounts();\n\n const layerManager = new LayerManager(device, {viewport, timeline});\n const deckRenderer = new DeckRenderer(device);\n\n const props = {\n layerFilter: null,\n drawPickingColors: false,\n onError: error => onError(error, testTitle)\n };\n layerManager.setProps(props);\n deckRenderer.setProps(props);\n\n return {layerManager, deckRenderer, oldResourceCounts};\n}\n\nfunction cleanupAfterLayerTests({\n layerManager,\n deckRenderer,\n oldResourceCounts\n}: TestResources): Error | null {\n layerManager.setLayers([]);\n layerManager.finalize();\n deckRenderer.finalize();\n\n return getResourceCountDelta(oldResourceCounts);\n}\n\n/**\n * Async cleanup that waits for pending async operations before finalizing resources.\n * This prevents unhandled rejections from luma.gl's async shader error reporting\n * which may try to access destroyed WebGL resources if cleanup happens too early.\n */\nasync function cleanupAfterLayerTestsAsync({\n layerManager,\n deckRenderer,\n oldResourceCounts\n}: TestResources): Promise<Error | null> {\n layerManager.setLayers([]);\n\n // Wait for any pending async operations (e.g., luma.gl's deferred shader compilation\n // error handling) to complete before destroying resources. This prevents\n // \"getProgramInfoLog\" errors when async error reporting tries to access\n // already-destroyed WebGL programs.\n await new Promise(resolve => setTimeout(resolve, 0));\n\n layerManager.finalize();\n deckRenderer.finalize();\n\n return getResourceCountDelta(oldResourceCounts);\n}\n\nfunction getResourceCounts(): Record<string, number> {\n /* global luma */\n const resourceStats = (luma.stats as StatsManager).get('Resource Counts');\n return {\n Texture2D: resourceStats.get('Texture2Ds Active').count,\n Buffer: resourceStats.get('Buffers Active').count\n };\n}\n\nfunction getResourceCountDelta(oldResourceCounts: Record<string, number>): Error | null {\n const resourceCounts = getResourceCounts();\n\n for (const resourceName in resourceCounts) {\n if (resourceCounts[resourceName] !== oldResourceCounts[resourceName]) {\n return new Error(\n `${resourceCounts[resourceName] - oldResourceCounts[resourceName]} ${resourceName}s`\n );\n }\n }\n return null;\n}\n\nfunction injectSpies(layer: Layer, spies: string[], spyFactory: SpyFactory): Record<string, Spy> {\n const spyMap: Record<string, Spy> = {};\n if (spies) {\n for (const functionName of spies) {\n spyMap[functionName] = spyFactory(Object.getPrototypeOf(layer), functionName);\n }\n }\n return spyMap;\n}\n\nfunction runLayerTestPostUpdateCheck<LayerT extends Layer>(\n testCase: LayerTestCase<LayerT>,\n newLayer: LayerT,\n oldState: any,\n spyMap: Record<string, Spy>\n) {\n // assert on updated layer\n if (testCase.onAfterUpdate) {\n // layer manager should handle match subLayer and tranfer state and props\n // here we assume subLayer matches copy over the new props from a new subLayer\n const subLayers = newLayer.isComposite\n ? (newLayer as Layer as CompositeLayer).getSubLayers()\n : [];\n const subLayer = subLayers.length ? subLayers[0] : null;\n\n testCase.onAfterUpdate({\n testCase,\n layer: newLayer,\n oldState,\n subLayers,\n subLayer,\n spies: spyMap\n });\n }\n}\n\nfunction runLayerTestUpdate<LayerT extends Layer>(\n testCase: LayerTestCase<LayerT>,\n {layerManager, deckRenderer}: TestResources,\n layer: LayerT,\n spies: string[],\n spyFactory: SpyFactory\n): {\n layer: LayerT;\n spyMap: Record<string, Spy>;\n} {\n const {props, updateProps, onBeforeUpdate, viewport = layerManager.context.viewport} = testCase;\n\n if (onBeforeUpdate) {\n onBeforeUpdate({layer, testCase});\n }\n\n if (props) {\n // Test case can reset the props on every iteration\n layer = new (layer.constructor as LayerClass<LayerT>)(props);\n } else if (updateProps) {\n // Test case can override with new props on every iteration\n layer = layer.clone(updateProps);\n }\n\n // Create a map of spies that the test case can inspect\n spies = testCase.spies || spies;\n const spyMap = injectSpies(layer, spies, spyFactory);\n const drawLayers = () => {\n deckRenderer.renderLayers({\n pass: 'test',\n views: {},\n effects: [],\n viewports: [viewport],\n layers: layerManager.getLayers(),\n onViewportActive: layerManager.activateViewport\n });\n };\n\n layerManager.setLayers([layer]);\n drawLayers();\n\n // clear update flags set by viewport change, if any\n if (layerManager.needsUpdate()) {\n layerManager.updateLayers();\n drawLayers();\n }\n\n return {layer, spyMap};\n}\n\n/* global setTimeout */\nfunction update({layerManager, deckRenderer}: TestResources): Promise<void> {\n return new Promise(resolve => {\n const onAnimationFrame = () => {\n if (layerManager.needsUpdate()) {\n layerManager.updateLayers();\n\n deckRenderer.renderLayers({\n pass: 'test',\n views: {},\n effects: [],\n viewports: [layerManager.context.viewport],\n layers: layerManager.getLayers(),\n onViewportActive: layerManager.activateViewport\n });\n resolve();\n return;\n }\n\n setTimeout(onAnimationFrame, 50);\n };\n\n onAnimationFrame();\n });\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {webglDevice, NullDevice} from '@luma.gl/test-utils';\n\n// Use pre-created device from @luma.gl/test-utils, fall back to NullDevice in Node\nexport const device = webglDevice || new NullDevice({});\nexport const gl = webglDevice?.gl || 1;\n\nglobalThis.glContext = globalThis.glContext || gl;\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {UniformValue} from '@luma.gl/core';\nimport {Layer} from '@deck.gl/core';\n\n/**\n * Extract uniform values set for a Layer in the underlying UniformBlock store\n */\nexport function getLayerUniforms(layer: Layer, blockName?: string): Record<string, UniformValue> {\n const uniforms = {};\n const uniformStore = layer.getModels()[0]._uniformStore;\n const uniformBlocks = blockName\n ? [uniformStore.uniformBlocks.get(blockName)]\n : uniformStore.uniformBlocks.values();\n for (const block of uniformBlocks) {\n Object.assign(uniforms, block!.uniforms);\n }\n\n return uniforms;\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nexport function toLowPrecision(input: number, precision?: number): number;\nexport function toLowPrecision(input: number[], precision?: number): number[];\nexport function toLowPrecision(\n input: Record<string, number>,\n precision?: number\n): Record<string, number>;\n\n/**\n * Covert all numbers in a deep structure to a given precision, allowing\n * reliable float comparisons. Converts data in-place.\n */\nexport function toLowPrecision(\n input: number | number[] | Record<string, number>,\n precision: number = 11\n): number | number[] | Record<string, number> {\n /* eslint-disable guard-for-in */\n if (typeof input === 'number') {\n return Number(input.toPrecision(precision));\n }\n if (Array.isArray(input)) {\n return input.map(item => toLowPrecision(item, precision));\n }\n if (typeof input === 'object') {\n for (const key in input) {\n input[key] = toLowPrecision(input[key], precision);\n }\n }\n return input;\n}\n", "// deck.gl\n// SPDX-License-Identifier: MIT\n// Copyright (c) vis.gl contributors\n\nimport {_count as count, Layer} from '@deck.gl/core';\n\nimport type {DefaultProps} from '@deck.gl/core';\nimport type {LayerTestCase, LayerClass} from './lifecycle-test';\n\n// eslint-disable-next-line @typescript-eslint/no-empty-function\nfunction noop() {}\n\nfunction defaultAssert(condition: any, comment: string): void {\n if (!condition) {\n throw new Error(comment);\n }\n}\n\n// Automatically generate testLayer test cases\nexport function generateLayerTests<LayerT extends Layer>({\n // eslint-disable-next-line @typescript-eslint/no-shadow\n Layer,\n sampleProps = {},\n assert = defaultAssert,\n onBeforeUpdate = noop,\n onAfterUpdate = noop,\n runDefaultAsserts = true\n}: {\n Layer: LayerClass<LayerT>;\n /**\n * Override default props during the test\n */\n sampleProps?: Partial<LayerT['props']>;\n assert?: (condition: any, comment: string) => void;\n onBeforeUpdate?: LayerTestCase<LayerT>['onBeforeUpdate'];\n onAfterUpdate?: LayerTestCase<LayerT>['onAfterUpdate'];\n\n /**\n * Test some typical assumptions after layer updates\n * For primitive layers, assert that layer has model(s).\n * For composite layers, assert that layer has sublayer(s).\n * @default true\n */\n runDefaultAsserts?: boolean;\n}): LayerTestCase<LayerT>[] {\n assert(Layer.layerName, 'Layer should have display name');\n\n function wrapTestCaseTitle(title: string): string {\n return `${Layer.layerName}#${title}`;\n }\n\n const testCases: LayerTestCase<LayerT>[] = [\n {\n title: 'Empty props',\n props: {}\n },\n {\n title: 'Null data',\n // @ts-expect-error null may not be an expected data type\n updateProps: {data: null}\n },\n {\n title: 'Sample data',\n updateProps: sampleProps\n }\n ];\n\n try {\n // Calling constructor for the first time resolves default props\n // eslint-disable-next-line\n new Layer({});\n } catch (error: unknown) {\n assert(false, `Construct ${Layer.layerName} throws: ${(error as Error).message}`);\n }\n\n // @ts-expect-error Access hidden properties\n const {_propTypes: propTypes, _mergedDefaultProps: defaultProps} = Layer;\n\n // Test alternative data formats\n testCases.push(...makeAltDataTestCases<LayerT>(sampleProps, propTypes));\n\n for (const propName in Layer.defaultProps) {\n if (!(propName in sampleProps)) {\n // Do not override user provided props - they may be layer-specific\n const newTestCase =\n makeAltPropTestCase({propName, propTypes, defaultProps, sampleProps, assert}) || [];\n testCases.push(...newTestCase);\n }\n }\n\n testCases.forEach(testCase => {\n testCase.title = wrapTestCaseTitle(testCase.title);\n const beforeFunc = testCase.onBeforeUpdate || noop;\n const afterFunc = testCase.onAfterUpdate || noop;\n testCase.onBeforeUpdate = params => {\n // Generated callback\n beforeFunc(params);\n // User callback\n onBeforeUpdate(params);\n };\n testCase.onAfterUpdate = params => {\n // Generated callback\n afterFunc(params);\n // User callback\n onAfterUpdate(params);\n\n // Default assert\n if (runDefaultAsserts) {\n if (params.layer.isComposite) {\n const {data} = params.layer.props;\n if (data && typeof data === 'object' && count(data)) {\n assert(params.subLayers.length, 'Layer should have sublayers');\n }\n } else {\n assert(params.layer.getModels().length, 'Layer should have models');\n }\n }\n };\n });\n\n return testCases;\n}\n\nfunction makeAltPropTestCase<LayerT extends Layer>({\n propName,\n propTypes,\n defaultProps,\n sampleProps,\n assert\n}: {\n propName: string;\n propTypes: DefaultProps<LayerT['props']>;\n defaultProps: LayerT['props'];\n sampleProps: Partial<LayerT['props']>;\n assert: (condition: any, comment: string) => void;\n}): LayerTestCase<LayerT>[] | null {\n const newProps = {...sampleProps};\n const propDef = propTypes[propName];\n\n if (!propDef) {\n return null;\n }\n\n switch (propDef.type) {\n case 'boolean':\n newProps[propName] = !defaultProps[propName];\n return [\n {\n title: `${propName}: ${String(newProps[propName])}`,\n props: newProps\n }\n ];\n\n case 'number':\n if ('max' in propDef) {\n newProps[propName] = propDef.max;\n } else if ('min' in propDef) {\n newProps[propName] = propDef.min;\n } else {\n newProps[propName] = defaultProps[propName] + 1;\n }\n return [\n {\n title: `${propName}: ${String(newProps[propName])}`,\n props: newProps\n }\n ];\n\n case 'accessor': {\n if (typeof defaultProps[propName] === 'function') {\n return null;\n }\n let callCount = 0;\n newProps[propName] = () => {\n callCount++;\n return defaultProps[propName];\n };\n newProps.updateTriggers = {\n [propName]: 'function'\n };\n const onBeforeUpdate = () => (callCount = 0);\n const onAfterUpdate = () => assert(callCount > 0, 'accessor function is called');\n\n return [\n {\n title: `${propName}: () => ${defaultProps[propName]}`,\n props: newProps,\n onBeforeUpdate,\n onAfterUpdate\n },\n {\n title: `${propName}: updateTrigger`,\n updateProps: {\n updateTriggers: {\n [propName]: 'function+trigger'\n }\n } as Partial<Layer['props']>,\n onBeforeUpdate,\n onAfterUpdate\n }\n ];\n }\n\n default:\n return null;\n }\n}\n\nfunction makeAltDataTestCases<LayerT extends Layer>(\n props: Partial<LayerT['props']>,\n propTypes: DefaultProps<LayerT['props']>\n): LayerTestCase<LayerT>[] {\n const originalData = props.data;\n if (!Array.isArray(originalData)) {\n return [];\n }\n // partial update\n const partialUpdateProps: Partial<Layer['props']> = {\n data: originalData.slice(),\n _dataDiff: () => [{startRow: 0, endRow: 2}]\n };\n // data should support any iterable\n const genIterableProps: Partial<Layer['props']> = {\n data: new Set(originalData),\n _dataDiff: null\n };\n // data in non-iterable form\n const nonIterableProps: Partial<Layer['props']> = {\n data: {\n length: originalData.length\n }\n };\n for (const propName in props) {\n // @ts-ignore propName cannot be used as index\n if (propTypes[propName].type === 'accessor') {\n // @ts-ignore propName cannot be used as index\n nonIterableProps[propName] = (_, info) => props[propName](originalData[info.index], info);\n }\n }\n\n return [\n {\n title: 'Partial update',\n updateProps: partialUpdateProps\n },\n {\n title: 'Generic iterable data',\n updateProps: genIterableProps\n },\n {\n title: 'non-iterable data',\n updateProps: nonIterableProps\n }\n ];\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["testLayer", "testLayerAsync", "import_core", "count", "_a", "testLayer", "testLayerAsync"]
7
+ }
@@ -0,0 +1,17 @@
1
+ import type { Layer } from '@deck.gl/core';
2
+ import type { ResetSpy, SpyFactory, TestLayerOptions } from "./lifecycle-test.js";
3
+ export declare function testLayer<LayerT extends Layer>(opts: Omit<TestLayerOptions<LayerT>, 'createSpy' | 'resetSpy'> & {
4
+ createSpy?: SpyFactory;
5
+ resetSpy?: ResetSpy;
6
+ }): void;
7
+ export declare function testLayerAsync<LayerT extends Layer>(opts: Omit<TestLayerOptions<LayerT>, 'createSpy' | 'resetSpy'> & {
8
+ createSpy?: SpyFactory;
9
+ resetSpy?: ResetSpy;
10
+ }): Promise<void>;
11
+ export { testInitializeLayer, testInitializeLayerAsync } from "./lifecycle-test.js";
12
+ export { getLayerUniforms } from "./utils/layer.js";
13
+ export { toLowPrecision } from "./utils/precision.js";
14
+ export { gl, device } from "./utils/setup-gl.js";
15
+ export { generateLayerTests } from "./generate-layer-tests.js";
16
+ export type { LayerTestCase, ResetSpy, SpyFactory, TestLayerOptions } from "./lifecycle-test.js";
17
+ //# sourceMappingURL=vitest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAC,KAAK,EAAC,MAAM,eAAe,CAAC;AACzC,OAAO,KAAK,EAAC,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAC,4BAAyB;AAQ7E,wBAAgB,SAAS,CAAC,MAAM,SAAS,KAAK,EAC5C,IAAI,EAAE,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC,GAAG;IAC/D,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,QAKF;AAED,wBAAgB,cAAc,CAAC,MAAM,SAAS,KAAK,EACjD,IAAI,EAAE,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC,GAAG;IAC/D,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,iBAKF;AAGD,OAAO,EAAC,mBAAmB,EAAE,wBAAwB,EAAC,4BAAyB;AAC/E,OAAO,EAAC,gBAAgB,EAAC,yBAAsB;AAC/C,OAAO,EAAC,cAAc,EAAC,6BAA0B;AACjD,OAAO,EAAC,EAAE,EAAE,MAAM,EAAC,4BAAyB;AAC5C,OAAO,EAAC,kBAAkB,EAAC,kCAA+B;AAG1D,YAAY,EAAC,aAAa,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAC,4BAAyB"}
package/dist/vitest.js ADDED
@@ -0,0 +1,28 @@
1
+ // deck.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ // Vitest-specific entry point with vi.spyOn default
5
+ // Use: import { testLayer } from '@deck.gl/test-utils/vitest'
6
+ import { vi } from 'vitest';
7
+ import { testLayer as testLayerCore, testLayerAsync as testLayerAsyncCore } from "./lifecycle-test.js";
8
+ /** Default spy factory using vi.spyOn */
9
+ const defaultSpyFactory = (obj, method) => vi.spyOn(obj, method);
10
+ /** Default reset for vitest spies - restores original implementation */
11
+ const defaultResetSpy = spy => spy.mockRestore?.();
12
+ export function testLayer(opts) {
13
+ const createSpy = opts.createSpy || defaultSpyFactory;
14
+ const resetSpy = opts.resetSpy || defaultResetSpy;
15
+ return testLayerCore({ ...opts, createSpy, resetSpy });
16
+ }
17
+ export function testLayerAsync(opts) {
18
+ const createSpy = opts.createSpy || defaultSpyFactory;
19
+ const resetSpy = opts.resetSpy || defaultResetSpy;
20
+ return testLayerAsyncCore({ ...opts, createSpy, resetSpy });
21
+ }
22
+ // Re-export non-spy utilities
23
+ export { testInitializeLayer, testInitializeLayerAsync } from "./lifecycle-test.js";
24
+ export { getLayerUniforms } from "./utils/layer.js";
25
+ export { toLowPrecision } from "./utils/precision.js";
26
+ export { gl, device } from "./utils/setup-gl.js";
27
+ export { generateLayerTests } from "./generate-layer-tests.js";
28
+ //# sourceMappingURL=vitest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../src/vitest.ts"],"names":[],"mappings":"AAAA,UAAU;AACV,+BAA+B;AAC/B,oCAAoC;AAEpC,oDAAoD;AACpD,8DAA8D;AAE9D,OAAO,EAAC,EAAE,EAAC,MAAM,QAAQ,CAAC;AAC1B,OAAO,EAAC,SAAS,IAAI,aAAa,EAAE,cAAc,IAAI,kBAAkB,EAAC,4BAAyB;AAIlG,yCAAyC;AACzC,MAAM,iBAAiB,GAAe,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,MAAe,CAAC,CAAC;AAEtF,wEAAwE;AACxE,MAAM,eAAe,GAAa,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC;AAE7D,MAAM,UAAU,SAAS,CACvB,IAGC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,iBAAiB,CAAC;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,eAAe,CAAC;IAClD,OAAO,aAAa,CAAC,EAAC,GAAG,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAC,CAAC,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,cAAc,CAC5B,IAGC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,iBAAiB,CAAC;IACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,eAAe,CAAC;IAClD,OAAO,kBAAkB,CAAC,EAAC,GAAG,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAC,CAAC,CAAC;AAC5D,CAAC;AAED,8BAA8B;AAC9B,OAAO,EAAC,mBAAmB,EAAE,wBAAwB,EAAC,4BAAyB;AAC/E,OAAO,EAAC,gBAAgB,EAAC,yBAAsB;AAC/C,OAAO,EAAC,cAAc,EAAC,6BAA0B;AACjD,OAAO,EAAC,EAAE,EAAE,MAAM,EAAC,4BAAyB;AAC5C,OAAO,EAAC,kBAAkB,EAAC,kCAA+B"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Test utilities for deck.gl layers",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
- "version": "9.3.0-alpha.2",
6
+ "version": "9.3.0-alpha.5",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -25,6 +25,11 @@
25
25
  "types": "./dist/index.d.ts",
26
26
  "import": "./dist/index.js",
27
27
  "require": "./dist/index.cjs"
28
+ },
29
+ "./vitest": {
30
+ "types": "./dist/vitest.d.ts",
31
+ "import": "./dist/vitest.js",
32
+ "require": "./dist/vitest.cjs"
28
33
  }
29
34
  },
30
35
  "files": [
@@ -32,14 +37,20 @@
32
37
  "src"
33
38
  ],
34
39
  "dependencies": {
35
- "@luma.gl/test-utils": "^9.3.0-alpha.6",
36
- "@luma.gl/webgl": "^9.3.0-alpha.6"
40
+ "@luma.gl/test-utils": "^9.3.0-alpha.10",
41
+ "@luma.gl/webgl": "^9.3.0-alpha.10"
37
42
  },
38
43
  "peerDependencies": {
39
- "@deck.gl/core": "~9.2.0",
40
- "@luma.gl/core": "~9.3.0-alpha.6",
41
- "@luma.gl/engine": "~9.3.0-alpha.6",
42
- "@probe.gl/test-utils": "^4.1.1"
44
+ "@deck.gl/core": "~9.3.0-alpha.1",
45
+ "@luma.gl/core": "~9.3.0-alpha.10",
46
+ "@luma.gl/engine": "~9.3.0-alpha.10",
47
+ "@probe.gl/test-utils": "^4.1.1",
48
+ "vitest": "^4.0.18"
43
49
  },
44
- "gitHead": "135d329f4a4b596ae2c16e4eb801eda30252f3bc"
50
+ "peerDependenciesMeta": {
51
+ "vitest": {
52
+ "optional": true
53
+ }
54
+ },
55
+ "gitHead": "c3ad1cee357af674cc31df37979d61325baf9d7a"
45
56
  }