Skip to content

Commit 30d0e82

Browse files
juan-fernandezsabrenner
authored andcommitted
fix(test-optimization): wait for WebDriverIO worker payloads (#9565)
1 parent 8cf328a commit 30d0e82

4 files changed

Lines changed: 153 additions & 14 deletions

File tree

packages/datadog-instrumentations/src/mocha/worker.js

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,20 +42,22 @@ let configurationRequestId = 0
4242
*
4343
* @param {object} message
4444
* @param {() => void} [onError]
45+
* @param {() => void} [onDone]
4546
* @returns {void}
4647
*/
47-
function sendWebdriverioMessage (message, onError) {
48+
function sendWebdriverioMessage (message, onError, onDone) {
4849
if (!process.send || !process.connected) {
4950
onError?.()
51+
onDone?.()
5052
return
5153
}
5254

5355
process.send(createWebdriverioWorkerMessage(message), (error) => {
54-
if (!error) {
55-
return
56+
if (error) {
57+
log.error('WebdriverIO Test Optimization IPC error', error)
58+
onError?.()
5659
}
57-
log.error('WebdriverIO Test Optimization IPC error', error)
58-
onError?.()
60+
onDone?.()
5961
})
6062
}
6163

@@ -266,10 +268,12 @@ function getWebdriverioSuiteResults (runner) {
266268
* Sends suite results to the WebdriverIO launcher.
267269
*
268270
* @param {object} runner
271+
* @param {() => void} [onDone]
269272
* @returns {void}
270273
*/
271-
function reportWebdriverioSuiteResults (runner) {
274+
function reportWebdriverioSuiteResults (runner, onDone) {
272275
if (!isWebdriverioWorker) {
276+
onDone?.()
273277
return
274278
}
275279

@@ -279,7 +283,32 @@ function reportWebdriverioSuiteResults (runner) {
279283
content: {
280284
results: getWebdriverioSuiteResults(runner),
281285
},
282-
})
286+
}, undefined, onDone)
287+
}
288+
289+
/**
290+
* Flushes worker payloads before reporting suite results and completing Mocha.
291+
*
292+
* @param {object} runner
293+
* @param {() => void} onDone
294+
* @returns {void}
295+
*/
296+
function finishWebdriverioWorker (runner, onDone) {
297+
try {
298+
workerFinishCh.publish({
299+
onDone: () => {
300+
try {
301+
reportWebdriverioSuiteResults(runner, onDone)
302+
} catch (error) {
303+
log.error('WebdriverIO Test Optimization worker completion error', error)
304+
onDone()
305+
}
306+
},
307+
})
308+
} catch (error) {
309+
log.error('WebdriverIO Test Optimization worker completion error', error)
310+
onDone()
311+
}
283312
}
284313

285314
function isFailedTestReplayEnabled () {
@@ -376,11 +405,22 @@ addHook({
376405
if (isFailedTestReplayEnabled()) {
377406
patchFailedTestReplayHookUp(Runner)
378407
}
379-
// Flush after the worker finishes its Mocha run, including grouped spec files.
380-
this.once('end', () => {
381-
workerFinishCh.publish()
382-
reportWebdriverioSuiteResults(this)
383-
})
408+
const onRunDone = args[0]
409+
if (isWebdriverioWorker && typeof onRunDone === 'function') {
410+
args[0] = (...onRunDoneArgs) => {
411+
finishWebdriverioWorker(this, () => onRunDone(...onRunDoneArgs))
412+
}
413+
} else {
414+
// Flush after the worker finishes its Mocha run, including grouped spec files.
415+
this.once('end', () => {
416+
try {
417+
workerFinishCh.publish()
418+
reportWebdriverioSuiteResults(this)
419+
} catch (error) {
420+
log.error('WebdriverIO Test Optimization worker completion error', error)
421+
}
422+
})
423+
}
384424
this.on('test', getOnTestHandler(false))
385425

386426
this.on('test end', getOnTestEndHandler(config))
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
'use strict'
2+
3+
const assert = require('node:assert/strict')
4+
const { EventEmitter } = require('node:events')
5+
6+
const { channel } = require('../../src/helpers/instrument')
7+
const instrumentations = require('../../src/helpers/instrumentations')
8+
const {
9+
SUITE_FINISH,
10+
WEBDRIVERIO_WORKER_ENV,
11+
WEBDRIVERIO_WORKER_EVENT,
12+
WEBDRIVERIO_WORKER_ORIGIN,
13+
} = require('../../src/mocha/webdriverio-protocol')
14+
15+
process.env[WEBDRIVERIO_WORKER_ENV] = 'true'
16+
const existingMochaHookCount = instrumentations.mocha?.length || 0
17+
require('../../src/mocha/worker')
18+
19+
const runnerHook = instrumentations.mocha
20+
.slice(existingMochaHookCount)
21+
.find(({ file }) => file === 'lib/runner.js')
22+
23+
assert.ok(runnerHook)
24+
25+
const workerFinishCh = channel('ci:mocha:worker:finish')
26+
let flushDone
27+
28+
function onWorkerFinish ({ onDone }) {
29+
flushDone = onDone
30+
}
31+
32+
workerFinishCh.subscribe(onWorkerFinish)
33+
34+
class FakeRunner extends EventEmitter {
35+
constructor () {
36+
super()
37+
this.failures = 0
38+
this.suite = {
39+
eachTest () {},
40+
}
41+
}
42+
43+
runTests () {}
44+
45+
run (onDone) {
46+
this.emit('end')
47+
onDone()
48+
}
49+
}
50+
51+
runnerHook.hook(FakeRunner)
52+
53+
let runDone = false
54+
let suiteMessageDone
55+
process.connected = true
56+
process.send = (message, onDone) => {
57+
assert.strictEqual(message.origin, WEBDRIVERIO_WORKER_ORIGIN)
58+
assert.strictEqual(message.name, WEBDRIVERIO_WORKER_EVENT)
59+
assert.strictEqual(message.args.name, SUITE_FINISH)
60+
suiteMessageDone = onDone
61+
}
62+
63+
new FakeRunner().run(() => {
64+
runDone = true
65+
})
66+
67+
assert.strictEqual(runDone, false)
68+
assert.strictEqual(typeof flushDone, 'function')
69+
assert.strictEqual(suiteMessageDone, undefined)
70+
71+
flushDone()
72+
assert.strictEqual(runDone, false)
73+
assert.strictEqual(typeof suiteMessageDone, 'function')
74+
75+
suiteMessageDone()
76+
assert.strictEqual(runDone, true)
77+
78+
runDone = false
79+
flushDone = undefined
80+
suiteMessageDone = undefined
81+
process.connected = false
82+
83+
new FakeRunner().run(() => {
84+
runDone = true
85+
})
86+
87+
assert.strictEqual(runDone, false)
88+
assert.strictEqual(typeof flushDone, 'function')
89+
90+
flushDone()
91+
assert.strictEqual(runDone, true)
92+
assert.strictEqual(suiteMessageDone, undefined)
93+
94+
workerFinishCh.unsubscribe(onWorkerFinish)

packages/datadog-instrumentations/test/webdriverio.spec.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const {
2424
} = require('../src/mocha/webdriverio-protocol')
2525

2626
const fixturePath = path.join(__dirname, 'fixtures', 'webdriverio-local-runner.mjs')
27+
const delayedWorkerFixturePath = path.join(__dirname, 'fixtures', 'webdriverio-delayed-worker.js')
2728
const disconnectedWorkerFixturePath = path.join(__dirname, 'fixtures', 'webdriverio-disconnected-worker.js')
2829
const regularMochaWorkerFixturePath = path.join(__dirname, 'fixtures', 'mocha-regular-worker.js')
2930
const fixtureModulePath = path.join(
@@ -150,6 +151,10 @@ describe('webdriverio instrumentation', () => {
150151
await execFileAsync(process.execPath, [disconnectedWorkerFixturePath])
151152
})
152153

154+
it('waits for worker payloads before completing Mocha', async () => {
155+
await execFileAsync(process.execPath, [delayedWorkerFixturePath])
156+
})
157+
153158
it('does not track WebdriverIO hook failures in regular Mocha workers', async () => {
154159
await execFileAsync(process.execPath, [regularMochaWorkerFixturePath])
155160
})

packages/datadog-plugin-mocha/src/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,8 @@ class MochaPlugin extends CiPlugin {
216216
return ctx.currentStore
217217
})
218218

219-
this.addSub('ci:mocha:worker:finish', () => {
220-
this.tracer._exporter.flush()
219+
this.addSub('ci:mocha:worker:finish', ({ onDone } = {}) => {
220+
this.tracer._exporter.flush(onDone)
221221
})
222222

223223
this.addSub('ci:mocha:test:finish', ({

0 commit comments

Comments
 (0)