0
# Workers and Messaging
1
2
Worker process management and inter-process communication for test execution in parallel environments, including message types and event handling.
3
4
## Capabilities
5
6
### Worker Process Management
7
8
Core worker interfaces for managing test execution processes.
9
10
```typescript { .api }
11
/**
12
* Worker job configuration
13
*/
14
interface Job {
15
/** Test capabilities */
16
caps: WebdriverIO.Capabilities;
17
/** Test specification files */
18
specs: string[];
19
/** Whether job has tests to run */
20
hasTests: boolean;
21
/** Base URL for tests */
22
baseUrl?: string;
23
/** Test runner configuration */
24
config?: TestrunnerOptions & { sessionId?: string };
25
/** Resolved capabilities */
26
capabilities?: WebdriverIO.Capabilities;
27
}
28
29
/**
30
* Worker message arguments (subset of Job)
31
*/
32
type WorkerMessageArgs = Omit<Job, 'caps' | 'specs' | 'hasTests'>;
33
34
/**
35
* Worker execution payload
36
*/
37
interface WorkerRunPayload {
38
/** Capability ID */
39
cid: string;
40
/** Configuration file path */
41
configFile: string;
42
/** Test capabilities */
43
caps: WebdriverIO.Capabilities;
44
/** Test specification files */
45
specs: string[];
46
/** Node.js execution arguments */
47
execArgv: string[];
48
/** Number of retry attempts */
49
retries: number;
50
}
51
52
/**
53
* Worker command structure
54
*/
55
interface WorkerCommand extends Omit<WorkerRunPayload, 'execArgv'> {
56
/** Command to execute */
57
command: string;
58
/** Command arguments */
59
args: any;
60
}
61
```
62
63
### Worker Instance Interface
64
65
Main worker interface extending Node.js EventEmitter.
66
67
```typescript { .api }
68
/**
69
* Worker instance interface
70
*/
71
interface Worker extends Omit<TestrunnerOptions, 'capabilities' | 'specs' | 'rootDir'>, EventEmitter {
72
/** Worker capabilities */
73
capabilities: WebdriverIO.Capabilities;
74
/** Worker configuration */
75
config: TestrunnerOptions;
76
/** Resolved capabilities */
77
caps: WebdriverIO.Capabilities;
78
/** Capability ID */
79
cid: string;
80
/** Whether worker is currently busy */
81
isBusy?: boolean;
82
/** Send message to worker */
83
postMessage: (command: string, args: WorkerMessageArgs) => void;
84
/** Test specification files */
85
specs: string[];
86
/** WebDriver session ID */
87
sessionId?: string;
88
/** Aggregated log messages */
89
logsAggregator: string[];
90
}
91
92
/**
93
* Worker pool type
94
*/
95
type WorkerPool = Record<string, Worker>;
96
```
97
98
### Message System
99
100
Comprehensive messaging system for worker communication.
101
102
```typescript { .api }
103
/**
104
* Message types for worker communication
105
*/
106
enum MESSAGE_TYPES {
107
// Browser runner messages
108
consoleMessage = 0,
109
commandRequestMessage,
110
commandResponseMessage,
111
hookTriggerMessage,
112
hookResultMessage,
113
expectRequestMessage,
114
expectResponseMessage,
115
expectMatchersRequest,
116
expectMatchersResponse,
117
coverageMap,
118
customCommand,
119
initiateBrowserStateRequest,
120
initiateBrowserStateResponse,
121
browserTestResult
122
}
123
124
/**
125
* Socket message value mapping
126
*/
127
type SocketMessageValue = {
128
[MESSAGE_TYPES.consoleMessage]: ConsoleEvent;
129
[MESSAGE_TYPES.commandRequestMessage]: CommandRequestEvent;
130
[MESSAGE_TYPES.commandResponseMessage]: CommandResponseEvent;
131
[MESSAGE_TYPES.hookTriggerMessage]: HookTriggerEvent;
132
[MESSAGE_TYPES.hookResultMessage]: HookResultEvent;
133
[MESSAGE_TYPES.expectRequestMessage]: ExpectRequestEvent;
134
[MESSAGE_TYPES.expectResponseMessage]: ExpectResponseEvent;
135
[MESSAGE_TYPES.expectMatchersRequest]: never;
136
[MESSAGE_TYPES.expectMatchersResponse]: ExpectMatchersResponse;
137
[MESSAGE_TYPES.coverageMap]: any;
138
[MESSAGE_TYPES.customCommand]: CustomCommandEvent;
139
[MESSAGE_TYPES.initiateBrowserStateRequest]: BrowserStateRequest;
140
[MESSAGE_TYPES.initiateBrowserStateResponse]: BrowserState;
141
[MESSAGE_TYPES.browserTestResult]: BrowserTestResults;
142
};
143
144
/**
145
* Generic socket message payload
146
*/
147
type SocketMessagePayload<T extends MESSAGE_TYPES> = T extends any
148
? { type: T; value: SocketMessageValue[T] }
149
: never;
150
151
/**
152
* Union of all socket message types
153
*/
154
type SocketMessage = SocketMessagePayload<MESSAGE_TYPES>;
155
```
156
157
### Message Event Types
158
159
Specific message event interfaces for different communication types.
160
161
```typescript { .api }
162
/**
163
* Console logging event
164
*/
165
interface ConsoleEvent {
166
/** Event name */
167
name: 'consoleEvent';
168
/** Log type */
169
type: 'log' | 'info' | 'warn' | 'debug' | 'error';
170
/** Log arguments */
171
args: unknown[];
172
/** Capability ID */
173
cid: string;
174
}
175
176
/**
177
* Browser test execution results
178
*/
179
interface BrowserTestResults {
180
/** Number of test failures */
181
failures: number;
182
/** Test events */
183
events: any[];
184
}
185
186
/**
187
* Custom command event
188
*/
189
interface CustomCommandEvent {
190
/** Command name */
191
commandName: string;
192
/** Capability ID */
193
cid: string;
194
}
195
196
/**
197
* Browser state request
198
*/
199
interface BrowserStateRequest {
200
/** Capability ID */
201
cid: string;
202
}
203
204
/**
205
* Browser state response
206
*/
207
interface BrowserState {
208
/** Available custom commands */
209
customCommands: string[];
210
}
211
212
/**
213
* Available matchers response
214
*/
215
interface ExpectMatchersResponse {
216
/** List of available matchers */
217
matchers: string[];
218
}
219
```
220
221
### Command and Hook Messages
222
223
Messages for WebDriver commands and hook execution.
224
225
```typescript { .api }
226
/**
227
* Base interface for messages with pending promise ID
228
*/
229
interface MessageWithPendingPromiseId {
230
/** Unique message identifier */
231
id: number;
232
}
233
234
/**
235
* Hook trigger event
236
*/
237
interface HookTriggerEvent extends MessageWithPendingPromiseId {
238
/** Capability ID */
239
cid: string;
240
/** Hook name */
241
name: string;
242
/** Hook arguments */
243
args: unknown[];
244
}
245
246
/**
247
* Hook execution result
248
*/
249
interface HookResultEvent extends MessageWithPendingPromiseId {
250
/** Error if hook failed */
251
error?: Error;
252
}
253
254
/**
255
* WebDriver command request
256
*/
257
interface CommandRequestEvent extends MessageWithPendingPromiseId {
258
/** Capability ID */
259
cid: string;
260
/** WebDriver command name */
261
commandName: string;
262
/** Command arguments */
263
args: unknown[];
264
/** Command scope (browser, element, etc.) */
265
scope?: string;
266
}
267
268
/**
269
* WebDriver command response
270
*/
271
interface CommandResponseEvent extends MessageWithPendingPromiseId {
272
/** Command result */
273
result?: unknown;
274
/** Error if command failed */
275
error?: Error;
276
}
277
```
278
279
### Assertion Messages
280
281
Messages for expectation/assertion handling.
282
283
```typescript { .api }
284
/**
285
* Assertion/expectation request
286
*/
287
interface ExpectRequestEvent extends MessageWithPendingPromiseId {
288
/** Capability ID */
289
cid: string;
290
/** Matcher name */
291
matcherName: string;
292
/** Matcher state (from expect library) */
293
scope: any;
294
/** Matcher arguments */
295
args: unknown[];
296
/** Element(s) being asserted */
297
element?: any | any[];
298
/** Additional context */
299
context?: unknown;
300
/** Error stack for inline snapshots */
301
errorStack?: string;
302
}
303
304
/**
305
* Assertion/expectation result
306
*/
307
interface ExpectResponseEvent extends MessageWithPendingPromiseId {
308
/** Whether assertion passed */
309
pass: boolean;
310
/** Assertion message */
311
message: string;
312
}
313
```
314
315
### Generic Worker Messages
316
317
Generic message structures for worker communication.
318
319
```typescript { .api }
320
/**
321
* Worker request message
322
*/
323
interface WorkerRequest {
324
/** Message type */
325
command: 'workerRequest';
326
/** Request arguments */
327
args: {
328
/** Request ID */
329
id: number;
330
/** Socket message */
331
message: SocketMessage;
332
};
333
}
334
335
/**
336
* Worker event message
337
*/
338
interface WorkerEvent {
339
/** Message type */
340
name: 'workerEvent';
341
/** Event origin */
342
origin: string;
343
/** Socket message arguments */
344
args: SocketMessage;
345
}
346
347
/**
348
* Generic worker message
349
*/
350
interface WorkerMessage {
351
/** Message name */
352
name: string;
353
/** Message content */
354
content: {
355
/** Session ID */
356
sessionId?: string;
357
/** Multiremote flag */
358
isMultiremote?: boolean;
359
/** Capabilities */
360
capabilities: WebdriverIO.Capabilities;
361
};
362
/** Message origin */
363
origin: string;
364
/** Message parameters */
365
params: Record<string, string>;
366
}
367
```
368
369
**Usage Examples:**
370
371
```typescript
372
import type { Workers } from "@wdio/types";
373
import { EventEmitter } from "node:events";
374
375
// Custom worker implementation
376
class CustomWorker extends EventEmitter implements Workers.Worker {
377
public capabilities: WebdriverIO.Capabilities;
378
public config: any;
379
public caps: WebdriverIO.Capabilities;
380
public cid: string;
381
public specs: string[];
382
public sessionId?: string;
383
public logsAggregator: string[] = [];
384
public isBusy: boolean = false;
385
386
constructor(
387
cid: string,
388
capabilities: WebdriverIO.Capabilities,
389
specs: string[],
390
config: any
391
) {
392
super();
393
this.cid = cid;
394
this.capabilities = capabilities;
395
this.caps = capabilities;
396
this.specs = specs;
397
this.config = config;
398
}
399
400
postMessage(command: string, args: Workers.WorkerMessageArgs) {
401
console.log(`Worker ${this.cid} received command: ${command}`);
402
403
// Handle different message types
404
switch (command) {
405
case 'run':
406
this.runTests(args);
407
break;
408
case 'stop':
409
this.stopTests();
410
break;
411
default:
412
console.warn(`Unknown command: ${command}`);
413
}
414
}
415
416
private runTests(args: Workers.WorkerMessageArgs) {
417
this.isBusy = true;
418
console.log(`Running tests: ${this.specs.join(', ')}`);
419
420
// Simulate test execution
421
setTimeout(() => {
422
this.emit('test:complete', {
423
cid: this.cid,
424
specs: this.specs,
425
passed: Math.random() > 0.5
426
});
427
this.isBusy = false;
428
}, 1000);
429
}
430
431
private stopTests() {
432
this.isBusy = false;
433
console.log(`Stopping tests for worker ${this.cid}`);
434
}
435
}
436
437
// Message handler for different message types
438
class MessageHandler {
439
handleMessage(message: Workers.SocketMessage) {
440
switch (message.type) {
441
case Workers.MESSAGE_TYPES.consoleMessage:
442
this.handleConsoleMessage(message.value);
443
break;
444
445
case Workers.MESSAGE_TYPES.commandRequestMessage:
446
this.handleCommandRequest(message.value);
447
break;
448
449
case Workers.MESSAGE_TYPES.commandResponseMessage:
450
this.handleCommandResponse(message.value);
451
break;
452
453
case Workers.MESSAGE_TYPES.expectRequestMessage:
454
this.handleExpectRequest(message.value);
455
break;
456
457
case Workers.MESSAGE_TYPES.expectResponseMessage:
458
this.handleExpectResponse(message.value);
459
break;
460
461
default:
462
console.log(`Unhandled message type: ${message.type}`);
463
}
464
}
465
466
private handleConsoleMessage(event: Workers.ConsoleEvent) {
467
console.log(`[${event.cid}] ${event.type.toUpperCase()}:`, ...event.args);
468
}
469
470
private handleCommandRequest(event: Workers.CommandRequestEvent) {
471
console.log(`Command request: ${event.commandName}`, event.args);
472
}
473
474
private handleCommandResponse(event: Workers.CommandResponseEvent) {
475
if (event.error) {
476
console.error(`Command failed:`, event.error);
477
} else {
478
console.log(`Command result:`, event.result);
479
}
480
}
481
482
private handleExpectRequest(event: Workers.ExpectRequestEvent) {
483
console.log(`Assertion: ${event.matcherName}`, event.args);
484
}
485
486
private handleExpectResponse(event: Workers.ExpectResponseEvent) {
487
const status = event.pass ? 'PASS' : 'FAIL';
488
console.log(`${status}: ${event.message}`);
489
}
490
}
491
492
// Worker pool management
493
class WorkerPool {
494
private workers: Workers.WorkerPool = {};
495
496
addWorker(worker: Workers.Worker) {
497
this.workers[worker.cid] = worker;
498
499
worker.on('test:complete', (result) => {
500
console.log(`Worker ${worker.cid} completed tests:`, result);
501
});
502
}
503
504
removeWorker(cid: string) {
505
const worker = this.workers[cid];
506
if (worker) {
507
worker.removeAllListeners();
508
delete this.workers[cid];
509
}
510
}
511
512
getWorker(cid: string): Workers.Worker | undefined {
513
return this.workers[cid];
514
}
515
516
getAllWorkers(): Workers.Worker[] {
517
return Object.values(this.workers);
518
}
519
520
getAvailableWorkers(): Workers.Worker[] {
521
return Object.values(this.workers).filter(worker => !worker.isBusy);
522
}
523
}
524
```