or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-snapshots.mdbuild-management.mdconfiguration.mdcore-operations.mdimage-uploads.mdindex.mdprogrammatic-api.mdstatic-snapshots.md

programmatic-api.mddocs/

0

# Programmatic API

1

2

Percy CLI provides extensive programmatic APIs through its core packages, enabling deep integration with custom tools, testing frameworks, and automation pipelines. These APIs are primarily intended for SDK developers and advanced users who need direct access to Percy's core functionality.

3

4

## Core Imports

5

6

```javascript

7

// Main Percy CLI entry point

8

import { percy, checkForUpdate } from "@percy/cli";

9

10

// Core functionality

11

import Percy from "@percy/core";

12

import * as PercyUtils from "@percy/core/utils";

13

14

// Configuration system

15

import { load, validate, migrate } from "@percy/config";

16

17

// API client

18

import PercyClient from "@percy/client";

19

20

// Environment detection

21

import PercyEnv from "@percy/env";

22

23

// DOM serialization

24

import { serializeDOM } from "@percy/dom";

25

26

// Logging

27

import logger from "@percy/logger";

28

29

// SDK utilities

30

import * as SDKUtils from "@percy/sdk-utils";

31

```

32

33

## Capabilities

34

35

### Percy Core Class

36

37

The main Percy class provides the core visual testing engine with browser automation, asset discovery, and snapshot coordination.

38

39

```javascript { .api }

40

/**

41

* Main Percy class for visual testing operations

42

* Manages browser sessions, asset discovery, and snapshot uploading

43

*/

44

class Percy {

45

constructor(options?: PercyOptions);

46

47

// Lifecycle methods

48

start(): Promise<void>;

49

stop(): Promise<void>;

50

idle(): Promise<void>;

51

52

// Snapshot methods

53

snapshot(options: SnapshotOptions): Promise<void>;

54

upload(files: FileUploadOptions): Promise<void>;

55

56

// Server methods

57

server(options?: ServerOptions): Promise<Server>;

58

59

// Utility methods

60

isRunning(): boolean;

61

address(): string;

62

port(): number;

63

64

// Getters

65

get client(): PercyClient;

66

get browser(): Browser;

67

get config(): PercyConfig;

68

}

69

70

interface PercyOptions {

71

token?: string;

72

config?: string | PercyConfig;

73

server?: boolean;

74

port?: number;

75

skipDiscovery?: boolean;

76

delayUploads?: boolean;

77

deferUploads?: boolean;

78

dryRun?: boolean;

79

labels?: string[];

80

}

81

82

interface SnapshotOptions {

83

name: string;

84

url?: string;

85

domSnapshot?: string;

86

clientInfo?: string;

87

environmentInfo?: string;

88

enableJavaScript?: boolean;

89

cliEnableJavaScript?: boolean;

90

disableShadowDOM?: boolean;

91

minHeight?: number;

92

scope?: string;

93

sync?: boolean;

94

testCase?: string;

95

thTestCaseExecutionId?: string;

96

additionalSnapshots?: AdditionalSnapshot[];

97

requestHeaders?: Record<string, string>;

98

authorization?: {

99

username: string;

100

password: string;

101

};

102

}

103

104

interface AdditionalSnapshot {

105

prefix?: string;

106

suffix?: string;

107

name?: string;

108

waitForTimeout?: number;

109

waitForSelector?: string;

110

execute?: string | Function;

111

percyCSS?: string;

112

ignoreRegions?: IgnoreRegion[];

113

}

114

115

interface IgnoreRegion {

116

ignoreRegion?: string;

117

customIgnoreRegions?: CustomIgnoreRegion[];

118

}

119

120

interface CustomIgnoreRegion {

121

selector?: string;

122

xpath?: string;

123

coordinates?: {

124

top: number;

125

bottom: number;

126

left: number;

127

right: number;

128

};

129

}

130

```

131

132

### Percy Utilities

133

134

Core utility functions for network operations, resource creation, async operations, and more.

135

136

```javascript { .api }

137

// Network utilities

138

function request(url: string, options?: RequestOptions): Promise<Response>;

139

function hostname(url: string): string;

140

function normalizeURL(url: string, baseURL?: string): string;

141

function hostnameMatches(hostname: string, patterns: string[]): boolean;

142

143

// Resource creation

144

function createResource(url: string, content: Buffer, options?: ResourceOptions): Resource;

145

function createRootResource(url: string, content: string): Resource;

146

function createPercyCSSResource(css: string): Resource;

147

function createLogResource(logs: LogEntry[]): Resource;

148

149

// Server utilities

150

class Server {

151

constructor(percy: Percy, port?: number);

152

listen(port?: number): Promise<void>;

153

close(): Promise<void>;

154

address(): string;

155

port(): number;

156

}

157

158

function createServer(percy: Percy, port?: number): Server;

159

160

// Async utilities with generator support

161

function generatePromise<T>(generator: Generator<any, T>): Promise<T>;

162

function yieldTo(promise: Promise<any>): Generator<Promise<any>, any>;

163

function yieldAll(promises: Promise<any>[]): Generator<Promise<any[]>, any[]>;

164

function yieldFor(condition: () => boolean, timeout?: number): Generator<Promise<boolean>, boolean>;

165

function waitFor(condition: () => boolean, options?: WaitOptions): Promise<boolean>;

166

function waitForTimeout(timeout: number): Promise<void>;

167

168

// Browser utilities

169

function serializeFunction(fn: Function): string;

170

function waitForSelectorInsideBrowser(selector: string, timeout?: number): string;

171

172

// Encoding utilities

173

function base64encode(data: string | Buffer): string;

174

function isGzipped(data: Buffer): boolean;

175

176

// URL utilities

177

function decodeAndEncodeURLWithLogging(url: string): string;

178

179

// Security utilities

180

function redactSecrets(data: any): any;

181

182

// Helper classes

183

class AbortController {

184

signal: AbortSignal;

185

abort(): void;

186

}

187

188

class AbortError extends Error {

189

name: "AbortError";

190

}

191

192

class DefaultMap<K, V> extends Map<K, V> {

193

constructor(defaultValue: () => V);

194

get(key: K): V;

195

}

196

197

// Retry utilities

198

function withRetries<T>(fn: () => Promise<T>, options?: RetryOptions): Promise<T>;

199

200

// Type utilities

201

function isGenerator(obj: any): obj is Generator;

202

function compareObjectTypes(obj1: any, obj2: any): boolean;

203

function normalizeOptions<T>(options: T, schema: any): T;

204

205

interface RequestOptions {

206

method?: string;

207

headers?: Record<string, string>;

208

body?: string | Buffer;

209

timeout?: number;

210

retries?: number;

211

retryBackoff?: number;

212

}

213

214

interface ResourceOptions {

215

mimetype?: string;

216

root?: boolean;

217

percyCSS?: boolean;

218

}

219

220

interface Resource {

221

url: string;

222

content: Buffer;

223

mimetype: string;

224

root?: boolean;

225

percyCSS?: boolean;

226

}

227

228

interface WaitOptions {

229

timeout?: number;

230

interval?: number;

231

}

232

233

interface RetryOptions {

234

retries?: number;

235

backoff?: number;

236

onRetry?: (error: Error, attempt: number) => void;

237

}

238

```

239

240

### Configuration System

241

242

Advanced configuration loading, validation, and migration system.

243

244

```javascript { .api }

245

// Configuration loading

246

function load(options?: LoadOptions): Promise<PercyConfig>;

247

function search(options?: SearchOptions): Promise<string | null>;

248

249

// Validation system

250

function validate(config: any, schema?: ConfigSchema): ValidationResult;

251

function addSchema(name: string, schema: any): void;

252

253

// Migration system

254

function migrate(config: any, options?: MigrateOptions): any;

255

function addMigration(version: number, migration: MigrationFunction): void;

256

257

// Configuration utilities

258

function getDefaults(): PercyConfig;

259

function merge(...configs: any[]): PercyConfig;

260

function normalize(config: any): PercyConfig;

261

function stringify(config: any, format?: 'json' | 'yaml' | 'js'): string;

262

263

interface LoadOptions {

264

path?: string;

265

search?: boolean;

266

bail?: boolean;

267

}

268

269

interface SearchOptions {

270

start?: string;

271

stop?: string;

272

names?: string[];

273

}

274

275

interface ValidationResult {

276

valid: boolean;

277

errors: ValidationError[];

278

config: PercyConfig;

279

}

280

281

interface ValidationError {

282

path: string[];

283

message: string;

284

value: any;

285

}

286

287

interface MigrateOptions {

288

dry?: boolean;

289

version?: number;

290

}

291

292

type MigrationFunction = (config: any) => any;

293

294

interface ConfigSchema {

295

type: string;

296

properties?: Record<string, any>;

297

additionalProperties?: boolean;

298

required?: string[];

299

}

300

```

301

302

### Percy Client

303

304

HTTP client for Percy API communication with authentication, retries, and error handling.

305

306

```javascript { .api }

307

/**

308

* Percy API client for interacting with Percy's visual testing service

309

* Handles authentication, request/response processing, and error handling

310

*/

311

class PercyClient {

312

constructor(options?: ClientOptions);

313

314

// Build methods

315

createBuild(options: CreateBuildOptions): Promise<Build>;

316

getBuild(buildId: string): Promise<Build>;

317

finalizeBuild(buildId: string): Promise<Build>;

318

319

// Snapshot methods

320

createSnapshot(buildId: string, snapshot: CreateSnapshotOptions): Promise<Snapshot>;

321

uploadResources(buildId: string, resources: Resource[]): Promise<void>;

322

createComparison(buildId: string, snapshotId: string, options: ComparisonOptions): Promise<Comparison>;

323

324

// Build management

325

approveBuild(buildId: string): Promise<Build>;

326

327

// Utility methods

328

request(path: string, options?: RequestOptions): Promise<any>;

329

}

330

331

interface ClientOptions {

332

token?: string;

333

apiUrl?: string;

334

clientInfo?: string;

335

environmentInfo?: string;

336

userAgent?: string;

337

}

338

339

interface CreateBuildOptions {

340

type?: 'web' | 'app';

341

branch?: string;

342

targetBranch?: string;

343

targetCommitSha?: string;

344

commitSha?: string;

345

commitCommittedAt?: string;

346

commitAuthorName?: string;

347

commitAuthorEmail?: string;

348

commitMessage?: string;

349

pullRequestNumber?: string;

350

parallel?: boolean;

351

partialBuild?: boolean;

352

}

353

354

interface Build {

355

id: string;

356

number: number;

357

url: string;

358

state: 'pending' | 'processing' | 'finished' | 'failed';

359

reviewState?: 'unreviewed' | 'approved' | 'rejected';

360

reviewStateReason?: string;

361

totalComparisons?: number;

362

totalComparisonsFinished?: number;

363

totalComparisonsDiff?: number;

364

}

365

366

interface CreateSnapshotOptions {

367

name: string;

368

clientInfo?: string;

369

environmentInfo?: string;

370

widths?: number[];

371

minHeight?: number;

372

enableJavaScript?: boolean;

373

percyCSS?: string;

374

scope?: string;

375

domSnapshot?: string;

376

}

377

378

interface Snapshot {

379

id: string;

380

name: string;

381

url?: string;

382

widths: number[];

383

minHeight: number;

384

enableJavaScript: boolean;

385

}

386

387

interface ComparisonOptions {

388

tag?: string;

389

tiles?: Tile[];

390

externalDebugUrl?: string;

391

ignoredElementsData?: IgnoreElementData[];

392

consideredElementsData?: ConsiderElementData[];

393

}

394

395

interface Tile {

396

filepath?: string;

397

sha?: string;

398

statusBarHeight?: number;

399

navBarHeight?: number;

400

headerHeight?: number;

401

footerHeight?: number;

402

fullscreen?: boolean;

403

}

404

405

interface Comparison {

406

id: string;

407

tag: string;

408

state: 'pending' | 'processing' | 'finished' | 'failed';

409

diffRatio?: number;

410

}

411

```

412

413

### Environment Detection

414

415

Comprehensive CI/CD environment detection and configuration.

416

417

```javascript { .api }

418

/**

419

* Environment detection for CI/CD systems and local development

420

* Automatically detects 15+ popular CI systems and extracts build information

421

*/

422

class PercyEnv {

423

constructor();

424

425

// Environment detection

426

get ci(): string | null;

427

get branch(): string | null;

428

get commit(): string | null;

429

get pullRequest(): string | null;

430

get parallel(): { nonce: string; total: number } | null;

431

get partial(): boolean;

432

433

// CI system specific info

434

get info(): EnvironmentInfo;

435

436

// Static methods

437

static current(): PercyEnv;

438

static load(path?: string): void;

439

}

440

441

interface EnvironmentInfo {

442

ci?: string;

443

project?: string;

444

branch?: string;

445

commit?: string;

446

commitMessage?: string;

447

committerName?: string;

448

committerEmail?: string;

449

authorName?: string;

450

authorEmail?: string;

451

pullRequest?: string;

452

parallel?: {

453

nonce: string;

454

total: number;

455

};

456

partial?: boolean;

457

url?: string;

458

}

459

460

// Supported CI systems:

461

// - Travis CI, Jenkins, CircleCI, GitHub Actions, GitLab CI

462

// - Azure DevOps, Buildkite, Drone, Semaphore, TeamCity

463

// - AppVeyor, Bitbucket Pipelines, Heroku CI, Netlify, Codeship

464

```

465

466

### DOM Serialization

467

468

Browser-compatible DOM serialization for visual testing.

469

470

```javascript { .api }

471

// DOM serialization functions

472

function serializeDOM(options?: SerializeOptions): string;

473

const serialize = serializeDOM; // Alias

474

475

function waitForResize(timeout?: number): Promise<void>;

476

function loadAllSrcsetLinks(): Promise<void>;

477

478

interface SerializeOptions {

479

enableJavaScript?: boolean;

480

disableShadowDOM?: boolean;

481

domTransformation?: (dom: Document) => Document;

482

reshuffleInvalidTags?: boolean;

483

}

484

```

485

486

### SDK Utilities

487

488

Utilities for Percy SDK development and integration.

489

490

```javascript { .api }

491

// Logger for SDK development

492

const logger: Logger;

493

494

// Percy utilities

495

function isPercyEnabled(): boolean;

496

function waitForPercyIdle(): Promise<void>;

497

498

// Percy communication

499

function fetchPercyDOM(): Promise<string>;

500

function postSnapshot(name: string, options?: PostSnapshotOptions): Promise<void>;

501

function postComparison(name: string, tag: string, tiles?: Tile[]): Promise<void>;

502

function postBuildEvents(events: BuildEvent[]): Promise<void>;

503

function flushSnapshots(): Promise<void>;

504

505

// Screenshot capture

506

function captureAutomateScreenshot(options: AutomateScreenshotOptions): Promise<Buffer>;

507

508

// HTTP utilities

509

function request(url: string, options?: RequestOptions): Promise<Response>;

510

511

interface PostSnapshotOptions {

512

url?: string;

513

domSnapshot?: string;

514

clientInfo?: string;

515

environmentInfo?: string;

516

widths?: number[];

517

minHeight?: number;

518

enableJavaScript?: boolean;

519

percyCSS?: string;

520

scope?: string;

521

}

522

523

interface BuildEvent {

524

type: string;

525

message: string;

526

timestamp?: number;

527

level?: 'debug' | 'info' | 'warn' | 'error';

528

}

529

530

interface AutomateScreenshotOptions {

531

sessionId: string;

532

commandId?: string;

533

screenshotName?: string;

534

ignoreRegions?: IgnoreRegion[];

535

customIgnoreRegions?: CustomIgnoreRegion[];

536

options?: {

537

freezeAnimatedImage?: boolean;

538

freezeImageBySelectors?: string[];

539

freezeImageByXpaths?: string[];

540

ignoreRegionSelectors?: string[];

541

ignoreRegionXpaths?: string[];

542

customIgnoreRegions?: CustomIgnoreRegionOptions[];

543

considerRegionSelectors?: string[];

544

considerRegionXpaths?: string[];

545

customConsiderRegions?: CustomConsiderRegionOptions[];

546

scrollToTopBottom?: boolean;

547

scrollToTopBottomMargin?: number;

548

};

549

}

550

551

interface CustomIgnoreRegionOptions {

552

top: number;

553

left: number;

554

bottom: number;

555

right: number;

556

}

557

558

interface CustomConsiderRegionOptions {

559

top: number;

560

left: number;

561

bottom: number;

562

right: number;

563

}

564

```

565

566

### Logger System

567

568

Structured logging system with group support and configurable output.

569

570

```javascript { .api }

571

/**

572

* Percy logger with group support and structured output

573

* Provides consistent logging across all Percy packages

574

*/

575

interface Logger {

576

// Logging methods

577

debug(message: string, meta?: any): void;

578

info(message: string, meta?: any): void;

579

warn(message: string, meta?: any): void;

580

error(message: string, error?: Error, meta?: any): void;

581

582

// Properties

583

stdout: NodeJS.WriteStream;

584

stderr: NodeJS.WriteStream;

585

586

// Methods

587

query(filter?: LogFilter): LogEntry[];

588

format(entry: LogEntry): string;

589

loglevel(level?: string): string;

590

timeit(label: string): () => void;

591

measure(label: string, fn: () => any): any;

592

measure(label: string, fn: () => Promise<any>): Promise<any>;

593

594

// Group management

595

group(name: string): Logger;

596

}

597

598

interface LogEntry {

599

level: 'debug' | 'info' | 'warn' | 'error';

600

message: string;

601

timestamp: number;

602

meta?: any;

603

group?: string;

604

}

605

606

interface LogFilter {

607

level?: string;

608

group?: string;

609

since?: number;

610

}

611

612

// Create logger instance

613

function createLogger(group?: string): Logger;

614

615

// Default export is root logger

616

const logger: Logger;

617

export default logger;

618

```

619

620

## Usage Examples

621

622

### Basic Percy Automation

623

624

```javascript

625

import Percy from "@percy/core";

626

627

const percy = new Percy({

628

token: process.env.PERCY_TOKEN,

629

server: false

630

});

631

632

await percy.start();

633

634

// Take a snapshot

635

await percy.snapshot({

636

name: "Homepage",

637

url: "http://localhost:3000",

638

widths: [1280, 768]

639

});

640

641

await percy.stop();

642

```

643

644

### Advanced Configuration

645

646

```javascript

647

import { load, validate, migrate } from "@percy/config";

648

649

// Load configuration with validation

650

const config = await load({

651

path: '.percy.yml',

652

search: true

653

});

654

655

// Validate custom configuration

656

const result = validate(config);

657

if (!result.valid) {

658

console.error('Config errors:', result.errors);

659

}

660

661

// Migrate old configuration

662

const migratedConfig = migrate(oldConfig);

663

```

664

665

### Custom CI Integration

666

667

```javascript

668

import PercyEnv from "@percy/env";

669

import PercyClient from "@percy/client";

670

671

const env = new PercyEnv();

672

const client = new PercyClient({ token: process.env.PERCY_TOKEN });

673

674

if (env.ci) {

675

const build = await client.createBuild({

676

branch: env.branch,

677

commit: env.commit,

678

pullRequest: env.pullRequest

679

});

680

681

console.log(`Created build: ${build.url}`);

682

}

683

```

684

685

### SDK Development

686

687

```javascript

688

import { isPercyEnabled, postSnapshot, logger } from "@percy/sdk-utils";

689

690

async function takeScreenshot(name, options = {}) {

691

if (!isPercyEnabled()) {

692

logger.info("Percy disabled, skipping screenshot");

693

return;

694

}

695

696

try {

697

await postSnapshot(name, {

698

url: options.url,

699

widths: options.widths || [1280]

700

});

701

702

logger.info(`Snapshot taken: ${name}`);

703

} catch (error) {

704

logger.error("Snapshot failed", error);

705

}

706

}

707

```