dd-trace 5.88.0 → 5.90.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 (103) hide show
  1. package/LICENSE-3rdparty.csv +0 -3
  2. package/ext/tags.js +2 -0
  3. package/index.d.ts +40 -0
  4. package/package.json +18 -14
  5. package/packages/datadog-instrumentations/src/azure-durable-functions.js +75 -0
  6. package/packages/datadog-instrumentations/src/cucumber.js +40 -1
  7. package/packages/datadog-instrumentations/src/elasticsearch.js +12 -3
  8. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  9. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +26 -111
  10. package/packages/datadog-instrumentations/src/helpers/rewriter/{compiler.js → orchestrion/compiler.js} +5 -5
  11. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/index.js +43 -0
  12. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/matcher.js +49 -0
  13. package/packages/datadog-instrumentations/src/helpers/rewriter/orchestrion/transformer.js +121 -0
  14. package/packages/datadog-instrumentations/src/helpers/rewriter/{transforms.js → orchestrion/transforms.js} +6 -6
  15. package/packages/datadog-instrumentations/src/jest.js +123 -43
  16. package/packages/datadog-instrumentations/src/mocha/main.js +10 -4
  17. package/packages/datadog-instrumentations/src/mocha/utils.js +6 -0
  18. package/packages/datadog-instrumentations/src/mocha/worker.js +10 -2
  19. package/packages/datadog-instrumentations/src/playwright.js +20 -2
  20. package/packages/datadog-instrumentations/src/prisma.js +4 -2
  21. package/packages/datadog-instrumentations/src/vitest.js +16 -0
  22. package/packages/datadog-plugin-apollo/src/gateway/execute.js +8 -0
  23. package/packages/datadog-plugin-apollo/src/gateway/fetch.js +5 -0
  24. package/packages/datadog-plugin-apollo/src/gateway/plan.js +8 -0
  25. package/packages/datadog-plugin-apollo/src/gateway/postprocessing.js +5 -0
  26. package/packages/datadog-plugin-apollo/src/gateway/request.js +4 -3
  27. package/packages/datadog-plugin-apollo/src/gateway/validate.js +4 -3
  28. package/packages/datadog-plugin-apollo/src/index.js +28 -0
  29. package/packages/datadog-plugin-azure-durable-functions/src/index.js +49 -0
  30. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +47 -6
  31. package/packages/datadog-plugin-cypress/src/source-map-utils.js +297 -0
  32. package/packages/datadog-plugin-cypress/src/support.js +4 -1
  33. package/packages/datadog-plugin-jest/src/index.js +6 -0
  34. package/packages/datadog-plugin-playwright/src/index.js +35 -8
  35. package/packages/dd-trace/src/aiguard/noop.js +1 -1
  36. package/packages/dd-trace/src/aiguard/sdk.js +18 -5
  37. package/packages/dd-trace/src/appsec/api_security_sampler.js +22 -1
  38. package/packages/dd-trace/src/appsec/index.js +11 -1
  39. package/packages/dd-trace/src/appsec/reporter.js +28 -11
  40. package/packages/dd-trace/src/appsec/waf/index.js +1 -1
  41. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  42. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +1 -0
  43. package/packages/dd-trace/src/config/index.js +3 -0
  44. package/packages/dd-trace/src/config/supported-configurations.json +17 -0
  45. package/packages/dd-trace/src/constants.js +1 -0
  46. package/packages/dd-trace/src/datastreams/checkpointer.js +13 -0
  47. package/packages/dd-trace/src/datastreams/index.js +3 -0
  48. package/packages/dd-trace/src/datastreams/manager.js +9 -0
  49. package/packages/dd-trace/src/datastreams/processor.js +126 -3
  50. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +1 -8
  51. package/packages/dd-trace/src/encode/agentless-json.js +82 -23
  52. package/packages/dd-trace/src/exporters/agent/writer.js +7 -8
  53. package/packages/dd-trace/src/exporters/agentless/index.js +58 -15
  54. package/packages/dd-trace/src/exporters/agentless/writer.js +35 -18
  55. package/packages/dd-trace/src/llmobs/constants/tags.js +2 -0
  56. package/packages/dd-trace/src/llmobs/plugins/anthropic.js +9 -0
  57. package/packages/dd-trace/src/llmobs/tagger.js +8 -0
  58. package/packages/dd-trace/src/opentracing/propagation/text_map.js +1 -0
  59. package/packages/dd-trace/src/pkg.js +1 -1
  60. package/packages/dd-trace/src/plugins/apollo.js +7 -2
  61. package/packages/dd-trace/src/plugins/index.js +1 -0
  62. package/packages/dd-trace/src/plugins/util/ci.js +95 -3
  63. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +36 -2
  64. package/packages/dd-trace/src/plugins/util/web.js +31 -11
  65. package/packages/dd-trace/src/proxy.js +2 -1
  66. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +7 -0
  67. package/packages/dd-trace/src/service-naming/schemas/v0/serverless.js +4 -0
  68. package/packages/dd-trace/src/service-naming/schemas/v1/serverless.js +4 -0
  69. package/packages/dd-trace/src/standalone/product.js +2 -1
  70. package/packages/dd-trace/src/startup-log.js +52 -18
  71. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  72. package/vendor/dist/@datadog/source-map/index.js +1 -1
  73. package/vendor/dist/@isaacs/ttlcache/index.js +1 -1
  74. package/vendor/dist/@opentelemetry/core/index.js +1 -1
  75. package/vendor/dist/@opentelemetry/resources/index.js +1 -1
  76. package/vendor/dist/astring/index.js +1 -1
  77. package/vendor/dist/crypto-randomuuid/index.js +1 -1
  78. package/vendor/dist/escape-string-regexp/index.js +1 -1
  79. package/vendor/dist/esquery/index.js +1 -1
  80. package/vendor/dist/ignore/index.js +1 -1
  81. package/vendor/dist/istanbul-lib-coverage/index.js +1 -1
  82. package/vendor/dist/jest-docblock/index.js +1 -1
  83. package/vendor/dist/jsonpath-plus/index.js +1 -1
  84. package/vendor/dist/limiter/index.js +1 -1
  85. package/vendor/dist/lodash.sortby/index.js +1 -1
  86. package/vendor/dist/lru-cache/index.js +1 -1
  87. package/vendor/dist/meriyah/index.js +1 -1
  88. package/vendor/dist/module-details-from-path/index.js +1 -1
  89. package/vendor/dist/mutexify/promise/index.js +1 -1
  90. package/vendor/dist/opentracing/index.js +1 -1
  91. package/vendor/dist/path-to-regexp/index.js +1 -1
  92. package/vendor/dist/pprof-format/index.js +1 -1
  93. package/vendor/dist/protobufjs/index.js +1 -1
  94. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  95. package/vendor/dist/retry/index.js +1 -1
  96. package/vendor/dist/rfdc/index.js +1 -1
  97. package/vendor/dist/semifies/index.js +1 -1
  98. package/vendor/dist/shell-quote/index.js +1 -1
  99. package/vendor/dist/source-map/index.js +1 -1
  100. package/vendor/dist/source-map/lib/util/index.js +1 -1
  101. package/vendor/dist/tlhunter-sorted-set/index.js +1 -1
  102. package/vendor/dist/ttl-set/index.js +1 -1
  103. package/packages/datadog-instrumentations/src/helpers/rewriter/transformer.js +0 -21
@@ -0,0 +1,43 @@
1
+ 'use strict'
2
+
3
+ /*
4
+ This folder is basically a JavaScript version of Orchestrion-JS. The goal is
5
+ not to replace Orchestrion-JS, but rather to make it easier and faster to write
6
+ new integrations in the short-term, especially as many changes to the rewriter
7
+ will be needed as all the patterns we need have not been identified yet. This
8
+ will avoid the back and forth of having to make Rust changes to an external
9
+ library for every integration change or addition that requires something new.
10
+
11
+ In the meantime, we'll work concurrently on a change to Orchestrion-JS that
12
+ adds an "arbitrary transform" or "plugin" system that can be used from
13
+ JavaScript, in order to enable quick iteration while still using Orchestrion-JS.
14
+ Once that's done we'll use that, so that we can remove this JS approach and
15
+ return to using Orchestrion-JS.
16
+
17
+ The long term goal is to backport any additional features we add to the JS
18
+ rewriter (or using the plugin system in Orchestrion-JS once we're using that)
19
+ to Orchestrion-JS once we're confident that the implementation is fairly
20
+ complete and has all features we need.
21
+
22
+ Here is a list of the additions and changes in this rewriter compared to
23
+ Orchestrion-JS that will need to be backported:
24
+
25
+ (NOTE: Please keep this list up-to-date whenever new features are added)
26
+
27
+ - Supports an `astQuery` field to filter AST nodes with an esquery query. This
28
+ is mostly meant to be used when experimenting or if what needs to be queried
29
+ is not a function. We'll see over time if something like this is needed to be
30
+ backported or if it can be replaced by simpler queries.
31
+ - Supports replacing methods of child class instances in the base constructor.
32
+ - Supports tracing iterator (sync/async) returning functions (sync/async).
33
+ */
34
+
35
+ /* eslint-disable camelcase */
36
+
37
+ const { InstrumentationMatcher } = require('./matcher')
38
+
39
+ function create (configs, dc_module) {
40
+ return new InstrumentationMatcher(configs, dc_module)
41
+ }
42
+
43
+ module.exports = { create }
@@ -0,0 +1,49 @@
1
+ 'use strict'
2
+
3
+ /* eslint-disable camelcase */
4
+
5
+ const semifies = require('../../../../../../vendor/dist/semifies')
6
+ const { Transformer } = require('./transformer')
7
+
8
+ // TODO: addTransform
9
+
10
+ class InstrumentationMatcher {
11
+ #configs = []
12
+ #dc_module = null
13
+ #transformers = {}
14
+
15
+ constructor (configs, dc_module) {
16
+ this.#configs = configs
17
+ this.#dc_module = dc_module || 'diagnostics_channel'
18
+ }
19
+
20
+ free () {
21
+ this.#transformers = {}
22
+ }
23
+
24
+ getTransformer (module_name, version, file_path) {
25
+ const id = `${module_name}/${file_path}@${version}`
26
+
27
+ if (this.#transformers[id]) return this.#transformers[id]
28
+
29
+ const configs = this.#configs.filter(({ module: { name, filePath, versionRange } }) =>
30
+ name === module_name &&
31
+ filePath === file_path &&
32
+ semifies(version, versionRange)
33
+ )
34
+
35
+ if (configs.length === 0) return
36
+
37
+ this.#transformers[id] = new Transformer(
38
+ module_name,
39
+ version,
40
+ file_path,
41
+ configs,
42
+ this.#dc_module
43
+ )
44
+
45
+ return this.#transformers[id]
46
+ }
47
+ }
48
+
49
+ module.exports = { InstrumentationMatcher }
@@ -0,0 +1,121 @@
1
+ 'use strict'
2
+
3
+ /* eslint-disable camelcase */
4
+
5
+ const { generate, parse, traverse } = require('./compiler')
6
+ const transforms = require('./transforms')
7
+
8
+ let SourceMapConsumer
9
+ let SourceMapGenerator
10
+
11
+ class Transformer {
12
+ #module_name = null
13
+ #file_path = null
14
+ #configs = []
15
+ #dc_module = null
16
+
17
+ // TODO: module_name false for user module
18
+ constructor (module_name, _version, file_path, configs, dc_module) {
19
+ this.#module_name = module_name
20
+ this.#file_path = file_path
21
+ this.#configs = configs
22
+ this.#dc_module = dc_module
23
+ }
24
+
25
+ free () {
26
+ // Freeing is not needed for a JavaScript implementation.
27
+ }
28
+
29
+ transform (code, module_type, sourcemap) {
30
+ if (!code) return { code }
31
+
32
+ const sourceType = module_type === 'esm' ? 'module' : 'script'
33
+
34
+ let ast
35
+
36
+ for (const config of this.#configs) {
37
+ const { astQuery, functionQuery = {} } = config
38
+
39
+ ast ??= parse(code.toString(), { range: true, sourceType })
40
+
41
+ const query = astQuery || this.#fromFunctionQuery(functionQuery)
42
+ const state = { ...config, dcModule: this.#dc_module, sourceType, functionQuery }
43
+
44
+ state.operator = this.#getOperator(state)
45
+
46
+ traverse(ast, query, (...args) => this.#visit(state, ...args))
47
+ }
48
+
49
+ if (ast) {
50
+ SourceMapConsumer ??= require('../../../../../../vendor/dist/@datadog/source-map').SourceMapConsumer
51
+ SourceMapGenerator ??= require('../../../../../../vendor/dist/@datadog/source-map').SourceMapGenerator
52
+
53
+ const file = `${this.#module_name}/${this.#file_path}`
54
+ const sourceMapInput = sourcemap ? new SourceMapConsumer(sourcemap) : { file }
55
+ const sourceMap = new SourceMapGenerator(sourceMapInput)
56
+ const code = generate(ast, { sourceMap })
57
+ const map = sourceMap.toString()
58
+
59
+ return { code, map }
60
+ }
61
+
62
+ return { code }
63
+ }
64
+
65
+ #visit (state, ...args) {
66
+ const transform = transforms[state.operator]
67
+ const { index } = state.functionQuery
68
+
69
+ if (index !== undefined) {
70
+ state.functionIndex = ++state.functionIndex || 0
71
+
72
+ if (index !== state.functionIndex) return
73
+ }
74
+
75
+ transform(state, ...args)
76
+ }
77
+
78
+ #getOperator ({ functionQuery: { kind } }) {
79
+ switch (kind) {
80
+ case 'Async': return 'tracePromise'
81
+ case 'AsyncIterator': return 'traceAsyncIterator'
82
+ case 'Callback': return 'traceCallback'
83
+ case 'Iterator': return 'traceIterator'
84
+ case 'Sync': return 'traceSync'
85
+ }
86
+ }
87
+
88
+ #fromFunctionQuery (functionQuery) {
89
+ const { functionName, expressionName, className } = functionQuery
90
+ const method = functionQuery.methodName || functionQuery.privateMethodName
91
+ const type = functionQuery.privateMethodName ? 'PrivateIdentifier' : 'Identifier'
92
+ const queries = []
93
+
94
+ if (className) {
95
+ queries.push(
96
+ `[id.name="${className}"]`,
97
+ `[id.name="${className}"] > ClassExpression`,
98
+ `[id.name="${className}"] > ClassBody > [key.name="${method}"][key.type=${type}] > [async]`,
99
+ `[id.name="${className}"] > ClassExpression > ClassBody > [key.name="${method}"][key.type=${type}] > [async]`
100
+ )
101
+ } else if (method) {
102
+ queries.push(
103
+ `ClassBody > [key.name="${method}"][key.type=${type}] > [async]`,
104
+ `Property[key.name="${method}"][key.type=${type}] > [async]`
105
+ )
106
+ }
107
+
108
+ if (functionName) {
109
+ queries.push(`FunctionDeclaration[id.name="${functionName}"][async]`)
110
+ } else if (expressionName) {
111
+ queries.push(
112
+ `FunctionExpression[id.name="${expressionName}"][async]`,
113
+ `ArrowFunctionExpression[id.name="${expressionName}"][async]`
114
+ )
115
+ }
116
+
117
+ return queries.join(', ')
118
+ }
119
+ }
120
+
121
+ module.exports = { Transformer }
@@ -8,13 +8,13 @@ const tracingChannelPredicate = (node) => (
8
8
  )
9
9
 
10
10
  const transforms = module.exports = {
11
- tracingChannelImport ({ sourceType }, node) {
11
+ tracingChannelImport ({ dcModule, sourceType }, node) {
12
12
  if (node.body.some(tracingChannelPredicate)) return
13
13
 
14
14
  const index = node.body.findIndex(child => child.directive === 'use strict')
15
15
  const code = sourceType === 'module'
16
- ? 'import { tracingChannel as tr_ch_apm_tracingChannel } from "diagnostics_channel"'
17
- : 'const {tracingChannel: tr_ch_apm_tracingChannel} = require("diagnostics_channel")'
16
+ ? `import { tracingChannel as tr_ch_apm_tracingChannel } from "${dcModule}"`
17
+ : `const {tracingChannel: tr_ch_apm_tracingChannel} = require("${dcModule}")`
18
18
 
19
19
  node.body.splice(index + 1, 0, parse(code, { sourceType }).body[0])
20
20
  },
@@ -196,11 +196,11 @@ function wrapSuper (_state, node) {
196
196
  }
197
197
 
198
198
  function wrapCallback (state, node) {
199
- const { channelName, functionQuery: { index = -1 } } = state
199
+ const { channelName, functionQuery: { callbackIndex = -1 } } = state
200
200
  const channelVariable = 'tr_ch_apm$' + channelName.replaceAll(':', '_')
201
201
  const wrapper = parse(`
202
202
  function wrapper () {
203
- const __apm$cb = Array.prototype.at.call(arguments, ${index});
203
+ const __apm$cb = Array.prototype.at.call(arguments, ${callbackIndex});
204
204
  const __apm$ctx = {
205
205
  arguments,
206
206
  self: this,
@@ -235,7 +235,7 @@ function wrapCallback (state, node) {
235
235
  if (typeof __apm$cb !== 'function') {
236
236
  return __apm$traced();
237
237
  }
238
- Array.prototype.splice.call(arguments, ${index}, 1, __apm$wrappedCb);
238
+ Array.prototype.splice.call(arguments, ${callbackIndex}, 1, __apm$wrappedCb);
239
239
 
240
240
  return ${channelVariable}.start.runStores(__apm$ctx, () => {
241
241
  try {
@@ -47,6 +47,7 @@ const testSkippedCh = channel('ci:jest:test:skip')
47
47
  const testFinishCh = channel('ci:jest:test:finish')
48
48
  const testErrCh = channel('ci:jest:test:err')
49
49
  const testFnCh = channel('ci:jest:test:fn')
50
+ const testSuiteHookFnCh = channel('ci:jest:test-suite:hook:fn')
50
51
 
51
52
  const skippableSuitesCh = channel('ci:jest:test-suite:skippable')
52
53
  const libraryConfigurationCh = channel('ci:jest:library-configuration')
@@ -69,7 +70,9 @@ const RETRY_TIMES = Symbol.for('RETRY_TIMES')
69
70
  let skippableSuites = []
70
71
  let knownTests = {}
71
72
  let isCodeCoverageEnabled = false
73
+ let isCodeCoverageEnabledBecauseOfUs = false
72
74
  let isSuitesSkippingEnabled = false
75
+ let isKeepingCoverageConfiguration = false
73
76
  let isUserCodeCoverageEnabled = false
74
77
  let isSuitesSkipped = false
75
78
  let numSkippedSuites = 0
@@ -503,6 +506,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
503
506
  })
504
507
  }
505
508
 
509
+ if (event.name === 'hook_start' && (event.hook.type === 'beforeAll' || event.hook.type === 'afterAll')) {
510
+ const ctx = { testSuiteAbsolutePath: this.testSuiteAbsolutePath }
511
+ let hookFn = event.hook.fn
512
+ if (originalHookFns.has(event.hook)) {
513
+ hookFn = originalHookFns.get(event.hook)
514
+ } else {
515
+ originalHookFns.set(event.hook, hookFn)
516
+ }
517
+ event.hook.fn = shimmer.wrapFunction(hookFn, hookFn => function () {
518
+ return testSuiteHookFnCh.runStores(ctx, () => hookFn.apply(this, arguments))
519
+ })
520
+ }
521
+
506
522
  if (event.name === 'add_test') {
507
523
  if (event.failing) {
508
524
  return
@@ -671,6 +687,14 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
671
687
  }
672
688
  }
673
689
 
690
+ // ATR: set failedAllTests when all auto test retries were exhausted and every attempt failed
691
+ if (this.isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry) {
692
+ const maxRetries = Number(this.global[RETRY_TIMES]) || 0
693
+ if (event.test?.invocations === maxRetries + 1 && status === 'fail') {
694
+ failedAllTests = true
695
+ }
696
+ }
697
+
674
698
  const promises = {}
675
699
  const numRetries = this.global[RETRY_TIMES]
676
700
  const numTestExecutions = event.test?.invocations
@@ -994,6 +1018,8 @@ function getCliWrapper (isNewJestVersion) {
994
1018
  if (!err) {
995
1019
  isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
996
1020
  isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
1021
+ isKeepingCoverageConfiguration =
1022
+ libraryConfig.isKeepingCoverageConfiguration ?? isKeepingCoverageConfiguration
997
1023
  isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
998
1024
  earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
999
1025
  earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
@@ -1327,9 +1353,10 @@ function coverageReporterWrapper (coverageReporter) {
1327
1353
  */
1328
1354
  // `_addUntestedFiles` is an async function
1329
1355
  shimmer.wrap(CoverageReporter.prototype, '_addUntestedFiles', addUntestedFiles => function () {
1330
- // If the user has added coverage manually, they're willing to pay the price of this execution, so
1331
- // we will not skip it.
1332
- if (isSuitesSkippingEnabled && !isUserCodeCoverageEnabled) {
1356
+ if (isKeepingCoverageConfiguration) {
1357
+ return addUntestedFiles.apply(this, arguments)
1358
+ }
1359
+ if (isCodeCoverageEnabledBecauseOfUs) {
1333
1360
  return Promise.resolve()
1334
1361
  }
1335
1362
  return addUntestedFiles.apply(this, arguments)
@@ -1509,12 +1536,13 @@ function configureTestEnvironment (readConfigsResult) {
1509
1536
  }
1510
1537
 
1511
1538
  isUserCodeCoverageEnabled = !!readConfigsResult.globalConfig.collectCoverage
1539
+ isCodeCoverageEnabledBecauseOfUs = isCodeCoverageEnabled && !isUserCodeCoverageEnabled
1512
1540
 
1513
1541
  if (readConfigsResult.globalConfig.forceExit) {
1514
1542
  log.warn("Jest's '--forceExit' flag has been passed. This may cause loss of data.")
1515
1543
  }
1516
1544
 
1517
- if (isCodeCoverageEnabled) {
1545
+ if (isCodeCoverageEnabledBecauseOfUs) {
1518
1546
  const globalConfig = {
1519
1547
  ...readConfigsResult.globalConfig,
1520
1548
  collectCoverage: true,
@@ -1522,14 +1550,18 @@ function configureTestEnvironment (readConfigsResult) {
1522
1550
  readConfigsResult.globalConfig = globalConfig
1523
1551
  }
1524
1552
  if (isSuitesSkippingEnabled) {
1525
- // If suite skipping is enabled, the code coverage results are not going to be relevant,
1526
- // so we do not show them.
1527
- // Also, we might skip every test, so we need to pass `passWithNoTests`
1553
+ // If suite skipping is enabled, we pass `passWithNoTests` in case every test gets skipped.
1528
1554
  const globalConfig = {
1529
1555
  ...readConfigsResult.globalConfig,
1530
- coverageReporters: ['none'],
1531
1556
  passWithNoTests: true,
1532
1557
  }
1558
+ if (isCodeCoverageEnabledBecauseOfUs && !isKeepingCoverageConfiguration) {
1559
+ globalConfig.coverageReporters = ['none']
1560
+ readConfigsResult.configs = configs.map(config => ({
1561
+ ...config,
1562
+ coverageReporters: ['none'],
1563
+ }))
1564
+ }
1533
1565
  readConfigsResult.globalConfig = globalConfig
1534
1566
  }
1535
1567
 
@@ -1552,49 +1584,97 @@ function jestConfigSyncWrapper (jestConfig) {
1552
1584
  })
1553
1585
  }
1554
1586
 
1587
+ const DD_TEST_ENVIRONMENT_OPTION_KEYS = [
1588
+ '_ddTestModuleId',
1589
+ '_ddTestSessionId',
1590
+ '_ddTestCommand',
1591
+ '_ddTestSessionName',
1592
+ '_ddForcedToRun',
1593
+ '_ddUnskippable',
1594
+ '_ddItrCorrelationId',
1595
+ '_ddKnownTests',
1596
+ '_ddIsEarlyFlakeDetectionEnabled',
1597
+ '_ddEarlyFlakeDetectionSlowTestRetries',
1598
+ '_ddRepositoryRoot',
1599
+ '_ddIsFlakyTestRetriesEnabled',
1600
+ '_ddFlakyTestRetriesCount',
1601
+ '_ddIsDiEnabled',
1602
+ '_ddIsKnownTestsEnabled',
1603
+ '_ddIsTestManagementTestsEnabled',
1604
+ '_ddTestManagementTests',
1605
+ '_ddTestManagementAttemptToFixRetries',
1606
+ '_ddModifiedFiles',
1607
+ ]
1608
+
1609
+ function removeDatadogTestEnvironmentOptions (testEnvironmentOptions) {
1610
+ const removedEntries = []
1611
+
1612
+ for (const key of DD_TEST_ENVIRONMENT_OPTION_KEYS) {
1613
+ if (!Object.hasOwn(testEnvironmentOptions, key)) {
1614
+ continue
1615
+ }
1616
+
1617
+ removedEntries.push([key, testEnvironmentOptions[key]])
1618
+ delete testEnvironmentOptions[key]
1619
+ }
1620
+
1621
+ return function restoreDatadogTestEnvironmentOptions () {
1622
+ for (const [key, value] of removedEntries) {
1623
+ testEnvironmentOptions[key] = value
1624
+ }
1625
+ }
1626
+ }
1627
+
1628
+ /**
1629
+ * Wrap `createScriptTransformer` to temporarily hide Datadog-specific
1630
+ * `testEnvironmentOptions` keys while Jest builds its transform config.
1631
+ *
1632
+ * @param {Function} createScriptTransformer
1633
+ * @returns {Function}
1634
+ */
1635
+ function wrapCreateScriptTransformer (createScriptTransformer) {
1636
+ return function (config) {
1637
+ const testEnvironmentOptions = config?.testEnvironmentOptions
1638
+
1639
+ if (!testEnvironmentOptions) {
1640
+ return createScriptTransformer.apply(this, arguments)
1641
+ }
1642
+
1643
+ const restoreTestEnvironmentOptions = removeDatadogTestEnvironmentOptions(testEnvironmentOptions)
1644
+
1645
+ try {
1646
+ const result = createScriptTransformer.apply(this, arguments)
1647
+
1648
+ if (result?.then) {
1649
+ return result.finally(restoreTestEnvironmentOptions)
1650
+ }
1651
+
1652
+ restoreTestEnvironmentOptions()
1653
+ return result
1654
+ } catch (e) {
1655
+ restoreTestEnvironmentOptions()
1656
+ throw e
1657
+ }
1658
+ }
1659
+ }
1660
+
1555
1661
  addHook({
1556
1662
  name: '@jest/transform',
1557
- versions: ['>=24.8.0'],
1663
+ versions: ['>=24.8.0 <30.0.0'],
1558
1664
  file: 'build/ScriptTransformer.js',
1559
1665
  }, transformPackage => {
1560
- const originalCreateScriptTransformer = transformPackage.createScriptTransformer
1561
-
1562
- // `createScriptTransformer` is an async function
1563
- transformPackage.createScriptTransformer = function (config) {
1564
- const { testEnvironmentOptions, ...restOfConfig } = config
1565
- const {
1566
- _ddTestModuleId,
1567
- _ddTestSessionId,
1568
- _ddTestCommand,
1569
- _ddTestSessionName,
1570
- _ddForcedToRun,
1571
- _ddUnskippable,
1572
- _ddItrCorrelationId,
1573
- _ddKnownTests,
1574
- _ddIsEarlyFlakeDetectionEnabled,
1575
- _ddEarlyFlakeDetectionSlowTestRetries,
1576
- _ddRepositoryRoot,
1577
- _ddIsFlakyTestRetriesEnabled,
1578
- _ddFlakyTestRetriesCount,
1579
- _ddIsDiEnabled,
1580
- _ddIsKnownTestsEnabled,
1581
- _ddIsTestManagementTestsEnabled,
1582
- _ddTestManagementTests,
1583
- _ddTestManagementAttemptToFixRetries,
1584
- _ddModifiedFiles,
1585
- ...restOfTestEnvironmentOptions
1586
- } = testEnvironmentOptions
1587
-
1588
- restOfConfig.testEnvironmentOptions = restOfTestEnvironmentOptions
1589
-
1590
- arguments[0] = restOfConfig
1591
-
1592
- return originalCreateScriptTransformer.apply(this, arguments)
1593
- }
1666
+ transformPackage.createScriptTransformer = wrapCreateScriptTransformer(transformPackage.createScriptTransformer)
1594
1667
 
1595
1668
  return transformPackage
1596
1669
  })
1597
1670
 
1671
+ addHook({
1672
+ name: '@jest/transform',
1673
+ versions: ['>=30.0.0'],
1674
+ }, transformPackage => {
1675
+ return shimmer.wrap(transformPackage, 'createScriptTransformer', wrapCreateScriptTransformer, { replaceGetter: true })
1676
+ })
1677
+
1598
1678
  /**
1599
1679
  * Hook to remove the test paths (test suite) that are part of `skippableSuites`
1600
1680
  */
@@ -315,10 +315,10 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
315
315
  config.isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
316
316
  config.testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
317
317
  config.isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
318
- // ITR and auto test retries are not supported in parallel mode yet
318
+ // ITR is not supported in parallel mode yet
319
319
  config.isSuitesSkippingEnabled = !isParallel && libraryConfig.isSuitesSkippingEnabled
320
- config.isFlakyTestRetriesEnabled = !isParallel && libraryConfig.isFlakyTestRetriesEnabled
321
- config.flakyTestRetriesCount = !isParallel && libraryConfig.flakyTestRetriesCount
320
+ config.isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
321
+ config.flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
322
322
 
323
323
  if (config.isKnownTestsEnabled) {
324
324
  ctx.onDone = onReceivedKnownTests
@@ -663,7 +663,8 @@ addHook({
663
663
  if (!testFinishCh.hasSubscribers ||
664
664
  (!config.isKnownTestsEnabled &&
665
665
  !config.isTestManagementTestsEnabled &&
666
- !config.isImpactedTestsEnabled)) {
666
+ !config.isImpactedTestsEnabled &&
667
+ !config.isFlakyTestRetriesEnabled)) {
667
668
  return run.apply(this, arguments)
668
669
  }
669
670
 
@@ -709,6 +710,11 @@ addHook({
709
710
  newWorkerArgs._ddModifiedFiles = config.modifiedFiles || {}
710
711
  }
711
712
 
713
+ if (config.isFlakyTestRetriesEnabled) {
714
+ newWorkerArgs._ddIsFlakyTestRetriesEnabled = true
715
+ newWorkerArgs._ddFlakyTestRetriesCount = config.flakyTestRetriesCount
716
+ }
717
+
712
718
  // We pass the known tests for the test file to the worker
713
719
  const testFileResult = await run.apply(
714
720
  this,
@@ -289,6 +289,12 @@ function getOnTestEndHandler (config) {
289
289
  hasFailedAllRetries = true
290
290
  }
291
291
 
292
+ // ATR: set hasFailedAllRetries when all auto test retries were exhausted and every attempt failed
293
+ if (config.isFlakyTestRetriesEnabled && !test._ddIsAttemptToFix && !test._ddIsEfdRetry &&
294
+ getIsLastRetry(test) && testStatuses.every(status => status === 'fail')) {
295
+ hasFailedAllRetries = true
296
+ }
297
+
292
298
  const isAttemptToFixRetry = test._ddIsAttemptToFix && testStatuses.length > 1
293
299
  const isAtrRetry = config.isFlakyTestRetriesEnabled &&
294
300
  !test._ddIsAttemptToFix &&
@@ -10,6 +10,7 @@ const {
10
10
  getOnHookEndHandler,
11
11
  getOnFailHandler,
12
12
  getOnPendingHandler,
13
+ getOnTestRetryHandler,
13
14
  getRunTestsWrapper,
14
15
  } = require('./utils')
15
16
  require('./common')
@@ -48,6 +49,12 @@ addHook({
48
49
  delete this.options._ddIsTestManagementTestsEnabled
49
50
  delete this.options._ddTestManagementTests
50
51
  }
52
+ if (this.options._ddIsFlakyTestRetriesEnabled) {
53
+ config.isFlakyTestRetriesEnabled = true
54
+ config.flakyTestRetriesCount = this.options._ddFlakyTestRetriesCount
55
+ delete this.options._ddIsFlakyTestRetriesEnabled
56
+ delete this.options._ddFlakyTestRetriesCount
57
+ }
51
58
  return run.apply(this, arguments)
52
59
  })
53
60
 
@@ -74,6 +81,8 @@ addHook({
74
81
 
75
82
  this.on('test end', getOnTestEndHandler(config))
76
83
 
84
+ this.on('retry', getOnTestRetryHandler(config))
85
+
77
86
  // If the hook passes, 'hook end' will be emitted. Otherwise, 'fail' will be emitted
78
87
  this.on('hook end', getOnHookEndHandler())
79
88
 
@@ -92,5 +101,4 @@ addHook({
92
101
  name: 'mocha',
93
102
  versions: ['>=5.2.0'],
94
103
  file: 'lib/runnable.js',
95
- }, runnableWrapper)
96
- // TODO: parallel mode does not support flaky test retries, so no library config is passed.
104
+ }, (runnablePackage) => runnableWrapper(runnablePackage, config))
@@ -302,6 +302,7 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
302
302
  const {
303
303
  _requireFile: testSuiteAbsolutePath,
304
304
  location: {
305
+ file: testSourceFileAbsolutePath,
305
306
  line: testSourceLine,
306
307
  },
307
308
  _type,
@@ -319,7 +320,7 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
319
320
 
320
321
  if (isNewTestSuite) {
321
322
  startedSuites.push(testSuiteAbsolutePath)
322
- const testSuiteCtx = { testSuiteAbsolutePath }
323
+ const testSuiteCtx = { testSuiteAbsolutePath, testSourceFileAbsolutePath }
323
324
  testSuiteToCtx.set(testSuiteAbsolutePath, testSuiteCtx)
324
325
  testSuiteStartCh.runStores(testSuiteCtx, () => {})
325
326
  }
@@ -335,6 +336,7 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
335
336
  const testCtx = {
336
337
  testName,
337
338
  testSuiteAbsolutePath,
339
+ testSourceFileAbsolutePath,
338
340
  testSourceLine,
339
341
  browserName,
340
342
  isDisabled: test._ddIsDisabled,
@@ -404,6 +406,15 @@ function testEndHandler ({
404
406
  test._ddHasFailedAllRetries = true
405
407
  }
406
408
 
409
+ // ATR: set _ddHasFailedAllRetries when all auto test retries were exhausted and every attempt failed
410
+ if (isFlakyTestRetriesEnabled && !testProperties.attemptToFix && !test._ddIsEfdRetry &&
411
+ !(test._ddIsNew || test._ddIsModified) &&
412
+ flakyTestRetriesCount != null && flakyTestRetriesCount > 0 &&
413
+ testStatuses.length === flakyTestRetriesCount + 1 &&
414
+ testStatuses.every(status => status === 'fail')) {
415
+ test._ddHasFailedAllRetries = true
416
+ }
417
+
407
418
  // this handles tests that do not go through the worker process (because they're skipped)
408
419
  if (shouldCreateTestSpan) {
409
420
  const testResult = results.at(-1)
@@ -459,6 +470,7 @@ function testEndHandler ({
459
470
  testSkipCh.publish({
460
471
  testName: getTestFullname(test),
461
472
  testSuiteAbsolutePath,
473
+ testSourceFileAbsolutePath: test.location.file,
462
474
  testSourceLine: test.location.line,
463
475
  browserName,
464
476
  isNew: test._ddIsNew,
@@ -1144,6 +1156,7 @@ addHook({
1144
1156
  const {
1145
1157
  _requireFile: testSuiteAbsolutePath,
1146
1158
  location: {
1159
+ file: testSourceFileAbsolutePath,
1147
1160
  line: testSourceLine,
1148
1161
  },
1149
1162
  } = test
@@ -1159,6 +1172,7 @@ addHook({
1159
1172
  const testCtx = {
1160
1173
  testName,
1161
1174
  testSuiteAbsolutePath,
1175
+ testSourceFileAbsolutePath,
1162
1176
  testSourceLine,
1163
1177
  browserName,
1164
1178
  }
@@ -1322,7 +1336,10 @@ function generateSummaryWrapper (generateSummary) {
1322
1336
  if (didNotRun) {
1323
1337
  const {
1324
1338
  _requireFile: testSuiteAbsolutePath,
1325
- location: { line: testSourceLine },
1339
+ location: {
1340
+ file: testSourceFileAbsolutePath,
1341
+ line: testSourceLine,
1342
+ },
1326
1343
  _ddIsNew: isNew,
1327
1344
  _ddIsDisabled: isDisabled,
1328
1345
  _ddIsModified: isModified,
@@ -1333,6 +1350,7 @@ function generateSummaryWrapper (generateSummary) {
1333
1350
  testSkipCh.publish({
1334
1351
  testName: getTestFullname(test),
1335
1352
  testSuiteAbsolutePath,
1353
+ testSourceFileAbsolutePath,
1336
1354
  testSourceLine,
1337
1355
  browserName,
1338
1356
  isNew,
@@ -172,8 +172,10 @@ const prismaHook = (runtime, versions, name, isIitm) => {
172
172
  prismaHelperInit.publish(prismaHelperCtx)
173
173
 
174
174
  const helper = prismaHelperCtx.helper
175
- this._tracingHelper = helper
176
- this._engine.tracingHelper = helper
175
+ if (helper) {
176
+ this._tracingHelper = helper
177
+ this._engine.tracingHelper = helper
178
+ }
177
179
  }
178
180
  }
179
181
  }