or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

worker-management.mddocs/

0

# Worker Process Management

1

2

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

3

4

## Capabilities

5

6

### WorkerInstance Class

7

8

Individual worker process manager that extends EventEmitter to handle test execution in isolated child processes.

9

10

```typescript { .api }

11

/**

12

* WorkerInstance manages individual worker processes

13

* Extends EventEmitter for message and event handling

14

*/

15

class WorkerInstance extends EventEmitter implements Workers.Worker {

16

cid: string;

17

config: WebdriverIO.Config;

18

configFile: string;

19

caps: WebdriverIO.Capabilities;

20

capabilities: WebdriverIO.Capabilities;

21

specs: string[];

22

execArgv: string[];

23

retries: number;

24

stdout: WritableStreamBuffer;

25

stderr: WritableStreamBuffer;

26

childProcess?: ChildProcess;

27

sessionId?: string;

28

server?: Record<string, string>;

29

instances?: Record<string, { sessionId: string }>;

30

isMultiremote?: boolean;

31

isBusy: boolean;

32

isKilled: boolean;

33

isReady: Promise<boolean>;

34

isSetup: Promise<boolean>;

35

}

36

```

37

38

### Constructor

39

40

Creates a new WorkerInstance with configuration and stream buffers.

41

42

```typescript { .api }

43

/**

44

* Create a WorkerInstance

45

* @param config - WebdriverIO configuration

46

* @param payload - Worker run payload with cid, specs, capabilities, etc.

47

* @param stdout - Stdout stream buffer for aggregated output

48

* @param stderr - Stderr stream buffer for aggregated output

49

* @param xvfbManager - Xvfb manager for virtual display handling

50

*/

51

constructor(

52

config: WebdriverIO.Config,

53

payload: Workers.WorkerRunPayload,

54

stdout: WritableStreamBuffer,

55

stderr: WritableStreamBuffer,

56

xvfbManager: XvfbManager

57

);

58

```

59

60

**Usage Example:**

61

62

```typescript

63

import { WritableStreamBuffer } from 'stream-buffers';

64

import { XvfbManager } from '@wdio/xvfb';

65

import WorkerInstance from '@wdio/local-runner';

66

67

const stdout = new WritableStreamBuffer();

68

const stderr = new WritableStreamBuffer();

69

const xvfbManager = new XvfbManager({ enabled: true });

70

71

const worker = new WorkerInstance(

72

config,

73

{

74

cid: "0-0",

75

configFile: "/path/to/wdio.conf.js",

76

caps: { browserName: "chrome" },

77

specs: ["./test/example.spec.js"],

78

execArgv: [],

79

retries: 0

80

},

81

stdout,

82

stderr,

83

xvfbManager

84

);

85

```

86

87

### Start Worker Process

88

89

Spawn the child process for the worker with proper environment setup.

90

91

```typescript { .api }

92

/**

93

* Spawn worker child process

94

* @returns Promise resolving to the spawned ChildProcess

95

*/

96

startProcess(): Promise<ChildProcess>;

97

```

98

99

**Usage Example:**

100

101

```typescript

102

// Process is started automatically, but can be started manually

103

const childProcess = await worker.startProcess();

104

105

console.log(`Worker process PID: ${childProcess.pid}`);

106

107

// Listen for process events

108

childProcess.on('exit', (code) => {

109

console.log(`Worker exited with code ${code}`);

110

});

111

```

112

113

### Send Messages to Worker

114

115

Send commands and messages to the worker process via IPC.

116

117

```typescript { .api }

118

/**

119

* Send command to worker process

120

* @param command - Command to execute in worker

121

* @param args - Arguments for the command

122

* @param requiresSetup - Whether command requires session setup

123

* @returns Promise that resolves when message is sent

124

*/

125

postMessage(

126

command: string,

127

args: Workers.WorkerMessageArgs,

128

requiresSetup?: boolean

129

): Promise<void>;

130

```

131

132

**Usage Example:**

133

134

```typescript

135

// Send run command to start test execution

136

await worker.postMessage('run', {

137

sessionId: 'abc123',

138

config: { baseUrl: 'https://example.com' }

139

});

140

141

// Send end session command

142

await worker.postMessage('endSession', {

143

sessionId: 'abc123'

144

});

145

146

// Command that requires session setup

147

await worker.postMessage('workerRequest', {

148

action: 'screenshot'

149

}, true);

150

```

151

152

## Event System

153

154

### Worker Events

155

156

WorkerInstance emits various events during its lifecycle.

157

158

```typescript { .api }

159

interface WorkerEvents {

160

'message': (payload: Workers.WorkerMessage & { cid: string }) => void;

161

'error': (error: Error & { cid: string }) => void;

162

'exit': (data: { cid: string; exitCode: number; specs: string[]; retries: number }) => void;

163

}

164

```

165

166

**Usage Examples:**

167

168

```typescript

169

// Listen for worker messages

170

worker.on('message', (payload) => {

171

console.log(`Worker ${payload.cid} sent:`, payload.name);

172

173

switch (payload.name) {

174

case 'ready':

175

console.log('Worker is ready to receive commands');

176

break;

177

case 'sessionStarted':

178

console.log('Browser session started:', payload.content.sessionId);

179

break;

180

case 'finishedCommand':

181

console.log('Command completed:', payload.content.command);

182

break;

183

}

184

});

185

186

// Listen for worker errors

187

worker.on('error', (error) => {

188

console.error(`Worker ${error.cid} error:`, error.message);

189

});

190

191

// Listen for worker exit

192

worker.on('exit', ({ cid, exitCode, specs, retries }) => {

193

console.log(`Worker ${cid} exited with code ${exitCode}`);

194

console.log(`Specs: ${specs.join(', ')}`);

195

console.log(`Retries remaining: ${retries}`);

196

});

197

```

198

199

### Message Types

200

201

Messages sent from worker processes to the main process.

202

203

```typescript { .api }

204

interface WorkerMessageTypes {

205

'ready': { name: 'ready' };

206

'finishedCommand': {

207

name: 'finishedCommand';

208

content: { command: string; result: any };

209

};

210

'sessionStarted': {

211

name: 'sessionStarted';

212

content: {

213

sessionId?: string;

214

capabilities?: WebdriverIO.Capabilities;

215

isMultiremote?: boolean;

216

instances?: Record<string, { sessionId: string }>;

217

};

218

};

219

'error': {

220

name: 'error';

221

content: { name: string; message: string; stack: string };

222

};

223

}

224

```

225

226

## Worker State Management

227

228

### State Properties

229

230

Track worker process state and lifecycle.

231

232

```typescript { .api }

233

interface WorkerState {

234

isBusy: boolean; // Worker is executing a command

235

isKilled: boolean; // Worker process has been terminated

236

isReady: Promise<boolean>; // Promise resolving when worker is ready

237

isSetup: Promise<boolean>; // Promise resolving when session is setup

238

sessionId?: string; // Browser session ID

239

instances?: Record<string, { sessionId: string }>; // Multiremote instances

240

isMultiremote?: boolean; // Whether worker handles multiremote

241

}

242

```

243

244

**Usage Examples:**

245

246

```typescript

247

// Check worker state

248

if (worker.isBusy) {

249

console.log('Worker is currently busy');

250

} else {

251

console.log('Worker is available');

252

}

253

254

// Wait for worker to be ready

255

await worker.isReady;

256

console.log('Worker is ready to receive commands');

257

258

// Wait for session setup (for commands that require it)

259

await worker.isSetup;

260

console.log('Browser session is established');

261

262

// Check session info

263

if (worker.sessionId) {

264

console.log(`Worker session ID: ${worker.sessionId}`);

265

}

266

267

if (worker.isMultiremote && worker.instances) {

268

console.log('Multiremote sessions:', Object.keys(worker.instances));

269

}

270

```

271

272

## Advanced Features

273

274

### REPL Integration

275

276

Workers support REPL debugging sessions for interactive test debugging.

277

278

```typescript

279

// Workers automatically handle REPL debug messages

280

// When a test calls browser.debug(), the worker will:

281

// 1. Queue the REPL session

282

// 2. Start interactive session

283

// 3. Handle debug commands

284

// 4. Resume test execution when complete

285

286

// No additional code needed - REPL integration is automatic

287

```

288

289

### Output Stream Management

290

291

Worker output is transformed and aggregated with capability ID prefixing.

292

293

```typescript

294

// Output streams are automatically managed

295

// All worker stdout/stderr is:

296

// 1. Prefixed with [cid] for identification

297

// 2. Filtered to remove debugger messages

298

// 3. Aggregated to main process streams

299

// 4. Optionally grouped by test spec

300

301

// Access aggregated output

302

const stdoutContent = worker.stdout.getContents();

303

const stderrContent = worker.stderr.getContents();

304

```

305

306

### Graceful Shutdown Handling

307

308

Workers handle graceful shutdown with proper cleanup.

309

310

```typescript

311

// Workers automatically handle:

312

// - SIGINT signals for graceful termination

313

// - Session cleanup before exit

314

// - Proper process termination

315

// - Xvfb cleanup when applicable

316

317

// Shutdown is handled automatically by LocalRunner.shutdown()

318

```

319

320

### Environment Variable Injection

321

322

Workers receive customized environment variables.

323

324

```typescript

325

// Workers automatically receive:

326

// - WDIO_WORKER_ID: Worker capability ID

327

// - NODE_ENV: Set to 'test' by default

328

// - WDIO_LOG_PATH: Path to worker log file (if outputDir configured)

329

// - Custom runnerEnv variables from config

330

// - Propagated NODE_OPTIONS from parent process

331

332

// Environment is set automatically during worker startup

333

```

334

335

## Error Handling

336

337

### Process Spawn Errors

338

339

```typescript

340

worker.on('error', (error) => {

341

if (error.code === 'ENOENT') {

342

console.error('Node.js not found for worker process');

343

} else if (error.code === 'EACCES') {

344

console.error('Permission denied starting worker process');

345

}

346

});

347

```

348

349

### Command Execution Errors

350

351

```typescript

352

// Commands sent to busy workers are logged and ignored

353

await worker.postMessage('run', args);

354

355

// Check if worker can accept commands

356

if (!worker.isBusy) {

357

await worker.postMessage('newCommand', args);

358

} else {

359

console.log(`Worker ${worker.cid} is busy, command skipped`);

360

}

361

```

362

363

### Session Management Errors

364

365

```typescript

366

worker.on('message', (payload) => {

367

if (payload.name === 'error') {

368

console.error('Worker session error:', payload.content);

369

// Handle session errors (network issues, browser crashes, etc.)

370

}

371

});

372

```