dd-trace 5.103.0 → 5.105.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 (213) hide show
  1. package/LICENSE-3rdparty.csv +90 -102
  2. package/index.d.ts +107 -6
  3. package/package.json +18 -17
  4. package/packages/datadog-core/src/storage.js +1 -1
  5. package/packages/datadog-instrumentations/src/aerospike.js +1 -1
  6. package/packages/datadog-instrumentations/src/ai.js +8 -7
  7. package/packages/datadog-instrumentations/src/aws-sdk.js +15 -2
  8. package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
  9. package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
  10. package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
  11. package/packages/datadog-instrumentations/src/cucumber.js +181 -35
  12. package/packages/datadog-instrumentations/src/dns.js +54 -18
  13. package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
  14. package/packages/datadog-instrumentations/src/fastify.js +142 -82
  15. package/packages/datadog-instrumentations/src/graphql.js +188 -67
  16. package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
  17. package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
  18. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
  19. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  20. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
  21. package/packages/datadog-instrumentations/src/helpers/kafka.js +17 -0
  22. package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
  23. package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
  24. package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
  25. package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
  26. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -6
  27. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
  28. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
  29. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
  30. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
  31. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +31 -229
  32. package/packages/datadog-instrumentations/src/hono.js +54 -3
  33. package/packages/datadog-instrumentations/src/http/client.js +2 -2
  34. package/packages/datadog-instrumentations/src/http/server.js +9 -4
  35. package/packages/datadog-instrumentations/src/ioredis.js +3 -3
  36. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
  37. package/packages/datadog-instrumentations/src/jest.js +390 -183
  38. package/packages/datadog-instrumentations/src/kafkajs.js +140 -17
  39. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  40. package/packages/datadog-instrumentations/src/memcached.js +2 -1
  41. package/packages/datadog-instrumentations/src/mocha/main.js +399 -107
  42. package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
  43. package/packages/datadog-instrumentations/src/mongodb-core.js +1 -1
  44. package/packages/datadog-instrumentations/src/mongoose.js +10 -12
  45. package/packages/datadog-instrumentations/src/mysql.js +2 -2
  46. package/packages/datadog-instrumentations/src/mysql2.js +1 -1
  47. package/packages/datadog-instrumentations/src/nats.js +182 -0
  48. package/packages/datadog-instrumentations/src/nyc.js +38 -1
  49. package/packages/datadog-instrumentations/src/openai.js +33 -18
  50. package/packages/datadog-instrumentations/src/oracledb.js +6 -1
  51. package/packages/datadog-instrumentations/src/pg.js +1 -1
  52. package/packages/datadog-instrumentations/src/pino.js +17 -5
  53. package/packages/datadog-instrumentations/src/playwright.js +537 -297
  54. package/packages/datadog-instrumentations/src/router.js +80 -34
  55. package/packages/datadog-instrumentations/src/stripe.js +1 -1
  56. package/packages/datadog-instrumentations/src/vitest.js +246 -149
  57. package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
  58. package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
  59. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
  60. package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
  61. package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
  62. package/packages/datadog-plugin-bunyan/src/index.js +28 -0
  63. package/packages/datadog-plugin-cucumber/src/index.js +17 -3
  64. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +223 -45
  65. package/packages/datadog-plugin-cypress/src/support.js +69 -1
  66. package/packages/datadog-plugin-dns/src/lookup.js +8 -6
  67. package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
  68. package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
  69. package/packages/datadog-plugin-graphql/src/execute.js +2 -0
  70. package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
  71. package/packages/datadog-plugin-graphql/src/utils.js +4 -1
  72. package/packages/datadog-plugin-http/src/server.js +40 -15
  73. package/packages/datadog-plugin-jest/src/index.js +11 -3
  74. package/packages/datadog-plugin-jest/src/util.js +15 -8
  75. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
  76. package/packages/datadog-plugin-kafkajs/src/producer.js +35 -0
  77. package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
  78. package/packages/datadog-plugin-mocha/src/index.js +19 -4
  79. package/packages/datadog-plugin-mongodb-core/src/index.js +311 -35
  80. package/packages/datadog-plugin-nats/src/consumer.js +43 -0
  81. package/packages/datadog-plugin-nats/src/index.js +20 -0
  82. package/packages/datadog-plugin-nats/src/producer.js +62 -0
  83. package/packages/datadog-plugin-nats/src/util.js +33 -0
  84. package/packages/datadog-plugin-next/src/index.js +5 -3
  85. package/packages/datadog-plugin-openai/src/tracing.js +15 -2
  86. package/packages/datadog-plugin-oracledb/src/index.js +13 -2
  87. package/packages/datadog-plugin-pino/src/index.js +42 -0
  88. package/packages/datadog-plugin-playwright/src/index.js +4 -4
  89. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
  90. package/packages/datadog-plugin-redis/src/index.js +37 -2
  91. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  92. package/packages/datadog-plugin-router/src/index.js +33 -44
  93. package/packages/datadog-plugin-selenium/src/index.js +1 -1
  94. package/packages/datadog-plugin-undici/src/index.js +19 -0
  95. package/packages/datadog-plugin-vitest/src/index.js +24 -20
  96. package/packages/datadog-plugin-winston/src/index.js +30 -0
  97. package/packages/datadog-shimmer/src/shimmer.js +49 -21
  98. package/packages/dd-trace/src/aiguard/index.js +1 -1
  99. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  100. package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
  101. package/packages/dd-trace/src/appsec/blocking.js +2 -2
  102. package/packages/dd-trace/src/appsec/index.js +11 -4
  103. package/packages/dd-trace/src/appsec/reporter.js +24 -11
  104. package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
  105. package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
  106. package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
  107. package/packages/dd-trace/src/baggage.js +7 -1
  108. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
  109. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
  110. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
  111. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
  112. package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
  113. package/packages/dd-trace/src/config/generated-config-types.d.ts +7 -2
  114. package/packages/dd-trace/src/config/supported-configurations.json +36 -8
  115. package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
  116. package/packages/dd-trace/src/datastreams/context.js +4 -2
  117. package/packages/dd-trace/src/datastreams/writer.js +2 -4
  118. package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
  119. package/packages/dd-trace/src/encode/0.4.js +124 -108
  120. package/packages/dd-trace/src/encode/0.5.js +114 -26
  121. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +57 -42
  122. package/packages/dd-trace/src/encode/agentless-json.js +4 -2
  123. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
  124. package/packages/dd-trace/src/encode/span-stats.js +16 -16
  125. package/packages/dd-trace/src/encode/tags-processors.js +16 -0
  126. package/packages/dd-trace/src/exporters/common/agents.js +3 -1
  127. package/packages/dd-trace/src/exporters/common/request.js +3 -1
  128. package/packages/dd-trace/src/id.js +17 -4
  129. package/packages/dd-trace/src/lambda/handler.js +2 -4
  130. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  131. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  132. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  133. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
  134. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  135. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
  136. package/packages/dd-trace/src/llmobs/sdk.js +10 -16
  137. package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
  138. package/packages/dd-trace/src/llmobs/tagger.js +9 -1
  139. package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
  140. package/packages/dd-trace/src/llmobs/util.js +66 -3
  141. package/packages/dd-trace/src/log/index.js +1 -1
  142. package/packages/dd-trace/src/log/writer.js +3 -1
  143. package/packages/dd-trace/src/msgpack/chunk.js +394 -10
  144. package/packages/dd-trace/src/msgpack/index.js +96 -2
  145. package/packages/dd-trace/src/noop/span.js +3 -1
  146. package/packages/dd-trace/src/openfeature/encoding.js +70 -0
  147. package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
  148. package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
  149. package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
  150. package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
  151. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
  152. package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
  153. package/packages/dd-trace/src/opentelemetry/span.js +1 -1
  154. package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
  155. package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
  156. package/packages/dd-trace/src/opentracing/span.js +59 -19
  157. package/packages/dd-trace/src/opentracing/span_context.js +49 -0
  158. package/packages/dd-trace/src/plugins/apollo.js +3 -1
  159. package/packages/dd-trace/src/plugins/ci_plugin.js +23 -33
  160. package/packages/dd-trace/src/plugins/database.js +7 -6
  161. package/packages/dd-trace/src/plugins/index.js +4 -0
  162. package/packages/dd-trace/src/plugins/log_injection.js +56 -0
  163. package/packages/dd-trace/src/plugins/log_plugin.js +3 -46
  164. package/packages/dd-trace/src/plugins/outbound.js +1 -1
  165. package/packages/dd-trace/src/plugins/plugin.js +15 -17
  166. package/packages/dd-trace/src/plugins/tracing.js +48 -8
  167. package/packages/dd-trace/src/plugins/util/git.js +3 -1
  168. package/packages/dd-trace/src/plugins/util/test.js +318 -13
  169. package/packages/dd-trace/src/plugins/util/web.js +89 -64
  170. package/packages/dd-trace/src/priority_sampler.js +2 -2
  171. package/packages/dd-trace/src/profiling/profiler.js +2 -2
  172. package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
  173. package/packages/dd-trace/src/sampling_rule.js +7 -7
  174. package/packages/dd-trace/src/scope.js +7 -5
  175. package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
  176. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
  177. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
  178. package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
  179. package/packages/dd-trace/src/span_format.js +190 -58
  180. package/packages/dd-trace/src/spanleak.js +1 -1
  181. package/packages/dd-trace/src/standalone/index.js +3 -3
  182. package/packages/dd-trace/src/tagger.js +0 -2
  183. package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
  184. package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
  185. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  186. package/vendor/dist/protobufjs/index.js +1 -1
  187. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  188. package/packages/dd-trace/src/msgpack/encoder.js +0 -308
  189. package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
  190. package/vendor/dist/opentracing/LICENSE +0 -201
  191. package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
  192. package/vendor/dist/opentracing/constants.d.ts +0 -61
  193. package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
  194. package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
  195. package/vendor/dist/opentracing/functions.d.ts +0 -20
  196. package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
  197. package/vendor/dist/opentracing/index.d.ts +0 -12
  198. package/vendor/dist/opentracing/index.js +0 -1
  199. package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
  200. package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
  201. package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
  202. package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
  203. package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
  204. package/vendor/dist/opentracing/noop.d.ts +0 -8
  205. package/vendor/dist/opentracing/reference.d.ts +0 -33
  206. package/vendor/dist/opentracing/span.d.ts +0 -147
  207. package/vendor/dist/opentracing/span_context.d.ts +0 -26
  208. package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
  209. package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
  210. package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
  211. package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
  212. package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
  213. package/vendor/dist/opentracing/tracer.d.ts +0 -127
@@ -4,7 +4,7 @@
4
4
  const realSetTimeout = setTimeout
5
5
 
6
6
  const { readFileSync } = require('node:fs')
7
- const { builtinModules } = require('node:module')
7
+ const { builtinModules, createRequire } = require('node:module')
8
8
  const path = require('path')
9
9
  const satisfies = require('../../../vendor/dist/semifies')
10
10
  const { DD_MAJOR } = require('../../../version')
@@ -12,7 +12,8 @@ const shimmer = require('../../datadog-shimmer')
12
12
  const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
13
13
  const log = require('../../dd-trace/src/log')
14
14
  const {
15
- getCoveredFilenamesFromCoverage,
15
+ getCoveredFilesFromCoverage,
16
+ getExecutableFilesFromCoverage,
16
17
  JEST_WORKER_TRACE_PAYLOAD_CODE,
17
18
  JEST_WORKER_COVERAGE_PAYLOAD_CODE,
18
19
  JEST_WORKER_TELEMETRY_PAYLOAD_CODE,
@@ -30,13 +31,21 @@ const {
30
31
  logAttemptToFixTestExecution,
31
32
  logTestOptimizationSummary,
32
33
  getEfdRetryCount,
34
+ getTestCoverageLinesPercentage,
35
+ applySkippedCoverageToCoverage,
36
+ getTestOptimizationRequestResults,
33
37
  } = require('../../dd-trace/src/plugins/util/test')
34
38
  const {
35
- SEED_SUFFIX_RE,
36
39
  getFormattedJestTestParameters,
37
40
  getJestTestName,
41
+ getRawJestTestName,
38
42
  getJestSuitesToRun,
43
+ removeSeedSuffixFromTestName,
39
44
  } = require('../../datadog-plugin-jest/src/util')
45
+ const {
46
+ addCoverageBackfillUntestedFiles,
47
+ getCoverageBackfillFiles,
48
+ } = require('./jest/coverage-backfill')
40
49
  const { addHook, channel } = require('./helpers/instrument')
41
50
 
42
51
  const testSessionStartCh = channel('ci:jest:session:start')
@@ -83,11 +92,13 @@ const isJestWorker = !!getEnvironmentVariable('JEST_WORKER_ID')
83
92
  const RETRY_TIMES = Symbol.for('RETRY_TIMES')
84
93
 
85
94
  let skippableSuites = []
95
+ let skippableSuitesCoverage = {}
96
+ let skippedSuitesCoverage = {}
86
97
  let knownTests = {}
87
98
  let isCodeCoverageEnabled = false
88
- let isCodeCoverageEnabledBecauseOfUs = false
99
+ let isCoverageReportUploadEnabled = false
100
+ let isItrEnabled = false
89
101
  let isSuitesSkippingEnabled = false
90
- let DD_TEST_TIA_KEEP_COV_CONFIG = false
91
102
  let isUserCodeCoverageEnabled = false
92
103
  let isSuitesSkipped = false
93
104
  let numSkippedSuites = 0
@@ -105,6 +116,13 @@ let testManagementTests = {}
105
116
  let testManagementAttemptToFixRetries = 0
106
117
  let isImpactedTestsEnabled = false
107
118
  let modifiedFiles = {}
119
+ let repositoryRoot
120
+ let lastCoverageMap
121
+ let lastCoverageMapRootDir
122
+ let coverageBackfillContexts
123
+ let coverageBackfillFiles
124
+ let coverageReporterClass
125
+ let coverageReporterRequire
108
126
  let activeTestSuiteAbsolutePath
109
127
  let isConsoleErrorWrapped = false
110
128
 
@@ -129,13 +147,13 @@ const efdSlowAbortedTests = new Set()
129
147
  const efdNewTestCandidates = new Set()
130
148
  // Tests that are genuinely new (not in known tests list).
131
149
  const newTests = new Set()
132
- const testSuiteAbsolutePathsWithFastCheck = new Set()
133
- const testSuiteFastCheckUsage = new Map()
134
150
  const testSuiteJestObjects = new Map()
135
151
  const wrappedJestGlobals = new WeakSet()
136
152
  const wrappedJestObjects = new WeakSet()
137
153
  const wrappedWorkerInitializers = new WeakSet()
138
154
  const publishedRuntimeReferenceErrors = new WeakMap()
155
+ const wrappedCoverageReporters = new WeakSet()
156
+ const coverageReporterRequires = new WeakMap()
139
157
 
140
158
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
141
159
  const ATR_RETRY_SUPPRESSION_FLAG = '_ddDisableAtrRetry'
@@ -144,12 +162,22 @@ const MINIMUM_JEST_VERSION_BEFORE_30 = DD_MAJOR >= 6 ? '>=28.0.0 <30.0.0' : '>=2
144
162
  const MINIMUM_JEST_WORKER_VERSION_BEFORE_30 = DD_MAJOR >= 6 ? '>=28.0.0 <30.0.0' : '>=24.9.0 <30.0.0'
145
163
  const MINIMUM_JEST_CONFIG_ASYNC_VERSION = DD_MAJOR >= 6 ? '>=28.0.0' : '>=25.1.0'
146
164
  const MINIMUM_JEST_TEST_SCHEDULER_VERSION = DD_MAJOR >= 6 ? '>=28.0.0' : '>=27.0.0'
165
+ const MINIMUM_JEST_COVERAGE_BACKFILL_VERSION = '>=28.0.0'
147
166
  const atrSuppressedErrors = new Map()
148
167
  let hasWarnedDeprecatedJestVersion = false
168
+ let isJestCoverageBackfillSupported = false
149
169
 
150
170
  // Track quarantined tests whose errors were suppressed, keyed by "suite › testName"
151
171
  const quarantinedFailingTests = new Set()
152
172
 
173
+ function getJestRepositoryRoot (readConfigsResult) {
174
+ const configuredRepositoryRoot = readConfigsResult.configs
175
+ ?.find(config => config.testEnvironmentOptions?._ddRepositoryRoot)
176
+ ?.testEnvironmentOptions._ddRepositoryRoot
177
+
178
+ return configuredRepositoryRoot || process.cwd()
179
+ }
180
+
153
181
  /**
154
182
  * Sends suppressed quarantine test names from a worker process to the main process.
155
183
  * Supports both child_process (process.send) and worker_threads (parentPort.postMessage).
@@ -292,9 +320,7 @@ function getAttemptToFixExecutionsFromJestResults (result) {
292
320
  if (!testManagementTestsForSuite) continue
293
321
 
294
322
  for (const { fullName, status } of testResults) {
295
- const testName = testSuiteAbsolutePathsWithFastCheck.has(testFilePath)
296
- ? fullName.replace(SEED_SUFFIX_RE, '')
297
- : fullName
323
+ const testName = removeSeedSuffixFromTestName(fullName)
298
324
  const testStatus = getTestStatusFromJestResult(status)
299
325
  if (!testStatus) continue
300
326
 
@@ -340,7 +366,6 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
340
366
  super(config, context)
341
367
  const rootDir = config.globalConfig ? config.globalConfig.rootDir : config.rootDir
342
368
  this.rootDir = rootDir
343
- this.testSuite = getTestSuitePath(context.testPath, rootDir)
344
369
  this.nameToParams = {}
345
370
  this.global._ddtrace = global._ddtrace
346
371
  this.hasSnapshotTests = undefined
@@ -353,6 +378,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
353
378
  this.testEnvironmentOptions = getTestEnvironmentOptions(config)
354
379
 
355
380
  const repositoryRoot = this.testEnvironmentOptions._ddRepositoryRoot
381
+ this.testSuite = getTestSuitePath(context.testPath, rootDir)
356
382
 
357
383
  // TODO: could we grab testPath from `this.getVmContext().expect.getState()` instead?
358
384
  // so we don't rely on context being passed (some custom test environment do not pass it)
@@ -541,14 +567,11 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
541
567
  }
542
568
  }
543
569
 
544
- getShouldStripSeedFromTestName () {
545
- return doesTestSuiteUseFastCheck(this.testSuiteAbsolutePath)
546
- }
547
-
548
570
  // At the `add_test` event we don't have the test object yet, so we can't use it
549
571
  getTestNameFromAddTestEvent (event, state) {
550
- const describeSuffix = getJestTestName(state.currentDescribeBlock, this.getShouldStripSeedFromTestName())
551
- return describeSuffix ? `${describeSuffix} ${event.testName}` : event.testName
572
+ const describeSuffix = getRawJestTestName(state.currentDescribeBlock)
573
+ const testName = describeSuffix ? `${describeSuffix} ${event.testName}` : event.testName
574
+ return removeSeedSuffixFromTestName(testName)
552
575
  }
553
576
 
554
577
  async handleTestEvent (event, state) {
@@ -570,7 +593,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
570
593
  })
571
594
  }
572
595
  if (event.name === 'test_start') {
573
- const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
596
+ const testName = getJestTestName(event.test)
574
597
  if (testsToBeRetried.has(testName)) {
575
598
  // This is needed because we're retrying tests with the same name
576
599
  this.resetSnapshotState()
@@ -774,7 +797,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
774
797
  let attemptToFixFailed = false
775
798
  let failedAllTests = false
776
799
  let isAttemptToFix = false
777
- const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
800
+ const testName = getJestTestName(event.test)
778
801
  if (this.isTestManagementTestsEnabled) {
779
802
  isAttemptToFix = this.testManagementTestsForThisSuite?.attemptToFix?.includes(testName)
780
803
  if (isAttemptToFix) {
@@ -954,7 +977,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
954
977
  // so Jest doesn't see the failure (prevents --bail from stopping the run).
955
978
  const ctx = testContexts.get(test)
956
979
  if (ctx?.isQuarantined && !ctx.isAttemptToFix) {
957
- const testName = getJestTestName(test, this.getShouldStripSeedFromTestName())
980
+ const testName = getJestTestName(test)
958
981
  quarantinedFailingTests.add(`${ctx.suite} › ${testName}`)
959
982
  } else {
960
983
  test.errors = errors
@@ -978,7 +1001,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
978
1001
  testsToBeRetried.clear()
979
1002
  }
980
1003
  if (event.name === 'test_skip' || event.name === 'test_todo') {
981
- const testName = getJestTestName(event.test, this.getShouldStripSeedFromTestName())
1004
+ const testName = getJestTestName(event.test)
982
1005
  testSkippedCh.publish({
983
1006
  test: {
984
1007
  name: testName,
@@ -1128,8 +1151,107 @@ function getTestEnvironment (pkg, jestVersion) {
1128
1151
  return getWrappedEnvironment(pkg, jestVersion)
1129
1152
  }
1130
1153
 
1154
+ function getRepositoryRootFromConfig (config, fallbackRootDir) {
1155
+ return config?.testEnvironmentOptions?._ddRepositoryRoot || repositoryRoot || fallbackRootDir || process.cwd()
1156
+ }
1157
+
1158
+ function getRepositoryRootFromContexts (contexts, fallbackRootDir) {
1159
+ const [firstContext] = contexts || []
1160
+ return getRepositoryRootFromConfig(firstContext?.config, fallbackRootDir)
1161
+ }
1162
+
1163
+ function getRepositoryRootFromTest (test, fallbackRootDir) {
1164
+ return getRepositoryRootFromConfig(test?.context?.config, fallbackRootDir)
1165
+ }
1166
+
1167
+ function hasSkippableSuitesCoverage () {
1168
+ return skippableSuitesCoverage &&
1169
+ typeof skippableSuitesCoverage === 'object' &&
1170
+ Object.keys(skippableSuitesCoverage).length > 0
1171
+ }
1172
+
1173
+ function shouldCollectJestCoverageForTia () {
1174
+ return shouldReportJestSuiteCoverageForTia() ||
1175
+ (isJestCoverageBackfillSupported && isItrEnabled && isCoverageReportUploadEnabled)
1176
+ }
1177
+
1178
+ function shouldReportJestSuiteCoverageForTia () {
1179
+ return isItrEnabled && isCodeCoverageEnabled
1180
+ }
1181
+
1182
+ function hasJestCoverageMap () {
1183
+ return isUserCodeCoverageEnabled || shouldCollectJestCoverageForTia()
1184
+ }
1185
+
1186
+ // TIA coverage backfill is part of Datadog Code Coverage, not the per-suite TIA coverage upload.
1187
+ function isTiaCoverageBackfillEnabled () {
1188
+ return isJestCoverageBackfillSupported && isItrEnabled && isCoverageReportUploadEnabled && hasJestCoverageMap()
1189
+ }
1190
+
1191
+ // Non-TIA Jest coverage keeps the legacy metric. TIA only reports it when Datadog Code Coverage is enabled and
1192
+ // either the run is complete locally or the skipped suites can be backfilled.
1193
+ function shouldReportCodeCoverageLinesPct () {
1194
+ if (!hasJestCoverageMap()) return false
1195
+ if (!isItrEnabled) return true
1196
+ if (!isCoverageReportUploadEnabled) return false
1197
+
1198
+ // If no suites were actually skipped, the local Jest coverage map is complete and does not need backfill.
1199
+ return !isSuitesSkipped || isTiaCoverageBackfillEnabled()
1200
+ }
1201
+
1202
+ function getHookRequire (hookMeta) {
1203
+ if (!hookMeta?.moduleBaseDir) return
1204
+
1205
+ return createRequire(path.join(hookMeta.moduleBaseDir, 'package.json'))
1206
+ }
1207
+
1208
+ function getCoverageBackfillRequire (CoverageReporter) {
1209
+ const hookedCoverageReporterRequire = CoverageReporter && coverageReporterRequires.get(CoverageReporter)
1210
+ if (hookedCoverageReporterRequire) return hookedCoverageReporterRequire
1211
+ if (coverageReporterRequire) return coverageReporterRequire
1212
+
1213
+ const coverageReporterFilename = CoverageReporter?.filename || coverageReporterClass?.filename
1214
+ if (coverageReporterFilename) {
1215
+ return createRequire(`${path.join(path.dirname(coverageReporterFilename), 'CoverageWorker')}.js`)
1216
+ }
1217
+
1218
+ return require
1219
+ }
1220
+
1221
+ function getTestContexts (tests) {
1222
+ if (!tests?.length) return
1223
+
1224
+ const contexts = new Set()
1225
+ for (const test of tests) {
1226
+ if (test.context) {
1227
+ contexts.add(test.context)
1228
+ }
1229
+ }
1230
+ return contexts.size ? contexts : undefined
1231
+ }
1232
+
1233
+ function getCoverageBackfillContexts (contexts) {
1234
+ return contexts?.size ? contexts : coverageBackfillContexts || contexts
1235
+ }
1236
+
1237
+ function resetSuiteSkippingRunState () {
1238
+ isSuitesSkipped = false
1239
+ numSkippedSuites = 0
1240
+ hasUnskippableSuites = false
1241
+ hasForcedToRunSuites = false
1242
+ hasFilteredSkippableSuites = false
1243
+ skippedSuitesCoverage = {}
1244
+ lastCoverageMap = undefined
1245
+ lastCoverageMapRootDir = undefined
1246
+ coverageBackfillContexts = undefined
1247
+ coverageBackfillFiles = undefined
1248
+ }
1249
+
1131
1250
  function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
1132
- const jestSuitesToRun = getJestSuitesToRun(skippableSuites, originalTests, rootDir || process.cwd())
1251
+ if (!isItrEnabled || !isSuitesSkippingEnabled) return originalTests
1252
+
1253
+ const suitePathRoot = getRepositoryRootFromTest(originalTests[0], rootDir)
1254
+ const jestSuitesToRun = getJestSuitesToRun(skippableSuites, originalTests, suitePathRoot)
1133
1255
  hasFilteredSkippableSuites = true
1134
1256
  log.debug('%d out of %d suites are going to run.', jestSuitesToRun.suitesToRun.length, originalTests.length)
1135
1257
  hasUnskippableSuites = jestSuitesToRun.hasUnskippableSuites
@@ -1137,12 +1259,112 @@ function applySuiteSkipping (originalTests, rootDir, frameworkVersion) {
1137
1259
 
1138
1260
  isSuitesSkipped = jestSuitesToRun.suitesToRun.length !== originalTests.length
1139
1261
  numSkippedSuites = jestSuitesToRun.skippedSuites.length
1262
+ skippedSuitesCoverage = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage()
1263
+ ? skippableSuitesCoverage
1264
+ : {}
1265
+ coverageBackfillContexts = isSuitesSkipped && isTiaCoverageBackfillEnabled()
1266
+ ? getTestContexts(originalTests)
1267
+ : undefined
1268
+ coverageBackfillFiles = isSuitesSkipped && isTiaCoverageBackfillEnabled() && hasSkippableSuitesCoverage()
1269
+ ? getCoverageBackfillFiles(skippableSuitesCoverage, suitePathRoot, getTestSuitePath)
1270
+ : undefined
1140
1271
 
1141
1272
  itrSkippedSuitesCh.publish({ skippedSuites: jestSuitesToRun.skippedSuites, frameworkVersion })
1142
1273
 
1143
1274
  return jestSuitesToRun.suitesToRun
1144
1275
  }
1145
1276
 
1277
+ function applySkippedCoverageToJestCoverageMap (coverageMap, rootDir) {
1278
+ if (!coverageMap || !isSuitesSkipped || !isTiaCoverageBackfillEnabled()) return
1279
+ applySkippedCoverageToCoverage(
1280
+ coverageMap,
1281
+ skippedSuitesCoverage,
1282
+ rootDir || process.cwd()
1283
+ )
1284
+ }
1285
+
1286
+ function reporterDispatcherWrapper (reporterDispatcherPackage) {
1287
+ const ReporterDispatcher = reporterDispatcherPackage.default ?? reporterDispatcherPackage
1288
+ if (ReporterDispatcher?.prototype?.onRunComplete) {
1289
+ shimmer.wrap(ReporterDispatcher.prototype, 'onRunComplete', onRunComplete => function (contexts, results) {
1290
+ if (isSuitesSkipped && isTiaCoverageBackfillEnabled()) {
1291
+ applySkippedCoverageToJestCoverageMap(results?.coverageMap, getRepositoryRootFromContexts(contexts))
1292
+ }
1293
+ return onRunComplete.apply(this, arguments)
1294
+ })
1295
+ }
1296
+
1297
+ return reporterDispatcherPackage
1298
+ }
1299
+
1300
+ function wrapCoverageReporter (CoverageReporter, hookMeta) {
1301
+ if (!CoverageReporter?.prototype?.onRunComplete || wrappedCoverageReporters.has(CoverageReporter)) {
1302
+ return
1303
+ }
1304
+
1305
+ coverageReporterRequire = getHookRequire(hookMeta) || coverageReporterRequire
1306
+ if (coverageReporterRequire) {
1307
+ coverageReporterRequires.set(CoverageReporter, coverageReporterRequire)
1308
+ }
1309
+ coverageReporterClass = CoverageReporter
1310
+ wrappedCoverageReporters.add(CoverageReporter)
1311
+ if (CoverageReporter.prototype._addUntestedFiles) {
1312
+ shimmer.wrap(CoverageReporter.prototype, '_addUntestedFiles', addUntestedFiles => function (...args) {
1313
+ const rootDir = repositoryRoot || this._globalConfig?.rootDir || process.cwd()
1314
+ args[0] = getCoverageBackfillContexts(args[0])
1315
+ const result = addUntestedFiles.apply(this, args)
1316
+ if (!isSuitesSkipped || !isTiaCoverageBackfillEnabled()) return result
1317
+
1318
+ const addBackfillAndApplyCoverage = () => {
1319
+ return addCoverageBackfillUntestedFiles({
1320
+ coverageMap: this._coverageMap,
1321
+ testContexts: args[0],
1322
+ rootDir,
1323
+ CoverageReporter,
1324
+ coverageBackfillFiles,
1325
+ getCoverageBackfillRequire,
1326
+ }).then(() => {
1327
+ applySkippedCoverageToJestCoverageMap(this._coverageMap, rootDir)
1328
+ })
1329
+ }
1330
+
1331
+ return Promise.resolve(result).then(value => {
1332
+ return addBackfillAndApplyCoverage().then(() => value)
1333
+ })
1334
+ })
1335
+ }
1336
+
1337
+ shimmer.wrap(CoverageReporter.prototype, 'onRunComplete', onRunComplete => async function (contexts, results) {
1338
+ const coverageContexts = getCoverageBackfillContexts(contexts)
1339
+ const rootDir = getRepositoryRootFromContexts(coverageContexts, this._globalConfig?.rootDir)
1340
+ const coverageMap = results?.coverageMap || this._coverageMap
1341
+ if (isSuitesSkipped && isTiaCoverageBackfillEnabled()) {
1342
+ await addCoverageBackfillUntestedFiles({
1343
+ coverageMap,
1344
+ testContexts: coverageContexts,
1345
+ rootDir,
1346
+ CoverageReporter,
1347
+ coverageBackfillFiles,
1348
+ getCoverageBackfillRequire,
1349
+ })
1350
+ applySkippedCoverageToJestCoverageMap(coverageMap, rootDir)
1351
+ }
1352
+ lastCoverageMap = coverageMap
1353
+ lastCoverageMapRootDir = rootDir
1354
+ return onRunComplete.call(this, coverageContexts, results)
1355
+ })
1356
+ }
1357
+
1358
+ function reportersWrapper (reportersPackage, _version, _isIitm, hookMeta) {
1359
+ wrapCoverageReporter(reportersPackage.CoverageReporter, hookMeta)
1360
+ return reportersPackage
1361
+ }
1362
+
1363
+ function coverageReporterWrapper (coverageReporterPackage, _version, _isIitm, hookMeta) {
1364
+ wrapCoverageReporter(coverageReporterPackage.default ?? coverageReporterPackage, hookMeta)
1365
+ return coverageReporterPackage
1366
+ }
1367
+
1146
1368
  addHook({
1147
1369
  name: 'jest-environment-node',
1148
1370
  versions: [MINIMUM_JEST_VERSION],
@@ -1173,6 +1395,12 @@ function getWrappedScheduleTests (scheduleTests, frameworkVersion) {
1173
1395
  }
1174
1396
  }
1175
1397
 
1398
+ function getChannelPromise (channelToPublishTo, payload = {}) {
1399
+ return new Promise(resolve => {
1400
+ channelToPublishTo.publish({ ...payload, onDone: resolve })
1401
+ })
1402
+ }
1403
+
1176
1404
  function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
1177
1405
  const SearchSource = searchSourcePackage.default ?? searchSourcePackage
1178
1406
 
@@ -1181,7 +1409,9 @@ function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
1181
1409
  const [{ rootDir, shard }] = arguments
1182
1410
 
1183
1411
  if (isKnownTestsEnabled) {
1184
- const projectSuites = testPaths.tests.map(test => getTestSuitePath(test.path, test.context.config.rootDir))
1412
+ const projectSuites = testPaths.tests.map(test => {
1413
+ return getTestSuitePath(test.path, getRepositoryRootFromTest(test, test.context.config.rootDir))
1414
+ })
1185
1415
 
1186
1416
  // If the `jest` key does not exist in the known tests response, we consider the Early Flake detection faulty.
1187
1417
  const isFaulty = !knownTests?.jest ||
@@ -1201,14 +1431,11 @@ function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
1201
1431
  }
1202
1432
  }
1203
1433
 
1434
+ // When Jest sharding is enabled, filter after Jest picks this process's shard. Different shards usually run in
1435
+ // different CI jobs, so their skippable requests can happen at different times and receive different responses.
1436
+ // Filtering before Jest shards would make each job shard a different base test list, which can cause duplicate
1437
+ // suite execution across shards.
1204
1438
  if (shard?.shardCount > 1 || !isSuitesSkippingEnabled || !skippableSuites.length) {
1205
- // If the user is using jest sharding, we want to apply the filtering of tests in the shard process.
1206
- // The reason for this is the following:
1207
- // The tests for different shards are likely being run in different CI jobs so
1208
- // the requests to the skippable endpoint might be done at different times and their responses might be different.
1209
- // If the skippable endpoint is returning different suites and we filter the list of tests here,
1210
- // the base list of tests that is used for sharding might be different,
1211
- // causing the shards to potentially run the same suite.
1212
1439
  return testPaths
1213
1440
  }
1214
1441
  const { tests } = testPaths
@@ -1223,6 +1450,8 @@ function searchSourceWrapper (searchSourcePackage, frameworkVersion) {
1223
1450
  function getCliWrapper (isNewJestVersion) {
1224
1451
  return function cliWrapper (cli, jestVersion) {
1225
1452
  warnDeprecatedJestVersion(jestVersion)
1453
+ isJestCoverageBackfillSupported = !!jestVersion &&
1454
+ satisfies(jestVersion, MINIMUM_JEST_COVERAGE_BACKFILL_VERSION)
1226
1455
 
1227
1456
  if (isNewJestVersion) {
1228
1457
  cli = shimmer.wrap(
@@ -1234,22 +1463,21 @@ function getCliWrapper (isNewJestVersion) {
1234
1463
  }
1235
1464
  return shimmer.wrap(cli, 'runCLI', runCLI => async function () {
1236
1465
  let onDone
1237
- const configurationPromise = new Promise((resolve) => {
1238
- onDone = resolve
1239
- })
1240
1466
  if (!libraryConfigurationCh.hasSubscribers) {
1241
1467
  return runCLI.apply(this, arguments)
1242
1468
  }
1243
1469
 
1244
- libraryConfigurationCh.publish({ onDone, frameworkVersion: jestVersion })
1470
+ resetSuiteSkippingRunState()
1245
1471
 
1246
1472
  try {
1247
- const { err, libraryConfig } = await configurationPromise
1473
+ const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, {
1474
+ frameworkVersion: jestVersion,
1475
+ })
1248
1476
  if (!err) {
1249
1477
  isCodeCoverageEnabled = libraryConfig.isCodeCoverageEnabled
1250
- isSuitesSkippingEnabled = libraryConfig.isSuitesSkippingEnabled
1251
- DD_TEST_TIA_KEEP_COV_CONFIG =
1252
- libraryConfig.DD_TEST_TIA_KEEP_COV_CONFIG ?? DD_TEST_TIA_KEEP_COV_CONFIG
1478
+ isCoverageReportUploadEnabled = libraryConfig.isCoverageReportUploadEnabled
1479
+ isItrEnabled = libraryConfig.isItrEnabled
1480
+ isSuitesSkippingEnabled = isItrEnabled && libraryConfig.isSuitesSkippingEnabled
1253
1481
  isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
1254
1482
  earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
1255
1483
  earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
@@ -1263,15 +1491,22 @@ function getCliWrapper (isNewJestVersion) {
1263
1491
  log.error('Jest library configuration error', err)
1264
1492
  }
1265
1493
 
1266
- if (isKnownTestsEnabled) {
1267
- const knownTestsPromise = new Promise((resolve) => {
1268
- onDone = resolve
1269
- })
1270
-
1271
- knownTestsCh.publish({ onDone })
1494
+ const {
1495
+ knownTestsResponse,
1496
+ testManagementTestsResponse,
1497
+ skippableSuitesResponse,
1498
+ } = await getTestOptimizationRequestResults({
1499
+ isKnownTestsEnabled,
1500
+ isTestManagementTestsEnabled,
1501
+ isSuitesSkippingEnabled,
1502
+ getKnownTests: () => getChannelPromise(knownTestsCh),
1503
+ getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
1504
+ getSkippableSuites: () => getChannelPromise(skippableSuitesCh),
1505
+ })
1272
1506
 
1507
+ if (isKnownTestsEnabled) {
1273
1508
  try {
1274
- const { err, knownTests: receivedKnownTests } = await knownTestsPromise
1509
+ const { err, knownTests: receivedKnownTests } = knownTestsResponse || await getChannelPromise(knownTestsCh)
1275
1510
  if (err) {
1276
1511
  // We disable EFD if there has been an error in the known tests request
1277
1512
  isEarlyFlakeDetectionEnabled = false
@@ -1285,16 +1520,19 @@ function getCliWrapper (isNewJestVersion) {
1285
1520
  }
1286
1521
 
1287
1522
  if (isSuitesSkippingEnabled) {
1288
- const skippableSuitesPromise = new Promise((resolve) => {
1289
- onDone = resolve
1290
- })
1291
-
1292
- skippableSuitesCh.publish({ onDone })
1293
-
1294
1523
  try {
1295
- const { err, skippableSuites: receivedSkippableSuites } = await skippableSuitesPromise
1296
- if (!err) {
1524
+ const {
1525
+ err,
1526
+ skippableSuites: receivedSkippableSuites,
1527
+ skippableSuitesCoverage: receivedSkippableSuitesCoverage,
1528
+ } = skippableSuitesResponse || await getChannelPromise(skippableSuitesCh)
1529
+ if (err) {
1530
+ skippableSuitesCoverage = {}
1531
+ skippedSuitesCoverage = {}
1532
+ } else {
1297
1533
  skippableSuites = receivedSkippableSuites
1534
+ skippableSuitesCoverage = receivedSkippableSuitesCoverage || {}
1535
+ skippedSuitesCoverage = {}
1298
1536
  }
1299
1537
  } catch (err) {
1300
1538
  log.error('Jest test-suite skippable error', err)
@@ -1302,14 +1540,9 @@ function getCliWrapper (isNewJestVersion) {
1302
1540
  }
1303
1541
 
1304
1542
  if (isTestManagementTestsEnabled) {
1305
- const testManagementTestsPromise = new Promise((resolve) => {
1306
- onDone = resolve
1307
- })
1308
-
1309
- testManagementTestsCh.publish({ onDone })
1310
-
1311
1543
  try {
1312
- const { err, testManagementTests: receivedTestManagementTests } = await testManagementTestsPromise
1544
+ const { err, testManagementTests: receivedTestManagementTests } =
1545
+ testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
1313
1546
  if (err) {
1314
1547
  isTestManagementTestsEnabled = false
1315
1548
  testManagementTests = {}
@@ -1323,14 +1556,8 @@ function getCliWrapper (isNewJestVersion) {
1323
1556
  }
1324
1557
 
1325
1558
  if (isImpactedTestsEnabled) {
1326
- const impactedTestsPromise = new Promise((resolve) => {
1327
- onDone = resolve
1328
- })
1329
-
1330
- modifiedFilesCh.publish({ onDone })
1331
-
1332
1559
  try {
1333
- const { err, modifiedFiles: receivedModifiedFiles } = await impactedTestsPromise
1560
+ const { err, modifiedFiles: receivedModifiedFiles } = await getChannelPromise(modifiedFilesCh)
1334
1561
  if (!err) {
1335
1562
  modifiedFiles = receivedModifiedFiles
1336
1563
  }
@@ -1340,13 +1567,16 @@ function getCliWrapper (isNewJestVersion) {
1340
1567
  }
1341
1568
 
1342
1569
  const processArgv = process.argv.slice(2).join(' ')
1343
- testSessionStartCh.publish({ command: `jest ${processArgv}`, frameworkVersion: jestVersion })
1570
+ testSessionStartCh.publish({
1571
+ command: `jest ${processArgv}`,
1572
+ frameworkVersion: jestVersion,
1573
+ })
1344
1574
 
1345
1575
  const result = await runCLI.apply(this, arguments)
1346
1576
 
1347
1577
  const {
1348
1578
  results: {
1349
- coverageMap,
1579
+ coverageMap: resultCoverageMap,
1350
1580
  numFailedTestSuites,
1351
1581
  numFailedTests,
1352
1582
  numRuntimeErrorTestSuites = 0,
@@ -1362,11 +1592,30 @@ function getCliWrapper (isNewJestVersion) {
1362
1592
  const mustNotFlipSuccess = hasSuiteLevelFailures || hasRunLevelFailure
1363
1593
 
1364
1594
  let testCodeCoverageLinesTotal
1595
+ let testSessionCoverageFiles
1596
+ const shouldReportTestSessionCoverage = isTiaCoverageBackfillEnabled()
1365
1597
 
1366
- if (isUserCodeCoverageEnabled) {
1598
+ if (shouldReportCodeCoverageLinesPct()) {
1367
1599
  try {
1368
- const { pct, total } = coverageMap.getCoverageSummary().lines
1369
- testCodeCoverageLinesTotal = total === 0 ? 0 : pct
1600
+ const coverageMap = resultCoverageMap || lastCoverageMap
1601
+ const coverageRootDir = lastCoverageMapRootDir ||
1602
+ repositoryRoot ||
1603
+ result.globalConfig?.rootDir ||
1604
+ process.cwd()
1605
+ if (isSuitesSkipped) {
1606
+ applySkippedCoverageToJestCoverageMap(coverageMap, coverageRootDir)
1607
+ }
1608
+ testCodeCoverageLinesTotal = getTestCoverageLinesPercentage(
1609
+ coverageMap,
1610
+ undefined,
1611
+ coverageRootDir
1612
+ )
1613
+ if (shouldReportTestSessionCoverage) {
1614
+ testSessionCoverageFiles = getExecutableFilesFromCoverage(coverageMap).map(({ filename, bitmap }) => ({
1615
+ filename: getTestSuitePath(filename, coverageRootDir),
1616
+ bitmap,
1617
+ }))
1618
+ }
1370
1619
  } catch {
1371
1620
  // ignore errors
1372
1621
  }
@@ -1388,9 +1637,7 @@ function getCliWrapper (isNewJestVersion) {
1388
1637
  for (const { testResults, testFilePath } of result.results.testResults) {
1389
1638
  const suite = getTestSuitePath(testFilePath, result.globalConfig.rootDir)
1390
1639
  for (const { fullName } of testResults) {
1391
- const name = testSuiteAbsolutePathsWithFastCheck.has(testFilePath)
1392
- ? fullName.replace(SEED_SUFFIX_RE, '')
1393
- : fullName
1640
+ const name = removeSeedSuffixFromTestName(fullName)
1394
1641
  fullNameToSuite.set(name, suite)
1395
1642
  }
1396
1643
  }
@@ -1429,10 +1676,8 @@ function getCliWrapper (isNewJestVersion) {
1429
1676
  .testResults.flatMap(({ testResults, testFilePath: testSuiteAbsolutePath }) => (
1430
1677
  testResults.map(({ fullName: testName, status }) => (
1431
1678
  {
1432
- // Strip @fast-check/jest seed suffix so the name matches what was reported via TEST_NAME
1433
- testName: testSuiteAbsolutePathsWithFastCheck.has(testSuiteAbsolutePath)
1434
- ? testName.replace(SEED_SUFFIX_RE, '')
1435
- : testName,
1679
+ // Strip seed suffix so the name matches what was reported via TEST_NAME.
1680
+ testName: removeSeedSuffixFromTestName(testName),
1436
1681
  testSuiteAbsolutePath,
1437
1682
  status,
1438
1683
  }
@@ -1551,7 +1796,9 @@ function getCliWrapper (isNewJestVersion) {
1551
1796
  isSuitesSkipped,
1552
1797
  isSuitesSkippingEnabled,
1553
1798
  isCodeCoverageEnabled,
1799
+ isCoverageReportUploadEnabled,
1554
1800
  testCodeCoverageLinesTotal,
1801
+ testSessionCoverageFiles,
1555
1802
  numSkippedSuites,
1556
1803
  hasUnskippableSuites,
1557
1804
  hasForcedToRunSuites,
@@ -1577,7 +1824,7 @@ function getCliWrapper (isNewJestVersion) {
1577
1824
 
1578
1825
  logSessionSummary(ignoredFailuresSummary, getAttemptToFixExecutionsFromJestResults(result))
1579
1826
 
1580
- numSkippedSuites = 0
1827
+ resetSuiteSkippingRunState()
1581
1828
 
1582
1829
  return result
1583
1830
  }, {
@@ -1586,28 +1833,6 @@ function getCliWrapper (isNewJestVersion) {
1586
1833
  }
1587
1834
  }
1588
1835
 
1589
- function coverageReporterWrapper (coverageReporter) {
1590
- const CoverageReporter = coverageReporter.default ?? coverageReporter
1591
-
1592
- /**
1593
- * If ITR is active, we're running fewer tests, so of course the total code coverage is reduced.
1594
- * This calculation adds no value, so we'll skip it, as long as the user has not manually opted in to code coverage,
1595
- * in which case we'll leave it.
1596
- */
1597
- // `_addUntestedFiles` is an async function
1598
- shimmer.wrap(CoverageReporter.prototype, '_addUntestedFiles', addUntestedFiles => function (...args) {
1599
- if (DD_TEST_TIA_KEEP_COV_CONFIG) {
1600
- return addUntestedFiles.apply(this, args)
1601
- }
1602
- if (isCodeCoverageEnabledBecauseOfUs) {
1603
- return Promise.resolve()
1604
- }
1605
- return addUntestedFiles.apply(this, args)
1606
- })
1607
-
1608
- return coverageReporter
1609
- }
1610
-
1611
1836
  function shouldWaitForTestSuiteFinish (environment) {
1612
1837
  return isJestWorker && environment.globalConfig?.workerIdleMemoryLimit !== undefined
1613
1838
  }
@@ -1631,7 +1856,6 @@ function publishTestSuiteFinish (payload, waitForFinish) {
1631
1856
 
1632
1857
  function cleanupTestSuiteState (testSuiteAbsolutePath) {
1633
1858
  testSuiteMockedFiles.delete(testSuiteAbsolutePath)
1634
- testSuiteFastCheckUsage.delete(testSuiteAbsolutePath)
1635
1859
  testSuiteJestObjects.delete(testSuiteAbsolutePath)
1636
1860
  }
1637
1861
 
@@ -1686,6 +1910,23 @@ addHook({
1686
1910
  return sequencerPackage
1687
1911
  })
1688
1912
 
1913
+ addHook({
1914
+ name: '@jest/core',
1915
+ file: 'build/cli/index.js',
1916
+ versions: [MINIMUM_JEST_VERSION_BEFORE_30],
1917
+ }, getCliWrapper(false))
1918
+
1919
+ addHook({
1920
+ name: '@jest/core',
1921
+ versions: ['>=30.0.0'],
1922
+ }, getCliWrapper(true))
1923
+
1924
+ addHook({
1925
+ name: '@jest/core',
1926
+ file: 'build/ReporterDispatcher.js',
1927
+ versions: [MINIMUM_JEST_VERSION],
1928
+ }, reporterDispatcherWrapper)
1929
+
1689
1930
  if (DD_MAJOR < 6) {
1690
1931
  addHook({
1691
1932
  name: '@jest/reporters',
@@ -1694,29 +1935,16 @@ if (DD_MAJOR < 6) {
1694
1935
  }, coverageReporterWrapper)
1695
1936
  }
1696
1937
 
1697
- addHook({
1698
- name: '@jest/reporters',
1699
- file: 'build/CoverageReporter.js',
1700
- versions: [DD_MAJOR >= 6 ? '>=28.0.0' : '>=26.6.2'],
1701
- }, coverageReporterWrapper)
1702
-
1703
1938
  addHook({
1704
1939
  name: '@jest/reporters',
1705
1940
  versions: ['>=30.0.0'],
1706
- }, (reporters) => {
1707
- return shimmer.wrap(reporters, 'CoverageReporter', coverageReporterWrapper, { replaceGetter: true })
1708
- })
1941
+ }, reportersWrapper)
1709
1942
 
1710
1943
  addHook({
1711
- name: '@jest/core',
1712
- file: 'build/cli/index.js',
1713
- versions: [MINIMUM_JEST_VERSION_BEFORE_30],
1714
- }, getCliWrapper(false))
1715
-
1716
- addHook({
1717
- name: '@jest/core',
1718
- versions: ['>=30.0.0'],
1719
- }, getCliWrapper(true))
1944
+ name: '@jest/reporters',
1945
+ file: 'build/CoverageReporter.js',
1946
+ versions: [DD_MAJOR >= 6 ? '>=28.0.0' : '>=26.6.2'],
1947
+ }, coverageReporterWrapper)
1720
1948
 
1721
1949
  function jestAdapterWrapper (jestAdapter, jestVersion) {
1722
1950
  const adapter = jestAdapter.default ?? jestAdapter
@@ -1750,10 +1978,13 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
1750
1978
  if (environment.testEnvironmentOptions?._ddTestCodeCoverageEnabled) {
1751
1979
  const root = environment.repositoryRoot || environment.rootDir
1752
1980
 
1753
- const getFilesWithPath = (files) => files.map(file => getTestSuitePath(file, root))
1754
-
1755
- const coverageFiles = getFilesWithPath(getCoveredFilenamesFromCoverage(environment.global.__coverage__))
1756
- const mockedFiles = getFilesWithPath(getMockedFiles(environment.testSuiteAbsolutePath))
1981
+ const coverageFiles = getCoveredFilesFromCoverage(environment.global.__coverage__)
1982
+ .map(file => ({
1983
+ ...file,
1984
+ filename: getTestSuitePath(file.filename, root),
1985
+ }))
1986
+ const mockedFiles = getMockedFiles(environment.testSuiteAbsolutePath)
1987
+ .map(file => getTestSuitePath(file, root))
1757
1988
 
1758
1989
  testSuiteCodeCoverageCh.publish({
1759
1990
  coverageFiles,
@@ -1833,41 +2064,48 @@ addHook({
1833
2064
  }, jestAdapterWrapper)
1834
2065
 
1835
2066
  function configureTestEnvironment (readConfigsResult) {
1836
- const { configs } = readConfigsResult
1837
- testSessionConfigurationCh.publish(configs.map(config => config.testEnvironmentOptions))
1838
- // We can't directly use isCodeCoverageEnabled when reporting coverage in `jestAdapterWrapper`
1839
- // because `jestAdapterWrapper` runs in a different process. We have to go through `testEnvironmentOptions`
1840
- for (const config of configs) {
1841
- config.testEnvironmentOptions._ddTestCodeCoverageEnabled = isCodeCoverageEnabled
1842
- }
1843
-
2067
+ repositoryRoot = getJestRepositoryRoot(readConfigsResult)
1844
2068
  isUserCodeCoverageEnabled = !!readConfigsResult.globalConfig.collectCoverage
1845
- isCodeCoverageEnabledBecauseOfUs = isCodeCoverageEnabled && !isUserCodeCoverageEnabled
1846
-
1847
- if (readConfigsResult.globalConfig.forceExit) {
1848
- log.warn("Jest's '--forceExit' flag has been passed. This may cause loss of data.")
1849
- }
2069
+ const isCodeCoverageEnabledBecauseOfUs = shouldCollectJestCoverageForTia() && !isUserCodeCoverageEnabled
1850
2070
 
1851
2071
  if (isCodeCoverageEnabledBecauseOfUs) {
1852
- const globalConfig = {
2072
+ readConfigsResult.globalConfig = {
1853
2073
  ...readConfigsResult.globalConfig,
1854
2074
  collectCoverage: true,
2075
+ coverageReporters: ['none'],
1855
2076
  }
1856
- readConfigsResult.globalConfig = globalConfig
2077
+ readConfigsResult.configs = readConfigsResult.configs.map(config => ({
2078
+ ...config,
2079
+ coverageReporters: ['none'],
2080
+ }))
1857
2081
  }
2082
+
2083
+ // We can't directly use the parent process flags when reporting suite coverage in `jestAdapterWrapper`
2084
+ // because `jestAdapterWrapper` runs in a different process. We have to go through `testEnvironmentOptions`.
2085
+ const configs = readConfigsResult.configs.map(config => {
2086
+ const testEnvironmentOptions = config.testEnvironmentOptions || {}
2087
+ testEnvironmentOptions._ddRepositoryRoot = repositoryRoot
2088
+ testEnvironmentOptions._ddTestCodeCoverageEnabled = shouldReportJestSuiteCoverageForTia()
2089
+
2090
+ return {
2091
+ ...config,
2092
+ testEnvironmentOptions,
2093
+ }
2094
+ })
2095
+ readConfigsResult.configs = configs
2096
+ testSessionConfigurationCh.publish(readConfigsResult.configs.map(config => config.testEnvironmentOptions))
2097
+ repositoryRoot = getJestRepositoryRoot(readConfigsResult)
2098
+
2099
+ if (readConfigsResult.globalConfig.forceExit) {
2100
+ log.warn("Jest's '--forceExit' flag has been passed. This may cause loss of data.")
2101
+ }
2102
+
1858
2103
  if (isSuitesSkippingEnabled) {
1859
2104
  // If suite skipping is enabled, we pass `passWithNoTests` in case every test gets skipped.
1860
2105
  const globalConfig = {
1861
2106
  ...readConfigsResult.globalConfig,
1862
2107
  passWithNoTests: true,
1863
2108
  }
1864
- if (isCodeCoverageEnabledBecauseOfUs && !DD_TEST_TIA_KEEP_COV_CONFIG) {
1865
- globalConfig.coverageReporters = ['none']
1866
- readConfigsResult.configs = configs.map(config => ({
1867
- ...config,
1868
- coverageReporters: ['none'],
1869
- }))
1870
- }
1871
2109
  readConfigsResult.globalConfig = globalConfig
1872
2110
  }
1873
2111
 
@@ -1902,6 +2140,7 @@ const DD_TEST_ENVIRONMENT_OPTION_KEYS = [
1902
2140
  '_ddIsEarlyFlakeDetectionEnabled',
1903
2141
  '_ddEarlyFlakeDetectionSlowTestRetries',
1904
2142
  '_ddRepositoryRoot',
2143
+ '_ddTestCodeCoverageEnabled',
1905
2144
  '_ddIsFlakyTestRetriesEnabled',
1906
2145
  '_ddFlakyTestRetriesCount',
1907
2146
  '_ddItrSkippingEnabledTags',
@@ -2047,10 +2286,12 @@ function getStaticMockedFiles (suiteFilePath) {
2047
2286
 
2048
2287
  function getMockedFiles (suiteFilePath) {
2049
2288
  const mockedFiles = testSuiteMockedFiles.get(suiteFilePath)
2289
+ const staticMockedFiles = getStaticMockedFiles(suiteFilePath)
2290
+
2050
2291
  if (mockedFiles?.length) {
2051
- return mockedFiles
2292
+ return [...new Set([...mockedFiles, ...staticMockedFiles])]
2052
2293
  }
2053
- return getStaticMockedFiles(suiteFilePath)
2294
+ return staticMockedFiles
2054
2295
  }
2055
2296
 
2056
2297
  function wrapJestObject (jestObject, suiteFilePath) {
@@ -2083,38 +2324,6 @@ function wrapJestGlobalsForRuntime (runtime) {
2083
2324
  })
2084
2325
  }
2085
2326
 
2086
- function recordFastCheckUsage (runtime, from, moduleName) {
2087
- if (moduleName !== '@fast-check/jest') return
2088
-
2089
- if (from) {
2090
- testSuiteAbsolutePathsWithFastCheck.add(from)
2091
- testSuiteFastCheckUsage.set(from, true)
2092
- }
2093
- if (runtime?._testPath) {
2094
- testSuiteAbsolutePathsWithFastCheck.add(runtime._testPath)
2095
- testSuiteFastCheckUsage.set(runtime._testPath, true)
2096
- }
2097
- }
2098
-
2099
- function doesTestSuiteUseFastCheck (testSuiteAbsolutePath) {
2100
- if (!testSuiteAbsolutePath) return false
2101
- if (testSuiteFastCheckUsage.has(testSuiteAbsolutePath)) {
2102
- return testSuiteFastCheckUsage.get(testSuiteAbsolutePath)
2103
- }
2104
-
2105
- try {
2106
- const usesFastCheck = readFileSync(testSuiteAbsolutePath, 'utf8').includes('@fast-check/jest')
2107
- testSuiteFastCheckUsage.set(testSuiteAbsolutePath, usesFastCheck)
2108
- if (usesFastCheck) {
2109
- testSuiteAbsolutePathsWithFastCheck.add(testSuiteAbsolutePath)
2110
- }
2111
- return usesFastCheck
2112
- } catch {
2113
- testSuiteFastCheckUsage.set(testSuiteAbsolutePath, false)
2114
- return false
2115
- }
2116
- }
2117
-
2118
2327
  function getLastLoggedReferenceError (runtime) {
2119
2328
  const loggedReferenceErrors = runtime?.loggedReferenceErrors
2120
2329
  if (!loggedReferenceErrors?.size) return
@@ -2223,8 +2432,6 @@ addHook({
2223
2432
  // To bypass jest's own require engine
2224
2433
  return requireOutsideJestRequireEngine(this, moduleName)
2225
2434
  }
2226
- // This means that `@fast-check/jest` is used in the test file.
2227
- recordFastCheckUsage(this, from, moduleName)
2228
2435
  let returnedValue
2229
2436
  try {
2230
2437
  returnedValue = requireModuleOrMock.apply(this, arguments)