Skip to content

Commit 6c8e945

Browse files
[test optimization] Activate test suite span in beforeAll/afterAll hooks in jest (#7770)
1 parent 38ce86a commit 6c8e945

File tree

7 files changed

+101
-12
lines changed

7 files changed

+101
-12
lines changed

eslint.config.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -673,6 +673,7 @@ export default [
673673
languageOptions: {
674674
globals: {
675675
afterAll: 'readonly',
676+
beforeAll: 'readonly',
676677
expect: 'readonly',
677678
jest: 'readonly',
678679
},
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use strict'
2+
3+
const tracer = require('dd-trace')
4+
const assert = require('assert')
5+
6+
const sum = require('../test/sum')
7+
8+
describe('test optimization', () => {
9+
beforeAll(() => {
10+
const suiteSpan = tracer.scope().active()
11+
suiteSpan.setTag('suite.beforeAll', 'true')
12+
13+
tracer.trace('beforeAll.setup', () => {})
14+
})
15+
16+
afterAll(() => {
17+
const suiteSpan = tracer.scope().active()
18+
suiteSpan.setTag('suite.afterAll', 'true')
19+
20+
tracer.trace('afterAll.teardown', () => {})
21+
})
22+
23+
it('can report tests', () => {
24+
assert.strictEqual(sum(1, 2), 3)
25+
})
26+
})

integration-tests/jest/jest.spec.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6386,6 +6386,54 @@ describe(`jest@${JEST_VERSION} commonJS`, () => {
63866386
}).catch(done)
63876387
})
63886388
})
6389+
6390+
it('does detect custom tags on test suites from beforeAll and afterAll hooks', (done) => {
6391+
const eventsPromise = receiver
6392+
.gatherPayloadsMaxTimeout(({ url }) => url.endsWith('/api/v2/citestcycle'), payloads => {
6393+
const events = payloads.flatMap(({ payload }) => payload.events)
6394+
const testSuite = events.find(event => event.type === 'test_suite_end').content
6395+
6396+
assertObjectContains(testSuite, {
6397+
meta: {
6398+
'suite.beforeAll': 'true',
6399+
'suite.afterAll': 'true',
6400+
},
6401+
})
6402+
6403+
const suiteSpanId = testSuite.test_suite_id.toString()
6404+
const sessionTraceId = testSuite.test_session_id.toString()
6405+
6406+
// Spans created in beforeAll/afterAll appear as 'span' events and are children of the test suite span
6407+
const spans = events.filter(event => event.type === 'span').map(event => event.content)
6408+
const beforeAllSpan = spans.find(span => span.resource === 'beforeAll.setup')
6409+
const afterAllSpan = spans.find(span => span.resource === 'afterAll.teardown')
6410+
6411+
assert.ok(beforeAllSpan)
6412+
assert.strictEqual(beforeAllSpan.parent_id.toString(), suiteSpanId)
6413+
assert.strictEqual(beforeAllSpan.trace_id.toString(), sessionTraceId)
6414+
6415+
assert.ok(afterAllSpan)
6416+
assert.strictEqual(afterAllSpan.parent_id.toString(), suiteSpanId)
6417+
assert.strictEqual(afterAllSpan.trace_id.toString(), sessionTraceId)
6418+
})
6419+
6420+
childProcess = exec(
6421+
runTestsCommand,
6422+
{
6423+
cwd,
6424+
env: {
6425+
...getCiVisAgentlessConfig(receiver.port),
6426+
TESTS_TO_RUN: 'ci-visibility/test-suite-custom-tags',
6427+
},
6428+
}
6429+
)
6430+
6431+
childProcess.on('exit', () => {
6432+
eventsPromise.then(() => {
6433+
done()
6434+
}).catch(done)
6435+
})
6436+
})
63896437
})
63906438

63916439
context('impacted tests', () => {

packages/datadog-instrumentations/src/jest.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ const testSkippedCh = channel('ci:jest:test:skip')
4747
const testFinishCh = channel('ci:jest:test:finish')
4848
const testErrCh = channel('ci:jest:test:err')
4949
const testFnCh = channel('ci:jest:test:fn')
50+
const testSuiteHookFnCh = channel('ci:jest:test-suite:hook:fn')
5051

5152
const skippableSuitesCh = channel('ci:jest:test-suite:skippable')
5253
const libraryConfigurationCh = channel('ci:jest:library-configuration')
@@ -505,6 +506,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
505506
})
506507
}
507508

509+
if (event.name === 'hook_start' && (event.hook.type === 'beforeAll' || event.hook.type === 'afterAll')) {
510+
const ctx = { testSuiteAbsolutePath: this.testSuiteAbsolutePath }
511+
let hookFn = event.hook.fn
512+
if (originalHookFns.has(event.hook)) {
513+
hookFn = originalHookFns.get(event.hook)
514+
} else {
515+
originalHookFns.set(event.hook, hookFn)
516+
}
517+
event.hook.fn = shimmer.wrapFunction(hookFn, hookFn => function () {
518+
return testSuiteHookFnCh.runStores(ctx, () => hookFn.apply(this, arguments))
519+
})
520+
}
521+
508522
if (event.name === 'add_test') {
509523
if (event.failing) {
510524
return

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,12 @@ class JestPlugin extends CiPlugin {
386386
return ctx.currentStore
387387
})
388388

389+
this.addBind('ci:jest:test-suite:hook:fn', (ctx) => {
390+
const testSuiteSpan = this.testSuiteSpanPerTestSuiteAbsolutePath.get(ctx.testSuiteAbsolutePath)
391+
const store = storage('legacy').getStore()
392+
return { ...store, span: testSuiteSpan }
393+
})
394+
389395
this.addSub('ci:jest:test:finish', ({
390396
span,
391397
status,

packages/dd-trace/src/encode/agentless-ci-visibility.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -265,14 +265,7 @@ class AgentlessCiVisibilityEncoder extends AgentEncoder {
265265
}
266266
const startTime = Date.now()
267267

268-
const rawEvents = trace.map(formatSpan)
269-
270-
const testSessionEvents = rawEvents.filter(
271-
event => event.type === 'test_session_end' || event.type === 'test_suite_end' || event.type === 'test_module_end'
272-
)
273-
274-
const isTestSessionTrace = !!testSessionEvents.length
275-
const events = isTestSessionTrace ? testSessionEvents : rawEvents
268+
const events = trace.map(formatSpan)
276269

277270
this._eventCount += events.length
278271

packages/dd-trace/test/encode/agentless-ci-visibility.spec.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,8 +224,8 @@ describe('agentless-ci-visibility-encode', () => {
224224
})
225225
})
226226

227-
it('should not encode events other than sessions and suites if the trace is a test session', () => {
228-
const traceToFilter = [
227+
it('should encode all events including non-test spans alongside test sessions', () => {
228+
const traceWithMixedSpans = [
229229
{
230230
trace_id: id('1234abcd1234abcd'),
231231
span_id: id('1234abcd1234abcd'),
@@ -256,13 +256,14 @@ describe('agentless-ci-visibility-encode', () => {
256256
},
257257
]
258258

259-
encoder.encode(traceToFilter)
259+
encoder.encode(traceWithMixedSpans)
260260

261261
const buffer = encoder.makePayload()
262262
const decodedTrace = msgpack.decode(buffer, { useBigInt64: true })
263-
assert.strictEqual(decodedTrace.events.length, 1)
263+
assert.strictEqual(decodedTrace.events.length, 2)
264264
assert.strictEqual(decodedTrace.events[0].type, 'test_session_end')
265265
assert.deepStrictEqual(decodedTrace.events[0].content.type, 'test_session_end')
266+
assert.strictEqual(decodedTrace.events[1].type, 'span')
266267
})
267268

268269
it('does not crash if test_session_id is in meta but not test_module_id', () => {

0 commit comments

Comments
 (0)