CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-deck-gl--test-utils

Test utilities for deck.gl layers including lifecycle testing, visual regression, and interaction testing

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

visual-testing.mddocs/

Visual Regression Testing

Screenshot-based testing that renders deck.gl scenes and compares against golden images for visual regression detection. Includes platform-specific overrides, diff image generation, and automated test orchestration.

Capabilities

Snapshot Test Runner

Primary class for managing visual regression test execution.

/**
 * Test runner that renders scenes and compares against golden images
 * Extends the base TestRunner with image comparison capabilities
 */
class SnapshotTestRunner extends TestRunner<SnapshotTestCase, DiffImageResult, { imageDiffOptions: ImageDiffOptions }> {
  /**
   * Create a new snapshot test runner
   * @param props - Deck.gl props for rendering configuration
   * @param options - Image comparison options
   */
  constructor(props: DeckProps, options?: { imageDiffOptions: ImageDiffOptions });
  
  /**
   * Add test cases to the runner
   * @param testCases - Array of snapshot test cases
   * @returns This runner for method chaining
   */
  add(testCases: SnapshotTestCase[]): this;
  
  /**
   * Execute all test cases
   * @param options - Runtime options for test execution
   * @returns Promise that resolves when all tests complete
   */
  run(options?: Partial<TestOptions<SnapshotTestCase, DiffImageResult> & { imageDiffOptions: ImageDiffOptions }>): Promise<void>;
}

Snapshot Test Case Configuration

Structure for defining individual visual regression test cases.

interface SnapshotTestCase {
  /** Descriptive name for the test case */
  name: string;
  /** Deck.gl props for rendering the scene */
  props: DeckProps;
  /** Path to the golden reference image */
  goldenImage: string;
  /** Called before rendering begins */
  onBeforeRender?: (params: { deck: Deck; layers: Layer[] }) => void;
  /** Called after each render frame - call done() when ready to capture */
  onAfterRender?: (params: { deck: Deck; layers: Layer[]; done: () => void }) => void;
  /** Maximum time to wait for this test case (milliseconds) */
  timeout?: number;
  /** Image comparison options specific to this test case */
  imageDiffOptions?: ImageDiffOptions;
}

Image Comparison Options

Configuration for controlling image comparison behavior.

interface ImageDiffOptions {
  /** Save failed comparison images to disk */
  saveOnFail?: boolean;
  /** Custom filename for saved images */
  saveAs?: string;  
  /** Similarity threshold (0-1, where 1 is exact match) */
  threshold?: number;
  /** Generate diff image highlighting differences */
  createDiffImage?: boolean;
  /** Color tolerance for pixel comparison (0-1) */
  tolerance?: number;
  /** Include anti-aliasing in comparison */
  includeAA?: boolean;
  /** Include empty/transparent areas in comparison */
  includeEmpty?: boolean;
  /** Platform identifier for platform-specific golden images */
  platform?: string;
}

interface DiffImageResult {
  /** Whether running in headless browser mode */
  headless: boolean;
  /** Similarity score or match status */
  match: string | number;
  /** Human-readable match percentage */
  matchPercentage: string;
  /** Whether the comparison passed */
  success: boolean;
  /** Error information if comparison failed */
  error: Error | string | null;
}

Usage Examples:

import { SnapshotTestRunner } from "@deck.gl/test-utils";
import { ScatterplotLayer } from "@deck.gl/layers";

// Create test runner
const testRunner = new SnapshotTestRunner({
  width: 800,
  height: 600,
  views: [new MapView()],
  initialViewState: {
    longitude: -122.4,
    latitude: 37.8,
    zoom: 12
  }
}, {
  imageDiffOptions: {
    threshold: 0.99,
    tolerance: 0.1,
    saveOnFail: true
  }
});

// Add test cases
testRunner.add([
  {
    name: "Scatterplot basic rendering",
    props: {
      layers: [
        new ScatterplotLayer({
          id: 'scatter',
          data: [
            { position: [-122.4, 37.8], size: 100, color: [255, 0, 0] },
            { position: [-122.41, 37.81], size: 150, color: [0, 255, 0] }
          ],
          getPosition: d => d.position,
          getRadius: d => d.size,
          getFillColor: d => d.color
        })
      ]
    },
    goldenImage: './test/golden-images/scatterplot-basic.png',
    onAfterRender: ({ deck, layers, done }) => {
      // Wait for all layers to be loaded
      if (layers.every(layer => layer.isLoaded)) {
        done();
      }
    }
  },
  {
    name: "Animated scatterplot",
    props: {
      layers: [
        new ScatterplotLayer({
          id: 'animated-scatter',
          data: generateAnimatedData(),
          getPosition: d => d.position,
          getRadius: d => d.size,
          transitions: {
            getRadius: 1000
          }
        })
      ]
    },
    goldenImage: './test/golden-images/scatterplot-animated.png',
    timeout: 5000,
    onBeforeRender: ({ deck }) => {
      console.log('Starting animation test');
    },
    onAfterRender: ({ deck, layers, done }) => {
      // Wait for transitions to complete
      const layer = layers[0];
      if (layer.isLoaded && !layer.state.transitions?.getRadius) {
        done();
      }
    }
  }
]);

// Run tests
await testRunner.run({
  timeout: 10000,
  onTestStart: testCase => console.log(`Starting: ${testCase.name}`),
  onTestPass: testCase => console.log(`✓ ${testCase.name}`),
  onTestFail: (testCase, result) => console.error(`✗ ${testCase.name}:`, result.error)
});

Platform-Specific Golden Images

The test runner supports platform-specific golden images for handling rendering differences across operating systems and GPU vendors:

  1. Primary golden image: Standard reference stored in golden-images/ directory
  2. Platform override: Alternative reference in golden-images/platform-overrides/{platform}/ directory

When a test fails against the primary golden image, the runner automatically attempts comparison against the platform-specific override if available.

// Platform detection is automatic
const testRunner = new SnapshotTestRunner({}, {
  imageDiffOptions: {
    platform: 'windows' // Detected automatically in browser
  }
});

// Golden image paths:
// Primary: ./golden-images/my-test.png  
// Override: ./golden-images/platform-overrides/windows/my-test.png

Render Lifecycle Control

Test cases have fine-grained control over the render cycle:

onBeforeRender

Called once before rendering begins. Use for:

  • Initial setup and configuration
  • Logging and debugging
  • Setting up test data

onAfterRender

Called after each render frame with a done callback. Use for:

  • Waiting for layers to load (layer.isLoaded)
  • Waiting for animations/transitions to complete
  • Checking render state before capture
  • Must call done() when ready for screenshot
{
  name: "Complex scene with loading states",
  props: { /* ... */ },
  onAfterRender: ({ deck, layers, done }) => {
    // Check multiple conditions
    const allLoaded = layers.every(layer => layer.isLoaded);
    const noTransitions = layers.every(layer => 
      !layer.state.transitions || 
      Object.keys(layer.state.transitions).length === 0
    );
    
    if (allLoaded && noTransitions) {
      // Additional wait for GPU to finish rendering
      setTimeout(done, 100);
    }
    // If conditions not met, onAfterRender will be called again next frame
  }
}

Error Handling and Debugging

Test Failure Information

Failed tests provide detailed information:

  • match: Similarity score or error code
  • matchPercentage: Human-readable percentage
  • error: Specific failure reason
  • headless: Whether running in headless mode (affects some rendering)

Image Saving Options

Configure automatic saving of failed comparison images:

{
  imageDiffOptions: {
    saveOnFail: true,
    saveAs: 'custom-failure-name.png',
    createDiffImage: true  // Also save highlighted diff
  }
}

Debugging Render Issues

Common patterns for debugging visual tests:

  • Use onBeforeRender to log layer configurations
  • Use onAfterRender to inspect layer states and loading status
  • Enable createDiffImage to see exactly what pixels differ
  • Check result.headless flag for browser mode differences

Install with Tessl CLI

npx tessl i tessl/npm-deck-gl--test-utils

docs

index.md

interaction-testing.md

lifecycle-testing.md

test-generation.md

testing-utilities.md

visual-testing.md

tile.json