Automatic generation of comprehensive test cases for deck.gl layers based on layer properties, default values, and prop types. Creates systematic tests for property validation, data formats, and accessor functions.
Primary function for generating exhaustive test cases from layer configuration.
/**
* Automatically generate comprehensive test cases for a layer
* @param opts - Configuration for test generation
* @returns Array of generated LayerTestCase objects
*/
function generateLayerTests<LayerT extends Layer>(opts: {
/** The layer class to generate tests for */
Layer: LayerClass<LayerT>;
/** Override default props during the test (prevents conflicts with generated tests) */
sampleProps?: Partial<LayerT['props']>;
/** Custom assertion function (default: throws Error on false condition) */
assert?: (condition: any, comment: string) => void;
/** Called before each generated test case update */
onBeforeUpdate?: LayerTestCase<LayerT>['onBeforeUpdate'];
/** Called after each generated test case update */
onAfterUpdate?: LayerTestCase<LayerT>['onAfterUpdate'];
/** Test typical assumptions after layer updates (default: true) */
runDefaultAsserts?: boolean;
}): LayerTestCase<LayerT>[];Usage Examples:
import { generateLayerTests, testLayer } from "@deck.gl/test-utils";
import { ScatterplotLayer } from "@deck.gl/layers";
// Basic test generation
const testCases = generateLayerTests({
Layer: ScatterplotLayer,
sampleProps: {
data: [
{ position: [0, 0], color: [255, 0, 0], size: 10 },
{ position: [1, 1], color: [0, 255, 0], size: 20 }
],
getPosition: d => d.position,
getFillColor: d => d.color,
getRadius: d => d.size
}
});
// Run the generated tests
testLayer({
Layer: ScatterplotLayer,
testCases
});
// Custom assertion and callbacks
const customTests = generateLayerTests({
Layer: ScatterplotLayer,
sampleProps: {
data: [{ position: [0, 0] }],
getPosition: d => d.position
},
assert: (condition, message) => {
if (!condition) {
console.error('Test failed:', message);
throw new Error(message);
}
},
onAfterUpdate: ({ layer, testCase }) => {
console.log(`Completed test: ${testCase.title}`);
console.log(`Layer has ${layer.getModels().length} models`);
},
runDefaultAsserts: true
});The generateLayerTests function creates several categories of test cases:
Always generated for every layer:
Generated based on layer's defaultProps and propTypes:
Tests toggling boolean values from their defaults:
// If defaultProps.visible = true, generates:
{
title: "LayerName#visible: false",
props: { visible: false }
}Tests boundary values and variations:
// If prop has max/min constraints:
{ title: "LayerName#opacity: 1", props: { opacity: 1 } } // max value
{ title: "LayerName#opacity: 0", props: { opacity: 0 } } // min value
// Otherwise tests increment:
{ title: "LayerName#radiusScale: 2", props: { radiusScale: 2 } } // default + 1Tests both constant values and function accessors:
// Tests function accessor with call tracking:
{
title: "LayerName#getPosition: () => [0, 0]",
props: {
getPosition: () => [0, 0],
updateTriggers: { getPosition: 'function' }
},
onBeforeUpdate: () => { /* reset call count */ },
onAfterUpdate: () => { /* assert function was called */ }
}
// Tests updateTrigger changes:
{
title: "LayerName#getPosition: updateTrigger",
updateProps: {
updateTriggers: { getPosition: 'function+trigger' }
}
}Generated when sampleProps.data is an array:
_dataDiff// Example generated data format tests:
[
{
title: "LayerName#Partial update",
updateProps: {
data: [...originalData],
_dataDiff: () => [{ startRow: 0, endRow: 2 }]
}
},
{
title: "LayerName#Generic iterable data",
updateProps: {
data: new Set(originalData),
_dataDiff: null
}
},
{
title: "LayerName#non-iterable data",
updateProps: {
data: { length: originalData.length },
// Accessor functions adapted for indexed access
getPosition: (_, info) => sampleProps.getPosition(originalData[info.index], info)
}
}
]When runDefaultAsserts: true (default), generated tests include automatic validation:
For layers with layer.isComposite === true:
assert(params.subLayers.length, 'Layer should have sublayers')For non-composite layers:
assert(params.layer.getModels().length, 'Layer should have models')Generated tests work seamlessly with manually written test cases:
import { generateLayerTests, testLayer } from "@deck.gl/test-utils";
import { MyCustomLayer } from "./my-custom-layer";
const generatedTests = generateLayerTests({
Layer: MyCustomLayer,
sampleProps: { /* ... */ }
});
const manualTests = [
{
title: "Custom edge case",
props: { /* specific configuration */ },
onAfterUpdate: ({ layer }) => {
// Custom validation logic
}
}
];
testLayer({
Layer: MyCustomLayer,
testCases: [...generatedTests, ...manualTests]
});The test generator includes error handling for layer construction:
layerName)All generated test failures include the full test case title with layer name prefix for easy identification (e.g., "ScatterplotLayer#getRadius: () => 10").