dd-trace 5.99.1 → 5.101.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.
Files changed (101) hide show
  1. package/LICENSE-3rdparty.csv +0 -1
  2. package/index.d.ts +14 -0
  3. package/package.json +8 -8
  4. package/packages/datadog-instrumentations/src/cucumber.js +69 -5
  5. package/packages/datadog-instrumentations/src/cypress.js +5 -3
  6. package/packages/datadog-instrumentations/src/express.js +3 -2
  7. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  8. package/packages/datadog-instrumentations/src/hono.js +15 -4
  9. package/packages/datadog-instrumentations/src/http/client.js +20 -3
  10. package/packages/datadog-instrumentations/src/jest.js +146 -90
  11. package/packages/datadog-instrumentations/src/mocha/common.js +4 -1
  12. package/packages/datadog-instrumentations/src/mocha/main.js +43 -26
  13. package/packages/datadog-instrumentations/src/mocha/utils.js +114 -96
  14. package/packages/datadog-instrumentations/src/mocha/worker.js +7 -4
  15. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +11 -6
  16. package/packages/datadog-instrumentations/src/path-to-regexp.js +44 -0
  17. package/packages/datadog-instrumentations/src/playwright.js +108 -18
  18. package/packages/datadog-instrumentations/src/router.js +53 -33
  19. package/packages/datadog-instrumentations/src/vitest.js +76 -30
  20. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -1
  21. package/packages/datadog-plugin-aws-sdk/src/services/dynamodb.js +1 -1
  22. package/packages/datadog-plugin-bullmq/src/consumer.js +5 -4
  23. package/packages/datadog-plugin-bullmq/src/producer.js +37 -29
  24. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +49 -9
  25. package/packages/datadog-plugin-cypress/src/plugin.js +5 -14
  26. package/packages/datadog-plugin-cypress/src/support.js +22 -21
  27. package/packages/datadog-plugin-grpc/src/client.js +1 -1
  28. package/packages/datadog-plugin-grpc/src/server.js +1 -1
  29. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -9
  30. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -8
  31. package/packages/datadog-plugin-mongodb-core/src/index.js +2 -3
  32. package/packages/datadog-plugin-playwright/src/index.js +6 -0
  33. package/packages/datadog-plugin-router/src/index.js +13 -0
  34. package/packages/dd-trace/index.js +4 -3
  35. package/packages/dd-trace/src/aiguard/sdk.js +2 -2
  36. package/packages/dd-trace/src/appsec/reporter.js +4 -1
  37. package/packages/dd-trace/src/baggage.js +10 -0
  38. package/packages/dd-trace/src/ci-visibility/lage.js +2 -1
  39. package/packages/dd-trace/src/ci-visibility/requests/request.js +11 -33
  40. package/packages/dd-trace/src/config/config-types.d.ts +0 -2
  41. package/packages/dd-trace/src/config/generated-config-types.d.ts +17 -41
  42. package/packages/dd-trace/src/config/index.js +7 -60
  43. package/packages/dd-trace/src/config/normalize-service.js +31 -0
  44. package/packages/dd-trace/src/config/supported-configurations.json +15 -32
  45. package/packages/dd-trace/src/datastreams/checkpointer.js +4 -10
  46. package/packages/dd-trace/src/datastreams/encoding.js +39 -28
  47. package/packages/dd-trace/src/datastreams/pathway.js +29 -26
  48. package/packages/dd-trace/src/datastreams/processor.js +17 -15
  49. package/packages/dd-trace/src/datastreams/size.js +6 -2
  50. package/packages/dd-trace/src/debugger/config.js +6 -3
  51. package/packages/dd-trace/src/debugger/devtools_client/index.js +2 -5
  52. package/packages/dd-trace/src/debugger/devtools_client/send.js +2 -1
  53. package/packages/dd-trace/src/dogstatsd.js +10 -7
  54. package/packages/dd-trace/src/encode/0.4.js +3 -3
  55. package/packages/dd-trace/src/encode/0.5.js +2 -2
  56. package/packages/dd-trace/src/encode/agentless-json.js +2 -2
  57. package/packages/dd-trace/src/encode/tags-processors.js +2 -27
  58. package/packages/dd-trace/src/exporters/common/request.js +22 -11
  59. package/packages/dd-trace/src/exporters/common/retry.js +104 -0
  60. package/packages/dd-trace/src/git_metadata.js +66 -0
  61. package/packages/dd-trace/src/git_metadata_tagger.js +13 -5
  62. package/packages/dd-trace/src/heap_snapshots.js +4 -4
  63. package/packages/dd-trace/src/id.js +15 -26
  64. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  65. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +27 -16
  66. package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +3 -0
  67. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +30 -13
  68. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +20 -50
  69. package/packages/dd-trace/src/llmobs/sdk.js +5 -1
  70. package/packages/dd-trace/src/llmobs/span_processor.js +28 -2
  71. package/packages/dd-trace/src/llmobs/tagger.js +42 -0
  72. package/packages/dd-trace/src/llmobs/telemetry.js +29 -0
  73. package/packages/dd-trace/src/llmobs/util.js +80 -5
  74. package/packages/dd-trace/src/openfeature/eval-metrics-hook.js +2 -2
  75. package/packages/dd-trace/src/opentelemetry/active-span-proxy.js +42 -0
  76. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +106 -0
  77. package/packages/dd-trace/src/opentelemetry/context_manager.js +22 -10
  78. package/packages/dd-trace/src/opentelemetry/span-helpers.js +308 -0
  79. package/packages/dd-trace/src/opentelemetry/span.js +42 -108
  80. package/packages/dd-trace/src/opentelemetry/tracer.js +11 -36
  81. package/packages/dd-trace/src/opentracing/propagation/text_map.js +95 -36
  82. package/packages/dd-trace/src/opentracing/propagation/tracestate.js +98 -32
  83. package/packages/dd-trace/src/opentracing/span.js +58 -49
  84. package/packages/dd-trace/src/opentracing/span_context.js +1 -0
  85. package/packages/dd-trace/src/plugins/util/ci.js +119 -32
  86. package/packages/dd-trace/src/plugins/util/test.js +293 -27
  87. package/packages/dd-trace/src/priority_sampler.js +6 -4
  88. package/packages/dd-trace/src/profiling/config.js +5 -4
  89. package/packages/dd-trace/src/profiling/ssi-heuristics.js +2 -2
  90. package/packages/dd-trace/src/propagation-hash/index.js +1 -1
  91. package/packages/dd-trace/src/proxy.js +3 -3
  92. package/packages/dd-trace/src/remote_config/index.js +5 -3
  93. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +1 -1
  94. package/packages/dd-trace/src/span_format.js +52 -5
  95. package/packages/dd-trace/src/span_processor.js +1 -5
  96. package/packages/dd-trace/src/spanleak.js +0 -1
  97. package/packages/dd-trace/src/telemetry/telemetry.js +7 -5
  98. package/packages/dd-trace/src/tracer_metadata.js +1 -1
  99. package/packages/dd-trace/src/util.js +17 -0
  100. package/vendor/dist/path-to-regexp/LICENSE +0 -21
  101. package/vendor/dist/path-to-regexp/index.js +0 -1
@@ -4,6 +4,8 @@
4
4
  const realSetTimeout = setTimeout
5
5
 
6
6
  const path = require('path')
7
+ const satisfies = require('../../../vendor/dist/semifies')
8
+ const { DD_MAJOR } = require('../../../version')
7
9
  const shimmer = require('../../datadog-shimmer')
8
10
  const log = require('../../dd-trace/src/log')
9
11
  const {
@@ -21,6 +23,9 @@ const {
21
23
  isModifiedTest,
22
24
  DYNAMIC_NAME_RE,
23
25
  collectDynamicNamesFromTraces,
26
+ recordAttemptToFixExecution,
27
+ logAttemptToFixTestExecution,
28
+ logTestOptimizationSummary,
24
29
  } = require('../../dd-trace/src/plugins/util/test')
25
30
  const {
26
31
  SEED_SUFFIX_RE,
@@ -107,6 +112,7 @@ const wrappedWorkerChannels = new WeakMap()
107
112
  // New tests whose names contain likely dynamic data (timestamps, UUIDs, etc.)
108
113
  // Populated in-process for runInBand, and via worker-report:trace for parallel mode.
109
114
  const newTestsWithDynamicNames = new Set()
115
+ const loggedAttemptToFixTests = new Set()
110
116
  const testSuiteMockedFiles = new Map()
111
117
  const testsToBeRetried = new Set()
112
118
  // Per-test: how many EFD retries were determined after the first execution.
@@ -122,7 +128,13 @@ const testSuiteJestObjects = new Map()
122
128
 
123
129
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
124
130
  const ATR_RETRY_SUPPRESSION_FLAG = '_ddDisableAtrRetry'
131
+ const MINIMUM_JEST_VERSION = DD_MAJOR >= 6 ? '>=28.0.0' : '>=24.8.0'
132
+ const MINIMUM_JEST_VERSION_BEFORE_30 = DD_MAJOR >= 6 ? '>=28.0.0 <30.0.0' : '>=24.8.0 <30.0.0'
133
+ const MINIMUM_JEST_WORKER_VERSION_BEFORE_30 = DD_MAJOR >= 6 ? '>=28.0.0 <30.0.0' : '>=24.9.0 <30.0.0'
134
+ const MINIMUM_JEST_CONFIG_ASYNC_VERSION = DD_MAJOR >= 6 ? '>=28.0.0' : '>=25.1.0'
135
+ const MINIMUM_JEST_TEST_SCHEDULER_VERSION = DD_MAJOR >= 6 ? '>=28.0.0' : '>=27.0.0'
125
136
  const atrSuppressedErrors = new Map()
137
+ let hasWarnedDeprecatedJestVersion = false
126
138
 
127
139
  // Track quarantined tests whose errors were suppressed, keyed by "suite › testName"
128
140
  const quarantinedFailingTests = new Set()
@@ -173,6 +185,20 @@ function formatJestError (errors) {
173
185
  return error
174
186
  }
175
187
 
188
+ function warnDeprecatedJestVersion (frameworkVersion) {
189
+ if (DD_MAJOR >= 6 || hasWarnedDeprecatedJestVersion || !frameworkVersion ||
190
+ !satisfies(frameworkVersion, '<28.0.0')) {
191
+ return
192
+ }
193
+
194
+ hasWarnedDeprecatedJestVersion = true
195
+ // eslint-disable-next-line no-console
196
+ console.warn(
197
+ 'dd-trace support for Jest<28.0.0 is deprecated and will be removed in dd-trace v6. ' +
198
+ 'Please upgrade Jest to >=28.0.0.'
199
+ )
200
+ }
201
+
176
202
  function getTestEnvironmentOptions (config) {
177
203
  if (config.projectConfig && config.projectConfig.testEnvironmentOptions) { // newer versions
178
204
  return config.projectConfig.testEnvironmentOptions
@@ -193,64 +219,88 @@ function getTestStats (testStatuses) {
193
219
  }
194
220
 
195
221
  /**
196
- * @param {string[]} efdNames
197
- * @param {string[]} quarantineNames
198
- * @param {number} totalCount
199
- */
200
- /**
201
- * Renders a truncated bullet list from an array of items.
222
+ * Formats the ignored-failure section for the Test Optimization summary.
202
223
  *
203
- * @param {Array<{ text: string, suffix?: string }>} items
224
+ * @param {{ efdNames: string[], quarantineNames: string[], totalCount: number } | undefined} ignoredFailures
204
225
  * @returns {string}
205
226
  */
206
- function formatList (items) {
227
+ function formatIgnoredFailuresSummary (ignoredFailures) {
228
+ if (!ignoredFailures) return ''
229
+
230
+ const items = []
231
+
232
+ for (const n of ignoredFailures.efdNames) {
233
+ items.push({ text: n, suffix: 'Early Flake Detection' })
234
+ }
235
+ for (const n of ignoredFailures.quarantineNames) {
236
+ items.push({ text: n, suffix: 'Quarantine' })
237
+ }
238
+
239
+ if (items.length === 0 || ignoredFailures.totalCount <= 0) return ''
240
+
207
241
  const shown = items.slice(0, MAX_IGNORED_TEST_NAMES)
208
242
  const more = items.length - shown.length
209
243
  const moreSuffix = more > 0 ? `\n ... and ${more} more` : ''
210
- return shown.map(({ text, suffix }) => ` • ${text}${suffix ? ` (${suffix})` : ''}`).join('\n') + moreSuffix
244
+ const formattedItems = shown
245
+ .map(({ text, suffix }) => ` • ${text}${suffix ? ` (${suffix})` : ''}`)
246
+ .join('\n') + moreSuffix
247
+
248
+ return `${ignoredFailures.totalCount} test failure(s) were ignored. Exit code set to 0.\n\n${formattedItems}`
211
249
  }
212
250
 
213
251
  /**
214
- * Logs a single "Datadog Test Optimization" summary at session end,
215
- * combining all relevant sections (ignored failures, dynamic names).
252
+ * Logs a single "Datadog Test Optimization" summary at session end.
216
253
  *
217
254
  * @param {{ efdNames: string[], quarantineNames: string[], totalCount: number } | undefined} ignoredFailures
218
255
  */
219
- function logSessionSummary (ignoredFailures) {
220
- const sections = []
256
+ function logSessionSummary (ignoredFailures, attemptToFixExecutions) {
257
+ logTestOptimizationSummary({
258
+ attemptToFixExecutions,
259
+ extraSections: [formatIgnoredFailuresSummary(ignoredFailures)],
260
+ newTestsWithDynamicNames,
261
+ })
262
+ loggedAttemptToFixTests.clear()
263
+ }
221
264
 
222
- if (ignoredFailures) {
223
- const items = []
224
- for (const n of ignoredFailures.efdNames) {
225
- items.push({ text: n, suffix: 'Early Flake Detection' })
226
- }
227
- for (const n of ignoredFailures.quarantineNames) {
228
- items.push({ text: n, suffix: 'Quarantine' })
229
- }
230
- sections.push(
231
- `${ignoredFailures.totalCount} test failure(s) were ignored. Exit code set to 0.\n\n` +
232
- formatList(items)
233
- )
234
- }
265
+ function getTestStatusFromJestResult (status) {
266
+ if (status === 'failed') return 'fail'
267
+ if (status === 'passed') return 'pass'
268
+ }
235
269
 
236
- if (newTestsWithDynamicNames.size > 0) {
237
- const items = [...newTestsWithDynamicNames].map(name => ({ text: name }))
238
- sections.push(
239
- `${newTestsWithDynamicNames.size} test(s) detected as new but their names contain ` +
240
- 'dynamic data (timestamps, UUIDs, etc.).\n' +
241
- 'Tests with changing names are always treated as new on every run, ' +
242
- 'causing unnecessary Early Flake Detection retries and preventing correct new test detection.\n' +
243
- 'Consider using stable, deterministic test names.\n\n' +
244
- formatList(items)
245
- )
246
- newTestsWithDynamicNames.clear()
270
+ function getAttemptToFixExecutionsFromJestResults (result) {
271
+ const executions = new Map()
272
+ const rootDir = result.globalConfig?.rootDir || process.cwd()
273
+
274
+ for (const { testResults, testFilePath } of result.results.testResults) {
275
+ const testSuite = getTestSuitePath(testFilePath, rootDir)
276
+ const testManagementTestsForSuite = testManagementTests
277
+ ?.jest
278
+ ?.suites
279
+ ?.[testSuite]
280
+ ?.tests
281
+ if (!testManagementTestsForSuite) continue
282
+
283
+ for (const { fullName, status } of testResults) {
284
+ const testName = testSuiteAbsolutePathsWithFastCheck.has(testFilePath)
285
+ ? fullName.replace(SEED_SUFFIX_RE, '')
286
+ : fullName
287
+ const testStatus = getTestStatusFromJestResult(status)
288
+ if (!testStatus) continue
289
+
290
+ const testManagementTest = testManagementTestsForSuite[testName]?.properties
291
+ if (!testManagementTest?.attempt_to_fix) continue
292
+
293
+ recordAttemptToFixExecution(executions, {
294
+ testSuite,
295
+ testName,
296
+ status: testStatus,
297
+ isDisabled: testManagementTest.disabled,
298
+ isQuarantined: testManagementTest.quarantined,
299
+ })
300
+ }
247
301
  }
248
302
 
249
- if (sections.length === 0) return
250
-
251
- const line = '-'.repeat(50)
252
- // eslint-disable-next-line no-console -- Intentional user-facing session summary
253
- console.warn(`\n${line}\nDatadog Test Optimization\n${line}\n${sections.join('\n\n')}\n`)
303
+ return executions
254
304
  }
255
305
 
256
306
  function getWrappedEnvironment (BaseEnvironment, jestVersion) {
@@ -556,6 +606,10 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
556
606
  }
557
607
  testContexts.set(event.test, ctx)
558
608
 
609
+ if (isAttemptToFix) {
610
+ logAttemptToFixTestExecution(this.testSuite, testName, loggedAttemptToFixTests)
611
+ }
612
+
559
613
  testStartCh.runStores(ctx, () => {
560
614
  let p = event.test.parent
561
615
  const hooks = []
@@ -795,7 +849,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
795
849
  // Only suppress on the final execution — not when ATR/EFD/ATF will retry the test.
796
850
  if (!event.test?.[ATR_RETRY_SUPPRESSION_FLAG] && !willBeRetriedByFailedTestReplay) {
797
851
  const quarantineCtx = testContexts.get(event.test)
798
- if (quarantineCtx?.isQuarantined && event.test.errors?.length) {
852
+ if (quarantineCtx?.isQuarantined && !quarantineCtx.isAttemptToFix && event.test.errors?.length) {
799
853
  quarantinedFailingTests.add(`${quarantineCtx.suite} › ${quarantineCtx.name}`)
800
854
  event.test.errors = []
801
855
  }
@@ -858,10 +912,10 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
858
912
  }
859
913
  if (event.name === 'run_finish') {
860
914
  for (const [test, errors] of atrSuppressedErrors) {
861
- // Do not restore errors for quarantined tests — they should stay suppressed
915
+ // Do not restore errors for non-ATF quarantined tests — they should stay suppressed
862
916
  // so Jest doesn't see the failure (prevents --bail from stopping the run).
863
917
  const ctx = testContexts.get(test)
864
- if (ctx?.isQuarantined) {
918
+ if (ctx?.isQuarantined && !ctx.isAttemptToFix) {
865
919
  const testName = getJestTestName(test, this.getShouldStripSeedFromTestName())
866
920
  quarantinedFailingTests.add(`${ctx.suite} › ${testName}`)
867
921
  } else {
@@ -984,7 +1038,8 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
984
1038
 
985
1039
  // If the test is quarantined, regardless of its actual execution result,
986
1040
  // the final status of its last execution should be reported as 'skip'.
987
- if (this.isTestManagementTestsEnabled &&
1041
+ if (!attemptToFixResult.isAttemptToFixEnabled &&
1042
+ this.isTestManagementTestsEnabled &&
988
1043
  this.testManagementTestsForThisSuite?.quarantined?.includes(testName)) {
989
1044
  return 'skip'
990
1045
  }
@@ -1040,12 +1095,12 @@ function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
1040
1095
 
1041
1096
  addHook({
1042
1097
  name: 'jest-environment-node',
1043
- versions: ['>=24.8.0'],
1098
+ versions: [MINIMUM_JEST_VERSION],
1044
1099
  }, getTestEnvironment)
1045
1100
 
1046
1101
  addHook({
1047
1102
  name: 'jest-environment-jsdom',
1048
- versions: ['>=24.8.0'],
1103
+ versions: [MINIMUM_JEST_VERSION],
1049
1104
  }, getTestEnvironment)
1050
1105
 
1051
1106
  addHook({
@@ -1117,6 +1172,8 @@ function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
1117
1172
 
1118
1173
  function getCliWrapper (isNewJestVersion) {
1119
1174
  return function cliWrapper (cli, jestVersion) {
1175
+ warnDeprecatedJestVersion(jestVersion)
1176
+
1120
1177
  if (isNewJestVersion) {
1121
1178
  cli = shimmer.wrap(
1122
1179
  cli,
@@ -1315,7 +1372,6 @@ function getCliWrapper (isNewJestVersion) {
1315
1372
  }
1316
1373
 
1317
1374
  let numFailedQuarantinedTests = 0
1318
- let numFailedQuarantinedOrDisabledAttemptedToFixTests = 0
1319
1375
  let numSuppressedQuarantinedTests = 0
1320
1376
  if (isTestManagementTestsEnabled) {
1321
1377
  const failedTests = result
@@ -1343,11 +1399,7 @@ function getCliWrapper (isNewJestVersion) {
1343
1399
  ?.tests
1344
1400
  ?.[testName]
1345
1401
  ?.properties
1346
- // This uses `attempt_to_fix` because this is always the main process and it's not formatted in camelCase
1347
- if (testManagementTest?.attempt_to_fix && (testManagementTest?.quarantined || testManagementTest?.disabled)) {
1348
- numFailedQuarantinedOrDisabledAttemptedToFixTests++
1349
- quarantineIgnoredNames.push(`${testSuite} › ${testName}`)
1350
- } else if (testManagementTest?.quarantined) {
1402
+ if (testManagementTest?.quarantined && !testManagementTest?.attempt_to_fix) {
1351
1403
  numFailedQuarantinedTests++
1352
1404
  quarantineIgnoredNames.push(`${testSuite} › ${testName}`)
1353
1405
  }
@@ -1365,13 +1417,11 @@ function getCliWrapper (isNewJestVersion) {
1365
1417
  quarantinedFailingTests.clear()
1366
1418
 
1367
1419
  // If every test that failed was quarantined, we'll consider the suite passed
1368
- // Note that if a test is attempted to fix,
1369
- // it's considered quarantined both if it's disabled and if it's quarantined
1370
- // (it'll run but its status is ignored)
1420
+ // Attempt-to-fix tests ignore quarantine/disabled suppression and keep their framework result.
1371
1421
  // Skip if EFD block already flipped (to avoid logging twice)
1372
1422
  // Only use visible failures (from Jest results) for the flip check.
1373
1423
  // Suppressed quarantine failures are not in numFailedTests.
1374
- const visibleQuarantineFailures = numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
1424
+ const visibleQuarantineFailures = numFailedQuarantinedTests
1375
1425
  if (
1376
1426
  !result.results.success &&
1377
1427
  !mustNotFlipSuccess &&
@@ -1406,7 +1456,7 @@ function getCliWrapper (isNewJestVersion) {
1406
1456
  (isEarlyFlakeDetectionEnabled || isTestManagementTestsEnabled)
1407
1457
  ) {
1408
1458
  const visibleIgnoredFailures =
1409
- numEfdFailedTestsToIgnore + numFailedQuarantinedTests + numFailedQuarantinedOrDisabledAttemptedToFixTests
1459
+ numEfdFailedTestsToIgnore + numFailedQuarantinedTests
1410
1460
  if (
1411
1461
  visibleIgnoredFailures !== 0 &&
1412
1462
  result.results.numFailedTests === visibleIgnoredFailures
@@ -1474,7 +1524,7 @@ function getCliWrapper (isNewJestVersion) {
1474
1524
  })
1475
1525
  }
1476
1526
 
1477
- logSessionSummary(ignoredFailuresSummary)
1527
+ logSessionSummary(ignoredFailuresSummary, getAttemptToFixExecutionsFromJestResults(result))
1478
1528
 
1479
1529
  numSkippedSuites = 0
1480
1530
 
@@ -1510,7 +1560,7 @@ function coverageReporterWrapper (coverageReporter) {
1510
1560
  addHook({
1511
1561
  name: '@jest/core',
1512
1562
  file: 'build/TestScheduler.js',
1513
- versions: ['>=27.0.0'],
1563
+ versions: [MINIMUM_JEST_TEST_SCHEDULER_VERSION],
1514
1564
  }, (testSchedulerPackage, frameworkVersion) => {
1515
1565
  const oldCreateTestScheduler = testSchedulerPackage.createTestScheduler
1516
1566
  const newCreateTestScheduler = async function () {
@@ -1526,17 +1576,19 @@ addHook({
1526
1576
  return testSchedulerPackage
1527
1577
  })
1528
1578
 
1529
- addHook({
1530
- name: '@jest/core',
1531
- file: 'build/TestScheduler.js',
1532
- versions: ['>=24.8.0 <27.0.0'],
1533
- }, (testSchedulerPackage, frameworkVersion) => {
1534
- shimmer.wrap(
1535
- testSchedulerPackage.default.prototype,
1536
- 'scheduleTests', scheduleTests => getWrappedScheduleTests(scheduleTests, frameworkVersion)
1537
- )
1538
- return testSchedulerPackage
1539
- })
1579
+ if (DD_MAJOR < 6) {
1580
+ addHook({
1581
+ name: '@jest/core',
1582
+ file: 'build/TestScheduler.js',
1583
+ versions: ['>=24.8.0 <27.0.0'],
1584
+ }, (testSchedulerPackage, frameworkVersion) => {
1585
+ shimmer.wrap(
1586
+ testSchedulerPackage.default.prototype,
1587
+ 'scheduleTests', scheduleTests => getWrappedScheduleTests(scheduleTests, frameworkVersion)
1588
+ )
1589
+ return testSchedulerPackage
1590
+ })
1591
+ }
1540
1592
 
1541
1593
  addHook({
1542
1594
  name: '@jest/test-sequencer',
@@ -1556,16 +1608,18 @@ addHook({
1556
1608
  return sequencerPackage
1557
1609
  })
1558
1610
 
1559
- addHook({
1560
- name: '@jest/reporters',
1561
- file: 'build/coverage_reporter.js',
1562
- versions: ['>=24.8.0 <26.6.2'],
1563
- }, coverageReporterWrapper)
1611
+ if (DD_MAJOR < 6) {
1612
+ addHook({
1613
+ name: '@jest/reporters',
1614
+ file: 'build/coverage_reporter.js',
1615
+ versions: ['>=24.8.0 <26.6.2'],
1616
+ }, coverageReporterWrapper)
1617
+ }
1564
1618
 
1565
1619
  addHook({
1566
1620
  name: '@jest/reporters',
1567
1621
  file: 'build/CoverageReporter.js',
1568
- versions: ['>=26.6.2'],
1622
+ versions: [DD_MAJOR >= 6 ? '>=28.0.0' : '>=26.6.2'],
1569
1623
  }, coverageReporterWrapper)
1570
1624
 
1571
1625
  addHook({
@@ -1578,7 +1632,7 @@ addHook({
1578
1632
  addHook({
1579
1633
  name: '@jest/core',
1580
1634
  file: 'build/cli/index.js',
1581
- versions: ['>=24.8.0 <30.0.0'],
1635
+ versions: [MINIMUM_JEST_VERSION_BEFORE_30],
1582
1636
  }, getCliWrapper(false))
1583
1637
 
1584
1638
  addHook({
@@ -1665,7 +1719,7 @@ addHook({
1665
1719
  addHook({
1666
1720
  name: 'jest-circus',
1667
1721
  file: 'build/legacy-code-todo-rewrite/jestAdapter.js',
1668
- versions: ['>=24.8.0'],
1722
+ versions: [MINIMUM_JEST_VERSION],
1669
1723
  }, jestAdapterWrapper)
1670
1724
 
1671
1725
  function configureTestEnvironment (readConfigsResult) {
@@ -1802,7 +1856,7 @@ function wrapCreateScriptTransformer (createScriptTransformer) {
1802
1856
 
1803
1857
  addHook({
1804
1858
  name: '@jest/transform',
1805
- versions: ['>=24.8.0 <30.0.0'],
1859
+ versions: [MINIMUM_JEST_VERSION_BEFORE_30],
1806
1860
  file: 'build/ScriptTransformer.js',
1807
1861
  }, transformPackage => {
1808
1862
  transformPackage.createScriptTransformer = wrapCreateScriptTransformer(transformPackage.createScriptTransformer)
@@ -1822,20 +1876,22 @@ addHook({
1822
1876
  */
1823
1877
  addHook({
1824
1878
  name: '@jest/core',
1825
- versions: ['>=24.8.0 <30.0.0'],
1879
+ versions: [MINIMUM_JEST_VERSION_BEFORE_30],
1826
1880
  file: 'build/SearchSource.js',
1827
1881
  }, searchSourceWrapper)
1828
1882
 
1829
1883
  // from 25.1.0 on, readConfigs becomes async
1830
1884
  addHook({
1831
1885
  name: 'jest-config',
1832
- versions: ['>=25.1.0'],
1886
+ versions: [MINIMUM_JEST_CONFIG_ASYNC_VERSION],
1833
1887
  }, jestConfigAsyncWrapper)
1834
1888
 
1835
- addHook({
1836
- name: 'jest-config',
1837
- versions: ['24.8.0 - 24.9.0'],
1838
- }, jestConfigSyncWrapper)
1889
+ if (DD_MAJOR < 6) {
1890
+ addHook({
1891
+ name: 'jest-config',
1892
+ versions: ['24.8.0 - 24.9.0'],
1893
+ }, jestConfigSyncWrapper)
1894
+ }
1839
1895
 
1840
1896
  const LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE = new Set([
1841
1897
  'selenium-webdriver',
@@ -1850,7 +1906,7 @@ const LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE = new Set([
1850
1906
 
1851
1907
  addHook({
1852
1908
  name: 'jest-runtime',
1853
- versions: ['>=24.8.0'],
1909
+ versions: [MINIMUM_JEST_VERSION],
1854
1910
  }, (runtimePackage) => {
1855
1911
  const Runtime = runtimePackage.default ?? runtimePackage
1856
1912
 
@@ -2051,7 +2107,7 @@ function enqueueWrapper (enqueue) {
2051
2107
  */
2052
2108
  addHook({
2053
2109
  name: 'jest-worker',
2054
- versions: ['>=24.9.0 <30.0.0'],
2110
+ versions: [MINIMUM_JEST_WORKER_VERSION_BEFORE_30],
2055
2111
  file: 'build/workers/ChildProcessWorker.js',
2056
2112
  }, (childProcessWorker) => {
2057
2113
  const ChildProcessWorker = childProcessWorker.default
@@ -2066,7 +2122,7 @@ addHook({
2066
2122
 
2067
2123
  addHook({
2068
2124
  name: 'jest-worker',
2069
- versions: ['>=24.9.0 <30.0.0'],
2125
+ versions: [MINIMUM_JEST_WORKER_VERSION_BEFORE_30],
2070
2126
  file: 'build/workers/NodeThreadsWorker.js',
2071
2127
  }, (nodeThreadsWorker) => {
2072
2128
  const ExperimentalWorker = nodeThreadsWorker.default
@@ -3,8 +3,11 @@
3
3
  const { addHook, channel } = require('../helpers/instrument')
4
4
  const shimmer = require('../../../datadog-shimmer')
5
5
  const { getCallSites } = require('../../../dd-trace/src/plugins/util/stacktrace')
6
+ const { DD_MAJOR } = require('../../../../version')
6
7
  const { testToStartLine } = require('./utils')
7
8
 
9
+ const MINIMUM_MOCHA_VERSION = DD_MAJOR >= 6 ? '>=8.0.0' : '>=5.2.0'
10
+
8
11
  const parameterizedTestCh = channel('ci:mocha:test:parameterize')
9
12
  const patched = new WeakSet()
10
13
 
@@ -33,7 +36,7 @@ addHook({
33
36
  // support for start line
34
37
  addHook({
35
38
  name: 'mocha',
36
- versions: ['>=5.2.0'],
39
+ versions: [MINIMUM_MOCHA_VERSION],
37
40
  file: 'lib/suite.js',
38
41
  }, (Suite) => {
39
42
  shimmer.wrap(Suite.prototype, 'addTest', addTest => function (test) {
@@ -1,6 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const { createCoverageMap } = require('../../../../vendor/dist/istanbul-lib-coverage')
4
+ const satisfies = require('../../../../vendor/dist/semifies')
5
+ const { DD_MAJOR } = require('../../../../version')
4
6
  const { addHook, channel } = require('../helpers/instrument')
5
7
  const shimmer = require('../../../datadog-shimmer')
6
8
  const { isMarkedAsUnskippable } = require('../../../datadog-plugin-jest/src/util')
@@ -14,8 +16,8 @@ const {
14
16
  mergeCoverage,
15
17
  resetCoverage,
16
18
  getIsFaultyEarlyFlakeDetection,
17
- collectDynamicNamesFromTraces,
18
- logDynamicNamesWarning,
19
+ collectTestOptimizationSummariesFromTraces,
20
+ logTestOptimizationSummary,
19
21
  } = require('../../../dd-trace/src/plugins/util/test')
20
22
 
21
23
  const {
@@ -34,14 +36,17 @@ const {
34
36
  testsQuarantined,
35
37
  getTestFullName,
36
38
  getRunTestsWrapper,
37
- testsAttemptToFix,
38
- testsStatuses,
39
39
  newTestsWithDynamicNames,
40
+ attemptToFixExecutions,
41
+ loggedAttemptToFixTests,
40
42
  } = require('./utils')
41
43
 
42
44
  require('./common')
43
45
 
46
+ const MINIMUM_MOCHA_VERSION = DD_MAJOR >= 6 ? '>=8.0.0' : '>=5.2.0'
47
+
44
48
  const patched = new WeakSet()
49
+ let hasWarnedDeprecatedMochaVersion = false
45
50
 
46
51
  const unskippableSuites = []
47
52
  let suitesToSkip = []
@@ -79,6 +84,20 @@ const itrSkippedSuitesCh = channel('ci:mocha:itr:skipped-suites')
79
84
 
80
85
  const getCodeCoverageCh = channel('ci:nyc:get-coverage')
81
86
 
87
+ function warnDeprecatedMochaVersion (frameworkVersion) {
88
+ if (DD_MAJOR >= 6 || hasWarnedDeprecatedMochaVersion || !frameworkVersion ||
89
+ !satisfies(frameworkVersion, '<8.0.0')) {
90
+ return
91
+ }
92
+
93
+ hasWarnedDeprecatedMochaVersion = true
94
+ // eslint-disable-next-line no-console
95
+ console.warn(
96
+ 'dd-trace support for Mocha<8.0.0 is deprecated and will be removed in dd-trace v6. ' +
97
+ 'Please upgrade Mocha to >=8.0.0.'
98
+ )
99
+ }
100
+
82
101
  // Tests from workers do not come with `isFailed` method
83
102
  function isTestFailed (test) {
84
103
  if (test.isFailed) {
@@ -146,26 +165,17 @@ function getOnEndHandler (isParallel) {
146
165
  }
147
166
  }
148
167
 
149
- // We substract the errors of attempt to fix tests (quarantined or disabled) from the total number of failures
150
- // We subtract the errors from quarantined tests from the total number of failures
168
+ // We subtract the errors from quarantined tests from the total number of failures.
169
+ // Attempt-to-fix tests ignore quarantine/disabled suppression and keep their framework result.
151
170
  if (config.isTestManagementTestsEnabled) {
152
171
  let numFailedQuarantinedTests = 0
153
- let numFailedRetriedQuarantinedOrDisabledTests = 0
154
- for (const test of testsAttemptToFix) {
155
- const testName = getTestFullName(test)
156
- const testProperties = getTestProperties(test, config.testManagementTests)
157
- if (isTestFailed(test) && (testProperties.isQuarantined || testProperties.isDisabled)) {
158
- const numFailedTests = testsStatuses.get(testName).filter(status => status === 'fail').length
159
- numFailedRetriedQuarantinedOrDisabledTests += numFailedTests
160
- }
161
- }
162
172
  for (const test of testsQuarantined) {
163
173
  if (isTestFailed(test)) {
164
174
  numFailedQuarantinedTests++
165
175
  }
166
176
  }
167
- this.stats.failures -= numFailedQuarantinedTests + numFailedRetriedQuarantinedOrDisabledTests
168
- this.failures -= numFailedQuarantinedTests + numFailedRetriedQuarantinedOrDisabledTests
177
+ this.stats.failures -= numFailedQuarantinedTests
178
+ this.failures -= numFailedQuarantinedTests
169
179
  }
170
180
 
171
181
  // Recompute status after EFD and quarantine adjustments have reduced failure counts
@@ -211,7 +221,8 @@ function getOnEndHandler (isParallel) {
211
221
  isParallel,
212
222
  })
213
223
 
214
- logDynamicNamesWarning(newTestsWithDynamicNames)
224
+ logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
225
+ loggedAttemptToFixTests.clear()
215
226
  }
216
227
  }
217
228
 
@@ -353,9 +364,11 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
353
364
  // It is called but skipped in parallel mode.
354
365
  addHook({
355
366
  name: 'mocha',
356
- versions: ['>=5.2.0'],
367
+ versions: [MINIMUM_MOCHA_VERSION],
357
368
  file: 'lib/mocha.js',
358
369
  }, (Mocha, frameworkVersion) => {
370
+ warnDeprecatedMochaVersion(frameworkVersion)
371
+
359
372
  shimmer.wrap(Mocha.prototype, 'run', run => function () {
360
373
  // Workers do not need to request any data, just run the tests
361
374
  if (!testFinishCh.hasSubscribers || getEnvironmentVariable('MOCHA_WORKER_ID') || this.options.parallel) {
@@ -409,7 +422,7 @@ addHook({
409
422
 
410
423
  addHook({
411
424
  name: 'mocha',
412
- versions: ['>=5.2.0'],
425
+ versions: [MINIMUM_MOCHA_VERSION],
413
426
  file: 'lib/cli/run-helpers.js',
414
427
  }, (run) => {
415
428
  // `runMocha` is an async function
@@ -440,7 +453,7 @@ addHook({
440
453
  // This hook is used to generate session, module, suite and test events
441
454
  addHook({
442
455
  name: 'mocha',
443
- versions: ['>=5.2.0'],
456
+ versions: [MINIMUM_MOCHA_VERSION],
444
457
  file: 'lib/runner.js',
445
458
  }, function (Runner, frameworkVersion) {
446
459
  if (patched.has(Runner)) return Runner
@@ -467,9 +480,9 @@ addHook({
467
480
  this.on('retry', getOnTestRetryHandler(config))
468
481
 
469
482
  // If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
470
- this.on('hook end', getOnHookEndHandler())
483
+ this.on('hook end', getOnHookEndHandler(config))
471
484
 
472
- this.on('fail', getOnFailHandler(true))
485
+ this.on('fail', getOnFailHandler(true, config))
473
486
 
474
487
  this.on('pending', getOnPendingHandler())
475
488
 
@@ -549,7 +562,7 @@ addHook({
549
562
  // Used to set the correct async resource to the test.
550
563
  addHook({
551
564
  name: 'mocha',
552
- versions: ['>=5.2.0'],
565
+ versions: [MINIMUM_MOCHA_VERSION],
553
566
  file: 'lib/runnable.js',
554
567
  }, (runnablePackage) => runnableWrapper(runnablePackage, config))
555
568
 
@@ -557,7 +570,10 @@ function onMessage (message) {
557
570
  if (Array.isArray(message)) {
558
571
  const [messageCode, payload] = message
559
572
  if (messageCode === MOCHA_WORKER_TRACE_PAYLOAD_CODE) {
560
- collectDynamicNamesFromTraces(payload, newTestsWithDynamicNames)
573
+ collectTestOptimizationSummariesFromTraces(payload, {
574
+ newTestsWithDynamicNames,
575
+ attemptToFixExecutions,
576
+ })
561
577
  workerReportTraceCh.publish(payload)
562
578
  }
563
579
  }
@@ -771,7 +787,8 @@ addHook({
771
787
  }
772
788
  }
773
789
  // `testsQuarantined` is filled in the worker process, so we need to use the test results to fill it here too.
774
- if (config.isTestManagementTestsEnabled && getTestProperties(test, config.testManagementTests).isQuarantined) {
790
+ const testProperties = getTestProperties(test, config.testManagementTests)
791
+ if (config.isTestManagementTestsEnabled && testProperties.isQuarantined && !testProperties.isAttemptToFix) {
775
792
  testsQuarantined.add(test)
776
793
  }
777
794
  }