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
```