Command line interface for Percy visual testing platform that enables developers to capture, upload, and manage visual snapshots for web applications.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
// 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";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;
};
}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;
}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[];
}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;
}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, CodeshipBrowser-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;
}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;
}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;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();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);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}`);
}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