or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

capabilities.mdframeworks.mdindex.mdnetwork.mdoptions.mdreporters.mdservices.mdworkers.md

workers.mddocs/

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

```