or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

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

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

```