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
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
const {
|
|
4
4
|
getTestSuitePath,
|
|
5
5
|
removeEfdStringFromTestName,
|
|
6
|
-
addEfdStringToTestName
|
|
6
|
+
addEfdStringToTestName,
|
|
7
|
+
addAttemptToFixStringToTestName,
|
|
8
|
+
removeAttemptToFixStringFromTestName
|
|
7
9
|
} = require('../../../dd-trace/src/plugins/util/test')
|
|
8
10
|
const { channel, AsyncResource } = require('../helpers/instrument')
|
|
9
11
|
const shimmer = require('../../../datadog-shimmer')
|
|
@@ -26,7 +28,9 @@ const testToStartLine = new WeakMap()
|
|
|
26
28
|
const testFileToSuiteAr = new Map()
|
|
27
29
|
const wrappedFunctions = new WeakSet()
|
|
28
30
|
const newTests = {}
|
|
31
|
+
const testsAttemptToFix = new Set()
|
|
29
32
|
const testsQuarantined = new Set()
|
|
33
|
+
const testsStatuses = new Map()
|
|
30
34
|
|
|
31
35
|
function getAfterEachHooks (testOrHook) {
|
|
32
36
|
const hooks = []
|
|
@@ -44,10 +48,10 @@ function getTestProperties (test, testManagementTests) {
|
|
|
44
48
|
const testSuite = getTestSuitePath(test.file, process.cwd())
|
|
45
49
|
const testName = test.fullTitle()
|
|
46
50
|
|
|
47
|
-
const { disabled: isDisabled, quarantined: isQuarantined } =
|
|
51
|
+
const { attempt_to_fix: isAttemptToFix, disabled: isDisabled, quarantined: isQuarantined } =
|
|
48
52
|
testManagementTests?.mocha?.suites?.[testSuite]?.tests?.[testName]?.properties || {}
|
|
49
53
|
|
|
50
|
-
return { isDisabled, isQuarantined }
|
|
54
|
+
return { isAttemptToFix, isDisabled, isQuarantined }
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
function isNewTest (test, knownTests) {
|
|
@@ -57,15 +61,18 @@ function isNewTest (test, knownTests) {
|
|
|
57
61
|
return !testsForSuite.includes(testName)
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
function retryTest (test,
|
|
64
|
+
function retryTest (test, numRetries, modifyTestName, tags) {
|
|
61
65
|
const originalTestName = test.title
|
|
62
66
|
const suite = test.parent
|
|
63
|
-
for (let retryIndex = 0; retryIndex <
|
|
67
|
+
for (let retryIndex = 0; retryIndex < numRetries; retryIndex++) {
|
|
64
68
|
const clonedTest = test.clone()
|
|
65
|
-
clonedTest.title =
|
|
69
|
+
clonedTest.title = modifyTestName(originalTestName, retryIndex + 1)
|
|
66
70
|
suite.addTest(clonedTest)
|
|
67
|
-
|
|
68
|
-
|
|
71
|
+
tags.forEach(tag => {
|
|
72
|
+
if (tag) {
|
|
73
|
+
clonedTest[tag] = true
|
|
74
|
+
}
|
|
75
|
+
})
|
|
69
76
|
}
|
|
70
77
|
}
|
|
71
78
|
|
|
@@ -102,7 +109,10 @@ function getIsLastRetry (test) {
|
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
function getTestFullName (test) {
|
|
105
|
-
|
|
112
|
+
const testName = removeEfdStringFromTestName(
|
|
113
|
+
removeAttemptToFixStringFromTestName(test.fullTitle())
|
|
114
|
+
)
|
|
115
|
+
return `mocha.${getTestSuitePath(test.file, process.cwd())}.${testName}`
|
|
106
116
|
}
|
|
107
117
|
|
|
108
118
|
function getTestStatus (test) {
|
|
@@ -195,12 +205,15 @@ function getOnTestHandler (isMain) {
|
|
|
195
205
|
title,
|
|
196
206
|
_ddIsNew: isNew,
|
|
197
207
|
_ddIsEfdRetry: isEfdRetry,
|
|
208
|
+
_ddIsAttemptToFix: isAttemptToFix,
|
|
198
209
|
_ddIsDisabled: isDisabled,
|
|
199
210
|
_ddIsQuarantined: isQuarantined
|
|
200
211
|
} = test
|
|
201
212
|
|
|
213
|
+
const testName = removeEfdStringFromTestName(removeAttemptToFixStringFromTestName(test.fullTitle()))
|
|
214
|
+
|
|
202
215
|
const testInfo = {
|
|
203
|
-
testName
|
|
216
|
+
testName,
|
|
204
217
|
testSuiteAbsolutePath,
|
|
205
218
|
title,
|
|
206
219
|
testStartLine
|
|
@@ -212,6 +225,7 @@ function getOnTestHandler (isMain) {
|
|
|
212
225
|
|
|
213
226
|
testInfo.isNew = isNew
|
|
214
227
|
testInfo.isEfdRetry = isEfdRetry
|
|
228
|
+
testInfo.isAttemptToFix = isAttemptToFix
|
|
215
229
|
testInfo.isDisabled = isDisabled
|
|
216
230
|
testInfo.isQuarantined = isQuarantined
|
|
217
231
|
// We want to store the result of the new tests
|
|
@@ -224,7 +238,7 @@ function getOnTestHandler (isMain) {
|
|
|
224
238
|
}
|
|
225
239
|
}
|
|
226
240
|
|
|
227
|
-
if (isDisabled) {
|
|
241
|
+
if (!isAttemptToFix && isDisabled) {
|
|
228
242
|
test.pending = true
|
|
229
243
|
}
|
|
230
244
|
|
|
@@ -234,7 +248,7 @@ function getOnTestHandler (isMain) {
|
|
|
234
248
|
}
|
|
235
249
|
}
|
|
236
250
|
|
|
237
|
-
function getOnTestEndHandler () {
|
|
251
|
+
function getOnTestEndHandler (config) {
|
|
238
252
|
return async function (test) {
|
|
239
253
|
const asyncResource = getTestAsyncResource(test)
|
|
240
254
|
const status = getTestStatus(test)
|
|
@@ -249,13 +263,40 @@ function getOnTestEndHandler () {
|
|
|
249
263
|
})
|
|
250
264
|
}
|
|
251
265
|
|
|
266
|
+
let hasFailedAllRetries = false
|
|
267
|
+
let attemptToFixPassed = false
|
|
268
|
+
|
|
269
|
+
const testName = getTestFullName(test)
|
|
270
|
+
|
|
271
|
+
if (!testsStatuses.get(testName)) {
|
|
272
|
+
testsStatuses.set(testName, [status])
|
|
273
|
+
} else {
|
|
274
|
+
testsStatuses.get(testName).push(status)
|
|
275
|
+
}
|
|
276
|
+
const testStatuses = testsStatuses.get(testName)
|
|
277
|
+
|
|
278
|
+
const isLastAttempt = testStatuses.length === config.testManagementAttemptToFixRetries + 1
|
|
279
|
+
|
|
280
|
+
if (test._ddIsAttemptToFix && isLastAttempt) {
|
|
281
|
+
if (testStatuses.every(status => status === 'fail')) {
|
|
282
|
+
hasFailedAllRetries = true
|
|
283
|
+
} else if (testStatuses.every(status => status === 'pass')) {
|
|
284
|
+
attemptToFixPassed = true
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const isAttemptToFixRetry = test._ddIsAttemptToFix && testStatuses.length > 1
|
|
289
|
+
|
|
252
290
|
// if there are afterEach to be run, we don't finish the test yet
|
|
253
291
|
if (asyncResource && !getAfterEachHooks(test).length) {
|
|
254
292
|
asyncResource.runInAsyncScope(() => {
|
|
255
293
|
testFinishCh.publish({
|
|
256
294
|
status,
|
|
257
295
|
hasBeenRetried: isMochaRetry(test),
|
|
258
|
-
isLastRetry: getIsLastRetry(test)
|
|
296
|
+
isLastRetry: getIsLastRetry(test),
|
|
297
|
+
hasFailedAllRetries,
|
|
298
|
+
attemptToFixPassed,
|
|
299
|
+
isAttemptToFixRetry
|
|
259
300
|
})
|
|
260
301
|
})
|
|
261
302
|
}
|
|
@@ -374,33 +415,50 @@ function getOnPendingHandler () {
|
|
|
374
415
|
}
|
|
375
416
|
}
|
|
376
417
|
|
|
377
|
-
// Hook to add retries to tests if EFD is enabled
|
|
418
|
+
// Hook to add retries to tests if Test Management or EFD is enabled
|
|
378
419
|
function getRunTestsWrapper (runTests, config) {
|
|
379
|
-
return function (suite
|
|
420
|
+
return function (suite) {
|
|
421
|
+
if (config.isTestManagementTestsEnabled) {
|
|
422
|
+
suite.tests.forEach((test) => {
|
|
423
|
+
const { isAttemptToFix, isDisabled, isQuarantined } = getTestProperties(test, config.testManagementTests)
|
|
424
|
+
if (isAttemptToFix && !test.isPending()) {
|
|
425
|
+
test._ddIsAttemptToFix = true
|
|
426
|
+
test._ddIsDisabled = isDisabled
|
|
427
|
+
test._ddIsQuarantined = isQuarantined
|
|
428
|
+
// This is needed to know afterwards which ones have been retried to ignore its result
|
|
429
|
+
testsAttemptToFix.add(test)
|
|
430
|
+
retryTest(
|
|
431
|
+
test,
|
|
432
|
+
config.testManagementAttemptToFixRetries,
|
|
433
|
+
addAttemptToFixStringToTestName,
|
|
434
|
+
['_ddIsAttemptToFix', isDisabled && '_ddIsDisabled', isQuarantined && '_ddIsQuarantined']
|
|
435
|
+
)
|
|
436
|
+
} else if (isDisabled) {
|
|
437
|
+
test._ddIsDisabled = true
|
|
438
|
+
} else if (isQuarantined) {
|
|
439
|
+
testsQuarantined.add(test)
|
|
440
|
+
test._ddIsQuarantined = true
|
|
441
|
+
}
|
|
442
|
+
})
|
|
443
|
+
}
|
|
444
|
+
|
|
380
445
|
if (config.isKnownTestsEnabled) {
|
|
381
446
|
// by the time we reach `this.on('test')`, it is too late. We need to add retries here
|
|
382
447
|
suite.tests.forEach(test => {
|
|
383
448
|
if (!test.isPending() && isNewTest(test, config.knownTests)) {
|
|
384
449
|
test._ddIsNew = true
|
|
385
450
|
if (config.isEarlyFlakeDetectionEnabled) {
|
|
386
|
-
retryTest(
|
|
451
|
+
retryTest(
|
|
452
|
+
test,
|
|
453
|
+
config.earlyFlakeDetectionNumRetries,
|
|
454
|
+
addEfdStringToTestName,
|
|
455
|
+
['_ddIsNew', '_ddIsEfdRetry']
|
|
456
|
+
)
|
|
387
457
|
}
|
|
388
458
|
}
|
|
389
459
|
})
|
|
390
460
|
}
|
|
391
461
|
|
|
392
|
-
if (config.isTestManagementTestsEnabled) {
|
|
393
|
-
suite.tests.forEach(test => {
|
|
394
|
-
const { isDisabled, isQuarantined } = getTestProperties(test, config.testManagementTests)
|
|
395
|
-
if (isDisabled) {
|
|
396
|
-
test._ddIsDisabled = true
|
|
397
|
-
} else if (isQuarantined) {
|
|
398
|
-
testsQuarantined.add(test)
|
|
399
|
-
test._ddIsQuarantined = true
|
|
400
|
-
}
|
|
401
|
-
})
|
|
402
|
-
}
|
|
403
|
-
|
|
404
462
|
return runTests.apply(this, arguments)
|
|
405
463
|
}
|
|
406
464
|
}
|
|
@@ -408,7 +466,6 @@ function getRunTestsWrapper (runTests, config) {
|
|
|
408
466
|
module.exports = {
|
|
409
467
|
isNewTest,
|
|
410
468
|
getTestProperties,
|
|
411
|
-
retryTest,
|
|
412
469
|
getSuitesByTestFile,
|
|
413
470
|
isMochaRetry,
|
|
414
471
|
getTestFullName,
|
|
@@ -427,5 +484,7 @@ module.exports = {
|
|
|
427
484
|
testFileToSuiteAr,
|
|
428
485
|
getRunTestsWrapper,
|
|
429
486
|
newTests,
|
|
430
|
-
testsQuarantined
|
|
487
|
+
testsQuarantined,
|
|
488
|
+
testsAttemptToFix,
|
|
489
|
+
testsStatuses
|
|
431
490
|
}
|
|
@@ -36,6 +36,8 @@ addHook({
|
|
|
36
36
|
}
|
|
37
37
|
if (this.options._ddIsTestManagementTestsEnabled) {
|
|
38
38
|
config.isTestManagementTestsEnabled = true
|
|
39
|
+
// TODO: attempt to fix does not work in parallel mode yet
|
|
40
|
+
// config.testManagementAttemptToFixRetries = this.options._ddTestManagementAttemptToFixRetries
|
|
39
41
|
config.testManagementTests = this.options._ddTestManagementTests
|
|
40
42
|
delete this.options._ddIsTestManagementTestsEnabled
|
|
41
43
|
delete this.options._ddTestManagementTests
|
|
@@ -64,7 +66,7 @@ addHook({
|
|
|
64
66
|
})
|
|
65
67
|
this.on('test', getOnTestHandler(false))
|
|
66
68
|
|
|
67
|
-
this.on('test end', getOnTestEndHandler())
|
|
69
|
+
this.on('test end', getOnTestEndHandler(config))
|
|
68
70
|
|
|
69
71
|
// If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
|
|
70
72
|
this.on('hook end', getOnHookEndHandler())
|
|
@@ -22,6 +22,7 @@ const testToAr = new WeakMap()
|
|
|
22
22
|
const testSuiteToAr = new Map()
|
|
23
23
|
const testSuiteToTestStatuses = new Map()
|
|
24
24
|
const testSuiteToErrors = new Map()
|
|
25
|
+
const testsToTestStatuses = new Map()
|
|
25
26
|
const testSessionAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
26
27
|
|
|
27
28
|
let applyRepeatEachIndex = null
|
|
@@ -43,7 +44,9 @@ let isFlakyTestRetriesEnabled = false
|
|
|
43
44
|
let flakyTestRetriesCount = 0
|
|
44
45
|
let knownTests = {}
|
|
45
46
|
let isTestManagementTestsEnabled = false
|
|
47
|
+
let testManagementAttemptToFixRetries = 0
|
|
46
48
|
let testManagementTests = {}
|
|
49
|
+
const quarantinedOrDisabledTestsAttemptToFix = []
|
|
47
50
|
let rootDir = ''
|
|
48
51
|
const MINIMUM_SUPPORTED_VERSION_RANGE_EFD = '>=1.38.0'
|
|
49
52
|
|
|
@@ -51,10 +54,9 @@ function getTestProperties (test) {
|
|
|
51
54
|
const testName = getTestFullname(test)
|
|
52
55
|
const testSuite = getTestSuitePath(test._requireFile, rootDir)
|
|
53
56
|
|
|
54
|
-
const { disabled, quarantined } =
|
|
57
|
+
const { attempt_to_fix: attemptToFix, disabled, quarantined } =
|
|
55
58
|
testManagementTests?.playwright?.suites?.[testSuite]?.tests?.[testName]?.properties || {}
|
|
56
|
-
|
|
57
|
-
return { disabled, quarantined }
|
|
59
|
+
return { attemptToFix, disabled, quarantined }
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
function isNewTest (test) {
|
|
@@ -73,16 +75,19 @@ function getSuiteType (test, type) {
|
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
// Copy of Suite#_deepClone but with a function to filter tests
|
|
76
|
-
function deepCloneSuite (suite, filterTest) {
|
|
78
|
+
function deepCloneSuite (suite, filterTest, tags = []) {
|
|
77
79
|
const copy = suite._clone()
|
|
78
80
|
for (const entry of suite._entries) {
|
|
79
81
|
if (entry.constructor.name === 'Suite') {
|
|
80
|
-
copy._addSuite(deepCloneSuite(entry, filterTest))
|
|
82
|
+
copy._addSuite(deepCloneSuite(entry, filterTest, tags))
|
|
81
83
|
} else {
|
|
82
84
|
if (filterTest(entry)) {
|
|
83
85
|
const copiedTest = entry._clone()
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
tags.forEach(tag => {
|
|
87
|
+
if (tag) {
|
|
88
|
+
copiedTest[tag] = true
|
|
89
|
+
}
|
|
90
|
+
})
|
|
86
91
|
copy._addTest(copiedTest)
|
|
87
92
|
}
|
|
88
93
|
}
|
|
@@ -276,6 +281,11 @@ function testBeginHandler (test, browserName) {
|
|
|
276
281
|
})
|
|
277
282
|
}
|
|
278
283
|
|
|
284
|
+
// We disable retries by default if attemptToFix is true
|
|
285
|
+
if (getTestProperties(test).attemptToFix) {
|
|
286
|
+
test.retries = 0
|
|
287
|
+
}
|
|
288
|
+
|
|
279
289
|
const testAsyncResource = new AsyncResource('bound-anonymous-fn')
|
|
280
290
|
testToAr.set(test, testAsyncResource)
|
|
281
291
|
testAsyncResource.runInAsyncScope(() => {
|
|
@@ -288,7 +298,6 @@ function testBeginHandler (test, browserName) {
|
|
|
288
298
|
})
|
|
289
299
|
})
|
|
290
300
|
}
|
|
291
|
-
|
|
292
301
|
function testEndHandler (test, annotations, testStatus, error, isTimeout) {
|
|
293
302
|
let annotationTags
|
|
294
303
|
if (annotations.length) {
|
|
@@ -305,6 +314,27 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
|
|
|
305
314
|
return
|
|
306
315
|
}
|
|
307
316
|
|
|
317
|
+
const testFullName = getTestFullname(test)
|
|
318
|
+
const testFqn = `${testSuiteAbsolutePath} ${testFullName}`
|
|
319
|
+
const testStatuses = testsToTestStatuses.get(testFqn) || []
|
|
320
|
+
|
|
321
|
+
if (testStatuses.length === 0) {
|
|
322
|
+
testsToTestStatuses.set(testFqn, [testStatus])
|
|
323
|
+
} else {
|
|
324
|
+
testStatuses.push(testStatus)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
let hasFailedAllRetries = false
|
|
328
|
+
let hasPassedAttemptToFixRetries = false
|
|
329
|
+
|
|
330
|
+
if (testStatuses.length === testManagementAttemptToFixRetries + 1) {
|
|
331
|
+
if (testStatuses.every(status => status === 'fail')) {
|
|
332
|
+
hasFailedAllRetries = true
|
|
333
|
+
} else if (testStatuses.every(status => status === 'pass')) {
|
|
334
|
+
hasPassedAttemptToFixRetries = true
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
308
338
|
const testResult = results[results.length - 1]
|
|
309
339
|
const testAsyncResource = testToAr.get(test)
|
|
310
340
|
testAsyncResource.runInAsyncScope(() => {
|
|
@@ -315,8 +345,12 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
|
|
|
315
345
|
error,
|
|
316
346
|
extraTags: annotationTags,
|
|
317
347
|
isNew: test._ddIsNew,
|
|
348
|
+
isAttemptToFix: test._ddIsAttemptToFix,
|
|
349
|
+
isAttemptToFixRetry: test._ddIsAttemptToFixRetry,
|
|
318
350
|
isQuarantined: test._ddIsQuarantined,
|
|
319
|
-
isEfdRetry: test._ddIsEfdRetry
|
|
351
|
+
isEfdRetry: test._ddIsEfdRetry,
|
|
352
|
+
hasFailedAllRetries,
|
|
353
|
+
hasPassedAttemptToFixRetries
|
|
320
354
|
})
|
|
321
355
|
})
|
|
322
356
|
|
|
@@ -338,7 +372,6 @@ function testEndHandler (test, annotations, testStatus, error, isTimeout) {
|
|
|
338
372
|
// Last test, we finish the suite
|
|
339
373
|
if (!remainingTestsByFile[testSuiteAbsolutePath].length) {
|
|
340
374
|
const testStatuses = testSuiteToTestStatuses.get(testSuiteAbsolutePath)
|
|
341
|
-
|
|
342
375
|
let testSuiteStatus = 'pass'
|
|
343
376
|
if (testStatuses.some(status => status === 'fail')) {
|
|
344
377
|
testSuiteStatus = 'fail'
|
|
@@ -445,6 +478,7 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
445
478
|
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
446
479
|
flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
|
|
447
480
|
isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
|
|
481
|
+
testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
|
|
448
482
|
}
|
|
449
483
|
} catch (e) {
|
|
450
484
|
isEarlyFlakeDetectionEnabled = false
|
|
@@ -485,7 +519,10 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
485
519
|
|
|
486
520
|
const projects = getProjectsFromRunner(this)
|
|
487
521
|
|
|
488
|
-
|
|
522
|
+
const shouldSetRetries = isFlakyTestRetriesEnabled &&
|
|
523
|
+
flakyTestRetriesCount > 0 &&
|
|
524
|
+
!isTestManagementTestsEnabled
|
|
525
|
+
if (shouldSetRetries) {
|
|
489
526
|
projects.forEach(project => {
|
|
490
527
|
if (project.retries === 0) { // Only if it hasn't been set by the user
|
|
491
528
|
project.retries = flakyTestRetriesCount
|
|
@@ -493,7 +530,7 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
493
530
|
})
|
|
494
531
|
}
|
|
495
532
|
|
|
496
|
-
|
|
533
|
+
let runAllTestsReturn = await runAllTests.apply(this, arguments)
|
|
497
534
|
|
|
498
535
|
Object.values(remainingTestsByFile).forEach(tests => {
|
|
499
536
|
// `tests` should normally be empty, but if it isn't,
|
|
@@ -508,6 +545,26 @@ function runnerHook (runnerExport, playwrightVersion) {
|
|
|
508
545
|
|
|
509
546
|
const sessionStatus = runAllTestsReturn.status || runAllTestsReturn
|
|
510
547
|
|
|
548
|
+
if (isTestManagementTestsEnabled && sessionStatus === 'failed') {
|
|
549
|
+
let totalFailedTestCount = 0
|
|
550
|
+
let totalAttemptToFixFailedTestCount = 0
|
|
551
|
+
|
|
552
|
+
for (const testStatuses of testsToTestStatuses.values()) {
|
|
553
|
+
totalFailedTestCount += testStatuses.filter(status => status === 'fail').length
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
for (const test of quarantinedOrDisabledTestsAttemptToFix) {
|
|
557
|
+
const fullname = getTestFullname(test)
|
|
558
|
+
const fqn = `${test._requireFile} ${fullname}`
|
|
559
|
+
const testStatuses = testsToTestStatuses.get(fqn)
|
|
560
|
+
totalAttemptToFixFailedTestCount += testStatuses.filter(status => status === 'fail').length
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
if (totalFailedTestCount === totalAttemptToFixFailedTestCount) {
|
|
564
|
+
runAllTestsReturn = 'passed'
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
511
568
|
const flushWait = new Promise(resolve => {
|
|
512
569
|
onDone = resolve
|
|
513
570
|
})
|
|
@@ -608,9 +665,28 @@ addHook({
|
|
|
608
665
|
const testProperties = getTestProperties(test)
|
|
609
666
|
if (testProperties.disabled) {
|
|
610
667
|
test._ddIsDisabled = true
|
|
611
|
-
test.expectedStatus = 'skipped'
|
|
612
668
|
} else if (testProperties.quarantined) {
|
|
613
669
|
test._ddIsQuarantined = true
|
|
670
|
+
}
|
|
671
|
+
if (testProperties.attemptToFix) {
|
|
672
|
+
test._ddIsAttemptToFix = true
|
|
673
|
+
const fileSuite = getSuiteType(test, 'file')
|
|
674
|
+
const projectSuite = getSuiteType(test, 'project')
|
|
675
|
+
const isAttemptToFix = test => getTestProperties(test).attemptToFix
|
|
676
|
+
for (let repeatEachIndex = 1; repeatEachIndex <= testManagementAttemptToFixRetries; repeatEachIndex++) {
|
|
677
|
+
const copyFileSuite = deepCloneSuite(fileSuite, isAttemptToFix, [
|
|
678
|
+
testProperties.disabled && '_ddIsDisabled',
|
|
679
|
+
testProperties.quarantined && '_ddIsQuarantined',
|
|
680
|
+
'_ddIsAttemptToFix',
|
|
681
|
+
'_ddIsAttemptToFixRetry'
|
|
682
|
+
])
|
|
683
|
+
applyRepeatEachIndex(projectSuite._fullProject, copyFileSuite, repeatEachIndex + 1)
|
|
684
|
+
projectSuite._addSuite(copyFileSuite)
|
|
685
|
+
}
|
|
686
|
+
if (testProperties.disabled || testProperties.quarantined) {
|
|
687
|
+
quarantinedOrDisabledTestsAttemptToFix.push(test)
|
|
688
|
+
}
|
|
689
|
+
} else if (testProperties.disabled || testProperties.quarantined) {
|
|
614
690
|
test.expectedStatus = 'skipped'
|
|
615
691
|
}
|
|
616
692
|
}
|
|
@@ -619,18 +695,22 @@ addHook({
|
|
|
619
695
|
if (isKnownTestsEnabled) {
|
|
620
696
|
const newTests = allTests.filter(isNewTest)
|
|
621
697
|
|
|
622
|
-
|
|
698
|
+
for (const newTest of newTests) {
|
|
699
|
+
// No need to filter out attempt to fix tests here because attempt to fix tests are never new
|
|
623
700
|
newTest._ddIsNew = true
|
|
624
701
|
if (isEarlyFlakeDetectionEnabled && newTest.expectedStatus !== 'skipped') {
|
|
625
702
|
const fileSuite = getSuiteType(newTest, 'file')
|
|
626
703
|
const projectSuite = getSuiteType(newTest, 'project')
|
|
627
|
-
for (let repeatEachIndex =
|
|
628
|
-
const copyFileSuite = deepCloneSuite(fileSuite, isNewTest
|
|
704
|
+
for (let repeatEachIndex = 1; repeatEachIndex <= earlyFlakeDetectionNumRetries; repeatEachIndex++) {
|
|
705
|
+
const copyFileSuite = deepCloneSuite(fileSuite, isNewTest, [
|
|
706
|
+
'_ddIsNew',
|
|
707
|
+
'_ddIsEfdRetry'
|
|
708
|
+
])
|
|
629
709
|
applyRepeatEachIndex(projectSuite._fullProject, copyFileSuite, repeatEachIndex + 1)
|
|
630
710
|
projectSuite._addSuite(copyFileSuite)
|
|
631
711
|
}
|
|
632
712
|
}
|
|
633
|
-
}
|
|
713
|
+
}
|
|
634
714
|
}
|
|
635
715
|
|
|
636
716
|
return rootSuite
|
|
@@ -5,6 +5,7 @@ const pathToRegExp = require('path-to-regexp')
|
|
|
5
5
|
const shimmer = require('../../datadog-shimmer')
|
|
6
6
|
const { addHook, channel } = require('./helpers/instrument')
|
|
7
7
|
|
|
8
|
+
// TODO: Move this function to a shared file between Express and Router
|
|
8
9
|
function createWrapRouterMethod (name) {
|
|
9
10
|
const enterChannel = channel(`apm:${name}:middleware:enter`)
|
|
10
11
|
const exitChannel = channel(`apm:${name}:middleware:exit`)
|
|
@@ -16,7 +16,7 @@ addHook({ name: 'tedious', versions: ['>=1.0.0'] }, tedious => {
|
|
|
16
16
|
return makeRequest.apply(this, arguments)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const queryOrProcedure = getQueryOrProcedure(request)
|
|
19
|
+
const [queryOrProcedure, queryParent, queryField] = getQueryOrProcedure(request)
|
|
20
20
|
|
|
21
21
|
if (!queryOrProcedure) {
|
|
22
22
|
return makeRequest.apply(this, arguments)
|
|
@@ -28,7 +28,9 @@ addHook({ name: 'tedious', versions: ['>=1.0.0'] }, tedious => {
|
|
|
28
28
|
const connectionConfig = this.config
|
|
29
29
|
|
|
30
30
|
return asyncResource.runInAsyncScope(() => {
|
|
31
|
-
|
|
31
|
+
const payload = { queryOrProcedure, connectionConfig }
|
|
32
|
+
startCh.publish(payload)
|
|
33
|
+
queryParent[queryField] = payload.sql
|
|
32
34
|
|
|
33
35
|
const cb = callbackResource.bind(request.callback, request)
|
|
34
36
|
request.callback = asyncResource.bind(function (error) {
|
|
@@ -53,14 +55,15 @@ addHook({ name: 'tedious', versions: ['>=1.0.0'] }, tedious => {
|
|
|
53
55
|
return tedious
|
|
54
56
|
})
|
|
55
57
|
|
|
58
|
+
// returns [queryOrProcedure, parentObjectToSet, propertyNameToSet]
|
|
56
59
|
function getQueryOrProcedure (request) {
|
|
57
|
-
if (!request.parameters) return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
return request.
|
|
60
|
+
if (!request.parameters) return [null]
|
|
61
|
+
|
|
62
|
+
if (request.parametersByName.statement) {
|
|
63
|
+
return [request.parametersByName.statement.value, request.parametersByName.statement, 'value']
|
|
64
|
+
} else if (request.parametersByName.stmt) {
|
|
65
|
+
return [request.parametersByName.stmt.value, request.parametersByName.stmt, 'value']
|
|
66
|
+
} else {
|
|
67
|
+
return [request.sqlTextOrProcedure, request, 'sqlTextOrProcedure']
|
|
63
68
|
}
|
|
64
|
-
|
|
65
|
-
return statement.value
|
|
66
69
|
}
|