or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.mdrunner-management.mdstream-debugging.mdworker-management.mdworker-process-execution.md
tile.json

tessl/npm-wdio--local-runner

A WebdriverIO runner to run tests locally within isolated worker processes

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/@wdio/local-runner@9.19.x

To install, run

npx @tessl/cli install tessl/npm-wdio--local-runner@9.19.0

index.mddocs/

WebdriverIO Local Runner

WebdriverIO Local Runner executes test files locally within isolated worker processes to achieve maximum concurrency and test isolation. It spawns a separate worker process per capability, with each worker maintaining its own browser session for complete test isolation.

Package Information

  • Package Name: @wdio/local-runner
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install --save-dev @wdio/local-runner

Core Imports

import LocalRunner from "@wdio/local-runner";
import type { WorkerInstance, RunArgs } from "@wdio/local-runner";

// Worker process runner (separate entry point)
import { runner } from "@wdio/local-runner/run";

For CommonJS:

const LocalRunner = require("@wdio/local-runner");
const { runner } = require("@wdio/local-runner/run");

Basic Usage

import LocalRunner from "@wdio/local-runner";

// Initialize the runner with WebdriverIO config
const runner = new LocalRunner({} as never, {
  autoXvfb: true,
  outputDir: "./logs",
  runnerEnv: { NODE_ENV: "test" }
} as WebdriverIO.Config);

// Initialize the runner
await runner.initialize();

// Run a test worker
const worker = await runner.run({
  cid: "0-0",
  command: "run",
  configFile: "/path/to/wdio.conf.js",
  args: {},
  caps: { browserName: "chrome" },
  specs: ["./test/example.spec.js"],
  execArgv: [],
  retries: 0
});

// Shutdown all workers when done
await runner.shutdown();

Architecture

WebdriverIO Local Runner is built around several key components:

  • LocalRunner: Main class that manages the worker pool and coordinates test execution
  • WorkerInstance: Individual worker process manager that handles test execution in isolation
  • Runner Process: Worker child process executor that runs the actual test framework
  • Process Management: Each worker runs as a separate Node.js child process for complete isolation
  • IPC Message System: Inter-process communication using Node.js child_process messaging
  • Xvfb Integration: Automatic virtual display setup for headless browser testing with lazy initialization
  • Stream Management: Aggregated output handling with capability ID prefixing and transformation
  • REPL Integration: Interactive debugging support with queue management for multiple workers
  • Buffer Management: Stream buffering for stdout/stderr aggregation with configurable options

Capabilities

Runner Management

Core runner functionality for managing worker processes and coordinating test execution across multiple isolated environments.

class LocalRunner {
  workerPool: Record<string, WorkerInstance>;
  stdout: WritableStreamBuffer;
  stderr: WritableStreamBuffer;
  
  constructor(options: never, config: WebdriverIO.Config);
  initialize(): Promise<void>;
  getWorkerCount(): number;
  run(args: RunArgs): Promise<WorkerInstance>;
  shutdown(): Promise<boolean>;
}

interface RunArgs extends Workers.WorkerRunPayload {
  command: string;
  args: Workers.WorkerMessageArgs;
  cid: string;
}

Runner Management

Worker Process Management

Individual worker process management with lifecycle control, message passing, and browser session handling.

class WorkerInstance extends EventEmitter implements Workers.Worker {
  cid: string;
  config: WebdriverIO.Config;
  configFile: string;
  caps: WebdriverIO.Capabilities;
  capabilities: WebdriverIO.Capabilities;
  specs: string[];
  execArgv: string[];
  retries: number;
  stdout: WritableStreamBuffer;
  stderr: WritableStreamBuffer;
  childProcess?: ChildProcess;
  sessionId?: string;
  server?: Record<string, string>;
  instances?: Record<string, { sessionId: string }>;
  isMultiremote?: boolean;
  isBusy: boolean;
  isKilled: boolean;
  isReady: Promise<boolean>;
  isSetup: Promise<boolean>;
  
  constructor(
    config: WebdriverIO.Config,
    payload: Workers.WorkerRunPayload,
    stdout: WritableStreamBuffer,
    stderr: WritableStreamBuffer,
    xvfbManager: XvfbManager
  );
  
  startProcess(): Promise<ChildProcess>;
  postMessage(
    command: string,
    args: Workers.WorkerMessageArgs,
    requiresSetup?: boolean
  ): Promise<void>;
}

Worker Process Management

Worker Process Execution

Worker child process executor that runs the actual test framework in isolated processes.

// Exported from './run' entry point
interface RunnerInterface extends NodeJS.EventEmitter {
  sigintWasCalled: boolean;
  [key: string]: unknown;
}

const runner: RunnerInterface;

Worker Process Execution

Stream Management and Debugging

Advanced stream handling, output transformation, and interactive debugging capabilities.

function runnerTransformStream(
  cid: string,
  inputStream: Readable,
  aggregator?: string[]
): Readable;

class ReplQueue {
  isRunning: boolean;
  add(childProcess: ChildProcess, config: ReplConfig, onStart: () => void, onResult: (ev: unknown) => void): void;
  next(): void;
}

const SHUTDOWN_TIMEOUT: 5000;
const DEBUGGER_MESSAGES: string[];
const BUFFER_OPTIONS: { initialSize: number; incrementAmount: number; };

Stream Management and Debugging

Types

interface Workers.WorkerRunPayload {
  cid: string;
  configFile: string;
  caps: WebdriverIO.Capabilities;
  specs: string[];
  execArgv: string[];
  retries: number;
}

interface Workers.WorkerMessageArgs {
  [key: string]: any;
}

interface Workers.WorkerMessage {
  name: string;
  origin: string;
  content?: any;
  params?: any;
}

interface Workers.WorkerCommand {
  cid: string;
  command: string;
  configFile: string;
  args: Workers.WorkerMessageArgs;
  caps: WebdriverIO.Capabilities;
  specs: string[];
  retries: number;
}

interface Workers.Worker {
  cid: string;
  isBusy: boolean;
  // Additional worker interface properties
}

interface WebdriverIO.Config {
  autoXvfb?: boolean;
  xvfbAutoInstall?: string | boolean | object;
  xvfbAutoInstallMode?: string;
  xvfbAutoInstallCommand?: string;
  xvfbMaxRetries?: number;
  xvfbRetryDelay?: number;
  outputDir?: string;
  runnerEnv?: Record<string, any>;
  watch?: boolean;
  groupLogsByTestSpec?: boolean;
}

interface WebdriverIO.Capabilities {
  browserName?: string;
  [key: string]: any;
}

// External dependency types
interface WritableStreamBuffer {
  // From 'stream-buffers' package
  getContents(): Buffer;
}

interface XvfbManager {
  // From '@wdio/xvfb' package
  init(capabilities: WebdriverIO.Capabilities): Promise<boolean>;
  shouldRun(): boolean;
}

interface ProcessFactory {
  // From '@wdio/xvfb' package
  createWorkerProcess(
    scriptPath: string,
    args: string[],
    options: {
      cwd: string;
      env: Record<string, string>;
      execArgv: string[];
      stdio: any[];
    }
  ): Promise<ChildProcess>;
}

interface ReplConfig {
  // From '@wdio/repl' package
  prompt?: string;
  [key: string]: any;
}

interface ChildProcess {
  // From Node.js 'child_process' module
  pid?: number;
  stdout: Readable | null;
  stderr: Readable | null;
  send(message: any): boolean;
  kill(signal?: string): boolean;
  on(event: string, listener: Function): this;
}

interface Readable {
  // From Node.js 'stream' module
  pipe(destination: any): any;
  on(event: string, listener: Function): this;
}

interface Transform extends Readable {
  // From Node.js 'stream' module
}

class EventEmitter {
  // From Node.js 'events' module
  on(event: string, listener: Function): this;
  emit(event: string, ...args: any[]): boolean;
}