or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdinteraction-testing.mdlifecycle-testing.mdtest-generation.mdtesting-utilities.mdvisual-testing.md
tile.json

lifecycle-testing.mddocs/

Layer Lifecycle Testing

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.

Capabilities

Layer Testing Functions

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>;

Layer Initialization Testing

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"
  })
});

Test Case Configuration

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);
      }
    }
  ]
});

Resource Tracking

The lifecycle testing functions automatically track WebGL resources to ensure layers properly clean up after themselves. The system monitors:

  • Texture2D: WebGL texture objects
  • Buffer: WebGL buffer objects

If resource counts don't match before and after testing, an error is thrown with details about leaked resources.

Error Handling

All lifecycle testing functions accept an onError callback that receives:

  • error: The thrown error or exception
  • title: Context string describing where the error occurred
testLayer({
  Layer: MyLayer,
  onError: (error, title) => {
    console.error(`Error in ${title}:`, error);
    // Custom error handling logic
  },
  testCases: [...]
});