CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-percy--cli

Command line interface for Percy visual testing platform that enables developers to capture, upload, and manage visual snapshots for web applications.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

programmatic-api.mddocs/

Programmatic API

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.

Core Imports

// Main Percy CLI entry point
import { percy, checkForUpdate } from "@percy/cli";

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

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

// API client
import PercyClient from "@percy/client";

// Environment detection
import PercyEnv from "@percy/env";

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

// Logging
import logger from "@percy/logger";

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

Capabilities

Percy Core Class

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

/**
 * Main Percy class for visual testing operations
 * Manages browser sessions, asset discovery, and snapshot uploading
 */
class Percy {
  constructor(options?: PercyOptions);
  
  // Lifecycle methods
  start(): Promise<void>;
  stop(): Promise<void>;
  idle(): Promise<void>;
  
  // Snapshot methods
  snapshot(options: SnapshotOptions): Promise<void>;
  upload(files: FileUploadOptions): Promise<void>;
  
  // Server methods
  server(options?: ServerOptions): Promise<Server>;
  
  // Utility methods
  isRunning(): boolean;
  address(): string;
  port(): number;
  
  // Getters
  get client(): PercyClient;
  get browser(): Browser;
  get config(): PercyConfig;
}

interface PercyOptions {
  token?: string;
  config?: string | PercyConfig;
  server?: boolean;
  port?: number;
  skipDiscovery?: boolean;
  delayUploads?: boolean;
  deferUploads?: boolean;
  dryRun?: boolean;
  labels?: string[];
}

interface SnapshotOptions {
  name: string;
  url?: string;
  domSnapshot?: string;
  clientInfo?: string;
  environmentInfo?: string;
  enableJavaScript?: boolean;
  cliEnableJavaScript?: boolean;
  disableShadowDOM?: boolean;
  minHeight?: number;
  scope?: string;
  sync?: boolean;
  testCase?: string;
  thTestCaseExecutionId?: string;
  additionalSnapshots?: AdditionalSnapshot[];
  requestHeaders?: Record<string, string>;
  authorization?: {
    username: string;
    password: string;
  };
}

interface AdditionalSnapshot {
  prefix?: string;
  suffix?: string;
  name?: string;
  waitForTimeout?: number;
  waitForSelector?: string;
  execute?: string | Function;
  percyCSS?: string;
  ignoreRegions?: IgnoreRegion[];
}

interface IgnoreRegion {
  ignoreRegion?: string;
  customIgnoreRegions?: CustomIgnoreRegion[];
}

interface CustomIgnoreRegion {
  selector?: string;
  xpath?: string;
  coordinates?: {
    top: number;
    bottom: number;
    left: number;
    right: number;
  };
}

Percy Utilities

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

// Network utilities
function request(url: string, options?: RequestOptions): Promise<Response>;
function hostname(url: string): string;
function normalizeURL(url: string, baseURL?: string): string;
function hostnameMatches(hostname: string, patterns: string[]): boolean;

// Resource creation
function createResource(url: string, content: Buffer, options?: ResourceOptions): Resource;
function createRootResource(url: string, content: string): Resource;
function createPercyCSSResource(css: string): Resource;
function createLogResource(logs: LogEntry[]): Resource;

// Server utilities
class Server {
  constructor(percy: Percy, port?: number);
  listen(port?: number): Promise<void>;
  close(): Promise<void>;
  address(): string;
  port(): number;
}

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

// Async utilities with generator support
function generatePromise<T>(generator: Generator<any, T>): Promise<T>;
function yieldTo(promise: Promise<any>): Generator<Promise<any>, any>;
function yieldAll(promises: Promise<any>[]): Generator<Promise<any[]>, any[]>;
function yieldFor(condition: () => boolean, timeout?: number): Generator<Promise<boolean>, boolean>;
function waitFor(condition: () => boolean, options?: WaitOptions): Promise<boolean>;
function waitForTimeout(timeout: number): Promise<void>;

// Browser utilities
function serializeFunction(fn: Function): string;
function waitForSelectorInsideBrowser(selector: string, timeout?: number): string;

// Encoding utilities
function base64encode(data: string | Buffer): string;
function isGzipped(data: Buffer): boolean;

// URL utilities
function decodeAndEncodeURLWithLogging(url: string): string;

// Security utilities
function redactSecrets(data: any): any;

// Helper classes
class AbortController {
  signal: AbortSignal;
  abort(): void;
}

class AbortError extends Error {
  name: "AbortError";
}

class DefaultMap<K, V> extends Map<K, V> {
  constructor(defaultValue: () => V);
  get(key: K): V;
}

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

// Type utilities
function isGenerator(obj: any): obj is Generator;
function compareObjectTypes(obj1: any, obj2: any): boolean;
function normalizeOptions<T>(options: T, schema: any): T;

interface RequestOptions {
  method?: string;
  headers?: Record<string, string>;
  body?: string | Buffer;
  timeout?: number;
  retries?: number;
  retryBackoff?: number;
}

interface ResourceOptions {
  mimetype?: string;
  root?: boolean;
  percyCSS?: boolean;
}

interface Resource {
  url: string;
  content: Buffer;
  mimetype: string;
  root?: boolean;
  percyCSS?: boolean;
}

interface WaitOptions {
  timeout?: number;
  interval?: number;
}

interface RetryOptions {
  retries?: number;
  backoff?: number;
  onRetry?: (error: Error, attempt: number) => void;
}

Configuration System

Advanced configuration loading, validation, and migration system.

// Configuration loading
function load(options?: LoadOptions): Promise<PercyConfig>;
function search(options?: SearchOptions): Promise<string | null>;

// Validation system
function validate(config: any, schema?: ConfigSchema): ValidationResult;
function addSchema(name: string, schema: any): void;

// Migration system
function migrate(config: any, options?: MigrateOptions): any;
function addMigration(version: number, migration: MigrationFunction): void;

// Configuration utilities
function getDefaults(): PercyConfig;
function merge(...configs: any[]): PercyConfig;
function normalize(config: any): PercyConfig;
function stringify(config: any, format?: 'json' | 'yaml' | 'js'): string;

interface LoadOptions {
  path?: string;
  search?: boolean;
  bail?: boolean;
}

interface SearchOptions {
  start?: string;
  stop?: string;
  names?: string[];
}

interface ValidationResult {
  valid: boolean;
  errors: ValidationError[];
  config: PercyConfig;
}

interface ValidationError {
  path: string[];
  message: string;
  value: any;
}

interface MigrateOptions {
  dry?: boolean;
  version?: number;
}

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

interface ConfigSchema {
  type: string;
  properties?: Record<string, any>;
  additionalProperties?: boolean;
  required?: string[];
}

Percy Client

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

/**
 * Percy API client for interacting with Percy's visual testing service
 * Handles authentication, request/response processing, and error handling
 */
class PercyClient {
  constructor(options?: ClientOptions);
  
  // Build methods
  createBuild(options: CreateBuildOptions): Promise<Build>;
  getBuild(buildId: string): Promise<Build>;
  finalizeBuild(buildId: string): Promise<Build>;
  
  // Snapshot methods
  createSnapshot(buildId: string, snapshot: CreateSnapshotOptions): Promise<Snapshot>;
  uploadResources(buildId: string, resources: Resource[]): Promise<void>;
  createComparison(buildId: string, snapshotId: string, options: ComparisonOptions): Promise<Comparison>;
  
  // Build management
  approveBuild(buildId: string): Promise<Build>;
  
  // Utility methods
  request(path: string, options?: RequestOptions): Promise<any>;
}

interface ClientOptions {
  token?: string;
  apiUrl?: string;
  clientInfo?: string;
  environmentInfo?: string;
  userAgent?: string;
}

interface CreateBuildOptions {
  type?: 'web' | 'app';
  branch?: string;
  targetBranch?: string;
  targetCommitSha?: string;
  commitSha?: string;
  commitCommittedAt?: string;
  commitAuthorName?: string;
  commitAuthorEmail?: string;
  commitMessage?: string;
  pullRequestNumber?: string;
  parallel?: boolean;
  partialBuild?: boolean;
}

interface Build {
  id: string;
  number: number;
  url: string;
  state: 'pending' | 'processing' | 'finished' | 'failed';
  reviewState?: 'unreviewed' | 'approved' | 'rejected';
  reviewStateReason?: string;
  totalComparisons?: number;
  totalComparisonsFinished?: number;
  totalComparisonsDiff?: number;
}

interface CreateSnapshotOptions {
  name: string;
  clientInfo?: string;
  environmentInfo?: string;
  widths?: number[];
  minHeight?: number;
  enableJavaScript?: boolean;
  percyCSS?: string;
  scope?: string;
  domSnapshot?: string;
}

interface Snapshot {
  id: string;
  name: string;
  url?: string;
  widths: number[];
  minHeight: number;
  enableJavaScript: boolean;
}

interface ComparisonOptions {
  tag?: string;
  tiles?: Tile[];  
  externalDebugUrl?: string;
  ignoredElementsData?: IgnoreElementData[];
  consideredElementsData?: ConsiderElementData[];
}

interface Tile {
  filepath?: string;
  sha?: string;
  statusBarHeight?: number;
  navBarHeight?: number;
  headerHeight?: number;
  footerHeight?: number;
  fullscreen?: boolean;
}

interface Comparison {
  id: string;
  tag: string;
  state: 'pending' | 'processing' | 'finished' | 'failed';
  diffRatio?: number;
}

Environment Detection

Comprehensive CI/CD environment detection and configuration.

/**
 * Environment detection for CI/CD systems and local development
 * Automatically detects 15+ popular CI systems and extracts build information
 */
class PercyEnv {
  constructor();
  
  // Environment detection
  get ci(): string | null;
  get branch(): string | null;
  get commit(): string | null;
  get pullRequest(): string | null;
  get parallel(): { nonce: string; total: number } | null;
  get partial(): boolean;
  
  // CI system specific info
  get info(): EnvironmentInfo;
  
  // Static methods
  static current(): PercyEnv;
  static load(path?: string): void;
}

interface EnvironmentInfo {
  ci?: string;
  project?: string;
  branch?: string;
  commit?: string;
  commitMessage?: string;
  committerName?: string;
  committerEmail?: string;
  authorName?: string;
  authorEmail?: string;
  pullRequest?: string;
  parallel?: {
    nonce: string;
    total: number;
  };
  partial?: boolean;
  url?: string;
}

// Supported CI systems:
// - Travis CI, Jenkins, CircleCI, GitHub Actions, GitLab CI
// - Azure DevOps, Buildkite, Drone, Semaphore, TeamCity
// - AppVeyor, Bitbucket Pipelines, Heroku CI, Netlify, Codeship

DOM Serialization

Browser-compatible DOM serialization for visual testing.

// DOM serialization functions
function serializeDOM(options?: SerializeOptions): string;
const serialize = serializeDOM; // Alias

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

interface SerializeOptions {
  enableJavaScript?: boolean;
  disableShadowDOM?: boolean;
  domTransformation?: (dom: Document) => Document;
  reshuffleInvalidTags?: boolean;
}

SDK Utilities

Utilities for Percy SDK development and integration.

// Logger for SDK development
const logger: Logger;

// Percy utilities
function isPercyEnabled(): boolean;
function waitForPercyIdle(): Promise<void>;

// Percy communication
function fetchPercyDOM(): Promise<string>;
function postSnapshot(name: string, options?: PostSnapshotOptions): Promise<void>;
function postComparison(name: string, tag: string, tiles?: Tile[]): Promise<void>;
function postBuildEvents(events: BuildEvent[]): Promise<void>;
function flushSnapshots(): Promise<void>;

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

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

interface PostSnapshotOptions {
  url?: string;
  domSnapshot?: string;
  clientInfo?: string;
  environmentInfo?: string;
  widths?: number[];
  minHeight?: number;
  enableJavaScript?: boolean;
  percyCSS?: string;
  scope?: string;
}

interface BuildEvent {
  type: string;
  message: string;
  timestamp?: number;
  level?: 'debug' | 'info' | 'warn' | 'error';
}

interface AutomateScreenshotOptions {
  sessionId: string;
  commandId?: string;
  screenshotName?: string;
  ignoreRegions?: IgnoreRegion[];
  customIgnoreRegions?: CustomIgnoreRegion[];
  options?: {
    freezeAnimatedImage?: boolean;
    freezeImageBySelectors?: string[];
    freezeImageByXpaths?: string[];
    ignoreRegionSelectors?: string[];
    ignoreRegionXpaths?: string[];
    customIgnoreRegions?: CustomIgnoreRegionOptions[];
    considerRegionSelectors?: string[];
    considerRegionXpaths?: string[];
    customConsiderRegions?: CustomConsiderRegionOptions[];
    scrollToTopBottom?: boolean;
    scrollToTopBottomMargin?: number;
  };
}

interface CustomIgnoreRegionOptions {
  top: number;
  left: number;
  bottom: number;  
  right: number;
}

interface CustomConsiderRegionOptions {
  top: number;
  left: number;
  bottom: number;
  right: number;
}

Logger System

Structured logging system with group support and configurable output.

/**
 * Percy logger with group support and structured output
 * Provides consistent logging across all Percy packages
 */
interface Logger {
  // Logging methods
  debug(message: string, meta?: any): void;
  info(message: string, meta?: any): void;
  warn(message: string, meta?: any): void;
  error(message: string, error?: Error, meta?: any): void;
  
  // Properties
  stdout: NodeJS.WriteStream;
  stderr: NodeJS.WriteStream;
  
  // Methods
  query(filter?: LogFilter): LogEntry[];
  format(entry: LogEntry): string;
  loglevel(level?: string): string;
  timeit(label: string): () => void;
  measure(label: string, fn: () => any): any;
  measure(label: string, fn: () => Promise<any>): Promise<any>;
  
  // Group management
  group(name: string): Logger;
}

interface LogEntry {
  level: 'debug' | 'info' | 'warn' | 'error';
  message: string;
  timestamp: number;
  meta?: any;
  group?: string;
}

interface LogFilter {
  level?: string;
  group?: string;
  since?: number;
}

// Create logger instance
function createLogger(group?: string): Logger;

// Default export is root logger
const logger: Logger;
export default logger;

Usage Examples

Basic Percy Automation

import Percy from "@percy/core";

const percy = new Percy({
  token: process.env.PERCY_TOKEN,
  server: false
});

await percy.start();

// Take a snapshot
await percy.snapshot({
  name: "Homepage",
  url: "http://localhost:3000",
  widths: [1280, 768]
});

await percy.stop();

Advanced Configuration

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

// Load configuration with validation
const config = await load({
  path: '.percy.yml',
  search: true
});

// Validate custom configuration
const result = validate(config);
if (!result.valid) {
  console.error('Config errors:', result.errors);
}

// Migrate old configuration
const migratedConfig = migrate(oldConfig);

Custom CI Integration

import PercyEnv from "@percy/env";
import PercyClient from "@percy/client";

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

if (env.ci) {
  const build = await client.createBuild({
    branch: env.branch,
    commit: env.commit,
    pullRequest: env.pullRequest
  });
  
  console.log(`Created build: ${build.url}`);
}

SDK Development

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

async function takeScreenshot(name, options = {}) {
  if (!isPercyEnabled()) {
    logger.info("Percy disabled, skipping screenshot");
    return;
  }
  
  try {
    await postSnapshot(name, {
      url: options.url,
      widths: options.widths || [1280]
    });
    
    logger.info(`Snapshot taken: ${name}`);
  } catch (error) {
    logger.error("Snapshot failed", error);
  }
}

Install with Tessl CLI

npx tessl i tessl/npm-percy--cli

docs

app-snapshots.md

build-management.md

configuration.md

core-operations.md

image-uploads.md

index.md

programmatic-api.md

static-snapshots.md

tile.json