dd-trace 4.28.0 → 4.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +98 -0
- package/README.md +8 -99
- package/ci/cypress/after-run.js +1 -0
- package/ci/cypress/after-spec.js +1 -0
- package/index.d.ts +1499 -1486
- package/package.json +3 -3
- package/packages/datadog-core/src/utils/src/get.js +11 -0
- package/packages/datadog-core/src/utils/src/has.js +14 -0
- package/packages/datadog-core/src/utils/src/set.js +16 -0
- package/packages/datadog-instrumentations/src/amqplib.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber.js +157 -42
- package/packages/datadog-instrumentations/src/grpc/server.js +3 -1
- package/packages/datadog-instrumentations/src/jest.js +80 -40
- package/packages/datadog-instrumentations/src/mocha.js +4 -1
- package/packages/datadog-instrumentations/src/mongodb-core.js +34 -3
- package/packages/datadog-instrumentations/src/playwright.js +78 -16
- package/packages/datadog-plugin-amqplib/src/consumer.js +8 -4
- package/packages/datadog-plugin-amqplib/src/producer.js +3 -4
- package/packages/datadog-plugin-aws-sdk/src/base.js +3 -2
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +60 -57
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +42 -22
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +64 -30
- package/packages/datadog-plugin-cucumber/src/index.js +25 -9
- package/packages/datadog-plugin-cypress/src/after-run.js +3 -0
- package/packages/datadog-plugin-cypress/src/after-spec.js +3 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +625 -0
- package/packages/datadog-plugin-cypress/src/plugin.js +6 -549
- package/packages/datadog-plugin-cypress/src/support.js +50 -3
- package/packages/datadog-plugin-graphql/src/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +10 -8
- package/packages/datadog-plugin-grpc/src/util.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +11 -2
- package/packages/datadog-plugin-kafkajs/src/consumer.js +4 -3
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -5
- package/packages/datadog-plugin-playwright/src/index.js +34 -3
- package/packages/datadog-plugin-rhea/src/consumer.js +8 -3
- package/packages/datadog-plugin-rhea/src/producer.js +3 -4
- package/packages/dd-trace/src/appsec/iast/index.js +10 -0
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +18 -5
- package/packages/dd-trace/src/appsec/recommended.json +67 -27
- package/packages/dd-trace/src/appsec/remote_config/index.js +1 -1
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -3
- package/packages/dd-trace/src/config.js +451 -459
- package/packages/dd-trace/src/data_streams_context.js +1 -1
- package/packages/dd-trace/src/datastreams/pathway.js +58 -1
- package/packages/dd-trace/src/datastreams/processor.js +3 -5
- package/packages/dd-trace/src/format.js +0 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +2 -2
- package/packages/dd-trace/src/opentracing/span.js +4 -4
- package/packages/dd-trace/src/plugins/util/test.js +2 -0
- package/packages/dd-trace/src/plugins/util/web.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/agent.js +77 -32
- package/packages/dd-trace/src/telemetry/index.js +22 -34
- package/packages/dd-trace/src/tracer.js +3 -3
- package/register.js +4 -0
- /package/packages/{utils → datadog-core/src/utils}/src/kebabcase.js +0 -0
- /package/packages/{utils → datadog-core/src/utils}/src/pick.js +0 -0
- /package/packages/{utils → datadog-core/src/utils}/src/uniq.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.30.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -69,11 +69,11 @@
|
|
|
69
69
|
"node": ">=16"
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
|
-
"@datadog/native-appsec": "7.
|
|
72
|
+
"@datadog/native-appsec": "7.1.0",
|
|
73
73
|
"@datadog/native-iast-rewriter": "2.2.3",
|
|
74
74
|
"@datadog/native-iast-taint-tracking": "1.7.0",
|
|
75
75
|
"@datadog/native-metrics": "^2.0.0",
|
|
76
|
-
"@datadog/pprof": "5.
|
|
76
|
+
"@datadog/pprof": "5.1.0",
|
|
77
77
|
"@datadog/sketches-js": "^2.1.0",
|
|
78
78
|
"@opentelemetry/api": "^1.0.0",
|
|
79
79
|
"@opentelemetry/core": "^1.14.0",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = (object, path) => {
|
|
4
|
+
const pathArr = path.split('.')
|
|
5
|
+
let property = object
|
|
6
|
+
for (const n of pathArr) {
|
|
7
|
+
if (property.hasOwnProperty(n)) {
|
|
8
|
+
property = property[n]
|
|
9
|
+
} else {
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return true
|
|
14
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
module.exports = (object, path, value) => {
|
|
4
|
+
const pathArr = path.split('.')
|
|
5
|
+
let property = object
|
|
6
|
+
let i
|
|
7
|
+
for (i = 0; i < pathArr.length - 1; i++) {
|
|
8
|
+
const n = pathArr[i]
|
|
9
|
+
if (property.hasOwnProperty(n)) {
|
|
10
|
+
property = property[n]
|
|
11
|
+
} else {
|
|
12
|
+
property[n] = property = {}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
property[pathArr[i]] = value
|
|
16
|
+
}
|
|
@@ -5,7 +5,7 @@ const {
|
|
|
5
5
|
addHook,
|
|
6
6
|
AsyncResource
|
|
7
7
|
} = require('./helpers/instrument')
|
|
8
|
-
const kebabCase = require('../../utils/src/kebabcase')
|
|
8
|
+
const kebabCase = require('../../datadog-core/src/utils/src/kebabcase')
|
|
9
9
|
const shimmer = require('../../datadog-shimmer')
|
|
10
10
|
|
|
11
11
|
const startCh = channel('apm:amqplib:command:start')
|
|
@@ -17,6 +17,7 @@ const testSuiteFinishCh = channel('ci:cucumber:test-suite:finish')
|
|
|
17
17
|
const testSuiteCodeCoverageCh = channel('ci:cucumber:test-suite:code-coverage')
|
|
18
18
|
|
|
19
19
|
const libraryConfigurationCh = channel('ci:cucumber:library-configuration')
|
|
20
|
+
const knownTestsCh = channel('ci:cucumber:known-tests')
|
|
20
21
|
const skippableSuitesCh = channel('ci:cucumber:test-suite:skippable')
|
|
21
22
|
const sessionStartCh = channel('ci:cucumber:session:start')
|
|
22
23
|
const sessionFinishCh = channel('ci:cucumber:session:finish')
|
|
@@ -41,12 +42,18 @@ const originalCoverageMap = createCoverageMap()
|
|
|
41
42
|
// TODO: remove in a later major version
|
|
42
43
|
const patched = new WeakSet()
|
|
43
44
|
|
|
45
|
+
const lastStatusByPickleId = new Map()
|
|
46
|
+
const numRetriesByPickleId = new Map()
|
|
47
|
+
|
|
44
48
|
let pickleByFile = {}
|
|
45
49
|
const pickleResultByFile = {}
|
|
46
50
|
let skippableSuites = []
|
|
47
51
|
let itrCorrelationId = ''
|
|
48
52
|
let isForcedToRun = false
|
|
49
53
|
let isUnskippable = false
|
|
54
|
+
let isEarlyFlakeDetectionEnabled = false
|
|
55
|
+
let earlyFlakeDetectionNumRetries = 0
|
|
56
|
+
let knownTests = []
|
|
50
57
|
|
|
51
58
|
function getSuiteStatusFromTestStatuses (testStatuses) {
|
|
52
59
|
if (testStatuses.some(status => status === 'fail')) {
|
|
@@ -84,6 +91,21 @@ function getStatusFromResultLatest (result) {
|
|
|
84
91
|
return { status: 'fail', errorMessage: result.message }
|
|
85
92
|
}
|
|
86
93
|
|
|
94
|
+
function isNewTest (testSuite, testName) {
|
|
95
|
+
const testsForSuite = knownTests.cucumber?.[testSuite] || []
|
|
96
|
+
return !testsForSuite.includes(testName)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getTestStatusFromRetries (testStatuses) {
|
|
100
|
+
if (testStatuses.every(status => status === 'fail')) {
|
|
101
|
+
return 'fail'
|
|
102
|
+
}
|
|
103
|
+
if (testStatuses.some(status => status === 'pass')) {
|
|
104
|
+
return 'pass'
|
|
105
|
+
}
|
|
106
|
+
return 'pass'
|
|
107
|
+
}
|
|
108
|
+
|
|
87
109
|
function wrapRun (pl, isLatestVersion) {
|
|
88
110
|
if (patched.has(pl)) return
|
|
89
111
|
|
|
@@ -98,18 +120,7 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
98
120
|
return asyncResource.runInAsyncScope(() => {
|
|
99
121
|
const testFileAbsolutePath = this.pickle.uri
|
|
100
122
|
|
|
101
|
-
|
|
102
|
-
isUnskippable = isMarkedAsUnskippable(this.pickle)
|
|
103
|
-
const testSuitePath = getTestSuitePath(testFileAbsolutePath, process.cwd())
|
|
104
|
-
isForcedToRun = isUnskippable && skippableSuites.includes(testSuitePath)
|
|
105
|
-
|
|
106
|
-
testSuiteStartCh.publish({ testSuitePath, isUnskippable, isForcedToRun, itrCorrelationId })
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const testSourceLine = this.gherkinDocument &&
|
|
110
|
-
this.gherkinDocument.feature &&
|
|
111
|
-
this.gherkinDocument.feature.location &&
|
|
112
|
-
this.gherkinDocument.feature.location.line
|
|
123
|
+
const testSourceLine = this.gherkinDocument?.feature?.location?.line
|
|
113
124
|
|
|
114
125
|
testStartCh.publish({
|
|
115
126
|
testName: this.pickle.name,
|
|
@@ -123,30 +134,20 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
123
134
|
const { status, skipReason, errorMessage } = isLatestVersion
|
|
124
135
|
? getStatusFromResultLatest(result) : getStatusFromResult(result)
|
|
125
136
|
|
|
126
|
-
if (
|
|
127
|
-
|
|
137
|
+
if (lastStatusByPickleId.has(this.pickle.id)) {
|
|
138
|
+
lastStatusByPickleId.get(this.pickle.id).push(status)
|
|
128
139
|
} else {
|
|
129
|
-
|
|
140
|
+
lastStatusByPickleId.set(this.pickle.id, [status])
|
|
130
141
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
if (
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
testSuiteCodeCoverageCh.publish({
|
|
139
|
-
coverageFiles,
|
|
140
|
-
suiteFile: testFileAbsolutePath
|
|
141
|
-
})
|
|
142
|
-
// We need to reset coverage to get a code coverage per suite
|
|
143
|
-
// Before that, we preserve the original coverage
|
|
144
|
-
mergeCoverage(global.__coverage__, originalCoverageMap)
|
|
145
|
-
resetCoverage(global.__coverage__)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
testSuiteFinishCh.publish(testSuiteStatus)
|
|
142
|
+
let isNew = false
|
|
143
|
+
let isEfdRetry = false
|
|
144
|
+
if (isEarlyFlakeDetectionEnabled && status !== 'skip') {
|
|
145
|
+
const numRetries = numRetriesByPickleId.get(this.pickle.id)
|
|
146
|
+
|
|
147
|
+
isNew = numRetries !== undefined
|
|
148
|
+
isEfdRetry = numRetries > 0
|
|
149
149
|
}
|
|
150
|
+
testFinishCh.publish({ status, skipReason, errorMessage, isNew, isEfdRetry })
|
|
150
151
|
})
|
|
151
152
|
return promise
|
|
152
153
|
} catch (err) {
|
|
@@ -258,12 +259,11 @@ function getPickleByFile (runtime) {
|
|
|
258
259
|
}, {})
|
|
259
260
|
}
|
|
260
261
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
shimmer.wrap(runtimePackage.default.prototype, 'start', start => async function () {
|
|
262
|
+
function getWrappedStart (start, frameworkVersion) {
|
|
263
|
+
return async function () {
|
|
264
|
+
if (!libraryConfigurationCh.hasSubscribers) {
|
|
265
|
+
return start.apply(this, arguments)
|
|
266
|
+
}
|
|
267
267
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
268
268
|
let onDone
|
|
269
269
|
|
|
@@ -275,7 +275,23 @@ addHook({
|
|
|
275
275
|
libraryConfigurationCh.publish({ onDone })
|
|
276
276
|
})
|
|
277
277
|
|
|
278
|
-
await configPromise
|
|
278
|
+
const configurationResponse = await configPromise
|
|
279
|
+
|
|
280
|
+
isEarlyFlakeDetectionEnabled = configurationResponse.libraryConfig?.isEarlyFlakeDetectionEnabled
|
|
281
|
+
earlyFlakeDetectionNumRetries = configurationResponse.libraryConfig?.earlyFlakeDetectionNumRetries
|
|
282
|
+
|
|
283
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
284
|
+
const knownTestsPromise = new Promise(resolve => {
|
|
285
|
+
onDone = resolve
|
|
286
|
+
})
|
|
287
|
+
asyncResource.runInAsyncScope(() => {
|
|
288
|
+
knownTestsCh.publish({ onDone })
|
|
289
|
+
})
|
|
290
|
+
const knownTestsResponse = await knownTestsPromise
|
|
291
|
+
if (!knownTestsResponse.err) {
|
|
292
|
+
knownTests = knownTestsResponse.knownTests
|
|
293
|
+
}
|
|
294
|
+
}
|
|
279
295
|
|
|
280
296
|
const skippableSuitesPromise = new Promise(resolve => {
|
|
281
297
|
onDone = resolve
|
|
@@ -342,11 +358,110 @@ addHook({
|
|
|
342
358
|
testCodeCoverageLinesTotal,
|
|
343
359
|
numSkippedSuites: skippedSuites.length,
|
|
344
360
|
hasUnskippableSuites: isUnskippable,
|
|
345
|
-
hasForcedToRunSuites: isForcedToRun
|
|
361
|
+
hasForcedToRunSuites: isForcedToRun,
|
|
362
|
+
isEarlyFlakeDetectionEnabled
|
|
346
363
|
})
|
|
347
364
|
})
|
|
348
365
|
return success
|
|
349
|
-
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function getWrappedRunTest (runTestFunction) {
|
|
370
|
+
return async function (pickleId) {
|
|
371
|
+
const test = this.eventDataCollector.getPickle(pickleId)
|
|
372
|
+
|
|
373
|
+
const testFileAbsolutePath = test.uri
|
|
374
|
+
const testSuitePath = getTestSuitePath(testFileAbsolutePath, process.cwd())
|
|
375
|
+
|
|
376
|
+
if (!pickleResultByFile[testFileAbsolutePath]) { // first test in suite
|
|
377
|
+
isUnskippable = isMarkedAsUnskippable(test)
|
|
378
|
+
isForcedToRun = isUnskippable && skippableSuites.includes(testSuitePath)
|
|
379
|
+
|
|
380
|
+
testSuiteStartCh.publish({ testSuitePath, isUnskippable, isForcedToRun, itrCorrelationId })
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
let isNew = false
|
|
384
|
+
|
|
385
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
386
|
+
isNew = isNewTest(testSuitePath, test.name)
|
|
387
|
+
if (isNew) {
|
|
388
|
+
numRetriesByPickleId.set(pickleId, 0)
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
const runTestCaseResult = await runTestFunction.apply(this, arguments)
|
|
392
|
+
|
|
393
|
+
const testStatuses = lastStatusByPickleId.get(pickleId)
|
|
394
|
+
const lastTestStatus = testStatuses[testStatuses.length - 1]
|
|
395
|
+
// If it's a new test and it hasn't been skipped, we run it again
|
|
396
|
+
if (isEarlyFlakeDetectionEnabled && lastTestStatus !== 'skip' && isNew) {
|
|
397
|
+
for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
|
|
398
|
+
numRetriesByPickleId.set(pickleId, retryIndex + 1)
|
|
399
|
+
await runTestFunction.apply(this, arguments)
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
let testStatus = lastTestStatus
|
|
403
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
404
|
+
/**
|
|
405
|
+
* If Early Flake Detection (EFD) is enabled the logic is as follows:
|
|
406
|
+
* - If all attempts for a test are failing, the test has failed and we will let the test process fail.
|
|
407
|
+
* - If just a single attempt passes, we will prevent the test process from failing.
|
|
408
|
+
* The rationale behind is the following: you may still be able to block your CI pipeline by gating
|
|
409
|
+
* on flakiness (the test will be considered flaky), but you may choose to unblock the pipeline too.
|
|
410
|
+
*/
|
|
411
|
+
testStatus = getTestStatusFromRetries(testStatuses)
|
|
412
|
+
if (testStatus === 'pass') {
|
|
413
|
+
this.success = true
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (!pickleResultByFile[testFileAbsolutePath]) {
|
|
418
|
+
pickleResultByFile[testFileAbsolutePath] = [testStatus]
|
|
419
|
+
} else {
|
|
420
|
+
pickleResultByFile[testFileAbsolutePath].push(testStatus)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// last test in suite
|
|
424
|
+
if (pickleResultByFile[testFileAbsolutePath].length === pickleByFile[testFileAbsolutePath].length) {
|
|
425
|
+
const testSuiteStatus = getSuiteStatusFromTestStatuses(pickleResultByFile[testFileAbsolutePath])
|
|
426
|
+
if (global.__coverage__) {
|
|
427
|
+
const coverageFiles = getCoveredFilenamesFromCoverage(global.__coverage__)
|
|
428
|
+
|
|
429
|
+
testSuiteCodeCoverageCh.publish({
|
|
430
|
+
coverageFiles,
|
|
431
|
+
suiteFile: testFileAbsolutePath
|
|
432
|
+
})
|
|
433
|
+
// We need to reset coverage to get a code coverage per suite
|
|
434
|
+
// Before that, we preserve the original coverage
|
|
435
|
+
mergeCoverage(global.__coverage__, originalCoverageMap)
|
|
436
|
+
resetCoverage(global.__coverage__)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
testSuiteFinishCh.publish(testSuiteStatus)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
return runTestCaseResult
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// From 7.3.0 onwards, runPickle becomes runTestCase
|
|
447
|
+
addHook({
|
|
448
|
+
name: '@cucumber/cucumber',
|
|
449
|
+
versions: ['>=7.3.0'],
|
|
450
|
+
file: 'lib/runtime/index.js'
|
|
451
|
+
}, (runtimePackage, frameworkVersion) => {
|
|
452
|
+
shimmer.wrap(runtimePackage.default.prototype, 'runTestCase', runTestCase => getWrappedRunTest(runTestCase))
|
|
453
|
+
shimmer.wrap(runtimePackage.default.prototype, 'start', start => getWrappedStart(start, frameworkVersion))
|
|
454
|
+
|
|
455
|
+
return runtimePackage
|
|
456
|
+
})
|
|
457
|
+
|
|
458
|
+
addHook({
|
|
459
|
+
name: '@cucumber/cucumber',
|
|
460
|
+
versions: ['>=7.0.0 <7.3.0'],
|
|
461
|
+
file: 'lib/runtime/index.js'
|
|
462
|
+
}, (runtimePackage, frameworkVersion) => {
|
|
463
|
+
shimmer.wrap(runtimePackage.default.prototype, 'runPickle', runPickle => getWrappedRunTest(runPickle))
|
|
464
|
+
shimmer.wrap(runtimePackage.default.prototype, 'start', start => getWrappedStart(start, frameworkVersion))
|
|
350
465
|
|
|
351
466
|
return runtimePackage
|
|
352
467
|
})
|
|
@@ -92,7 +92,9 @@ function createWrapEmit (call, ctx, onCancel) {
|
|
|
92
92
|
finishChannel.publish(ctx)
|
|
93
93
|
call.removeListener('cancelled', onCancel)
|
|
94
94
|
break
|
|
95
|
-
|
|
95
|
+
// Streams are always cancelled before `finish` since 1.10.0 so we have
|
|
96
|
+
// to use `prefinish` instead to avoid cancellation false positives.
|
|
97
|
+
case 'prefinish':
|
|
96
98
|
if (call.status) {
|
|
97
99
|
updateChannel.publish(call.status)
|
|
98
100
|
}
|
|
@@ -58,6 +58,7 @@ let hasUnskippableSuites = false
|
|
|
58
58
|
let hasForcedToRunSuites = false
|
|
59
59
|
let isEarlyFlakeDetectionEnabled = false
|
|
60
60
|
let earlyFlakeDetectionNumRetries = 0
|
|
61
|
+
let hasFilteredSkippableSuites = false
|
|
61
62
|
|
|
62
63
|
const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
63
64
|
|
|
@@ -114,6 +115,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
114
115
|
this.nameToParams = {}
|
|
115
116
|
this.global._ddtrace = global._ddtrace
|
|
116
117
|
|
|
118
|
+
this.displayName = config.projectConfig?.displayName?.name
|
|
117
119
|
this.testEnvironmentOptions = getTestEnvironmentOptions(config)
|
|
118
120
|
|
|
119
121
|
const repositoryRoot = this.testEnvironmentOptions._ddRepositoryRoot
|
|
@@ -138,14 +140,16 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
138
140
|
// Function that receives a list of known tests for a test service and
|
|
139
141
|
// returns the ones that belong to the current suite
|
|
140
142
|
getKnownTestsForSuite (knownTests) {
|
|
143
|
+
if (this.knownTestsForThisSuite) {
|
|
144
|
+
return this.knownTestsForThisSuite
|
|
145
|
+
}
|
|
141
146
|
let knownTestsForSuite = knownTests
|
|
142
|
-
// If jest
|
|
143
|
-
|
|
147
|
+
// If jest is using workers, known tests are serialized to json.
|
|
148
|
+
// If jest runs in band, they are not.
|
|
149
|
+
if (typeof knownTestsForSuite === 'string') {
|
|
144
150
|
knownTestsForSuite = JSON.parse(knownTestsForSuite)
|
|
145
151
|
}
|
|
146
|
-
return knownTestsForSuite
|
|
147
|
-
.filter(test => test.includes(this.testSuite))
|
|
148
|
-
.map(test => test.replace(`jest.${this.testSuite}.`, '').trim())
|
|
152
|
+
return knownTestsForSuite.jest?.[this.testSuite] || []
|
|
149
153
|
}
|
|
150
154
|
|
|
151
155
|
// Add the `add_test` event we don't have the test object yet, so
|
|
@@ -200,6 +204,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
200
204
|
suite: this.testSuite,
|
|
201
205
|
testSourceFile: this.testSourceFile,
|
|
202
206
|
runner: 'jest-circus',
|
|
207
|
+
displayName: this.displayName,
|
|
203
208
|
testParameters,
|
|
204
209
|
frameworkVersion: jestVersion,
|
|
205
210
|
isNew: isNewTest,
|
|
@@ -251,6 +256,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
251
256
|
suite: this.testSuite,
|
|
252
257
|
testSourceFile: this.testSourceFile,
|
|
253
258
|
runner: 'jest-circus',
|
|
259
|
+
displayName: this.displayName,
|
|
254
260
|
frameworkVersion: jestVersion,
|
|
255
261
|
testStartLine: getTestLineStart(event.test.asyncError, this.testSuite)
|
|
256
262
|
})
|
|
@@ -270,6 +276,23 @@ function getTestEnvironment (pkg, jestVersion) {
|
|
|
270
276
|
return getWrappedEnvironment(pkg, jestVersion)
|
|
271
277
|
}
|
|
272
278
|
|
|
279
|
+
function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
|
|
280
|
+
const jestSuitesToRun = getJestSuitesToRun(skippableSuites, originalTests, rootDir || process.cwd())
|
|
281
|
+
hasFilteredSkippableSuites = true
|
|
282
|
+
log.debug(
|
|
283
|
+
() => `${jestSuitesToRun.suitesToRun.length} out of ${originalTests.length} suites are going to run.`
|
|
284
|
+
)
|
|
285
|
+
hasUnskippableSuites = jestSuitesToRun.hasUnskippableSuites
|
|
286
|
+
hasForcedToRunSuites = jestSuitesToRun.hasForcedToRunSuites
|
|
287
|
+
|
|
288
|
+
isSuitesSkipped = jestSuitesToRun.suitesToRun.length !== originalTests.length
|
|
289
|
+
numSkippedSuites = jestSuitesToRun.skippedSuites.length
|
|
290
|
+
|
|
291
|
+
itrSkippedSuitesCh.publish({ skippedSuites: jestSuitesToRun.skippedSuites, frameworkVersion })
|
|
292
|
+
skippableSuites = []
|
|
293
|
+
return jestSuitesToRun.suitesToRun
|
|
294
|
+
}
|
|
295
|
+
|
|
273
296
|
addHook({
|
|
274
297
|
name: 'jest-environment-node',
|
|
275
298
|
versions: ['>=24.8.0']
|
|
@@ -280,6 +303,51 @@ addHook({
|
|
|
280
303
|
versions: ['>=24.8.0']
|
|
281
304
|
}, getTestEnvironment)
|
|
282
305
|
|
|
306
|
+
function getWrappedScheduleTests (scheduleTests, frameworkVersion) {
|
|
307
|
+
return async function (tests) {
|
|
308
|
+
if (!isSuitesSkippingEnabled || hasFilteredSkippableSuites) {
|
|
309
|
+
return scheduleTests.apply(this, arguments)
|
|
310
|
+
}
|
|
311
|
+
const [test] = tests
|
|
312
|
+
const rootDir = test?.context?.config?.rootDir
|
|
313
|
+
|
|
314
|
+
arguments[0] = applySuiteSkipping(tests, rootDir, frameworkVersion)
|
|
315
|
+
|
|
316
|
+
return scheduleTests.apply(this, arguments)
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
addHook({
|
|
321
|
+
name: '@jest/core',
|
|
322
|
+
file: 'build/TestScheduler.js',
|
|
323
|
+
versions: ['>=27.0.0']
|
|
324
|
+
}, (testSchedulerPackage, frameworkVersion) => {
|
|
325
|
+
const oldCreateTestScheduler = testSchedulerPackage.createTestScheduler
|
|
326
|
+
const newCreateTestScheduler = async function () {
|
|
327
|
+
if (!isSuitesSkippingEnabled || hasFilteredSkippableSuites) {
|
|
328
|
+
return oldCreateTestScheduler.apply(this, arguments)
|
|
329
|
+
}
|
|
330
|
+
// If suite skipping is enabled and has not filtered skippable suites yet, we'll attempt to do it
|
|
331
|
+
const scheduler = await oldCreateTestScheduler.apply(this, arguments)
|
|
332
|
+
shimmer.wrap(scheduler, 'scheduleTests', scheduleTests => getWrappedScheduleTests(scheduleTests, frameworkVersion))
|
|
333
|
+
return scheduler
|
|
334
|
+
}
|
|
335
|
+
testSchedulerPackage.createTestScheduler = newCreateTestScheduler
|
|
336
|
+
return testSchedulerPackage
|
|
337
|
+
})
|
|
338
|
+
|
|
339
|
+
addHook({
|
|
340
|
+
name: '@jest/core',
|
|
341
|
+
file: 'build/TestScheduler.js',
|
|
342
|
+
versions: ['>=24.8.0 <27.0.0']
|
|
343
|
+
}, (testSchedulerPackage, frameworkVersion) => {
|
|
344
|
+
shimmer.wrap(
|
|
345
|
+
testSchedulerPackage.default.prototype,
|
|
346
|
+
'scheduleTests', scheduleTests => getWrappedScheduleTests(scheduleTests, frameworkVersion)
|
|
347
|
+
)
|
|
348
|
+
return testSchedulerPackage
|
|
349
|
+
})
|
|
350
|
+
|
|
283
351
|
addHook({
|
|
284
352
|
name: '@jest/test-sequencer',
|
|
285
353
|
versions: ['>=24.8.0']
|
|
@@ -287,29 +355,13 @@ addHook({
|
|
|
287
355
|
shimmer.wrap(sequencerPackage.default.prototype, 'shard', shard => function () {
|
|
288
356
|
const shardedTests = shard.apply(this, arguments)
|
|
289
357
|
|
|
290
|
-
if (!shardedTests.length) {
|
|
358
|
+
if (!shardedTests.length || !isSuitesSkippingEnabled || !skippableSuites.length) {
|
|
291
359
|
return shardedTests
|
|
292
360
|
}
|
|
293
|
-
// TODO: could we get the rootDir from each test?
|
|
294
361
|
const [test] = shardedTests
|
|
295
362
|
const rootDir = test?.context?.config?.rootDir
|
|
296
363
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
log.debug(
|
|
300
|
-
() => `${jestSuitesToRun.suitesToRun.length} out of ${shardedTests.length} suites are going to run.`
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
hasUnskippableSuites = jestSuitesToRun.hasUnskippableSuites
|
|
304
|
-
hasForcedToRunSuites = jestSuitesToRun.hasForcedToRunSuites
|
|
305
|
-
|
|
306
|
-
isSuitesSkipped = jestSuitesToRun.suitesToRun.length !== shardedTests.length
|
|
307
|
-
numSkippedSuites = jestSuitesToRun.skippedSuites.length
|
|
308
|
-
|
|
309
|
-
itrSkippedSuitesCh.publish({ skippedSuites: jestSuitesToRun.skippedSuites, frameworkVersion })
|
|
310
|
-
|
|
311
|
-
skippableSuites = []
|
|
312
|
-
return jestSuitesToRun.suitesToRun
|
|
364
|
+
return applySuiteSkipping(shardedTests, rootDir, frameworkVersion)
|
|
313
365
|
})
|
|
314
366
|
return sequencerPackage
|
|
315
367
|
})
|
|
@@ -512,6 +564,7 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
|
|
|
512
564
|
testSuiteStartCh.publish({
|
|
513
565
|
testSuite: environment.testSuite,
|
|
514
566
|
testEnvironmentOptions: environment.testEnvironmentOptions,
|
|
567
|
+
displayName: environment.displayName,
|
|
515
568
|
frameworkVersion: jestVersion
|
|
516
569
|
})
|
|
517
570
|
return adapter.apply(this, arguments).then(suiteResults => {
|
|
@@ -660,13 +713,13 @@ addHook({
|
|
|
660
713
|
const SearchSource = searchSourcePackage.default ? searchSourcePackage.default : searchSourcePackage
|
|
661
714
|
|
|
662
715
|
shimmer.wrap(SearchSource.prototype, 'getTestPaths', getTestPaths => async function () {
|
|
663
|
-
if (!skippableSuites.length) {
|
|
716
|
+
if (!isSuitesSkippingEnabled || !skippableSuites.length) {
|
|
664
717
|
return getTestPaths.apply(this, arguments)
|
|
665
718
|
}
|
|
666
719
|
|
|
667
720
|
const [{ rootDir, shard }] = arguments
|
|
668
721
|
|
|
669
|
-
if (shard
|
|
722
|
+
if (shard?.shardCount > 1) {
|
|
670
723
|
// If the user is using jest sharding, we want to apply the filtering of tests in the shard process.
|
|
671
724
|
// The reason for this is the following:
|
|
672
725
|
// The tests for different shards are likely being run in different CI jobs so
|
|
@@ -680,21 +733,8 @@ addHook({
|
|
|
680
733
|
const testPaths = await getTestPaths.apply(this, arguments)
|
|
681
734
|
const { tests } = testPaths
|
|
682
735
|
|
|
683
|
-
const
|
|
684
|
-
|
|
685
|
-
log.debug(() => `${jestSuitesToRun.suitesToRun.length} out of ${tests.length} suites are going to run.`)
|
|
686
|
-
|
|
687
|
-
hasUnskippableSuites = jestSuitesToRun.hasUnskippableSuites
|
|
688
|
-
hasForcedToRunSuites = jestSuitesToRun.hasForcedToRunSuites
|
|
689
|
-
|
|
690
|
-
isSuitesSkipped = jestSuitesToRun.suitesToRun.length !== tests.length
|
|
691
|
-
numSkippedSuites = jestSuitesToRun.skippedSuites.length
|
|
692
|
-
|
|
693
|
-
itrSkippedSuitesCh.publish({ skippedSuites: jestSuitesToRun.skippedSuites, frameworkVersion })
|
|
694
|
-
|
|
695
|
-
skippableSuites = []
|
|
696
|
-
|
|
697
|
-
return { ...testPaths, tests: jestSuitesToRun.suitesToRun }
|
|
736
|
+
const suitesToRun = applySuiteSkipping(tests, rootDir, frameworkVersion)
|
|
737
|
+
return { ...testPaths, tests: suitesToRun }
|
|
698
738
|
})
|
|
699
739
|
|
|
700
740
|
return searchSourcePackage
|
|
@@ -106,7 +106,10 @@ function getTestFullName (test) {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function isNewTest (test) {
|
|
109
|
-
|
|
109
|
+
const testSuite = getTestSuitePath(test.file, process.cwd())
|
|
110
|
+
const testName = removeEfdStringFromTestName(test.fullTitle())
|
|
111
|
+
const testsForSuite = knownTests.mocha?.[testSuite] || []
|
|
112
|
+
return !testsForSuite.includes(testName)
|
|
110
113
|
}
|
|
111
114
|
|
|
112
115
|
function retryTest (test) {
|
|
@@ -32,12 +32,18 @@ addHook({ name: 'mongodb', versions: ['>=4 <4.6.0'], file: 'lib/cmap/connection.
|
|
|
32
32
|
return Connection
|
|
33
33
|
})
|
|
34
34
|
|
|
35
|
-
addHook({ name: 'mongodb', versions: ['>=4.6.0'], file: 'lib/cmap/connection.js' }, Connection => {
|
|
35
|
+
addHook({ name: 'mongodb', versions: ['>=4.6.0 <6.4.0'], file: 'lib/cmap/connection.js' }, Connection => {
|
|
36
36
|
const proto = Connection.Connection.prototype
|
|
37
37
|
shimmer.wrap(proto, 'command', command => wrapConnectionCommand(command, 'command'))
|
|
38
38
|
return Connection
|
|
39
39
|
})
|
|
40
40
|
|
|
41
|
+
addHook({ name: 'mongodb', versions: ['>=6.4.0'], file: 'lib/cmap/connection.js' }, Connection => {
|
|
42
|
+
const proto = Connection.Connection.prototype
|
|
43
|
+
shimmer.wrap(proto, 'command', command => wrapConnectionCommand(command, 'command', undefined, instrumentPromise))
|
|
44
|
+
return Connection
|
|
45
|
+
})
|
|
46
|
+
|
|
41
47
|
addHook({ name: 'mongodb', versions: ['>=3.3 <4'], file: 'lib/core/wireprotocol/index.js' }, wp => wrapWp(wp))
|
|
42
48
|
|
|
43
49
|
addHook({ name: 'mongodb-core', versions: ['>=3.2'], file: 'lib/wireprotocol/index.js' }, wp => wrapWp(wp))
|
|
@@ -89,7 +95,7 @@ function wrapUnifiedCommand (command, operation, name) {
|
|
|
89
95
|
return shimmer.wrap(command, wrapped)
|
|
90
96
|
}
|
|
91
97
|
|
|
92
|
-
function wrapConnectionCommand (command, operation, name) {
|
|
98
|
+
function wrapConnectionCommand (command, operation, name, instrumentFn = instrument) {
|
|
93
99
|
const wrapped = function (ns, ops) {
|
|
94
100
|
if (!startCh.hasSubscribers) {
|
|
95
101
|
return command.apply(this, arguments)
|
|
@@ -101,7 +107,7 @@ function wrapConnectionCommand (command, operation, name) {
|
|
|
101
107
|
const topology = { s: { options } }
|
|
102
108
|
|
|
103
109
|
ns = `${ns.db}.${ns.collection}`
|
|
104
|
-
return
|
|
110
|
+
return instrumentFn(operation, command, this, arguments, topology, ns, ops, { name })
|
|
105
111
|
}
|
|
106
112
|
return shimmer.wrap(command, wrapped)
|
|
107
113
|
}
|
|
@@ -179,3 +185,28 @@ function instrument (operation, command, ctx, args, server, ns, ops, options = {
|
|
|
179
185
|
}
|
|
180
186
|
})
|
|
181
187
|
}
|
|
188
|
+
|
|
189
|
+
function instrumentPromise (operation, command, ctx, args, server, ns, ops, options = {}) {
|
|
190
|
+
const name = options.name || (ops && Object.keys(ops)[0])
|
|
191
|
+
|
|
192
|
+
const serverInfo = server && server.s && server.s.options
|
|
193
|
+
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
194
|
+
|
|
195
|
+
return asyncResource.runInAsyncScope(() => {
|
|
196
|
+
startCh.publish({ ns, ops, options: serverInfo, name })
|
|
197
|
+
|
|
198
|
+
const promise = command.apply(ctx, args)
|
|
199
|
+
|
|
200
|
+
promise.then(function (res) {
|
|
201
|
+
finishCh.publish()
|
|
202
|
+
return res
|
|
203
|
+
}, function (err) {
|
|
204
|
+
errorCh.publish(err)
|
|
205
|
+
finishCh.publish()
|
|
206
|
+
|
|
207
|
+
return Promise.reject(err)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
return promise
|
|
211
|
+
})
|
|
212
|
+
}
|