dd-trace 5.43.0 → 5.45.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/package.json +4 -4
- package/packages/datadog-instrumentations/src/cucumber.js +61 -23
- package/packages/datadog-instrumentations/src/dd-trace-api.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +134 -48
- package/packages/datadog-instrumentations/src/mocha/main.js +20 -4
- package/packages/datadog-instrumentations/src/mocha/utils.js +89 -30
- package/packages/datadog-instrumentations/src/mocha/worker.js +3 -1
- package/packages/datadog-instrumentations/src/playwright.js +97 -17
- package/packages/datadog-instrumentations/src/router.js +1 -0
- package/packages/datadog-instrumentations/src/tedious.js +13 -10
- package/packages/datadog-instrumentations/src/vitest.js +77 -17
- package/packages/datadog-plugin-cucumber/src/index.js +24 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +69 -20
- package/packages/datadog-plugin-cypress/src/support.js +39 -10
- package/packages/datadog-plugin-google-cloud-vertexai/src/index.js +8 -186
- package/packages/datadog-plugin-google-cloud-vertexai/src/tracing.js +186 -0
- package/packages/datadog-plugin-google-cloud-vertexai/src/utils.js +19 -0
- package/packages/datadog-plugin-jest/src/index.js +38 -5
- package/packages/datadog-plugin-mocha/src/index.js +28 -5
- package/packages/datadog-plugin-playwright/src/index.js +22 -2
- package/packages/datadog-plugin-tedious/src/index.js +14 -9
- package/packages/datadog-plugin-vitest/src/index.js +46 -14
- package/packages/dd-trace/src/appsec/blocking.js +2 -0
- package/packages/dd-trace/src/appsec/graphql.js +3 -1
- package/packages/dd-trace/src/appsec/reporter.js +13 -8
- package/packages/dd-trace/src/appsec/sdk/track_event.js +7 -0
- package/packages/dd-trace/src/appsec/telemetry/common.js +6 -3
- package/packages/dd-trace/src/appsec/telemetry/index.js +28 -5
- package/packages/dd-trace/src/appsec/telemetry/user.js +9 -1
- package/packages/dd-trace/src/appsec/telemetry/waf.js +29 -9
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +16 -7
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +3 -1
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +4 -2
- package/packages/dd-trace/src/dogstatsd.js +94 -77
- package/packages/dd-trace/src/histogram.js +12 -23
- package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
- package/packages/dd-trace/src/llmobs/index.js +3 -0
- package/packages/dd-trace/src/llmobs/noop.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/base.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/vertexai.js +196 -0
- package/packages/dd-trace/src/llmobs/sdk.js +2 -0
- package/packages/dd-trace/src/llmobs/span_processor.js +6 -0
- package/packages/dd-trace/src/llmobs/tagger.js +8 -2
- package/packages/dd-trace/src/llmobs/telemetry.js +108 -1
- package/packages/dd-trace/src/llmobs/writers/base.js +4 -0
- package/packages/dd-trace/src/llmobs/writers/spans/base.js +10 -1
- package/packages/dd-trace/src/plugin_manager.js +0 -3
- package/packages/dd-trace/src/plugins/ci_plugin.js +16 -26
- package/packages/dd-trace/src/plugins/database.js +4 -4
- package/packages/dd-trace/src/plugins/plugin.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +62 -1
- package/packages/dd-trace/src/remote_config/manager.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +7 -6
- package/packages/dd-trace/src/telemetry/send-data.js +5 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dd-trace",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.45.0",
|
|
4
4
|
"description": "Datadog APM tracing client for JavaScript",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "index.d.ts",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
},
|
|
86
86
|
"dependencies": {
|
|
87
87
|
"@datadog/libdatadog": "^0.5.0",
|
|
88
|
-
"@datadog/native-appsec": "8.5.
|
|
88
|
+
"@datadog/native-appsec": "8.5.1",
|
|
89
89
|
"@datadog/native-iast-rewriter": "2.8.0",
|
|
90
90
|
"@datadog/native-iast-taint-tracking": "3.3.0",
|
|
91
91
|
"@datadog/native-metrics": "^3.1.0",
|
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"@opentelemetry/api": ">=1.0.0 <1.9.0",
|
|
96
96
|
"@opentelemetry/core": "^1.14.0",
|
|
97
97
|
"crypto-randomuuid": "^1.0.0",
|
|
98
|
-
"dc-polyfill": "
|
|
98
|
+
"dc-polyfill": "0.1.6",
|
|
99
99
|
"ignore": "^5.2.4",
|
|
100
100
|
"import-in-the-middle": "1.13.1",
|
|
101
101
|
"istanbul-lib-coverage": "3.2.0",
|
|
@@ -125,7 +125,7 @@
|
|
|
125
125
|
"@msgpack/msgpack": "^3.0.0-beta3",
|
|
126
126
|
"@stylistic/eslint-plugin-js": "^3.0.1",
|
|
127
127
|
"@types/node": "^16.0.0",
|
|
128
|
-
"application-config-path": "^1.
|
|
128
|
+
"application-config-path": "^0.1.1",
|
|
129
129
|
"autocannon": "^4.5.2",
|
|
130
130
|
"aws-sdk": "^2.1446.0",
|
|
131
131
|
"axios": "^1.8.2",
|
|
@@ -73,6 +73,7 @@ let isEarlyFlakeDetectionFaulty = false
|
|
|
73
73
|
let isFlakyTestRetriesEnabled = false
|
|
74
74
|
let isKnownTestsEnabled = false
|
|
75
75
|
let isTestManagementTestsEnabled = false
|
|
76
|
+
let testManagementAttemptToFixRetries = 0
|
|
76
77
|
let testManagementTests = {}
|
|
77
78
|
let numTestRetries = 0
|
|
78
79
|
let knownTests = []
|
|
@@ -121,10 +122,10 @@ function isNewTest (testSuite, testName) {
|
|
|
121
122
|
}
|
|
122
123
|
|
|
123
124
|
function getTestProperties (testSuite, testName) {
|
|
124
|
-
const { disabled, quarantined } =
|
|
125
|
+
const { attempt_to_fix: attemptToFix, disabled, quarantined } =
|
|
125
126
|
testManagementTests?.cucumber?.suites?.[testSuite]?.tests?.[testName]?.properties || {}
|
|
126
127
|
|
|
127
|
-
return { disabled, quarantined }
|
|
128
|
+
return { attemptToFix, disabled, quarantined }
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
function getTestStatusFromRetries (testStatuses) {
|
|
@@ -303,22 +304,42 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
303
304
|
}
|
|
304
305
|
let isNew = false
|
|
305
306
|
let isEfdRetry = false
|
|
307
|
+
let isAttemptToFix = false
|
|
308
|
+
let isAttemptToFixRetry = false
|
|
309
|
+
let hasFailedAllRetries = false
|
|
310
|
+
let hasPassedAllRetries = false
|
|
306
311
|
let isDisabled = false
|
|
307
312
|
let isQuarantined = false
|
|
308
|
-
if (isKnownTestsEnabled && status !== 'skip') {
|
|
309
|
-
const numRetries = numRetriesByPickleId.get(this.pickle.id)
|
|
310
313
|
|
|
311
|
-
isNew = numRetries !== undefined
|
|
312
|
-
isEfdRetry = numRetries > 0
|
|
313
|
-
}
|
|
314
314
|
if (isTestManagementTestsEnabled) {
|
|
315
315
|
const testSuitePath = getTestSuitePath(testFileAbsolutePath, process.cwd())
|
|
316
316
|
const testProperties = getTestProperties(testSuitePath, this.pickle.name)
|
|
317
|
+
const numRetries = numRetriesByPickleId.get(this.pickle.id)
|
|
318
|
+
isAttemptToFix = testProperties.attemptToFix
|
|
319
|
+
isAttemptToFixRetry = isAttemptToFix && numRetries > 0
|
|
317
320
|
isDisabled = testProperties.disabled
|
|
318
|
-
|
|
319
|
-
|
|
321
|
+
isQuarantined = testProperties.quarantined
|
|
322
|
+
|
|
323
|
+
if (isAttemptToFixRetry) {
|
|
324
|
+
const statuses = lastStatusByPickleId.get(this.pickle.id)
|
|
325
|
+
if (statuses.length === testManagementAttemptToFixRetries + 1) {
|
|
326
|
+
const { pass, fail } = statuses.reduce((acc, status) => {
|
|
327
|
+
acc[status]++
|
|
328
|
+
return acc
|
|
329
|
+
}, { pass: 0, fail: 0 })
|
|
330
|
+
hasFailedAllRetries = fail === testManagementAttemptToFixRetries + 1
|
|
331
|
+
hasPassedAllRetries = pass === testManagementAttemptToFixRetries + 1
|
|
332
|
+
}
|
|
320
333
|
}
|
|
321
334
|
}
|
|
335
|
+
|
|
336
|
+
if (isKnownTestsEnabled && status !== 'skip' && !isAttemptToFix) {
|
|
337
|
+
const numRetries = numRetriesByPickleId.get(this.pickle.id)
|
|
338
|
+
|
|
339
|
+
isNew = numRetries !== undefined
|
|
340
|
+
isEfdRetry = numRetries > 0
|
|
341
|
+
}
|
|
342
|
+
|
|
322
343
|
const attemptAsyncResource = numAttemptToAsyncResource.get(numAttempt)
|
|
323
344
|
|
|
324
345
|
const error = getErrorFromCucumberResult(result)
|
|
@@ -334,6 +355,10 @@ function wrapRun (pl, isLatestVersion) {
|
|
|
334
355
|
isNew,
|
|
335
356
|
isEfdRetry,
|
|
336
357
|
isFlakyRetry: numAttempt > 0,
|
|
358
|
+
isAttemptToFix,
|
|
359
|
+
isAttemptToFixRetry,
|
|
360
|
+
hasFailedAllRetries,
|
|
361
|
+
hasPassedAllRetries,
|
|
337
362
|
isDisabled,
|
|
338
363
|
isQuarantined
|
|
339
364
|
})
|
|
@@ -426,6 +451,7 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
|
|
|
426
451
|
numTestRetries = configurationResponse.libraryConfig?.flakyTestRetriesCount
|
|
427
452
|
isKnownTestsEnabled = configurationResponse.libraryConfig?.isKnownTestsEnabled
|
|
428
453
|
isTestManagementTestsEnabled = configurationResponse.libraryConfig?.isTestManagementEnabled
|
|
454
|
+
testManagementAttemptToFixRetries = configurationResponse.libraryConfig?.testManagementAttemptToFixRetries
|
|
429
455
|
|
|
430
456
|
if (isKnownTestsEnabled) {
|
|
431
457
|
const knownTestsResponse = await getChannelPromise(knownTestsCh)
|
|
@@ -576,22 +602,25 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
576
602
|
}
|
|
577
603
|
|
|
578
604
|
let isNew = false
|
|
605
|
+
let isAttemptToFix = false
|
|
579
606
|
let isDisabled = false
|
|
580
607
|
let isQuarantined = false
|
|
581
608
|
|
|
582
|
-
if (isKnownTestsEnabled) {
|
|
583
|
-
isNew = isNewTest(testSuitePath, pickle.name)
|
|
584
|
-
if (isNew) {
|
|
585
|
-
numRetriesByPickleId.set(pickle.id, 0)
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
609
|
if (isTestManagementTestsEnabled) {
|
|
589
610
|
const testProperties = getTestProperties(testSuitePath, pickle.name)
|
|
611
|
+
isAttemptToFix = testProperties.attemptToFix
|
|
590
612
|
isDisabled = testProperties.disabled
|
|
591
|
-
|
|
613
|
+
isQuarantined = testProperties.quarantined
|
|
614
|
+
// If attempt to fix is enabled, we run even if the test is disabled
|
|
615
|
+
if (!isAttemptToFix && isDisabled) {
|
|
592
616
|
this.options.dryRun = true
|
|
593
|
-
}
|
|
594
|
-
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (isKnownTestsEnabled && !isAttemptToFix) {
|
|
621
|
+
isNew = isNewTest(testSuitePath, pickle.name)
|
|
622
|
+
if (isNew) {
|
|
623
|
+
numRetriesByPickleId.set(pickle.id, 0)
|
|
595
624
|
}
|
|
596
625
|
}
|
|
597
626
|
// TODO: for >=11 we could use `runTestCaseResult` instead of accumulating results in `lastStatusByPickleId`
|
|
@@ -599,6 +628,15 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
599
628
|
|
|
600
629
|
const testStatuses = lastStatusByPickleId.get(pickle.id)
|
|
601
630
|
const lastTestStatus = testStatuses[testStatuses.length - 1]
|
|
631
|
+
|
|
632
|
+
// New tests should not be marked as attempt to fix, so EFD + Attempt to fix should not be enabled at the same time
|
|
633
|
+
if (isAttemptToFix && lastTestStatus !== 'skip') {
|
|
634
|
+
for (let retryIndex = 0; retryIndex < testManagementAttemptToFixRetries; retryIndex++) {
|
|
635
|
+
numRetriesByPickleId.set(pickle.id, retryIndex + 1)
|
|
636
|
+
runTestCaseResult = await runTestCaseFunction.apply(this, arguments)
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
|
|
602
640
|
// If it's a new test and it hasn't been skipped, we run it again
|
|
603
641
|
if (isEarlyFlakeDetectionEnabled && lastTestStatus !== 'skip' && isNew) {
|
|
604
642
|
for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
|
|
@@ -608,7 +646,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
608
646
|
}
|
|
609
647
|
let testStatus = lastTestStatus
|
|
610
648
|
let shouldBePassedByEFD = false
|
|
611
|
-
let
|
|
649
|
+
let shouldBePassedByTestManagement = false
|
|
612
650
|
if (isNew && isEarlyFlakeDetectionEnabled) {
|
|
613
651
|
/**
|
|
614
652
|
* If Early Flake Detection (EFD) is enabled the logic is as follows:
|
|
@@ -625,9 +663,9 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
625
663
|
}
|
|
626
664
|
}
|
|
627
665
|
|
|
628
|
-
if (isTestManagementTestsEnabled && isQuarantined) {
|
|
666
|
+
if (isTestManagementTestsEnabled && (isDisabled || isQuarantined)) {
|
|
629
667
|
this.success = true
|
|
630
|
-
|
|
668
|
+
shouldBePassedByTestManagement = true
|
|
631
669
|
}
|
|
632
670
|
|
|
633
671
|
if (!pickleResultByFile[testFileAbsolutePath]) {
|
|
@@ -661,8 +699,8 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
|
|
|
661
699
|
return shouldBePassedByEFD
|
|
662
700
|
}
|
|
663
701
|
|
|
664
|
-
if (isNewerCucumberVersion && isTestManagementTestsEnabled && isQuarantined) {
|
|
665
|
-
return
|
|
702
|
+
if (isNewerCucumberVersion && isTestManagementTestsEnabled && (isQuarantined || isDisabled)) {
|
|
703
|
+
return shouldBePassedByTestManagement
|
|
666
704
|
}
|
|
667
705
|
|
|
668
706
|
return runTestCaseResult
|
|
@@ -43,6 +43,7 @@ module.exports = {
|
|
|
43
43
|
couchbase: () => require('../couchbase'),
|
|
44
44
|
crypto: () => require('../crypto'),
|
|
45
45
|
cypress: () => require('../cypress'),
|
|
46
|
+
'dd-trace-api': () => require('../dd-trace-api'),
|
|
46
47
|
dns: () => require('../dns'),
|
|
47
48
|
elasticsearch: () => require('../elasticsearch'),
|
|
48
49
|
express: () => require('../express'),
|
|
@@ -158,7 +158,7 @@ for (const packageName of names) {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
function matchVersion (version, ranges) {
|
|
161
|
-
return !version ||
|
|
161
|
+
return !version || !ranges || ranges.some(range => satisfies(version, range))
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
function getVersion (moduleBaseDir) {
|
|
@@ -13,7 +13,9 @@ const {
|
|
|
13
13
|
addEfdStringToTestName,
|
|
14
14
|
removeEfdStringFromTestName,
|
|
15
15
|
getIsFaultyEarlyFlakeDetection,
|
|
16
|
-
JEST_WORKER_LOGS_PAYLOAD_CODE
|
|
16
|
+
JEST_WORKER_LOGS_PAYLOAD_CODE,
|
|
17
|
+
addAttemptToFixStringToTestName,
|
|
18
|
+
removeAttemptToFixStringFromTestName
|
|
17
19
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
18
20
|
const {
|
|
19
21
|
getFormattedJestTestParameters,
|
|
@@ -73,6 +75,7 @@ let hasFilteredSkippableSuites = false
|
|
|
73
75
|
let isKnownTestsEnabled = false
|
|
74
76
|
let isTestManagementTestsEnabled = false
|
|
75
77
|
let testManagementTests = {}
|
|
78
|
+
let testManagementAttemptToFixRetries = 0
|
|
76
79
|
|
|
77
80
|
const sessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
78
81
|
|
|
@@ -80,6 +83,7 @@ const asyncResources = new WeakMap()
|
|
|
80
83
|
const originalTestFns = new WeakMap()
|
|
81
84
|
const retriedTestsToNumAttempts = new Map()
|
|
82
85
|
const newTestsTestStatuses = new Map()
|
|
86
|
+
const attemptToFixRetriedTestsStatuses = new Map()
|
|
83
87
|
|
|
84
88
|
const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
|
|
85
89
|
|
|
@@ -110,7 +114,7 @@ function getTestEnvironmentOptions (config) {
|
|
|
110
114
|
return {}
|
|
111
115
|
}
|
|
112
116
|
|
|
113
|
-
function
|
|
117
|
+
function getTestStats (testStatuses) {
|
|
114
118
|
return testStatuses.reduce((acc, testStatus) => {
|
|
115
119
|
acc[testStatus]++
|
|
116
120
|
return acc
|
|
@@ -169,6 +173,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
169
173
|
if (this.isTestManagementTestsEnabled) {
|
|
170
174
|
try {
|
|
171
175
|
const hasTestManagementTests = !!testManagementTests.jest
|
|
176
|
+
testManagementAttemptToFixRetries = this.testEnvironmentOptions._ddTestManagementAttemptToFixRetries
|
|
172
177
|
this.testManagementTestsForThisSuite = hasTestManagementTests
|
|
173
178
|
? this.getTestManagementTestsForSuite(testManagementTests.jest.suites?.[this.testSuite]?.tests)
|
|
174
179
|
: this.getTestManagementTestsForSuite(this.testEnvironmentOptions._ddTestManagementTests)
|
|
@@ -213,9 +218,9 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
213
218
|
if (this.testManagementTestsForThisSuite) {
|
|
214
219
|
return this.testManagementTestsForThisSuite
|
|
215
220
|
}
|
|
216
|
-
// TODO - ADD ATTEMPT_TO_FIX tests
|
|
217
221
|
if (!testManagementTests) {
|
|
218
222
|
return {
|
|
223
|
+
attemptToFix: [],
|
|
219
224
|
disabled: [],
|
|
220
225
|
quarantined: []
|
|
221
226
|
}
|
|
@@ -228,14 +233,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
228
233
|
}
|
|
229
234
|
|
|
230
235
|
const result = {
|
|
236
|
+
attemptToFix: [],
|
|
231
237
|
disabled: [],
|
|
232
238
|
quarantined: []
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
Object.entries(testManagementTestsForSuite).forEach(([testName, { properties }]) => {
|
|
242
|
+
if (properties?.attempt_to_fix) {
|
|
243
|
+
result.attemptToFix.push(testName)
|
|
244
|
+
}
|
|
236
245
|
if (properties?.disabled) {
|
|
237
246
|
result.disabled.push(testName)
|
|
238
|
-
}
|
|
247
|
+
}
|
|
248
|
+
if (properties?.quarantined) {
|
|
239
249
|
result.quarantined.push(testName)
|
|
240
250
|
}
|
|
241
251
|
})
|
|
@@ -243,11 +253,30 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
243
253
|
return result
|
|
244
254
|
}
|
|
245
255
|
|
|
246
|
-
//
|
|
256
|
+
// Generic function to handle test retries
|
|
257
|
+
retryTest (testName, retryCount, addRetryStringToTestName, retryType, event) {
|
|
258
|
+
// Retrying snapshots has proven to be problematic, so we'll skip them for now
|
|
259
|
+
// We'll still detect new tests, but we won't retry them.
|
|
260
|
+
// TODO: do not bail out of retrying tests for the whole test suite
|
|
261
|
+
if (this.getHasSnapshotTests()) {
|
|
262
|
+
log.warn(`${retryType} is disabled for suites with snapshots`)
|
|
263
|
+
return
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
for (let retryIndex = 0; retryIndex < retryCount; retryIndex++) {
|
|
267
|
+
if (this.global.test) {
|
|
268
|
+
this.global.test(addRetryStringToTestName(testName, retryIndex), event.fn, event.timeout)
|
|
269
|
+
} else {
|
|
270
|
+
log.error(`${retryType} could not retry test because global.test is undefined`)
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// At the `add_test` event we don't have the test object yet, so we can't use it
|
|
247
276
|
getTestNameFromAddTestEvent (event, state) {
|
|
248
277
|
const describeSuffix = getJestTestName(state.currentDescribeBlock)
|
|
249
278
|
const fullTestName = describeSuffix ? `${describeSuffix} ${event.testName}` : event.testName
|
|
250
|
-
return removeEfdStringFromTestName(fullTestName)
|
|
279
|
+
return removeAttemptToFixStringFromTestName(removeEfdStringFromTestName(fullTestName))
|
|
251
280
|
}
|
|
252
281
|
|
|
253
282
|
async handleTestEvent (event, state) {
|
|
@@ -273,15 +302,31 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
273
302
|
if (event.name === 'test_start') {
|
|
274
303
|
let isNewTest = false
|
|
275
304
|
let numEfdRetry = null
|
|
305
|
+
let numOfAttemptsToFixRetries = null
|
|
276
306
|
const testParameters = getTestParametersString(this.nameToParams, event.test.name)
|
|
277
307
|
// Async resource for this test is created here
|
|
278
308
|
// It is used later on by the test_done handler
|
|
279
309
|
const asyncResource = new AsyncResource('bound-anonymous-fn')
|
|
280
310
|
asyncResources.set(event.test, asyncResource)
|
|
281
311
|
const testName = getJestTestName(event.test)
|
|
312
|
+
const originalTestName = removeEfdStringFromTestName(removeAttemptToFixStringFromTestName(testName))
|
|
313
|
+
|
|
314
|
+
let isAttemptToFix = false
|
|
315
|
+
let isDisabled = false
|
|
316
|
+
let isQuarantined = false
|
|
317
|
+
if (this.isTestManagementTestsEnabled) {
|
|
318
|
+
isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(originalTestName)
|
|
319
|
+
isDisabled = this.testManagementTestsForThisSuite?.disabled?.includes(originalTestName)
|
|
320
|
+
isQuarantined = this.testManagementTestsForThisSuite?.quarantined?.includes(originalTestName)
|
|
321
|
+
if (isAttemptToFix) {
|
|
322
|
+
numOfAttemptsToFixRetries = retriedTestsToNumAttempts.get(originalTestName)
|
|
323
|
+
retriedTestsToNumAttempts.set(originalTestName, numOfAttemptsToFixRetries + 1)
|
|
324
|
+
} else if (isDisabled) {
|
|
325
|
+
event.test.mode = 'skip'
|
|
326
|
+
}
|
|
327
|
+
}
|
|
282
328
|
|
|
283
329
|
if (this.isKnownTestsEnabled) {
|
|
284
|
-
const originalTestName = removeEfdStringFromTestName(testName)
|
|
285
330
|
isNewTest = retriedTestsToNumAttempts.has(originalTestName)
|
|
286
331
|
if (isNewTest) {
|
|
287
332
|
numEfdRetry = retriedTestsToNumAttempts.get(originalTestName)
|
|
@@ -289,16 +334,10 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
289
334
|
}
|
|
290
335
|
}
|
|
291
336
|
|
|
292
|
-
if (this.isTestManagementTestsEnabled) {
|
|
293
|
-
const isDisabled = this.testManagementTestsForThisSuite?.disabled?.includes(testName)
|
|
294
|
-
if (isDisabled) {
|
|
295
|
-
event.test.mode = 'skip'
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
337
|
const isJestRetry = event.test?.invocations > 1
|
|
299
338
|
asyncResource.runInAsyncScope(() => {
|
|
300
339
|
testStartCh.publish({
|
|
301
|
-
name:
|
|
340
|
+
name: originalTestName,
|
|
302
341
|
suite: this.testSuite,
|
|
303
342
|
testSourceFile: this.testSourceFile,
|
|
304
343
|
displayName: this.displayName,
|
|
@@ -306,34 +345,46 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
306
345
|
frameworkVersion: jestVersion,
|
|
307
346
|
isNew: isNewTest,
|
|
308
347
|
isEfdRetry: numEfdRetry > 0,
|
|
309
|
-
|
|
348
|
+
isAttemptToFix,
|
|
349
|
+
isAttemptToFixRetry: numOfAttemptsToFixRetries > 0,
|
|
350
|
+
isJestRetry,
|
|
351
|
+
isDisabled,
|
|
352
|
+
isQuarantined
|
|
310
353
|
})
|
|
311
354
|
originalTestFns.set(event.test, event.test.fn)
|
|
312
355
|
event.test.fn = asyncResource.bind(event.test.fn)
|
|
313
356
|
})
|
|
314
357
|
}
|
|
358
|
+
|
|
315
359
|
if (event.name === 'add_test') {
|
|
360
|
+
const originalTestName = this.getTestNameFromAddTestEvent(event, state)
|
|
361
|
+
|
|
362
|
+
const isSkipped = event.mode === 'todo' || event.mode === 'skip'
|
|
363
|
+
if (this.isTestManagementTestsEnabled) {
|
|
364
|
+
const isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(originalTestName)
|
|
365
|
+
if (isAttemptToFix && !isSkipped && !retriedTestsToNumAttempts.has(originalTestName)) {
|
|
366
|
+
retriedTestsToNumAttempts.set(originalTestName, 0)
|
|
367
|
+
this.retryTest(
|
|
368
|
+
event.testName,
|
|
369
|
+
testManagementAttemptToFixRetries,
|
|
370
|
+
addAttemptToFixStringToTestName,
|
|
371
|
+
'Test Management (Attempt to Fix)',
|
|
372
|
+
event
|
|
373
|
+
)
|
|
374
|
+
}
|
|
375
|
+
}
|
|
316
376
|
if (this.isKnownTestsEnabled) {
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
if (isNew && !isSkipped && !retriedTestsToNumAttempts.has(testName)) {
|
|
321
|
-
retriedTestsToNumAttempts.set(testName, 0)
|
|
377
|
+
const isNew = !this.knownTestsForThisSuite?.includes(originalTestName)
|
|
378
|
+
if (isNew && !isSkipped && !retriedTestsToNumAttempts.has(originalTestName)) {
|
|
379
|
+
retriedTestsToNumAttempts.set(originalTestName, 0)
|
|
322
380
|
if (this.isEarlyFlakeDetectionEnabled) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
for (let retryIndex = 0; retryIndex < earlyFlakeDetectionNumRetries; retryIndex++) {
|
|
331
|
-
if (this.global.test) {
|
|
332
|
-
this.global.test(addEfdStringToTestName(event.testName, retryIndex), event.fn, event.timeout)
|
|
333
|
-
} else {
|
|
334
|
-
log.error('Early flake detection could not retry test because global.test is undefined')
|
|
335
|
-
}
|
|
336
|
-
}
|
|
381
|
+
this.retryTest(
|
|
382
|
+
event.testName,
|
|
383
|
+
earlyFlakeDetectionNumRetries,
|
|
384
|
+
addEfdStringToTestName,
|
|
385
|
+
'Early flake detection',
|
|
386
|
+
event
|
|
387
|
+
)
|
|
337
388
|
}
|
|
338
389
|
}
|
|
339
390
|
}
|
|
@@ -346,6 +397,32 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
346
397
|
// restore in case it is retried
|
|
347
398
|
event.test.fn = originalTestFns.get(event.test)
|
|
348
399
|
|
|
400
|
+
let attemptToFixPassed = false
|
|
401
|
+
let failedAllTests = false
|
|
402
|
+
if (this.isTestManagementTestsEnabled) {
|
|
403
|
+
const testName = getJestTestName(event.test)
|
|
404
|
+
const originalTestName = removeAttemptToFixStringFromTestName(testName)
|
|
405
|
+
const isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(originalTestName)
|
|
406
|
+
if (isAttemptToFix) {
|
|
407
|
+
if (attemptToFixRetriedTestsStatuses.has(originalTestName)) {
|
|
408
|
+
attemptToFixRetriedTestsStatuses.get(originalTestName).push(status)
|
|
409
|
+
} else {
|
|
410
|
+
attemptToFixRetriedTestsStatuses.set(originalTestName, [status])
|
|
411
|
+
}
|
|
412
|
+
const testStatuses = attemptToFixRetriedTestsStatuses.get(originalTestName)
|
|
413
|
+
// Check if this is the last attempt to fix.
|
|
414
|
+
// If it is, we'll set the failedAllTests flag to true if all the tests failed
|
|
415
|
+
// If all tests passed, we'll set the attemptToFixPassed flag to true
|
|
416
|
+
if (testStatuses.length === testManagementAttemptToFixRetries + 1) {
|
|
417
|
+
if (testStatuses.every(status => status === 'fail')) {
|
|
418
|
+
failedAllTests = true
|
|
419
|
+
} else if (testStatuses.every(status => status === 'pass')) {
|
|
420
|
+
attemptToFixPassed = true
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
349
426
|
// We'll store the test statuses of the retries
|
|
350
427
|
if (this.isKnownTestsEnabled) {
|
|
351
428
|
const testName = getJestTestName(event.test)
|
|
@@ -359,12 +436,6 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
359
436
|
}
|
|
360
437
|
}
|
|
361
438
|
}
|
|
362
|
-
let isQuarantined = false
|
|
363
|
-
|
|
364
|
-
if (this.isTestManagementTestsEnabled) {
|
|
365
|
-
const testName = getJestTestName(event.test)
|
|
366
|
-
isQuarantined = this.testManagementTestsForThisSuite?.quarantined?.includes(testName)
|
|
367
|
-
}
|
|
368
439
|
|
|
369
440
|
const promises = {}
|
|
370
441
|
const numRetries = this.global[RETRY_TIMES]
|
|
@@ -399,7 +470,8 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
|
|
|
399
470
|
testFinishCh.publish({
|
|
400
471
|
status,
|
|
401
472
|
testStartLine: getTestLineStart(event.test.asyncError, this.testSuite),
|
|
402
|
-
|
|
473
|
+
attemptToFixPassed,
|
|
474
|
+
failedAllTests
|
|
403
475
|
})
|
|
404
476
|
})
|
|
405
477
|
|
|
@@ -552,6 +624,7 @@ function cliWrapper (cli, jestVersion) {
|
|
|
552
624
|
earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
|
|
553
625
|
isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
|
|
554
626
|
isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
|
|
627
|
+
testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
|
|
555
628
|
}
|
|
556
629
|
} catch (err) {
|
|
557
630
|
log.error('Jest library configuration error', err)
|
|
@@ -710,7 +783,7 @@ function cliWrapper (cli, jestVersion) {
|
|
|
710
783
|
if (isEarlyFlakeDetectionEnabled) {
|
|
711
784
|
let numFailedTestsToIgnore = 0
|
|
712
785
|
for (const testStatuses of newTestsTestStatuses.values()) {
|
|
713
|
-
const { pass, fail } =
|
|
786
|
+
const { pass, fail } = getTestStats(testStatuses)
|
|
714
787
|
if (pass > 0) { // as long as one passes, we'll consider the test passed
|
|
715
788
|
numFailedTestsToIgnore += fail
|
|
716
789
|
}
|
|
@@ -725,29 +798,41 @@ function cliWrapper (cli, jestVersion) {
|
|
|
725
798
|
const failedTests = result
|
|
726
799
|
.results
|
|
727
800
|
.testResults.flatMap(({ testResults, testFilePath: testSuiteAbsolutePath }) => (
|
|
728
|
-
testResults.map(({ fullName: testName, status }) => (
|
|
801
|
+
testResults.map(({ fullName: testName, status }) => (
|
|
802
|
+
{ testName, testSuiteAbsolutePath, status }
|
|
803
|
+
))
|
|
729
804
|
))
|
|
730
805
|
.filter(({ status }) => status === 'failed')
|
|
731
806
|
|
|
732
807
|
let numFailedQuarantinedTests = 0
|
|
808
|
+
let numFailedQuarantinedOrDisabledAttemptedToFixTests = 0
|
|
733
809
|
|
|
734
810
|
for (const { testName, testSuiteAbsolutePath } of failedTests) {
|
|
735
811
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, result.globalConfig.rootDir)
|
|
736
|
-
const
|
|
812
|
+
const originalName = removeAttemptToFixStringFromTestName(testName)
|
|
813
|
+
const testManagementTest = testManagementTests
|
|
737
814
|
?.jest
|
|
738
815
|
?.suites
|
|
739
816
|
?.[testSuite]
|
|
740
817
|
?.tests
|
|
741
|
-
?.[
|
|
818
|
+
?.[originalName]
|
|
742
819
|
?.properties
|
|
743
|
-
|
|
744
|
-
if (
|
|
820
|
+
// This uses `attempt_to_fix` because this is always the main process and it's not formatted in camelCase
|
|
821
|
+
if (testManagementTest?.attempt_to_fix && (testManagementTest?.quarantined || testManagementTest?.disabled)) {
|
|
822
|
+
numFailedQuarantinedOrDisabledAttemptedToFixTests++
|
|
823
|
+
} else if (testManagementTest?.quarantined) {
|
|
745
824
|
numFailedQuarantinedTests++
|
|
746
825
|
}
|
|
747
826
|
}
|
|
748
827
|
|
|
749
828
|
// If every test that failed was quarantined, we'll consider the suite passed
|
|
750
|
-
|
|
829
|
+
// Note that if a test is attempted to fix,
|
|
830
|
+
// it's considered quarantined both if it's disabled and if it's quarantined (it'll run but its status is ignored)
|
|
831
|
+
if (
|
|
832
|
+
(numFailedQuarantinedOrDisabledAttemptedToFixTests !== 0 || numFailedQuarantinedTests !== 0) &&
|
|
833
|
+
result.results.numFailedTests ===
|
|
834
|
+
numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
|
|
835
|
+
) {
|
|
751
836
|
result.results.success = true
|
|
752
837
|
}
|
|
753
838
|
}
|
|
@@ -947,6 +1032,7 @@ addHook({
|
|
|
947
1032
|
_ddIsKnownTestsEnabled,
|
|
948
1033
|
_ddIsTestManagementTestsEnabled,
|
|
949
1034
|
_ddTestManagementTests,
|
|
1035
|
+
_ddTestManagementAttemptToFixRetries,
|
|
950
1036
|
...restOfTestEnvironmentOptions
|
|
951
1037
|
} = testEnvironmentOptions
|
|
952
1038
|
|
|
@@ -30,7 +30,9 @@ const {
|
|
|
30
30
|
newTests,
|
|
31
31
|
testsQuarantined,
|
|
32
32
|
getTestFullName,
|
|
33
|
-
getRunTestsWrapper
|
|
33
|
+
getRunTestsWrapper,
|
|
34
|
+
testsAttemptToFix,
|
|
35
|
+
testsStatuses
|
|
34
36
|
} = require('./utils')
|
|
35
37
|
|
|
36
38
|
require('./common')
|
|
@@ -138,16 +140,26 @@ function getOnEndHandler (isParallel) {
|
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
|
|
143
|
+
// We substract the errors of attempt to fix tests (quarantined or disabled) from the total number of failures
|
|
141
144
|
// We subtract the errors from quarantined tests from the total number of failures
|
|
142
145
|
if (config.isTestManagementTestsEnabled) {
|
|
143
146
|
let numFailedQuarantinedTests = 0
|
|
147
|
+
let numFailedRetriedQuarantinedOrDisabledTests = 0
|
|
148
|
+
for (const test of testsAttemptToFix) {
|
|
149
|
+
const testName = getTestFullName(test)
|
|
150
|
+
const testProperties = getTestProperties(test, config.testManagementTests)
|
|
151
|
+
if (isTestFailed(test) && (testProperties.isQuarantined || testProperties.isDisabled)) {
|
|
152
|
+
const numFailedTests = testsStatuses.get(testName).filter(status => status === 'fail').length
|
|
153
|
+
numFailedRetriedQuarantinedOrDisabledTests += numFailedTests
|
|
154
|
+
}
|
|
155
|
+
}
|
|
144
156
|
for (const test of testsQuarantined) {
|
|
145
157
|
if (isTestFailed(test)) {
|
|
146
158
|
numFailedQuarantinedTests++
|
|
147
159
|
}
|
|
148
160
|
}
|
|
149
|
-
this.stats.failures -= numFailedQuarantinedTests
|
|
150
|
-
this.failures -= numFailedQuarantinedTests
|
|
161
|
+
this.stats.failures -= numFailedQuarantinedTests + numFailedRetriedQuarantinedOrDisabledTests
|
|
162
|
+
this.failures -= numFailedQuarantinedTests + numFailedRetriedQuarantinedOrDisabledTests
|
|
151
163
|
}
|
|
152
164
|
|
|
153
165
|
if (status === 'fail') {
|
|
@@ -193,6 +205,7 @@ function getExecutionConfiguration (runner, isParallel, onFinishRequest) {
|
|
|
193
205
|
if (err) {
|
|
194
206
|
config.testManagementTests = {}
|
|
195
207
|
config.isTestManagementTestsEnabled = false
|
|
208
|
+
config.testManagementAttemptToFixRetries = 0
|
|
196
209
|
} else {
|
|
197
210
|
config.testManagementTests = receivedTestManagementTests
|
|
198
211
|
}
|
|
@@ -260,6 +273,7 @@ function getExecutionConfiguration (runner, isParallel, onFinishRequest) {
|
|
|
260
273
|
config.earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
|
|
261
274
|
config.isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
|
|
262
275
|
config.isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
|
|
276
|
+
config.testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
|
|
263
277
|
// ITR and auto test retries are not supported in parallel mode yet
|
|
264
278
|
config.isSuitesSkippingEnabled = !isParallel && libraryConfig.isSuitesSkippingEnabled
|
|
265
279
|
config.isFlakyTestRetriesEnabled = !isParallel && libraryConfig.isFlakyTestRetriesEnabled
|
|
@@ -401,7 +415,7 @@ addHook({
|
|
|
401
415
|
|
|
402
416
|
this.on('test', getOnTestHandler(true))
|
|
403
417
|
|
|
404
|
-
this.on('test end', getOnTestEndHandler())
|
|
418
|
+
this.on('test end', getOnTestEndHandler(config))
|
|
405
419
|
|
|
406
420
|
this.on('retry', getOnTestRetryHandler())
|
|
407
421
|
|
|
@@ -637,6 +651,8 @@ addHook({
|
|
|
637
651
|
if (config.isTestManagementTestsEnabled) {
|
|
638
652
|
const testSuiteTestManagementTests = config.testManagementTests?.mocha?.suites?.[testPath] || {}
|
|
639
653
|
newWorkerArgs._ddIsTestManagementTestsEnabled = true
|
|
654
|
+
// TODO: attempt to fix does not work in parallel mode yet
|
|
655
|
+
// newWorkerArgs._ddTestManagementAttemptToFixRetries = config.testManagementAttemptToFixRetries
|
|
640
656
|
newWorkerArgs._ddTestManagementTests = {
|
|
641
657
|
mocha: {
|
|
642
658
|
suites: {
|