Implementation:Webdriverio Webdriverio Launcher Class
Overview
Concrete tool for orchestrating parallel test execution provided by the @wdio/cli package.
Metadata
| Field | Value |
|---|---|
| Page Type | Implementation |
| Repository | webdriverio/webdriverio |
| Package | @wdio/cli
|
| Source File | packages/wdio-cli/src/launcher.ts, Lines L40-683
|
| CLI Handler | packages/wdio-cli/src/commands/run.ts, Lines L17-272
|
| Related Principle | Principle: Test_Execution_Orchestration |
Description
The Launcher class is the top-level test execution coordinator. It parses configuration via ConfigParser, initializes services (onPrepare hooks), creates a schedule of spec-to-capability assignments, spawns worker processes via the Runner (typically LocalRunner), monitors progress, handles retries, and collects results. It provides both CLI (npx wdio run) and programmatic interfaces.
The Launcher manages the full lifecycle:
- Initialization: Loads
tsxfor TypeScript support, initializes ConfigParser, validates config - Service setup: Initializes launcher services, runs
onPreparehooks - Driver setup: Pre-configures browser drivers via
setupDriverandsetupBrowser - Scheduling: Creates a schedule mapping spec files to capabilities with instance limits
- Execution: Spawns workers, monitors completion, handles retries
- Cleanup: Runs
onCompletehooks, shuts down the runner, returns exit code
Source Reference
| File | Lines | Description |
|---|---|---|
packages/wdio-cli/src/launcher.ts |
L40-683 | Launcher class with full orchestration logic |
packages/wdio-cli/src/commands/run.ts |
L17-272 | CLI command handler, tsconfig resolution, watch mode entry point |
packages/wdio-cli/src/watcher.ts |
-- | Watch mode file monitoring and re-execution |
packages/wdio-cli/src/interface.ts |
-- | CLInterface for terminal output management |
Signature
class Launcher {
public configParser: ConfigParser
public isMultiremote: boolean
public isParallelMultiremote: boolean
public runner?: Services.RunnerInstance
public interface?: CLInterface
constructor(
_configFilePath: string,
_args?: Partial<RunCommandArguments>,
_isWatchMode?: boolean
)
async initialize(): Promise<void>
async run(): Promise<undefined | number>
}
Import
import Launcher from '@wdio/cli'
Inputs / Outputs Contract
Inputs
| Parameter | Type | Required | Description |
|---|---|---|---|
_configFilePath |
string |
Yes | Path to the wdio.conf.ts/js configuration file |
_args |
Partial<RunCommandArguments> |
No | CLI overrides: spec, suite, watch, bail, baseUrl, logLevel, shard, etc. |
_isWatchMode |
boolean |
No | Whether to run in watch mode (default: false) |
CLI Arguments (RunCommandArguments)
| Argument | Type | Description |
|---|---|---|
--spec |
string[] |
Run specific spec files (overrides config specs) |
--suite |
string[] |
Run named suites defined in config |
--exclude |
string[] |
Exclude spec files or suite names |
--watch |
boolean |
Enable watch mode |
--bail |
number |
Stop after N failures |
--baseUrl |
string |
Override base URL |
--logLevel |
string |
Override log level (trace/debug/info/warn/error/silent) |
--shard |
string |
Shard spec: "current/total" (e.g., "1/4") |
--repeat |
number |
Repeat specs N times (requires --spec or --suite) |
--coverage |
boolean |
Enable coverage for browser runner |
Outputs
| Output | Type | Description |
|---|---|---|
| Exit code | number |
0 = all tests passed, 1 = one or more failures
|
| Worker processes | Child processes | Spawned via Runner for each spec-capability pair |
| Console output | via CLInterface | Progress indicators, reporter output, error messages |
Execution Chain
The full execution chain from CLI to test execution:
CLI (npx wdio run wdio.conf.ts)
-> handler() in packages/wdio-cli/src/commands/run.ts
-> new Launcher(configPath, params)
-> new ConfigParser(configPath, args)
-> launcher.run()
-> launcher.initialize()
-> load tsx for TypeScript support
-> configParser.initialize(args)
-> configParser.getConfig()
-> configParser.getCapabilities()
-> initializeLauncherService(config, caps)
-> runner.initialize()
-> runLauncherHook(config.onPrepare)
-> setupDriver(config, caps)
-> _runMode(config, caps)
-> build schedule (spec-to-capability mapping)
-> _runSpecs() loop
-> _startInstance(specs, caps, cid, rid, retries)
-> runLauncherHook(config.onWorkerStart)
-> runner.run({ cid, command, configFile, args, caps, specs })
-> worker.on('exit', _endHandler)
-> _endHandler()
-> handle retries if specFileRetries > 0
-> update schedule, spawn next spec
-> resolve when all complete
-> runner.shutdown()
-> runOnCompleteHook(config.onComplete)
-> return exitCode
Usage Example
Programmatic Usage
import Launcher from '@wdio/cli'
// Basic test run
const launcher = new Launcher('./wdio.conf.ts')
const exitCode = await launcher.run()
console.log(`Tests completed with exit code: ${exitCode}`)
// With CLI overrides
const launcher2 = new Launcher('./wdio.conf.ts', {
spec: ['./test/specs/smoke/*.ts'],
baseUrl: 'http://staging.example.com',
logLevel: 'debug',
bail: 1
})
const exitCode2 = await launcher2.run()
// Access parsed config after initialization
await launcher2.initialize()
const config = launcher2.configParser.getConfig()
console.log(`Framework: ${config.framework}`)
console.log(`Max instances: ${config.maxInstances}`)
CLI Handler Flow
// packages/wdio-cli/src/commands/run.ts L161-177
export async function launch(wdioConfPath: string, params: Partial<RunCommandArguments>) {
const launcher = new Launcher(wdioConfPath, params)
return launcher.run()
.then((...args) => {
if (!process.env.WDIO_UNIT_TESTS) {
process.exit(...args)
}
})
.catch(err => {
console.error(err)
if (!process.env.WDIO_UNIT_TESTS) {
process.exit(1)
}
})
}
Scheduling Algorithm
The _runSpecs() method implements the scheduling loop:
// packages/wdio-cli/src/launcher.ts L371-435 (simplified)
private _runSpecs(): boolean {
const config = this.configParser.getConfig()
while (this._getNumberOfRunningInstances() < config.maxInstances) {
const schedulableCaps = this._schedule
.filter((session) => {
// Bail check: stop if too many failures
if (config.bail > 0 && config.bail <= this._runnerFailed) return false
// Global maxInstances check
if (this._getNumberOfRunningInstances() >= config.maxInstances) return false
// Capability has available slots and pending specs
return session.availableInstances > 0 && session.specs.length > 0
})
// Load balance: run capability with fewest running instances first
.sort((a, b) => a.runningInstances - b.runningInstances)
if (schedulableCaps.length === 0) break
const specs = schedulableCaps[0].specs.shift()
this._startInstance(specs.files, schedulableCaps[0].caps, ...)
schedulableCaps[0].availableInstances--
schedulableCaps[0].runningInstances++
}
// Return true when all done
return this._getNumberOfRunningInstances() === 0 &&
this._getNumberOfSpecsLeft() === 0
}
Retry Handling
When a worker exits with a non-zero code and retries remain, the spec is re-queued:
// packages/wdio-cli/src/launcher.ts L596-606
if (!passed && retries > 0) {
// specFileRetriesDeferred: true -> push (run after others)
// specFileRetriesDeferred: false -> unshift (run immediately)
const requeue = this.configParser.getConfig().specFileRetriesDeferred
? 'push' : 'unshift'
this._schedule[cid].specs[requeue]({
files: specs, retries: retries - 1, rid
})
}