0
# Loki
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: loki
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install loki`
10
- **Global Installation**: `npm install -g loki`
11
12
## Core Imports
13
14
As a CLI tool, loki is primarily used as a command-line executable:
15
16
```bash
17
# Via global installation
18
loki <command> [options]
19
20
# Via npm script
21
npm run loki <command> -- [options]
22
23
# Via yarn
24
yarn loki <command> [options]
25
26
# Via npx
27
npx loki <command> [options]
28
```
29
30
For programmatic usage (not the primary use case):
31
32
```javascript
33
// Direct CLI execution
34
const { spawn } = require('child_process');
35
spawn('loki', ['test'], { stdio: 'inherit' });
36
```
37
38
## Basic Usage
39
40
```bash
41
# Initialize loki configuration in your project
42
loki init
43
44
# Run visual regression tests
45
loki test
46
47
# Update reference images
48
loki update
49
50
# Approve current images as new references
51
loki approve
52
```
53
54
## Architecture
55
56
Loki orchestrates visual testing through several key components:
57
58
- **Command System**: Three main commands (init, test/update, approve) with comprehensive option parsing
59
- **Configuration Management**: Cosmiconfig-based configuration discovery with platform-specific defaults
60
- **Target System**: Pluggable targets for different testing environments (Chrome, mobile simulators)
61
- **Rendering System**: Multiple output renderers (interactive, verbose, CI-friendly, silent)
62
- **Test Execution**: Parallel test execution with batch processing and error handling
63
64
## Capabilities
65
66
### Initialization Command
67
68
Initialize loki configuration in a project and set up Storybook integration.
69
70
```bash { .api }
71
loki init [storybook-path] [options]
72
73
# Options:
74
--force, -f # Force reconfiguration if already exists
75
--config, -c <path> # Specify Storybook config path
76
```
77
78
**Behavior:**
79
- Adds loki configuration to package.json with platform-appropriate defaults
80
- For React Native projects: Configures storybook.js with loki imports
81
- Detects Storybook port from package.json scripts and applies to configuration
82
- Uses `.storybook` directory by default for React projects, `storybook` for React Native
83
84
### Test Command
85
86
Execute visual regression tests against reference images.
87
88
```bash { .api }
89
loki test [configuration-filter] [options]
90
91
# Core Options:
92
--requireReference # Require reference images (auto-enabled in CI)
93
--verboseRenderer # Use verbose output renderer
94
--silent # Silent mode with no output
95
96
# Filtering Options:
97
--targetFilter <regex> # Filter configurations by target
98
--configurationFilter <regex> # Filter configurations by name
99
--storiesFilter <regex> # Filter stories by pattern
100
--skipStories <pattern> # Skip stories matching pattern
101
102
# Directory Options:
103
--output <path> # Current images directory (.loki/current)
104
--reference <path> # Reference images directory (.loki/reference)
105
--difference <path> # Diff images directory (.loki/difference)
106
107
# Chrome Options:
108
--chromeConcurrency <n> # Parallel Chrome instances (4)
109
--chromeLoadTimeout <ms> # Page load timeout (60000)
110
--chromeRetries <n> # Retry attempts (0)
111
--chromeSelector <css> # Element selector for screenshots
112
--chromeTolerance <n> # Diff tolerance (0)
113
--chromeFlags <flags> # Chrome launch flags
114
--chromeEnableAnimations # Enable animations in Chrome
115
--chromeEmulatedMedia <type> # Media type emulation
116
117
# Docker Options:
118
--chromeDockerImage <image> # Docker image for Chrome
119
--dockerWithSudo # Use sudo for Docker commands
120
--chromeDockerWithoutSeccomp # Disable seccomp for Chrome Docker
121
122
# AWS Lambda Options:
123
--chromeAwsLambdaFunctionName <name> # Lambda function name
124
--chromeAwsLambdaRetries <n> # Lambda retry attempts
125
--chromeAwsLambdaBatchSize <n> # Batch size for Lambda
126
--chromeAwsLambdaBatchConcurrency <n> # Lambda concurrency
127
128
# Network Options:
129
--host <hostname> # Host for connections (localhost)
130
--port <port> # Override port (uses config defaults)
131
--reactUri <uri> # React Storybook URL
132
--reactNativeUri <uri> # React Native WebSocket URL
133
--dockerNet <network> # Docker network for Chrome container
134
135
# Diffing Options:
136
--diffingEngine <engine> # Engine: pixelmatch, looks-same, gm
137
--fetchFailIgnore # Ignore fetch failures
138
--passWithNoStories # Pass when no stories found
139
140
# Mobile Options:
141
--device <name> # Device name for mobile targets
142
```
143
144
**Behavior:**
145
- Loads configuration using cosmiconfig pattern (package.json, .loki.js, etc.)
146
- Applies filtering to select which configurations and stories to test
147
- Executes tests in parallel across specified targets
148
- Compares screenshots against reference images using specified diffing engine
149
- Generates diff images highlighting visual changes
150
- Provides update command suggestion when visual differences are detected
151
152
### Update Command
153
154
Update reference images with current screenshots (alias for test with update flag).
155
156
```bash { .api }
157
loki update [configuration-filter] [options]
158
159
# Accepts all test command options
160
# Behavior: Creates new reference images instead of comparing
161
```
162
163
**Behavior:**
164
- Identical to test command but saves current screenshots as new reference images
165
- Does not perform comparison against existing references
166
- Used to establish initial references or approve visual changes
167
168
### Approve Command
169
170
Approve current images as new reference images.
171
172
```bash { .api }
173
loki approve [options]
174
175
# Options:
176
--diffOnly # Only approve changed images (based on difference directory)
177
--output <path> # Current images directory (.loki/current)
178
--reference <path> # Reference images directory (.loki/reference)
179
--difference <path> # Diff images directory (.loki/difference)
180
```
181
182
**Behavior:**
183
- Standard mode: Moves all current images to reference directory (empties current directory)
184
- Diff-only mode: Copies only changed images from difference directory to reference directory
185
- Validates that images exist before processing
186
187
## Configuration System
188
189
### Configuration Discovery
190
191
Loki Runner uses cosmiconfig to discover configuration files in the following order:
192
193
```javascript { .api }
194
// Configuration search pattern: 'loki'
195
// Supported files:
196
// - package.json (loki field)
197
// - .loki.js
198
// - .loki.json
199
// - loki.config.js
200
// - loki.config.json
201
```
202
203
### Default Configurations
204
205
**React Projects:**
206
```json { .api }
207
{
208
"configurations": {
209
"chrome.laptop": {
210
"target": "chrome.app",
211
"width": 1366,
212
"height": 768,
213
"deviceScaleFactor": 1,
214
"mobile": false
215
},
216
"chrome.iphone7": {
217
"target": "chrome.app",
218
"preset": "iPhone 7"
219
}
220
}
221
}
222
```
223
224
**React Native Projects:**
225
```json { .api }
226
{
227
"configurations": {
228
"ios.iphone7": {
229
"target": "ios.simulator"
230
}
231
}
232
}
233
```
234
235
### Configuration Options
236
237
```javascript { .api }
238
// Complete configuration schema
239
{
240
// Target configurations
241
"configurations": {
242
"<config-name>": {
243
"target": "chrome.app|chrome.docker|chrome.aws-lambda|ios.simulator|android.emulator",
244
"width": number, // Screen width
245
"height": number, // Screen height
246
"deviceScaleFactor": number, // Device pixel ratio
247
"mobile": boolean, // Mobile emulation
248
"preset": string // Device preset name
249
}
250
},
251
252
// Directory paths
253
"output": string, // Current images (.loki/current)
254
"reference": string, // Reference images (.loki/reference)
255
"difference": string, // Diff images (.loki/difference)
256
"fileNameFormatter": function, // Custom filename formatter function
257
258
// Connection settings
259
"host": string, // Host (localhost)
260
"reactPort": string, // React port (6006)
261
"reactNativePort": string, // React Native port (7007)
262
"reactUri": string, // Custom React URI
263
"reactNativeUri": string, // Custom React Native URI
264
"dockerNet": string, // Docker network for Chrome container
265
266
// Chrome settings
267
"chromeConcurrency": number, // Parallel instances (4)
268
"chromeDockerImage": string, // Docker image
269
"chromeFlags": string, // Launch flags
270
"chromeLoadTimeout": number, // Load timeout (60000)
271
"chromeRetries": number, // Retry attempts (0)
272
"chromeSelector": string, // Screenshot selector
273
"chromeTolerance": number, // Diff tolerance (0)
274
"chromeEnableAnimations": boolean, // Enable animations
275
"chromeEmulatedMedia": string, // Media emulation
276
277
// AWS Lambda settings
278
"chromeAwsLambdaFunctionName": string, // Function name
279
"chromeAwsLambdaRetries": number, // Retries (0)
280
"chromeAwsLambdaBatchSize": number, // Batch size (1)
281
"chromeAwsLambdaBatchConcurrency": number, // Concurrency (1)
282
283
// Filtering
284
"storiesFilter": string, // Story filter regex
285
"skipStories": string, // Skip stories pattern
286
"targetFilter": string, // Target filter regex
287
"configurationFilter": string, // Configuration filter regex
288
289
// Diffing engines
290
"diffingEngine": string, // pixelmatch|looks-same|gm
291
"pixelmatch": object, // Pixelmatch options
292
"looksSame": object, // looks-same options
293
"gm": object, // GraphicsMagick options
294
295
// Behavior
296
"requireReference": boolean, // Require references (auto in CI)
297
"verboseRenderer": boolean, // Verbose output
298
"silent": boolean, // Silent mode
299
"passWithNoStories": boolean, // Pass with no stories
300
"dockerWithSudo": boolean, // Use sudo for Docker
301
"chromeDockerWithoutSeccomp": boolean, // Disable seccomp
302
"fetchFailIgnore": boolean, // Ignore fetch failures
303
"diffOnly": boolean, // Diff-only mode for approve
304
"device": string // Mobile device name
305
}
306
```
307
308
## Error Handling
309
310
Loki Runner handles several categories of errors with specific messages and instructions:
311
312
```javascript { .api }
313
// Error types from @loki/core
314
MissingDependencyError // Required dependency not available
315
ServerError // Server connection/startup errors
316
ChromeError // Chrome target execution errors
317
FetchingURLsError // Story fetching failures
318
ReferenceImageError // Image comparison failures
319
TimeoutError // Operation timeout errors
320
NativeError // Native platform (iOS/Android) errors
321
TaskRunnerError // Test execution errors (from runner)
322
```
323
324
**Common Error Scenarios:**
325
- Missing dependencies (GraphicsMagick for 'gm' diffing engine)
326
- Storybook server connection failures
327
- Chrome launch/connection issues
328
- Missing reference images (when requireReference is true)
329
- Network timeouts during story fetching
330
- File system permission issues
331
332
## Environment Integration
333
334
### CI/CD Integration
335
336
```bash { .api }
337
# CI-friendly execution
338
loki test --requireReference --verboseRenderer
339
340
# Environment variables automatically detected:
341
# - CI environments enable requireReference by default
342
# - Uses non-interactive renderer in CI
343
```
344
345
### Docker Integration
346
347
```bash { .api }
348
# Automatic Docker detection and configuration
349
# Switches chrome.app to chrome.docker when Docker is available
350
# Supports custom Docker images and security options
351
```
352
353
### Package Manager Integration
354
355
The tool automatically detects and uses appropriate package manager commands:
356
357
```bash { .api }
358
# Detection order:
359
# 1. Global loki command
360
# 2. yarn loki <command> -- <args>
361
# 3. npm run loki <command> -- <args>
362
# 4. ./node_modules/.bin/loki <command> <args>
363
```
364
365
## Types
366
367
```typescript { .api }
368
// Configuration types
369
interface Configuration {
370
target: 'chrome.app' | 'chrome.docker' | 'chrome.aws-lambda' | 'ios.simulator' | 'android.emulator';
371
width?: number;
372
height?: number;
373
deviceScaleFactor?: number;
374
mobile?: boolean;
375
preset?: string;
376
}
377
378
interface LokiConfig {
379
configurations: Record<string, Configuration>;
380
output?: string;
381
reference?: string;
382
difference?: string;
383
host?: string;
384
reactPort?: string;
385
reactNativePort?: string;
386
chromeConcurrency?: number;
387
chromeFlags?: string;
388
chromeLoadTimeout?: number;
389
chromeRetries?: number;
390
chromeSelector?: string;
391
chromeTolerance?: number;
392
diffingEngine?: 'pixelmatch' | 'looks-same' | 'gm';
393
requireReference?: boolean;
394
verboseRenderer?: boolean;
395
silent?: boolean;
396
// ... additional options
397
}
398
399
// Command arguments
400
interface CommandArgs extends Array<string> {
401
// Parsed by minimist with typed options
402
}
403
404
// File name formatter function
405
interface FileNameFormatter {
406
(context: {
407
configurationName: string;
408
kind: string;
409
story: string;
410
parameters?: any;
411
}): string;
412
}
413
414
// Error types
415
class MissingDependencyError extends Error {
416
message: string;
417
instructions?: string;
418
}
419
420
class ReferenceImageError extends Error {
421
kind: string; // Story kind
422
story: string; // Story name
423
}
424
425
class TimeoutError extends Error {
426
message: string;
427
}
428
429
class NativeError extends Error {
430
message: string;
431
}
432
433
class TaskRunnerError extends Error {
434
message: string;
435
errors: Error[];
436
}
437
```