CLI tool for visual regression testing of Storybook components across multiple platforms including Chrome, iOS simulator, and Android emulator
npx @tessl/cli install tessl/npm-loki@0.35.0Loki 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.
npm install lokinpm install -g lokiAs 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' });# 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 approveLoki orchestrates visual testing through several key components:
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 pathBehavior:
.storybook directory by default for React projects, storybook for React NativeExecute 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 targetsBehavior:
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 comparingBehavior:
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:
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.jsonReact 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"
}
}
}// 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
}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:
# CI-friendly execution
loki test --requireReference --verboseRenderer
# Environment variables automatically detected:
# - CI environments enable requireReference by default
# - Uses non-interactive renderer in CI# Automatic Docker detection and configuration
# Switches chrome.app to chrome.docker when Docker is available
# Supports custom Docker images and security optionsThe 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>// 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[];
}