dd-trace 5.102.0 → 5.103.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 (133) hide show
  1. package/ext/exporters.js +1 -0
  2. package/package.json +12 -11
  3. package/packages/datadog-esbuild/src/utils.js +2 -2
  4. package/packages/datadog-instrumentations/src/ai.js +1 -1
  5. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +32 -15
  6. package/packages/datadog-instrumentations/src/couchbase.js +69 -220
  7. package/packages/datadog-instrumentations/src/cucumber.js +1 -1
  8. package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
  9. package/packages/datadog-instrumentations/src/electron.js +240 -0
  10. package/packages/datadog-instrumentations/src/fetch.js +5 -5
  11. package/packages/datadog-instrumentations/src/graphql.js +13 -12
  12. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
  13. package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
  14. package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
  15. package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
  16. package/packages/datadog-instrumentations/src/helpers/kafka.js +41 -0
  17. package/packages/datadog-instrumentations/src/ioredis.js +16 -12
  18. package/packages/datadog-instrumentations/src/jest.js +351 -50
  19. package/packages/datadog-instrumentations/src/kafkajs.js +164 -173
  20. package/packages/datadog-instrumentations/src/mocha/main.js +73 -1
  21. package/packages/datadog-instrumentations/src/mongodb-core.js +33 -8
  22. package/packages/datadog-instrumentations/src/pg.js +24 -10
  23. package/packages/datadog-instrumentations/src/playwright.js +427 -55
  24. package/packages/datadog-instrumentations/src/redis.js +19 -10
  25. package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
  26. package/packages/datadog-plugin-aws-sdk/src/base.js +18 -24
  27. package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
  28. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
  29. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
  30. package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
  31. package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
  32. package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
  33. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
  34. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
  35. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
  36. package/packages/datadog-plugin-couchbase/src/index.js +58 -52
  37. package/packages/datadog-plugin-cucumber/src/index.js +1 -0
  38. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +214 -22
  39. package/packages/datadog-plugin-cypress/src/support.js +13 -1
  40. package/packages/datadog-plugin-electron/src/index.js +17 -0
  41. package/packages/datadog-plugin-electron/src/ipc.js +143 -0
  42. package/packages/datadog-plugin-electron/src/net.js +82 -0
  43. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
  44. package/packages/datadog-plugin-graphql/src/execute.js +6 -28
  45. package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
  46. package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
  47. package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
  48. package/packages/datadog-plugin-graphql/src/utils.js +29 -0
  49. package/packages/datadog-plugin-grpc/src/client.js +6 -7
  50. package/packages/datadog-plugin-grpc/src/util.js +57 -22
  51. package/packages/datadog-plugin-http/src/client.js +2 -2
  52. package/packages/datadog-plugin-jest/src/index.js +92 -50
  53. package/packages/datadog-plugin-mocha/src/index.js +1 -0
  54. package/packages/datadog-plugin-mongodb-core/src/index.js +36 -70
  55. package/packages/datadog-plugin-mysql/src/index.js +1 -1
  56. package/packages/datadog-plugin-openai/src/services.js +2 -1
  57. package/packages/datadog-plugin-pg/src/index.js +3 -3
  58. package/packages/datadog-plugin-playwright/src/index.js +4 -0
  59. package/packages/datadog-plugin-redis/src/index.js +18 -23
  60. package/packages/dd-trace/src/aiguard/index.js +3 -1
  61. package/packages/dd-trace/src/aiguard/sdk.js +36 -30
  62. package/packages/dd-trace/src/aiguard/tags.js +20 -11
  63. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
  64. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
  65. package/packages/dd-trace/src/azure_metadata.js +17 -6
  66. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
  67. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
  68. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
  69. package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
  70. package/packages/dd-trace/src/config/defaults.js +3 -14
  71. package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -1
  72. package/packages/dd-trace/src/config/helper.js +4 -0
  73. package/packages/dd-trace/src/config/index.js +2 -2
  74. package/packages/dd-trace/src/config/major-overrides.js +98 -0
  75. package/packages/dd-trace/src/config/parsers.js +7 -1
  76. package/packages/dd-trace/src/config/supported-configurations.json +51 -38
  77. package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
  78. package/packages/dd-trace/src/datastreams/manager.js +1 -1
  79. package/packages/dd-trace/src/datastreams/processor.js +2 -2
  80. package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
  81. package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
  82. package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
  83. package/packages/dd-trace/src/debugger/index.js +7 -7
  84. package/packages/dd-trace/src/dogstatsd.js +2 -2
  85. package/packages/dd-trace/src/encode/0.4.js +45 -54
  86. package/packages/dd-trace/src/encode/0.5.js +34 -3
  87. package/packages/dd-trace/src/encode/agentless-json.js +1 -1
  88. package/packages/dd-trace/src/exporter.js +2 -0
  89. package/packages/dd-trace/src/exporters/agent/index.js +2 -1
  90. package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
  91. package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
  92. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
  93. package/packages/dd-trace/src/exporters/common/request.js +1 -1
  94. package/packages/dd-trace/src/exporters/electron/index.js +49 -0
  95. package/packages/dd-trace/src/external-logger/src/index.js +2 -1
  96. package/packages/dd-trace/src/git_metadata.js +10 -8
  97. package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
  98. package/packages/dd-trace/src/lambda/index.js +62 -14
  99. package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
  100. package/packages/dd-trace/src/llmobs/index.js +13 -2
  101. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
  102. package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
  103. package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
  104. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +2 -1
  105. package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
  106. package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
  107. package/packages/dd-trace/src/plugins/ci_plugin.js +49 -4
  108. package/packages/dd-trace/src/plugins/database.js +54 -12
  109. package/packages/dd-trace/src/plugins/index.js +1 -0
  110. package/packages/dd-trace/src/plugins/plugin.js +2 -4
  111. package/packages/dd-trace/src/plugins/util/ci.js +8 -8
  112. package/packages/dd-trace/src/plugins/util/git-cache.js +20 -18
  113. package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
  114. package/packages/dd-trace/src/plugins/util/test.js +37 -5
  115. package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -15
  116. package/packages/dd-trace/src/priority_sampler.js +1 -1
  117. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  118. package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
  119. package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
  120. package/packages/dd-trace/src/rate_limiter.js +1 -1
  121. package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
  122. package/packages/dd-trace/src/ritm.js +2 -1
  123. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
  124. package/packages/dd-trace/src/serverless.js +5 -2
  125. package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +20 -0
  126. package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
  127. package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +20 -0
  128. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
  129. package/packages/dd-trace/src/span_stats.js +1 -1
  130. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  131. package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
  132. package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
  133. package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
@@ -3,10 +3,13 @@
3
3
  // Capture real timers at module load time, before any test can install fake timers.
4
4
  const realSetTimeout = setTimeout
5
5
 
6
+ const { readFileSync } = require('node:fs')
7
+ const { builtinModules } = require('node:module')
6
8
  const path = require('path')
7
9
  const satisfies = require('../../../vendor/dist/semifies')
8
10
  const { DD_MAJOR } = require('../../../version')
9
11
  const shimmer = require('../../datadog-shimmer')
12
+ const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
10
13
  const log = require('../../dd-trace/src/log')
11
14
  const {
12
15
  getCoveredFilenamesFromCoverage,
@@ -74,6 +77,7 @@ const CHILD_MESSAGE_CALL = 1
74
77
 
75
78
  // Maximum time we'll wait for the tracer to flush
76
79
  const FLUSH_TIMEOUT = 10_000
80
+ const isJestWorker = !!getEnvironmentVariable('JEST_WORKER_ID')
77
81
 
78
82
  // https://github.com/jestjs/jest/blob/41f842a46bb2691f828c3a5f27fc1d6290495b82/packages/jest-circus/src/types.ts#L9C8-L9C54
79
83
  const RETRY_TIMES = Symbol.for('RETRY_TIMES')
@@ -101,6 +105,8 @@ let testManagementTests = {}
101
105
  let testManagementAttemptToFixRetries = 0
102
106
  let isImpactedTestsEnabled = false
103
107
  let modifiedFiles = {}
108
+ let activeTestSuiteAbsolutePath
109
+ let isConsoleErrorWrapped = false
104
110
 
105
111
  const testContexts = new WeakMap()
106
112
  const originalTestFns = new WeakMap()
@@ -124,7 +130,12 @@ const efdNewTestCandidates = new Set()
124
130
  // Tests that are genuinely new (not in known tests list).
125
131
  const newTests = new Set()
126
132
  const testSuiteAbsolutePathsWithFastCheck = new Set()
133
+ const testSuiteFastCheckUsage = new Map()
127
134
  const testSuiteJestObjects = new Map()
135
+ const wrappedJestGlobals = new WeakSet()
136
+ const wrappedJestObjects = new WeakSet()
137
+ const wrappedWorkerInitializers = new WeakSet()
138
+ const publishedRuntimeReferenceErrors = new WeakMap()
128
139
 
129
140
  const BREAKPOINT_HIT_GRACE_PERIOD_MS = 200
130
141
  const ATR_RETRY_SUPPRESSION_FLAG = '_ddDisableAtrRetry'
@@ -303,6 +314,26 @@ function getAttemptToFixExecutionsFromJestResults (result) {
303
314
  return executions
304
315
  }
305
316
 
317
+ function wrapConsoleErrorForJestReferenceErrors () {
318
+ if (isConsoleErrorWrapped) return
319
+
320
+ isConsoleErrorWrapped = true
321
+ // eslint-disable-next-line no-console
322
+ const originalConsoleError = console.error
323
+ // eslint-disable-next-line no-console
324
+ console.error = function () {
325
+ const [message] = arguments
326
+ if (
327
+ typeof message === 'string' &&
328
+ message.includes('Jest environment has been torn down') &&
329
+ activeTestSuiteAbsolutePath
330
+ ) {
331
+ publishRuntimeReferenceError({ _testPath: activeTestSuiteAbsolutePath }, message)
332
+ }
333
+ return originalConsoleError.apply(this, arguments)
334
+ }
335
+ }
336
+
306
337
  function getWrappedEnvironment (BaseEnvironment, jestVersion) {
307
338
  return class DatadogEnvironment extends BaseEnvironment {
308
339
  constructor (config, context) {
@@ -314,6 +345,9 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
314
345
  this.global._ddtrace = global._ddtrace
315
346
  this.hasSnapshotTests = undefined
316
347
  this.testSuiteAbsolutePath = context.testPath
348
+ activeTestSuiteAbsolutePath = this.testSuiteAbsolutePath
349
+ wrapConsoleErrorForJestReferenceErrors()
350
+ this.globalConfig = config.globalConfig
317
351
 
318
352
  this.displayName = config.projectConfig?.displayName?.name || config.displayName
319
353
  this.testEnvironmentOptions = getTestEnvironmentOptions(config)
@@ -423,6 +457,10 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
423
457
  */
424
458
  resetMockState () {
425
459
  try {
460
+ if (this.moduleMocker?.clearAllMocks) {
461
+ this.moduleMocker.clearAllMocks()
462
+ return
463
+ }
426
464
  const jestObject = testSuiteJestObjects.get(this.testSuiteAbsolutePath)
427
465
  if (jestObject?.clearAllMocks) {
428
466
  jestObject.clearAllMocks()
@@ -504,7 +542,7 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
504
542
  }
505
543
 
506
544
  getShouldStripSeedFromTestName () {
507
- return testSuiteAbsolutePathsWithFastCheck.has(this.testSuiteAbsolutePath)
545
+ return doesTestSuiteUseFastCheck(this.testSuiteAbsolutePath)
508
546
  }
509
547
 
510
548
  // At the `add_test` event we don't have the test object yet, so we can't use it
@@ -843,8 +881,8 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
843
881
  const willBeRetriedByFailedTestReplay = numRetries > 0 && numTestExecutions - 1 < numRetries
844
882
  const mightHitBreakpoint = this.isDiEnabled && numTestExecutions >= 2
845
883
 
846
- // For quarantined tests, suppress errors so Jest doesn't count them as failures.
847
- // This prevents --bail from stopping the test run on quarantined test failures.
884
+ // For quarantined tests, track failures so the session can be marked as passing later,
885
+ // and suppress errors so Jest does not mark the test suite as failing.
848
886
  // The actual status ('fail') is already captured above for dd-trace reporting.
849
887
  // Only suppress on the final execution — not when ATR/EFD/ATF will retry the test.
850
888
  if (!event.test?.[ATR_RETRY_SUPPRESSION_FLAG] && !willBeRetriedByFailedTestReplay) {
@@ -1055,7 +1093,19 @@ function getWrappedEnvironment (BaseEnvironment, jestVersion) {
1055
1093
  }
1056
1094
  }
1057
1095
  }
1058
- return super.teardown()
1096
+ const clearActiveTestSuite = () => {
1097
+ realSetTimeout(() => {
1098
+ if (activeTestSuiteAbsolutePath === this.testSuiteAbsolutePath) {
1099
+ activeTestSuiteAbsolutePath = undefined
1100
+ }
1101
+ }, 0)
1102
+ }
1103
+ const result = super.teardown()
1104
+ if (result?.then) {
1105
+ return result.finally(clearActiveTestSuite)
1106
+ }
1107
+ clearActiveTestSuite()
1108
+ return result
1059
1109
  }
1060
1110
  }
1061
1111
  }
@@ -1492,7 +1542,8 @@ function getCliWrapper (isNewJestVersion) {
1492
1542
  const timeoutPromise = new Promise((resolve) => {
1493
1543
  timeoutId = realSetTimeout(() => {
1494
1544
  resolve('timeout')
1495
- }, FLUSH_TIMEOUT).unref()
1545
+ }, FLUSH_TIMEOUT)
1546
+ timeoutId.unref?.()
1496
1547
  })
1497
1548
 
1498
1549
  testSessionFinishCh.publish({
@@ -1557,6 +1608,33 @@ function coverageReporterWrapper (coverageReporter) {
1557
1608
  return coverageReporter
1558
1609
  }
1559
1610
 
1611
+ function shouldWaitForTestSuiteFinish (environment) {
1612
+ return isJestWorker && environment.globalConfig?.workerIdleMemoryLimit !== undefined
1613
+ }
1614
+
1615
+ function publishTestSuiteFinish (payload, waitForFinish) {
1616
+ if (!testSuiteFinishCh.hasSubscribers) return
1617
+
1618
+ if (!waitForFinish) {
1619
+ testSuiteFinishCh.publish(payload)
1620
+ return
1621
+ }
1622
+
1623
+ return new Promise(resolve => {
1624
+ testSuiteFinishCh.publish({
1625
+ ...payload,
1626
+ waitForFinish,
1627
+ onDone: resolve,
1628
+ })
1629
+ })
1630
+ }
1631
+
1632
+ function cleanupTestSuiteState (testSuiteAbsolutePath) {
1633
+ testSuiteMockedFiles.delete(testSuiteAbsolutePath)
1634
+ testSuiteFastCheckUsage.delete(testSuiteAbsolutePath)
1635
+ testSuiteJestObjects.delete(testSuiteAbsolutePath)
1636
+ }
1637
+
1560
1638
  addHook({
1561
1639
  name: '@jest/core',
1562
1640
  file: 'build/TestScheduler.js',
@@ -1675,7 +1753,7 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
1675
1753
  const getFilesWithPath = (files) => files.map(file => getTestSuitePath(file, root))
1676
1754
 
1677
1755
  const coverageFiles = getFilesWithPath(getCoveredFilenamesFromCoverage(environment.global.__coverage__))
1678
- const mockedFiles = getFilesWithPath(testSuiteMockedFiles.get(environment.testSuiteAbsolutePath) || [])
1756
+ const mockedFiles = getFilesWithPath(getMockedFiles(environment.testSuiteAbsolutePath))
1679
1757
 
1680
1758
  testSuiteCodeCoverageCh.publish({
1681
1759
  coverageFiles,
@@ -1684,19 +1762,51 @@ function jestAdapterWrapper (jestAdapter, jestVersion) {
1684
1762
  testSuiteAbsolutePath: environment.testSuiteAbsolutePath,
1685
1763
  })
1686
1764
  }
1687
- testSuiteFinishCh.publish({ status, errorMessage, testSuiteAbsolutePath: environment.testSuiteAbsolutePath })
1765
+ const waitForFinish = shouldWaitForTestSuiteFinish(environment)
1766
+ const finishPayload = {
1767
+ status,
1768
+ errorMessage,
1769
+ testSuiteAbsolutePath: environment.testSuiteAbsolutePath,
1770
+ }
1771
+ if (waitForFinish) {
1772
+ const finishPromise = publishTestSuiteFinish(finishPayload, waitForFinish)
1773
+ if (finishPromise) {
1774
+ return finishPromise.then(() => {
1775
+ // Cleanup per-suite state to avoid memory leaks
1776
+ cleanupTestSuiteState(environment.testSuiteAbsolutePath)
1777
+
1778
+ return suiteResults
1779
+ })
1780
+ }
1781
+ }
1782
+ publishTestSuiteFinish(finishPayload, waitForFinish)
1688
1783
 
1689
1784
  // Cleanup per-suite state to avoid memory leaks
1690
- testSuiteMockedFiles.delete(environment.testSuiteAbsolutePath)
1691
- testSuiteJestObjects.delete(environment.testSuiteAbsolutePath)
1785
+ cleanupTestSuiteState(environment.testSuiteAbsolutePath)
1692
1786
 
1693
1787
  return suiteResults
1694
1788
  }).catch(error => {
1695
- testSuiteFinishCh.publish({ status: 'fail', error, testSuiteAbsolutePath: environment.testSuiteAbsolutePath })
1789
+ const waitForFinish = shouldWaitForTestSuiteFinish(environment)
1790
+ const finishPayload = {
1791
+ status: 'fail',
1792
+ error,
1793
+ testSuiteAbsolutePath: environment.testSuiteAbsolutePath,
1794
+ }
1795
+ if (waitForFinish) {
1796
+ const finishPromise = publishTestSuiteFinish(finishPayload, waitForFinish)
1797
+ if (finishPromise) {
1798
+ return finishPromise.then(() => {
1799
+ // Cleanup per-suite state to avoid memory leaks
1800
+ cleanupTestSuiteState(environment.testSuiteAbsolutePath)
1801
+
1802
+ throw error
1803
+ })
1804
+ }
1805
+ }
1806
+ publishTestSuiteFinish(finishPayload, waitForFinish)
1696
1807
 
1697
1808
  // Cleanup per-suite state to avoid memory leaks
1698
- testSuiteMockedFiles.delete(environment.testSuiteAbsolutePath)
1699
- testSuiteJestObjects.delete(environment.testSuiteAbsolutePath)
1809
+ cleanupTestSuiteState(environment.testSuiteAbsolutePath)
1700
1810
 
1701
1811
  throw error
1702
1812
  })
@@ -1794,6 +1904,7 @@ const DD_TEST_ENVIRONMENT_OPTION_KEYS = [
1794
1904
  '_ddRepositoryRoot',
1795
1905
  '_ddIsFlakyTestRetriesEnabled',
1796
1906
  '_ddFlakyTestRetriesCount',
1907
+ '_ddItrSkippingEnabledTags',
1797
1908
  '_ddIsDiEnabled',
1798
1909
  '_ddIsKnownTestsEnabled',
1799
1910
  '_ddIsTestManagementTestsEnabled',
@@ -1904,39 +2015,195 @@ const LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE = new Set([
1904
2015
  'winston',
1905
2016
  ])
1906
2017
 
2018
+ function recordMockedFile (suiteFilePath, moduleName) {
2019
+ if (!suiteFilePath || typeof moduleName !== 'string') return
2020
+
2021
+ const existingMockedFiles = testSuiteMockedFiles.get(suiteFilePath) || []
2022
+ const suiteDir = path.dirname(suiteFilePath)
2023
+ const mockPath = path.resolve(suiteDir, moduleName)
2024
+ existingMockedFiles.push(mockPath)
2025
+ testSuiteMockedFiles.set(suiteFilePath, existingMockedFiles)
2026
+ }
2027
+
2028
+ const JEST_STATIC_MOCK_CALL_RE = /\bjest\.(?:mock|doMock|unstable_mockModule)\(\s*(['"`])([^'"`]+)\1/g
2029
+
2030
+ function getStaticMockedFiles (suiteFilePath) {
2031
+ if (!suiteFilePath) return []
2032
+
2033
+ const mockedFiles = []
2034
+ try {
2035
+ const source = readFileSync(suiteFilePath, 'utf8')
2036
+ let match
2037
+ JEST_STATIC_MOCK_CALL_RE.lastIndex = 0
2038
+ while ((match = JEST_STATIC_MOCK_CALL_RE.exec(source)) !== null) {
2039
+ mockedFiles.push(path.resolve(path.dirname(suiteFilePath), match[2]))
2040
+ }
2041
+ } catch {
2042
+ // ignore errors
2043
+ }
2044
+
2045
+ return mockedFiles
2046
+ }
2047
+
2048
+ function getMockedFiles (suiteFilePath) {
2049
+ const mockedFiles = testSuiteMockedFiles.get(suiteFilePath)
2050
+ if (mockedFiles?.length) {
2051
+ return mockedFiles
2052
+ }
2053
+ return getStaticMockedFiles(suiteFilePath)
2054
+ }
2055
+
2056
+ function wrapJestObject (jestObject, suiteFilePath) {
2057
+ if (!jestObject || !suiteFilePath || wrappedJestObjects.has(jestObject)) return
2058
+
2059
+ testSuiteJestObjects.set(suiteFilePath, jestObject)
2060
+ wrappedJestObjects.add(jestObject)
2061
+
2062
+ shimmer.wrap(jestObject, 'mock', mock => function (moduleName) {
2063
+ // If the library is mocked with `jest.mock`, we don't want to bypass jest's own require engine
2064
+ if (LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.has(moduleName)) {
2065
+ LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.delete(moduleName)
2066
+ }
2067
+ recordMockedFile(suiteFilePath, moduleName)
2068
+ return mock.apply(this, arguments)
2069
+ })
2070
+ }
2071
+
2072
+ function wrapJestGlobalsForRuntime (runtime) {
2073
+ const jestGlobals = runtime?.jestGlobals
2074
+ if (!jestGlobals || wrappedJestGlobals.has(jestGlobals) || typeof jestGlobals.jestObjectFor !== 'function') {
2075
+ return
2076
+ }
2077
+
2078
+ wrappedJestGlobals.add(jestGlobals)
2079
+ shimmer.wrap(jestGlobals, 'jestObjectFor', jestObjectFor => function (from) {
2080
+ const jestObject = jestObjectFor.apply(this, arguments)
2081
+ wrapJestObject(jestObject, from)
2082
+ return jestObject
2083
+ })
2084
+ }
2085
+
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
+ function getLastLoggedReferenceError (runtime) {
2119
+ const loggedReferenceErrors = runtime?.loggedReferenceErrors
2120
+ if (!loggedReferenceErrors?.size) return
2121
+ return [...loggedReferenceErrors].pop()
2122
+ }
2123
+
2124
+ function publishRuntimeReferenceError (runtime, errorMessage) {
2125
+ if (!errorMessage || !runtime?._testPath) return
2126
+
2127
+ let publishedErrors = publishedRuntimeReferenceErrors.get(runtime)
2128
+ if (!publishedErrors) {
2129
+ publishedErrors = new Set()
2130
+ publishedRuntimeReferenceErrors.set(runtime, publishedErrors)
2131
+ }
2132
+ if (publishedErrors.has(errorMessage)) return
2133
+
2134
+ publishedErrors.add(errorMessage)
2135
+ testSuiteErrorCh.publish({
2136
+ errorMessage,
2137
+ testSuiteAbsolutePath: runtime._testPath,
2138
+ })
2139
+ }
2140
+
2141
+ function isBetweenTestsReferenceError (error) {
2142
+ return error?.name === 'ReferenceError' &&
2143
+ typeof error.message === 'string' &&
2144
+ error.message.includes('outside of the scope of the test code')
2145
+ }
2146
+
2147
+ function reportBetweenTestsReferenceError (runtime, moduleName, originalErrorMessage) {
2148
+ if (typeof moduleName !== 'string') return false
2149
+
2150
+ const fallbackErrorMessage = moduleName.startsWith('node:') || builtinModules.includes(moduleName)
2151
+ ? 'You are trying to access a Node.js module outside of the scope of the test code.'
2152
+ : 'You are trying to `require` a file after the Jest environment has been torn down.'
2153
+ const errorMessage = originalErrorMessage || fallbackErrorMessage
2154
+
2155
+ if (typeof runtime._logFormattedReferenceError === 'function') {
2156
+ runtime._logFormattedReferenceError(errorMessage)
2157
+ }
2158
+ publishRuntimeReferenceError(runtime, getLastLoggedReferenceError(runtime) || errorMessage)
2159
+ process.exitCode = 1
2160
+ return true
2161
+ }
2162
+
2163
+ function requireOutsideJestRequireEngine (runtime, moduleName) {
2164
+ if (typeof runtime._requireCoreModule === 'function') {
2165
+ return runtime._requireCoreModule(moduleName)
2166
+ }
2167
+ return require(moduleName)
2168
+ }
2169
+
2170
+ function formatDefaultStackTrace (error, structuredStackTrace) {
2171
+ const errorString = Error.prototype.toString.call(error)
2172
+ if (structuredStackTrace.length === 0) return errorString
2173
+
2174
+ return `${errorString}\n at ${structuredStackTrace.join('\n at ')}`
2175
+ }
2176
+
1907
2177
  addHook({
1908
2178
  name: 'jest-runtime',
1909
2179
  versions: [MINIMUM_JEST_VERSION],
1910
2180
  }, (runtimePackage) => {
1911
2181
  const Runtime = runtimePackage.default ?? runtimePackage
1912
2182
 
1913
- shimmer.wrap(Runtime.prototype, '_createJestObjectFor', _createJestObjectFor => function (from) {
1914
- const result = _createJestObjectFor.apply(this, arguments)
1915
- const suiteFilePath = this._testPath || from
2183
+ if (typeof Runtime.prototype._createJestObjectFor === 'function') {
2184
+ shimmer.wrap(Runtime.prototype, '_createJestObjectFor', _createJestObjectFor => function (from) {
2185
+ const result = _createJestObjectFor.apply(this, arguments)
2186
+ const suiteFilePath = this._testPath || from
1916
2187
 
1917
- // Store the jest object so we can access it later for resetting mock state
1918
- if (suiteFilePath) {
1919
- testSuiteJestObjects.set(suiteFilePath, result)
1920
- }
2188
+ wrapJestObject(result, suiteFilePath)
2189
+ return result
2190
+ })
2191
+ }
1921
2192
 
1922
- shimmer.wrap(result, 'mock', mock => function (moduleName) {
1923
- // If the library is mocked with `jest.mock`, we don't want to bypass jest's own require engine
1924
- if (LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.has(moduleName)) {
1925
- LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.delete(moduleName)
1926
- }
1927
- if (suiteFilePath) {
1928
- const existingMockedFiles = testSuiteMockedFiles.get(suiteFilePath) || []
1929
- const suiteDir = path.dirname(suiteFilePath)
1930
- const mockPath = path.resolve(suiteDir, moduleName)
1931
- existingMockedFiles.push(mockPath)
1932
- testSuiteMockedFiles.set(suiteFilePath, existingMockedFiles)
2193
+ shimmer.wrap(Runtime.prototype, 'requireModule', requireModule => function (from, moduleName) {
2194
+ wrapJestGlobalsForRuntime(this)
2195
+ try {
2196
+ return requireModule.apply(this, arguments)
2197
+ } catch (error) {
2198
+ if (isBetweenTestsReferenceError(error)) {
2199
+ reportBetweenTestsReferenceError(this, moduleName, error.message)
1933
2200
  }
1934
- return mock.apply(this, arguments)
1935
- })
1936
- return result
2201
+ throw error
2202
+ }
1937
2203
  })
1938
2204
 
1939
2205
  shimmer.wrap(Runtime.prototype, 'requireModuleOrMock', requireModuleOrMock => function (from, moduleName) {
2206
+ wrapJestGlobalsForRuntime(this)
1940
2207
  // `requireModuleOrMock` may log errors to the console. If we don't remove ourselves
1941
2208
  // from the stack trace, the user might see a useless stack trace rather than the error
1942
2209
  // that `jest` tries to show.
@@ -1945,32 +2212,33 @@ addHook({
1945
2212
  const filteredStackTrace = structuredStackTrace
1946
2213
  .filter(callSite => !callSite.getFileName()?.includes('datadog-instrumentations/src/jest.js'))
1947
2214
 
1948
- return originalPrepareStackTrace(error, filteredStackTrace)
2215
+ if (typeof originalPrepareStackTrace === 'function') {
2216
+ return originalPrepareStackTrace(error, filteredStackTrace)
2217
+ }
2218
+ return formatDefaultStackTrace(error, filteredStackTrace)
1949
2219
  }
1950
2220
  try {
1951
2221
  // TODO: do this for every library that we instrument
1952
2222
  if (LIBRARIES_BYPASSING_JEST_REQUIRE_ENGINE.has(moduleName)) {
1953
2223
  // To bypass jest's own require engine
1954
- return this._requireCoreModule(moduleName)
2224
+ return requireOutsideJestRequireEngine(this, moduleName)
1955
2225
  }
1956
2226
  // This means that `@fast-check/jest` is used in the test file.
1957
- if (moduleName === '@fast-check/jest') {
1958
- testSuiteAbsolutePathsWithFastCheck.add(this._testPath)
2227
+ recordFastCheckUsage(this, from, moduleName)
2228
+ let returnedValue
2229
+ try {
2230
+ returnedValue = requireModuleOrMock.apply(this, arguments)
2231
+ } catch (error) {
2232
+ if (isBetweenTestsReferenceError(error)) {
2233
+ reportBetweenTestsReferenceError(this, moduleName, error.message)
2234
+ }
2235
+ throw error
1959
2236
  }
1960
- const returnedValue = requireModuleOrMock.apply(this, arguments)
1961
2237
  if (process.exitCode === 1) {
1962
- if (this.loggedReferenceErrors?.size > 0) {
1963
- const errorMessage = [...this.loggedReferenceErrors][0]
1964
- testSuiteErrorCh.publish({
1965
- errorMessage,
1966
- testSuiteAbsolutePath: this._testPath,
1967
- })
1968
- } else {
1969
- testSuiteErrorCh.publish({
1970
- errorMessage: 'An error occurred while importing a module',
1971
- testSuiteAbsolutePath: this._testPath,
1972
- })
1973
- }
2238
+ publishRuntimeReferenceError(
2239
+ this,
2240
+ getLastLoggedReferenceError(this) || 'An error occurred while importing a module'
2241
+ )
1974
2242
  }
1975
2243
  return returnedValue
1976
2244
  } finally {
@@ -1979,6 +2247,27 @@ addHook({
1979
2247
  }
1980
2248
  })
1981
2249
 
2250
+ if (Runtime.prototype._logFormattedReferenceError) {
2251
+ shimmer.wrap(Runtime.prototype, '_logFormattedReferenceError', logFormattedReferenceError => function () {
2252
+ // eslint-disable-next-line no-console
2253
+ const originalConsoleError = console.error
2254
+ let loggedReferenceError
2255
+ // eslint-disable-next-line no-console
2256
+ console.error = function () {
2257
+ loggedReferenceError = arguments[0]
2258
+ return originalConsoleError.apply(this, arguments)
2259
+ }
2260
+ try {
2261
+ const result = logFormattedReferenceError.apply(this, arguments)
2262
+ publishRuntimeReferenceError(this, getLastLoggedReferenceError(this) || loggedReferenceError)
2263
+ return result
2264
+ } finally {
2265
+ // eslint-disable-next-line no-console
2266
+ console.error = originalConsoleError
2267
+ }
2268
+ })
2269
+ }
2270
+
1982
2271
  return runtimePackage
1983
2272
  })
1984
2273
 
@@ -2065,11 +2354,23 @@ function wrapWorkerChannel (worker) {
2065
2354
  shimmer.wrap(workerChannel, worker._child ? 'send' : 'postMessage', sendWrapper)
2066
2355
  }
2067
2356
 
2357
+ function wrapWorkerInitializer (worker) {
2358
+ if (wrappedWorkerInitializers.has(worker) || typeof worker.initialize !== 'function') return
2359
+
2360
+ wrappedWorkerInitializers.add(worker)
2361
+ shimmer.wrap(worker, 'initialize', initialize => function () {
2362
+ const result = initialize.apply(this, arguments)
2363
+ wrapWorkerChannel(this)
2364
+ return result
2365
+ })
2366
+ }
2367
+
2068
2368
  function wrapWorker (worker) {
2069
2369
  // ChildProcessWorker uses _child (child_process), ExperimentalWorker uses _worker (worker_threads)
2070
2370
  const workerChannel = worker._child || worker._worker
2071
2371
  if (!workerChannel) return
2072
2372
 
2373
+ wrapWorkerInitializer(worker)
2073
2374
  wrapWorkerChannel(worker)
2074
2375
  shimmer.wrap(worker, '_onMessage', onMessageWrapper)
2075
2376
  workerChannel.removeAllListeners('message')