or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-loki

CLI tool for visual regression testing of Storybook components across multiple platforms including Chrome, iOS simulator, and Android emulator

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/loki@0.35.x

To install, run

npx @tessl/cli install tessl/npm-loki@0.35.0

index.mddocs/

Loki

Loki is a command-line interface (CLI) tool for visual regression testing of Storybook components. It provides comprehensive visual testing capabilities across multiple platforms including Chrome (local, Docker, AWS Lambda), iOS simulator, and Android emulator, enabling developers to detect visual changes in their UI components with reproducible results across different environments.

Package Information

  • Package Name: loki
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install loki
  • Global Installation: npm install -g loki

Core Imports

As a CLI tool, loki is primarily used as a command-line executable:

# Via global installation
loki <command> [options]

# Via npm script
npm run loki <command> -- [options]

# Via yarn
yarn loki <command> [options]

# Via npx
npx loki <command> [options]

For programmatic usage (not the primary use case):

// Direct CLI execution
const { spawn } = require('child_process');
spawn('loki', ['test'], { stdio: 'inherit' });

Basic Usage

# Initialize loki configuration in your project
loki init

# Run visual regression tests
loki test

# Update reference images 
loki update

# Approve current images as new references
loki approve

Architecture

Loki orchestrates visual testing through several key components:

  • Command System: Three main commands (init, test/update, approve) with comprehensive option parsing
  • Configuration Management: Cosmiconfig-based configuration discovery with platform-specific defaults
  • Target System: Pluggable targets for different testing environments (Chrome, mobile simulators)
  • Rendering System: Multiple output renderers (interactive, verbose, CI-friendly, silent)
  • Test Execution: Parallel test execution with batch processing and error handling

Capabilities

Initialization Command

Initialize loki configuration in a project and set up Storybook integration.

loki init [storybook-path] [options]

# Options:
--force, -f          # Force reconfiguration if already exists
--config, -c <path>  # Specify Storybook config path

Behavior:

  • Adds loki configuration to package.json with platform-appropriate defaults
  • For React Native projects: Configures storybook.js with loki imports
  • Detects Storybook port from package.json scripts and applies to configuration
  • Uses .storybook directory by default for React projects, storybook for React Native

Test Command

Execute visual regression tests against reference images.

loki test [configuration-filter] [options]

# Core Options:
--requireReference       # Require reference images (auto-enabled in CI)
--verboseRenderer       # Use verbose output renderer
--silent               # Silent mode with no output

# Filtering Options:
--targetFilter <regex>      # Filter configurations by target
--configurationFilter <regex> # Filter configurations by name  
--storiesFilter <regex>     # Filter stories by pattern
--skipStories <pattern>     # Skip stories matching pattern

# Directory Options:
--output <path>        # Current images directory (.loki/current)
--reference <path>     # Reference images directory (.loki/reference)
--difference <path>    # Diff images directory (.loki/difference)

# Chrome Options:
--chromeConcurrency <n>    # Parallel Chrome instances (4)
--chromeLoadTimeout <ms>   # Page load timeout (60000)
--chromeRetries <n>        # Retry attempts (0)
--chromeSelector <css>     # Element selector for screenshots
--chromeTolerance <n>      # Diff tolerance (0)
--chromeFlags <flags>      # Chrome launch flags
--chromeEnableAnimations   # Enable animations in Chrome
--chromeEmulatedMedia <type> # Media type emulation

# Docker Options:
--chromeDockerImage <image>    # Docker image for Chrome
--dockerWithSudo               # Use sudo for Docker commands
--chromeDockerWithoutSeccomp   # Disable seccomp for Chrome Docker

# AWS Lambda Options:
--chromeAwsLambdaFunctionName <name>   # Lambda function name
--chromeAwsLambdaRetries <n>           # Lambda retry attempts
--chromeAwsLambdaBatchSize <n>         # Batch size for Lambda
--chromeAwsLambdaBatchConcurrency <n>  # Lambda concurrency

# Network Options:
--host <hostname>         # Host for connections (localhost)
--port <port>            # Override port (uses config defaults)
--reactUri <uri>         # React Storybook URL
--reactNativeUri <uri>   # React Native WebSocket URL
--dockerNet <network>    # Docker network for Chrome container

# Diffing Options:
--diffingEngine <engine>  # Engine: pixelmatch, looks-same, gm
--fetchFailIgnore        # Ignore fetch failures
--passWithNoStories      # Pass when no stories found

# Mobile Options:
--device <name>          # Device name for mobile targets

Behavior:

  • Loads configuration using cosmiconfig pattern (package.json, .loki.js, etc.)
  • Applies filtering to select which configurations and stories to test
  • Executes tests in parallel across specified targets
  • Compares screenshots against reference images using specified diffing engine
  • Generates diff images highlighting visual changes
  • Provides update command suggestion when visual differences are detected

Update Command

Update reference images with current screenshots (alias for test with update flag).

loki update [configuration-filter] [options]

# Accepts all test command options
# Behavior: Creates new reference images instead of comparing

Behavior:

  • Identical to test command but saves current screenshots as new reference images
  • Does not perform comparison against existing references
  • Used to establish initial references or approve visual changes

Approve Command

Approve current images as new reference images.

loki approve [options]

# Options:
--diffOnly          # Only approve changed images (based on difference directory)
--output <path>     # Current images directory (.loki/current)
--reference <path>  # Reference images directory (.loki/reference)
--difference <path> # Diff images directory (.loki/difference)

Behavior:

  • Standard mode: Moves all current images to reference directory (empties current directory)
  • Diff-only mode: Copies only changed images from difference directory to reference directory
  • Validates that images exist before processing

Configuration System

Configuration Discovery

Loki Runner uses cosmiconfig to discover configuration files in the following order:

// Configuration search pattern: 'loki'
// Supported files:
// - package.json (loki field)
// - .loki.js
// - .loki.json
// - loki.config.js
// - loki.config.json

Default Configurations

React Projects:

{
  "configurations": {
    "chrome.laptop": {
      "target": "chrome.app",
      "width": 1366,
      "height": 768,
      "deviceScaleFactor": 1,
      "mobile": false
    },
    "chrome.iphone7": {
      "target": "chrome.app", 
      "preset": "iPhone 7"
    }
  }
}

React Native Projects:

{
  "configurations": {
    "ios.iphone7": {
      "target": "ios.simulator"
    }
  }
}

Configuration Options

// Complete configuration schema
{
  // Target configurations
  "configurations": {
    "<config-name>": {
      "target": "chrome.app|chrome.docker|chrome.aws-lambda|ios.simulator|android.emulator",
      "width": number,           // Screen width
      "height": number,          // Screen height  
      "deviceScaleFactor": number, // Device pixel ratio
      "mobile": boolean,         // Mobile emulation
      "preset": string          // Device preset name
    }
  },
  
  // Directory paths
  "output": string,           // Current images (.loki/current)
  "reference": string,        // Reference images (.loki/reference)  
  "difference": string,       // Diff images (.loki/difference)
  "fileNameFormatter": function, // Custom filename formatter function
  
  // Connection settings
  "host": string,            // Host (localhost)
  "reactPort": string,       // React port (6006)
  "reactNativePort": string, // React Native port (7007)
  "reactUri": string,        // Custom React URI
  "reactNativeUri": string,  // Custom React Native URI
  "dockerNet": string,       // Docker network for Chrome container
  
  // Chrome settings
  "chromeConcurrency": number,        // Parallel instances (4)
  "chromeDockerImage": string,        // Docker image
  "chromeFlags": string,              // Launch flags
  "chromeLoadTimeout": number,        // Load timeout (60000)
  "chromeRetries": number,            // Retry attempts (0)
  "chromeSelector": string,           // Screenshot selector
  "chromeTolerance": number,          // Diff tolerance (0)
  "chromeEnableAnimations": boolean,  // Enable animations
  "chromeEmulatedMedia": string,      // Media emulation
  
  // AWS Lambda settings
  "chromeAwsLambdaFunctionName": string,     // Function name
  "chromeAwsLambdaRetries": number,          // Retries (0)
  "chromeAwsLambdaBatchSize": number,        // Batch size (1)
  "chromeAwsLambdaBatchConcurrency": number, // Concurrency (1)
  
  // Filtering
  "storiesFilter": string,      // Story filter regex
  "skipStories": string,        // Skip stories pattern
  "targetFilter": string,       // Target filter regex
  "configurationFilter": string, // Configuration filter regex
  
  // Diffing engines
  "diffingEngine": string,      // pixelmatch|looks-same|gm
  "pixelmatch": object,         // Pixelmatch options
  "looksSame": object,          // looks-same options  
  "gm": object,                 // GraphicsMagick options
  
  // Behavior
  "requireReference": boolean,   // Require references (auto in CI)
  "verboseRenderer": boolean,    // Verbose output
  "silent": boolean,            // Silent mode
  "passWithNoStories": boolean, // Pass with no stories
  "dockerWithSudo": boolean,    // Use sudo for Docker
  "chromeDockerWithoutSeccomp": boolean, // Disable seccomp
  "fetchFailIgnore": boolean,   // Ignore fetch failures
  "diffOnly": boolean,          // Diff-only mode for approve
  "device": string             // Mobile device name
}

Error Handling

Loki Runner handles several categories of errors with specific messages and instructions:

// Error types from @loki/core
MissingDependencyError    // Required dependency not available
ServerError              // Server connection/startup errors  
ChromeError             // Chrome target execution errors
FetchingURLsError       // Story fetching failures
ReferenceImageError     // Image comparison failures
TimeoutError            // Operation timeout errors
NativeError             // Native platform (iOS/Android) errors
TaskRunnerError         // Test execution errors (from runner)

Common Error Scenarios:

  • Missing dependencies (GraphicsMagick for 'gm' diffing engine)
  • Storybook server connection failures
  • Chrome launch/connection issues
  • Missing reference images (when requireReference is true)
  • Network timeouts during story fetching
  • File system permission issues

Environment Integration

CI/CD Integration

# CI-friendly execution
loki test --requireReference --verboseRenderer

# Environment variables automatically detected:
# - CI environments enable requireReference by default
# - Uses non-interactive renderer in CI

Docker Integration

# Automatic Docker detection and configuration
# Switches chrome.app to chrome.docker when Docker is available
# Supports custom Docker images and security options

Package Manager Integration

The tool automatically detects and uses appropriate package manager commands:

# Detection order:
# 1. Global loki command
# 2. yarn loki <command> -- <args>  
# 3. npm run loki <command> -- <args>
# 4. ./node_modules/.bin/loki <command> <args>

Types

// Configuration types
interface Configuration {
  target: 'chrome.app' | 'chrome.docker' | 'chrome.aws-lambda' | 'ios.simulator' | 'android.emulator';
  width?: number;
  height?: number;
  deviceScaleFactor?: number;
  mobile?: boolean;
  preset?: string;
}

interface LokiConfig {
  configurations: Record<string, Configuration>;
  output?: string;
  reference?: string;
  difference?: string;
  host?: string;
  reactPort?: string;
  reactNativePort?: string;
  chromeConcurrency?: number;
  chromeFlags?: string;
  chromeLoadTimeout?: number;
  chromeRetries?: number;
  chromeSelector?: string;
  chromeTolerance?: number;
  diffingEngine?: 'pixelmatch' | 'looks-same' | 'gm';
  requireReference?: boolean;
  verboseRenderer?: boolean;
  silent?: boolean;
  // ... additional options
}

// Command arguments
interface CommandArgs extends Array<string> {
  // Parsed by minimist with typed options
}

// File name formatter function
interface FileNameFormatter {
  (context: {
    configurationName: string;
    kind: string;
    story: string;
    parameters?: any;
  }): string;
}

// Error types  
class MissingDependencyError extends Error {
  message: string;
  instructions?: string;
}

class ReferenceImageError extends Error {
  kind: string;    // Story kind
  story: string;   // Story name
}

class TimeoutError extends Error {
  message: string;
}

class NativeError extends Error {
  message: string;
}

class TaskRunnerError extends Error {
  message: string;
  errors: Error[];
}