UNPKG

@vizzly-testing/cli

Version:

Visual review platform for UI developers and designers

526 lines (456 loc) 13.5 kB
/** * Vizzly CLI Type Definitions * @module @vizzly-testing/cli */ import { EventEmitter } from 'node:events'; // ============================================================================ // Configuration Types // ============================================================================ export interface ServerConfig { port?: number; timeout?: number; } export interface BuildConfig { name?: string; environment?: string; branch?: string; commit?: string; message?: string; } export interface UploadConfig { screenshotsDir?: string | string[]; batchSize?: number; timeout?: number; } export interface ComparisonConfig { /** CIEDE2000 Delta E threshold (0=exact, 1=JND, 2=recommended default) */ threshold?: number; /** * Minimum cluster size to count as a real difference. * Filters out scattered single-pixel noise from rendering variance. * - 1 = Exact matching (any different pixel counts) * - 2 = Default (filters single isolated pixels as noise) * - 3+ = More permissive (only larger clusters detected) * @default 2 */ minClusterSize?: number; } export interface TddConfig { openReport?: boolean; } export interface VizzlyConfig { apiKey?: string; apiUrl?: string; server?: ServerConfig; build?: BuildConfig; upload?: UploadConfig; comparison?: ComparisonConfig; tdd?: TddConfig; /** Custom properties for baseline matching (e.g., ['theme', 'device']) */ signatureProperties?: string[]; plugins?: string[]; parallelId?: string; baselineBuildId?: string; baselineComparisonId?: string; eager?: boolean; wait?: boolean; allowNoToken?: boolean; /** Allow additional plugin-specific configuration */ [key: string]: unknown; } // ============================================================================ // Screenshot Types // ============================================================================ export interface ScreenshotOptions { properties?: Record<string, unknown>; threshold?: number; minClusterSize?: number; fullPage?: boolean; buildId?: string; } export interface ScreenshotResult { success: boolean; status?: 'passed' | 'failed' | 'new'; name?: string; diffPercentage?: number; } // ============================================================================ // Comparison Types // ============================================================================ export interface ComparisonResult { id: string; name: string; status: 'passed' | 'failed' | 'new' | 'error' | 'baseline-updated'; baseline: string; current: string; diff: string | null; properties: Record<string, unknown>; signature: string; threshold?: number; minClusterSize?: number; diffPercentage?: number; diffCount?: number; error?: string; } export interface TddResults { total: number; passed: number; failed: number; new: number; errors: number; comparisons: ComparisonResult[]; baseline: BaselineData | null; } export interface BaselineData { buildId: string; buildName: string; environment?: string; branch?: string; threshold: number; createdAt?: string; screenshots: BaselineScreenshot[]; } export interface BaselineScreenshot { name: string; originalName?: string; sha256?: string; id?: string; properties: Record<string, unknown>; path: string; signature: string; } // ============================================================================ // Upload Types // ============================================================================ export interface UploadOptions { screenshotsDir?: string; buildName?: string; branch?: string; commit?: string; message?: string; environment?: string; threshold?: number; pullRequestNumber?: string; parallelId?: string; onProgress?: (progress: UploadProgress) => void; } export interface UploadProgress { phase: | 'scanning' | 'processing' | 'deduplication' | 'uploading' | 'completed'; message: string; total?: number; current?: number; toUpload?: number; existing?: number; buildId?: string; url?: string; } export interface UploadResult { success: boolean; buildId: string; url: string | null; stats: { total: number; uploaded: number; skipped: number; }; } export interface BuildResult { status: 'completed' | 'failed' | 'pending'; build: unknown; comparisons?: number; passedComparisons?: number; failedComparisons?: number; url?: string; } // ============================================================================ // Error Types // ============================================================================ export class VizzlyError extends Error { code: string; context: Record<string, unknown>; timestamp: string; constructor( message: string, code?: string, context?: Record<string, unknown> ); getUserMessage(): string; toJSON(): { name: string; code: string; message: string; context: Record<string, unknown>; timestamp: string; stack?: string; }; } export class ConfigError extends VizzlyError { constructor(message: string, context?: Record<string, unknown>); } export class AuthError extends VizzlyError { constructor(message: string, context?: Record<string, unknown>); } export class NetworkError extends VizzlyError { constructor(message: string, context?: Record<string, unknown>); } export class UploadError extends VizzlyError { constructor(message: string, context?: Record<string, unknown>); } export class ScreenshotError extends VizzlyError { constructor(message: string, context?: Record<string, unknown>); } export class BuildError extends VizzlyError { constructor(message: string, context?: Record<string, unknown>); } export class TimeoutError extends VizzlyError { duration: number; constructor( message: string, duration?: number, context?: Record<string, unknown> ); } export class ValidationError extends VizzlyError { errors: string[]; constructor( message: string, errors?: string[], context?: Record<string, unknown> ); } // ============================================================================ // SDK Types // ============================================================================ export interface VizzlySDKInstance extends EventEmitter { config: VizzlyConfig; /** Start the Vizzly server */ start(): Promise<{ port: number; url: string }>; /** Stop the Vizzly server */ stop(): Promise<void>; /** Get current configuration */ getConfig(): VizzlyConfig; /** Capture a screenshot */ screenshot( name: string, imageBuffer: Buffer | string, options?: ScreenshotOptions ): Promise<void>; /** Upload all captured screenshots */ upload(options?: UploadOptions): Promise<UploadResult>; /** Run local comparison in TDD mode */ compare( name: string, imageBuffer: Buffer | string ): Promise<ComparisonResult>; } export class VizzlySDK extends EventEmitter implements VizzlySDKInstance { config: VizzlyConfig; constructor(config: VizzlyConfig, services: unknown); start(): Promise<{ port: number; url: string }>; stop(): Promise<void>; getConfig(): VizzlyConfig; screenshot( name: string, imageBuffer: Buffer | string, options?: ScreenshotOptions ): Promise<void>; upload(options?: UploadOptions): Promise<UploadResult>; compare( name: string, imageBuffer: Buffer | string ): Promise<ComparisonResult>; } // ============================================================================ // Service Types // ============================================================================ export interface Uploader { upload(options: UploadOptions): Promise<UploadResult>; waitForBuild(buildId: string, timeout?: number): Promise<BuildResult>; } export interface TddService { downloadBaselines( environment?: string, branch?: string, buildId?: string, comparisonId?: string ): Promise<BaselineData | null>; loadBaseline(): Promise<BaselineData | null>; compareScreenshot( name: string, imageBuffer: Buffer, properties?: Record<string, unknown> ): Promise<ComparisonResult>; getResults(): TddResults; printResults(): Promise<TddResults>; updateBaselines(): number; acceptBaseline(idOrComparison: string | ComparisonResult): Promise<{ name: string; status: string; message: string; }>; } export interface Services { apiService: unknown; authService: unknown; configService: unknown; projectService: unknown; uploader: Uploader; buildManager: unknown; serverManager: unknown; tddService: TddService; testRunner: unknown; } // ============================================================================ // Plugin API Types (Stable Contract) // ============================================================================ /** * Stable TestRunner interface for plugins. * Only these methods are guaranteed to remain stable across minor versions. */ export interface PluginTestRunner { /** Listen for a single event emission */ once(event: string, callback: (...args: unknown[]) => void): void; /** Subscribe to events */ on(event: string, callback: (...args: unknown[]) => void): void; /** Unsubscribe from events */ off(event: string, callback: (...args: unknown[]) => void): void; /** Create a new build and return the build ID */ createBuild(options: BuildOptions, isTddMode: boolean): Promise<string>; /** Finalize a build after all screenshots are captured */ finalizeBuild( buildId: string, isTddMode: boolean, success: boolean, executionTime: number ): Promise<void>; } /** * Stable ServerManager interface for plugins. * Only these methods are guaranteed to remain stable across minor versions. */ export interface PluginServerManager { /** Start the screenshot server */ start(buildId: string, tddMode: boolean, setBaseline: boolean): Promise<void>; /** Stop the screenshot server */ stop(): Promise<void>; } /** * Stable services interface for plugins. * This is the public API contract - internal services are NOT exposed. */ export interface PluginServices { testRunner: PluginTestRunner; serverManager: PluginServerManager; } /** * Build options for createBuild() */ export interface BuildOptions { port?: number; timeout?: number; buildName?: string; branch?: string; commit?: string; message?: string; environment?: string; threshold?: number; eager?: boolean; allowNoToken?: boolean; wait?: boolean; uploadAll?: boolean; pullRequestNumber?: string; parallelId?: string; } /** * Context object passed to plugin register() function. * This is the stable plugin API contract. */ export interface PluginContext { /** Merged Vizzly configuration */ config: VizzlyConfig; /** Stable services for plugins */ services: PluginServices; /** Output utilities for logging */ output: OutputUtils; /** @deprecated Use output instead. Alias for backwards compatibility. */ logger: OutputUtils; } /** Create stable plugin services from internal services */ export function createPluginServices(services: Services): PluginServices; // ============================================================================ // Output Utilities // ============================================================================ export interface OutputUtils { info(message: string): void; warn(message: string): void; error(message: string): void; success(message: string): void; debug(category: string, ...args: unknown[]): void; configure(options: { verbose?: boolean }): void; } // ============================================================================ // Main Exports // ============================================================================ /** Create a new Vizzly SDK instance */ export function createVizzly( config?: VizzlyConfig, options?: { verbose?: boolean } ): Promise<VizzlySDKInstance>; /** Take a screenshot for visual regression testing */ export function vizzlyScreenshot( name: string, imageBuffer: Buffer | string, options?: { properties?: Record<string, unknown>; threshold?: number; minClusterSize?: number; fullPage?: boolean; } ): Promise<void>; /** Configure the Vizzly client */ export function configure(config?: { serverUrl?: string; enabled?: boolean; }): void; /** Enable or disable screenshot capture */ export function setEnabled(enabled: boolean): void; /** Create an uploader instance */ export function createUploader( config?: { apiKey?: string; apiUrl?: string; userAgent?: string; command?: string; upload?: UploadConfig; }, options?: { signal?: AbortSignal; batchSize?: number; timeout?: number; } ): Uploader; /** Create a TDD service instance */ export function createTDDService( config: VizzlyConfig, options?: { workingDir?: string; setBaseline?: boolean; authService?: unknown; } ): TddService; /** Create all services with dependencies */ export function createServices( config: VizzlyConfig, command?: string ): Services; /** Load configuration from file and environment */ export function loadConfig(options?: { cwd?: string }): Promise<VizzlyConfig>; /** Define Vizzly configuration with type hints */ export function defineConfig(config: VizzlyConfig): VizzlyConfig; /** Output utilities namespace */ export const output: OutputUtils;