Core testing functionality for deck.gl layer initialization, state management, and resource tracking. Provides both synchronous and asynchronous testing patterns with automatic WebGL resource monitoring.
Primary functions for testing layer lifecycle through sequences of test cases.
/**
* Initialize and update a layer over a sequence of test cases (synchronous)
* @param opts - Configuration including Layer class, test cases, and options
*/
function testLayer<LayerT extends Layer>(opts: {
/** The layer class to test against */
Layer: LayerClass<LayerT>;
/** The initial viewport (default: WebMercatorViewport) */
viewport?: Viewport;
/** Timeline for controlling time progression in transitions/animations */
timeline?: Timeline;
/** Array of test cases to execute sequentially */
testCases?: LayerTestCase<LayerT>[];
/** List of layer method names to spy on across all test cases */
spies?: string[];
/** Callback if any error is thrown */
onError?: (error: Error, title: string) => void;
}): void;
/**
* Initialize and update a layer over a sequence of test cases (asynchronous)
* Waits for each test case until the layer's isLoaded flag becomes true
* @param opts - Configuration including Layer class, test cases, and options
*/
function testLayerAsync<LayerT extends Layer>(opts: {
/** The layer class to test against */
Layer: LayerClass<LayerT>;
/** The initial viewport (default: WebMercatorViewport) */
viewport?: Viewport;
/** Timeline for controlling time progression in transitions/animations */
timeline?: Timeline;
/** Array of test cases to execute sequentially */
testCases?: LayerTestCase<LayerT>[];
/** List of layer method names to spy on across all test cases */
spies?: string[];
/** Callback if any error is thrown */
onError?: (error: Error, title: string) => void;
}): Promise<void>;Simplified functions for testing just layer initialization without full test case sequences.
/**
* Test that initializing a layer does not throw (synchronous)
* @param opts - Layer and initialization options
* @returns Finalize function if finalize: false, otherwise null
*/
function testInitializeLayer(opts: {
/** The layer instance to test */
layer: Layer;
/** The initial viewport (default: WebMercatorViewport) */
viewport?: Viewport;
/** Callback if any error is thrown */
onError?: (error: unknown, title: string) => void;
/** Automatically finalize the layer and release all resources after the test */
finalize?: boolean;
}): { finalize: () => void } | null;
/**
* Test that initializing a layer does not throw (asynchronous)
* Resolves when the layer's isLoaded flag becomes true
* @param opts - Layer and initialization options
* @returns Promise of finalize function if finalize: false, otherwise null
*/
function testInitializeLayerAsync(opts: {
/** The layer instance to test */
layer: Layer;
/** The initial viewport (default: WebMercatorViewport) */
viewport?: Viewport;
/** Callback if any error is thrown */
onError?: (error: unknown, title: string) => void;
/** Automatically finalize the layer and release all resources after the test */
finalize?: boolean;
}): Promise<{ finalize: () => void } | null>;Usage Examples:
import { testInitializeLayer, testInitializeLayerAsync } from "@deck.gl/test-utils";
import { ScatterplotLayer } from "@deck.gl/layers";
// Simple initialization test
testInitializeLayer({
layer: new ScatterplotLayer({
data: [{ position: [0, 0] }],
getPosition: d => d.position
})
});
// Manual resource management
const result = testInitializeLayer({
layer: new ScatterplotLayer({
data: [{ position: [0, 0] }],
getPosition: d => d.position
}),
finalize: false
});
// Later, manually finalize
if (result) {
result.finalize();
}
// Async initialization test
await testInitializeLayerAsync({
layer: new AsyncDataLayer({
dataUrl: "https://api.example.com/data"
})
});Structure for defining individual test cases within a layer test sequence.
interface LayerTestCase<LayerT extends Layer> {
/** Descriptive title for the test case */
title: string;
/** Override viewport for this test case */
viewport?: Viewport;
/** Reset the props of the test layer instance */
props?: Partial<LayerT['props']>;
/** Update the given props of the test layer instance */
updateProps?: Partial<LayerT['props']>;
/** List of layer method names to watch for this test case */
spies?: string[];
/** Called before layer updates */
onBeforeUpdate?: (params: {
layer: Layer;
testCase: LayerTestCase<LayerT>;
}) => void;
/** Called after layer is updated */
onAfterUpdate?: (params: {
testCase: LayerTestCase<LayerT>;
layer: LayerT;
oldState: any;
subLayers: Layer[];
subLayer: Layer | null;
spies: Record<string, Spy>;
}) => void;
}
interface LayerClass<LayerT extends Layer> {
new (...args: any[]): LayerT;
layerName: string;
defaultProps: any;
}Usage Examples:
import { testLayer } from "@deck.gl/test-utils";
import { ScatterplotLayer } from "@deck.gl/layers";
testLayer({
Layer: ScatterplotLayer,
testCases: [
{
title: "Empty layer initialization",
props: {}
},
{
title: "Layer with data",
updateProps: {
data: [
{ position: [0, 0], size: 10 },
{ position: [1, 1], size: 20 }
],
getPosition: d => d.position,
getRadius: d => d.size
}
},
{
title: "Layer property updates",
updateProps: {
radiusScale: 2,
radiusMinPixels: 5
},
spies: ['updateState'],
onAfterUpdate: ({ spies }) => {
console.log('updateState called:', spies.updateState.callCount);
}
}
]
});The lifecycle testing functions automatically track WebGL resources to ensure layers properly clean up after themselves. The system monitors:
If resource counts don't match before and after testing, an error is thrown with details about leaked resources.
All lifecycle testing functions accept an onError callback that receives:
error: The thrown error or exceptiontitle: Context string describing where the error occurredtestLayer({
Layer: MyLayer,
onError: (error, title) => {
console.error(`Error in ${title}:`, error);
// Custom error handling logic
},
testCases: [...]
});