dd-trace 5.109.0 → 5.111.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 (208) hide show
  1. package/README.md +13 -8
  2. package/ci/init.js +25 -5
  3. package/ci/vitest-no-worker-init-setup.mjs +430 -0
  4. package/ext/tags.js +2 -0
  5. package/index.d.ts +79 -24
  6. package/initialize.mjs +5 -6
  7. package/loader-hook.mjs +154 -47
  8. package/package.json +20 -16
  9. package/packages/datadog-esbuild/index.js +26 -0
  10. package/packages/datadog-esbuild/src/utils.js +46 -3
  11. package/packages/datadog-instrumentations/src/connect.js +4 -3
  12. package/packages/datadog-instrumentations/src/fastify.js +4 -12
  13. package/packages/datadog-instrumentations/src/fs.js +8 -6
  14. package/packages/datadog-instrumentations/src/graphql.js +26 -484
  15. package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
  16. package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
  17. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
  18. package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
  19. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +17 -0
  20. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
  21. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  22. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
  23. package/packages/datadog-instrumentations/src/koa.js +3 -2
  24. package/packages/datadog-instrumentations/src/mariadb.js +4 -2
  25. package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
  26. package/packages/datadog-instrumentations/src/mongoose.js +14 -2
  27. package/packages/datadog-instrumentations/src/next.js +10 -10
  28. package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
  29. package/packages/datadog-instrumentations/src/playwright.js +1 -1
  30. package/packages/datadog-instrumentations/src/restify.js +4 -3
  31. package/packages/datadog-instrumentations/src/router.js +18 -5
  32. package/packages/datadog-instrumentations/src/selenium.js +1 -2
  33. package/packages/datadog-instrumentations/src/tedious.js +28 -0
  34. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
  35. package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
  36. package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
  37. package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
  38. package/packages/datadog-instrumentations/src/vitest.js +11 -1598
  39. package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
  40. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
  41. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +18 -10
  42. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
  43. package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
  44. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
  45. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
  46. package/packages/datadog-plugin-cucumber/src/index.js +1 -1
  47. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
  48. package/packages/datadog-plugin-cypress/src/support.js +45 -1
  49. package/packages/datadog-plugin-express/src/code_origin.js +1 -1
  50. package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
  51. package/packages/datadog-plugin-graphql/src/execute.js +627 -11
  52. package/packages/datadog-plugin-graphql/src/index.js +20 -8
  53. package/packages/datadog-plugin-graphql/src/parse.js +24 -4
  54. package/packages/datadog-plugin-graphql/src/validate.js +16 -5
  55. package/packages/datadog-plugin-http/src/client.js +8 -3
  56. package/packages/datadog-plugin-http/src/index.js +6 -8
  57. package/packages/datadog-plugin-http2/src/client.js +5 -2
  58. package/packages/datadog-plugin-jest/src/index.js +1 -1
  59. package/packages/datadog-plugin-mocha/src/index.js +1 -1
  60. package/packages/datadog-plugin-mysql/src/index.js +7 -2
  61. package/packages/datadog-plugin-openai/src/services.js +2 -2
  62. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  63. package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
  64. package/packages/datadog-plugin-undici/src/index.js +5 -2
  65. package/packages/datadog-plugin-vitest/src/index.js +76 -64
  66. package/packages/datadog-shimmer/src/shimmer.js +37 -16
  67. package/packages/datadog-webpack/index.js +17 -1
  68. package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
  69. package/packages/dd-trace/src/aiguard/sdk.js +15 -3
  70. package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
  71. package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
  72. package/packages/dd-trace/src/appsec/channels.js +3 -1
  73. package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
  74. package/packages/dd-trace/src/appsec/graphql.js +5 -5
  75. package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
  76. package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
  77. package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
  78. package/packages/dd-trace/src/appsec/index.js +20 -7
  79. package/packages/dd-trace/src/appsec/lambda.js +135 -0
  80. package/packages/dd-trace/src/appsec/reporter.js +50 -10
  81. package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
  82. package/packages/dd-trace/src/appsec/telemetry/index.js +27 -1
  83. package/packages/dd-trace/src/appsec/waf/index.js +16 -4
  84. package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
  85. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
  86. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -2
  87. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
  88. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
  89. package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +3 -2
  90. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
  91. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -13
  92. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
  93. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
  94. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
  95. package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
  96. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
  97. package/packages/dd-trace/src/ci-visibility/requests/request.js +1 -17
  98. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
  99. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
  100. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
  101. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
  102. package/packages/dd-trace/src/config/defaults.js +25 -5
  103. package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
  104. package/packages/dd-trace/src/config/helper.js +134 -33
  105. package/packages/dd-trace/src/config/index.js +39 -23
  106. package/packages/dd-trace/src/config/major-overrides.js +4 -2
  107. package/packages/dd-trace/src/config/parsers.js +19 -1
  108. package/packages/dd-trace/src/config/remote_config.js +1 -1
  109. package/packages/dd-trace/src/config/supported-configurations.json +109 -57
  110. package/packages/dd-trace/src/constants.js +7 -0
  111. package/packages/dd-trace/src/datastreams/processor.js +11 -3
  112. package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
  113. package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
  114. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
  115. package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
  116. package/packages/dd-trace/src/debugger/index.js +5 -0
  117. package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
  118. package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
  119. package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
  120. package/packages/dd-trace/src/exporters/agent/index.js +1 -1
  121. package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
  122. package/packages/dd-trace/src/exporters/common/request.js +1 -20
  123. package/packages/dd-trace/src/exporters/common/url.js +34 -0
  124. package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
  125. package/packages/dd-trace/src/guardrails/index.js +8 -5
  126. package/packages/dd-trace/src/index.js +6 -4
  127. package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
  128. package/packages/dd-trace/src/llmobs/index.js +17 -6
  129. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
  130. package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
  131. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
  132. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  133. package/packages/dd-trace/src/llmobs/sdk.js +8 -6
  134. package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
  135. package/packages/dd-trace/src/llmobs/tagger.js +58 -3
  136. package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
  137. package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
  138. package/packages/dd-trace/src/log/index.js +18 -20
  139. package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
  140. package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
  141. package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
  142. package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
  143. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
  144. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
  145. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +4 -3
  146. package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
  147. package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
  148. package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
  149. package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
  150. package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
  151. package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
  152. package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
  153. package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
  154. package/packages/dd-trace/src/opentelemetry/span.js +14 -3
  155. package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
  156. package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
  157. package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
  158. package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
  159. package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
  160. package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
  161. package/packages/dd-trace/src/opentracing/span_context.js +17 -0
  162. package/packages/dd-trace/src/opentracing/tracer.js +6 -1
  163. package/packages/dd-trace/src/plugin_manager.js +14 -8
  164. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
  165. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
  166. package/packages/dd-trace/src/plugins/util/llm.js +6 -1
  167. package/packages/dd-trace/src/plugins/util/test.js +91 -9
  168. package/packages/dd-trace/src/plugins/util/url.js +68 -1
  169. package/packages/dd-trace/src/plugins/util/web.js +19 -36
  170. package/packages/dd-trace/src/priority_sampler.js +1 -13
  171. package/packages/dd-trace/src/profiling/config.js +129 -198
  172. package/packages/dd-trace/src/profiling/constants.js +4 -7
  173. package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
  174. package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
  175. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
  176. package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
  177. package/packages/dd-trace/src/profiling/oom.js +71 -0
  178. package/packages/dd-trace/src/profiling/profiler.js +35 -23
  179. package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
  180. package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
  181. package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
  182. package/packages/dd-trace/src/proxy.js +8 -8
  183. package/packages/dd-trace/src/ritm.js +4 -2
  184. package/packages/dd-trace/src/serverless.js +1 -2
  185. package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
  186. package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
  187. package/packages/dd-trace/src/span_format.js +2 -1
  188. package/packages/dd-trace/src/span_processor.js +11 -6
  189. package/packages/dd-trace/src/span_stats.js +96 -78
  190. package/packages/dd-trace/src/startup-log.js +2 -1
  191. package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
  192. package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
  193. package/packages/dd-trace/src/telemetry/index.js +2 -2
  194. package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
  195. package/packages/dd-trace/src/telemetry/send-data.js +20 -18
  196. package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
  197. package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
  198. package/packages/dd-trace/src/tracer.js +15 -1
  199. package/packages/dd-trace/src/util.js +14 -0
  200. package/register.js +58 -1
  201. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  202. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  203. package/vendor/dist/pprof-format/index.js +1 -1
  204. package/vendor/dist/protobufjs/index.js +1 -1
  205. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  206. package/vendor/dist/shell-quote/index.js +1 -1
  207. package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
  208. package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
@@ -1,1604 +1,17 @@
1
1
  'use strict'
2
2
 
3
- // Capture real timers at module load time, before any test can install fake timers.
4
- const realSetTimeout = setTimeout
3
+ const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
4
+ const { isTrue } = require('../../dd-trace/src/util')
5
5
 
6
- const path = require('node:path')
7
- const { performance } = require('node:perf_hooks')
6
+ const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTIVE'
8
7
 
9
- const shimmer = require('../../datadog-shimmer')
10
- const log = require('../../dd-trace/src/log')
11
- const {
12
- VITEST_WORKER_TRACE_PAYLOAD_CODE,
13
- VITEST_WORKER_LOGS_PAYLOAD_CODE,
14
- DYNAMIC_NAME_RE,
15
- getTestSuitePath,
16
- getEfdRetryCount,
17
- getMaxEfdRetryCount,
18
- recordAttemptToFixExecution,
19
- collectTestOptimizationSummariesFromTraces,
20
- logAttemptToFixTestExecution,
21
- logTestOptimizationSummary,
22
- getTestOptimizationRequestResults,
23
- } = require('../../dd-trace/src/plugins/util/test')
24
- const { addHook, channel } = require('./helpers/instrument')
8
+ if (getEnvironmentVariable('DD_VITEST_WORKER') === '1') {
9
+ // In no-worker-init mode the main process reports Vitest events from reporter hooks.
10
+ // Loading worker instrumentation here would reintroduce the worker tracer path.
11
+ // eslint-disable-next-line eslint-rules/eslint-process-env
12
+ if (isTrue(process.env[VITEST_NO_WORKER_INIT_ACTIVE_ENV])) return
25
13
 
26
- // test hooks
27
- const testStartCh = channel('ci:vitest:test:start')
28
- const testFinishTimeCh = channel('ci:vitest:test:finish-time')
29
- const testPassCh = channel('ci:vitest:test:pass')
30
- const testErrorCh = channel('ci:vitest:test:error')
31
- const testSkipCh = channel('ci:vitest:test:skip')
32
- const isNewTestCh = channel('ci:vitest:test:is-new')
33
- const isAttemptToFixCh = channel('ci:vitest:test:is-attempt-to-fix')
34
- const isDisabledCh = channel('ci:vitest:test:is-disabled')
35
- const isQuarantinedCh = channel('ci:vitest:test:is-quarantined')
36
- const isModifiedCh = channel('ci:vitest:test:is-modified')
37
- const testFnCh = channel('ci:vitest:test:fn')
38
-
39
- // test suite hooks
40
- const testSuiteStartCh = channel('ci:vitest:test-suite:start')
41
- const testSuiteFinishCh = channel('ci:vitest:test-suite:finish')
42
- const testSuiteErrorCh = channel('ci:vitest:test-suite:error')
43
-
44
- // test session hooks
45
- const testSessionStartCh = channel('ci:vitest:session:start')
46
- const testSessionFinishCh = channel('ci:vitest:session:finish')
47
- const testSessionConfigurationCh = channel('ci:vitest:session:configuration')
48
- const libraryConfigurationCh = channel('ci:vitest:library-configuration')
49
- const knownTestsCh = channel('ci:vitest:known-tests')
50
- const isEarlyFlakeDetectionFaultyCh = channel('ci:vitest:is-early-flake-detection-faulty')
51
- const testManagementTestsCh = channel('ci:vitest:test-management-tests')
52
- const modifiedFilesCh = channel('ci:vitest:modified-files')
53
-
54
- const workerReportTraceCh = channel('ci:vitest:worker-report:trace')
55
- const workerReportLogsCh = channel('ci:vitest:worker-report:logs')
56
- const codeCoverageReportCh = channel('ci:vitest:coverage-report')
57
-
58
- const taskToCtx = new WeakMap()
59
- const taskToStatuses = new WeakMap()
60
- const taskToReportedErrorCount = new WeakMap()
61
- const attemptToFixTaskToStatuses = new WeakMap()
62
- const originalHookFns = new WeakMap()
63
- const newTasks = new WeakSet()
64
- const dynamicNameTasks = new WeakSet()
65
- const newTestsWithDynamicNames = new Set()
66
- const disabledTasks = new WeakSet()
67
- const quarantinedTasks = new WeakSet()
68
- const attemptToFixTasks = new WeakSet()
69
- const modifiedTasks = new WeakSet()
70
- const efdDeterminedRetries = new WeakMap()
71
- const efdSlowAbortedTasks = new WeakSet()
72
- const efdExecutionStartByTask = new WeakMap()
73
- const efdSkippedRetryResults = new WeakMap()
74
- const attemptToFixExecutions = new Map()
75
- const loggedAttemptToFixTests = new Set()
76
- let isRetryReasonEfd = false
77
- let isRetryReasonAttemptToFix = false
78
- const switchedStatuses = new WeakSet()
79
- const workerProcesses = new WeakSet()
80
- const mainProcessSetupPromises = new WeakMap()
81
- const coverageWrappedProviders = new WeakSet()
82
- const finishWrappedContexts = new WeakSet()
83
- let isFlakyTestRetriesEnabled = false
84
- let flakyTestRetriesCount = 0
85
- let isEarlyFlakeDetectionEnabled = false
86
- let earlyFlakeDetectionNumRetries = 0
87
- let earlyFlakeDetectionSlowTestRetries = {}
88
- let isEarlyFlakeDetectionFaulty = false
89
- let isKnownTestsEnabled = false
90
- let isTestManagementTestsEnabled = false
91
- let isImpactedTestsEnabled = false
92
- let vitestGetFn = null
93
- let vitestSetFn = null
94
- let vitestGetHooks = null
95
- let testManagementAttemptToFixRetries = 0
96
- let isDiEnabled = false
97
- let testCodeCoverageLinesTotal
98
- let coverageRootDir
99
- let isSessionStarted = false
100
- let vitestPool = null
101
-
102
- const BREAKPOINT_HIT_GRACE_PERIOD_MS = 400
103
-
104
- function getConfiguredEfdRetryCount (slowTestRetries, fallbackRetryCount) {
105
- if (!slowTestRetries || !Object.keys(slowTestRetries).length) {
106
- return fallbackRetryCount
107
- }
108
- return getMaxEfdRetryCount(slowTestRetries)
109
- }
110
-
111
- function getTestCommand () {
112
- return `vitest ${process.argv.slice(2).join(' ')}`
113
- }
114
-
115
- function waitForHitProbe () {
116
- return new Promise(resolve => {
117
- realSetTimeout(() => {
118
- resolve()
119
- }, BREAKPOINT_HIT_GRACE_PERIOD_MS)
120
- })
121
- }
122
-
123
- function isValidKnownTests (receivedKnownTests) {
124
- return !!receivedKnownTests.vitest
125
- }
126
-
127
- function getProvidedContext () {
128
- try {
129
- const {
130
- _ddIsEarlyFlakeDetectionEnabled,
131
- _ddIsDiEnabled,
132
- _ddKnownTests: knownTests,
133
- _ddEarlyFlakeDetectionNumRetries: numRepeats,
134
- _ddEarlyFlakeDetectionSlowTestRetries: slowTestRetries,
135
- _ddIsKnownTestsEnabled: isKnownTestsEnabled,
136
- _ddIsTestManagementTestsEnabled: isTestManagementTestsEnabled,
137
- _ddTestManagementAttemptToFixRetries: testManagementAttemptToFixRetries,
138
- _ddTestManagementTests: testManagementTests,
139
- _ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
140
- _ddFlakyTestRetriesCount: flakyTestRetriesCount,
141
- _ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
142
- _ddModifiedFiles: modifiedFiles,
143
- _ddTestSessionId: testSessionId,
144
- _ddTestModuleId: testModuleId,
145
- _ddTestCommand: testCommand,
146
- _ddRepositoryRoot: repositoryRoot,
147
- _ddCodeOwnersEntries: codeOwnersEntries,
148
- } = globalThis.__vitest_worker__.providedContext
149
-
150
- return {
151
- isDiEnabled: _ddIsDiEnabled,
152
- isEarlyFlakeDetectionEnabled: _ddIsEarlyFlakeDetectionEnabled,
153
- knownTests,
154
- numRepeats,
155
- slowTestRetries: slowTestRetries ?? {},
156
- isKnownTestsEnabled,
157
- isTestManagementTestsEnabled,
158
- testManagementAttemptToFixRetries,
159
- testManagementTests,
160
- isFlakyTestRetriesEnabled,
161
- flakyTestRetriesCount: flakyTestRetriesCount ?? 0,
162
- isImpactedTestsEnabled,
163
- modifiedFiles,
164
- testSessionId,
165
- testModuleId,
166
- testCommand,
167
- repositoryRoot,
168
- codeOwnersEntries,
169
- }
170
- } catch {
171
- log.error('Vitest workers could not parse provided context, so some features will not work.')
172
- return {
173
- isDiEnabled: false,
174
- isEarlyFlakeDetectionEnabled: false,
175
- knownTests: {},
176
- numRepeats: 0,
177
- slowTestRetries: {},
178
- isKnownTestsEnabled: false,
179
- isTestManagementTestsEnabled: false,
180
- testManagementAttemptToFixRetries: 0,
181
- testManagementTests: {},
182
- isFlakyTestRetriesEnabled: false,
183
- flakyTestRetriesCount: 0,
184
- isImpactedTestsEnabled: false,
185
- modifiedFiles: {},
186
- testSessionId: undefined,
187
- testModuleId: undefined,
188
- testCommand: undefined,
189
- repositoryRoot: undefined,
190
- codeOwnersEntries: undefined,
191
- }
192
- }
193
- }
194
-
195
- function isReporterPackage (vitestPackage) {
196
- return vitestPackage.B?.name === 'BaseSequencer'
197
- }
198
-
199
- // from 2.0.0
200
- function isReporterPackageNew (vitestPackage) {
201
- return vitestPackage.e?.name === 'BaseSequencer'
202
- }
203
-
204
- function isReporterPackageNewest (vitestPackage) {
205
- return vitestPackage.h?.name === 'BaseSequencer'
206
- }
207
-
208
- /**
209
- * Finds an export by its `.name` property in a minified vitest chunk.
210
- * Minified export keys change across versions, so we search by function/class name.
211
- * @param {object} pkg - The module exports object
212
- * @param {string} name - The `.name` value to look for
213
- * @returns {{ key: string, value: Function } | undefined}
214
- */
215
- function findExportByName (pkg, name) {
216
- for (const [key, value] of Object.entries(pkg)) {
217
- if (value?.name === name) {
218
- return { key, value }
219
- }
220
- }
221
- }
222
-
223
- function getBaseSequencerExport (vitestPackage) {
224
- return findExportByName(vitestPackage, 'BaseSequencer')
225
- }
226
-
227
- function getChannelPromise (channelToPublishTo, frameworkVersion) {
228
- return new Promise(resolve => {
229
- channelToPublishTo.publish({ onDone: resolve, frameworkVersion })
230
- })
231
- }
232
-
233
- function isCliApiPackage (vitestPackage) {
234
- return !!findExportByName(vitestPackage, 'startVitest')
235
- }
236
-
237
- function getTestRunnerExport (testPackage) {
238
- return findExportByName(testPackage, 'VitestTestRunner') || findExportByName(testPackage, 'TestRunner')
239
- }
240
-
241
- function getVitestExport (vitestPackage) {
242
- return findExportByName(vitestPackage, 'Vitest')
243
- }
244
-
245
- function getForksPoolWorkerExport (vitestPackage) {
246
- return findExportByName(vitestPackage, 'ForksPoolWorker')
247
- }
248
-
249
- function getThreadsPoolWorkerExport (vitestPackage) {
250
- return findExportByName(vitestPackage, 'ThreadsPoolWorker')
251
- }
252
-
253
- function getSessionStatus (state) {
254
- if (state.getCountOfFailedTests() > 0) {
255
- return 'fail'
256
- }
257
- if (state.pathsSet.size === 0) {
258
- return 'skip'
259
- }
260
- return 'pass'
261
- }
262
-
263
- // From https://github.com/vitest-dev/vitest/blob/51c04e2f44d91322b334f8ccbcdb368facc3f8ec/packages/runner/src/run.ts#L243-L250
264
- function getVitestTestStatus (test, retryCount) {
265
- if (test.result.state !== 'fail' && (!test.repeats || (test.retry ?? 0) === retryCount)) {
266
- return 'pass'
267
- }
268
- return 'fail'
269
- }
270
-
271
- function getTypeTasks (fileTasks, type = 'test') {
272
- const typeTasks = []
273
-
274
- function getTasks (tasks) {
275
- for (const task of tasks) {
276
- if (task.type === type) {
277
- typeTasks.push(task)
278
- } else if (task.tasks) {
279
- getTasks(task.tasks)
280
- }
281
- }
282
- }
283
-
284
- getTasks(fileTasks)
285
-
286
- return typeTasks
287
- }
288
-
289
- function getTestName (task) {
290
- let testName = task.name
291
- let currentTask = task.suite
292
-
293
- while (currentTask) {
294
- if (currentTask.name) {
295
- testName = `${currentTask.name} ${testName}`
296
- }
297
- currentTask = currentTask.suite
298
- }
299
-
300
- return testName
301
- }
302
-
303
- function getFinalAttemptToFixStatus (task, state, isSwitchedStatus, testCtx) {
304
- if (isSwitchedStatus && attemptToFixTasks.has(task) && testCtx?.status) {
305
- return testCtx.status
306
- }
307
-
308
- return state === 'fail' ? 'fail' : 'pass'
309
- }
310
-
311
- function recordFinalAttemptToFixExecution (task, status, providedContext) {
312
- const statuses = attemptToFixTaskToStatuses.get(task)
313
- if (statuses && statuses.length <= providedContext.testManagementAttemptToFixRetries) {
314
- statuses.push(status)
315
- }
316
-
317
- recordAttemptToFixExecution(attemptToFixExecutions, {
318
- testSuite: getTestSuitePath(task.file.filepath, process.cwd()),
319
- testName: getTestName(task),
320
- status,
321
- isDisabled: disabledTasks.has(task),
322
- isQuarantined: quarantinedTasks.has(task),
323
- })
324
- }
325
-
326
- function disableFrameworkRetries (task) {
327
- task.retry = 0
328
- }
329
-
330
- /**
331
- * Vitest accumulates retry and repeat errors on one task result. The first error added since
332
- * the last reported attempt is the primary error for the failed attempt currently being reported.
333
- *
334
- * @param {object} task
335
- * @param {Array<object> | undefined} errors
336
- * @returns {object | undefined}
337
- */
338
- function getCurrentAttemptTestError (task, errors) {
339
- if (!errors?.length) return
340
-
341
- const previousErrorCount = taskToReportedErrorCount.get(task) ?? 0
342
- const testError = errors[previousErrorCount] ?? errors[0]
343
- taskToReportedErrorCount.set(task, errors.length)
344
- return testError
345
- }
346
-
347
- /**
348
- * Wraps a function so it runs inside the current test span context.
349
- * @param {object} task
350
- * @param {Function} fn
351
- * @returns {Function}
352
- */
353
- function wrapTestScopedFn (task, fn) {
354
- return shimmer.wrapFunction(fn, fn => function (...args) {
355
- return testFnCh.runStores(taskToCtx.get(task), () => fn.apply(this, args))
356
- })
357
- }
358
-
359
- /**
360
- * Wraps a `beforeEach` cleanup callback so it inherits the test span context.
361
- * Vitest allows `beforeEach` to return a cleanup function, including via a promise.
362
- * @param {object} task
363
- * @param {unknown} result
364
- * @returns {unknown}
365
- */
366
- function wrapBeforeEachCleanupResult (task, result) {
367
- if (typeof result === 'function') {
368
- return wrapTestScopedFn(task, result)
369
- }
370
-
371
- if (result && typeof result.then === 'function') {
372
- return result.then(cleanupFn => wrapBeforeEachCleanupResult(task, cleanupFn))
373
- }
374
-
375
- return result
376
- }
377
-
378
- function getWorkspaceProject (ctx) {
379
- return ctx.getCoreWorkspaceProject
380
- ? ctx.getCoreWorkspaceProject()
381
- : ctx.getRootProject()
382
- }
383
-
384
- function setProvidedContext (ctx, values, warningMessage) {
385
- try {
386
- Object.assign(getWorkspaceProject(ctx)._provided, values)
387
- } catch {
388
- log.warn(warningMessage)
389
- }
390
- }
391
-
392
- function getTestFilepathsFromSpecifications (testSpecifications) {
393
- if (!Array.isArray(testSpecifications) || !testSpecifications.length) {
394
- return
395
- }
396
-
397
- return testSpecifications.map(testSpecification => {
398
- const testFile = Array.isArray(testSpecification) ? testSpecification[1] : testSpecification
399
- return testFile?.moduleId || testFile?.filepath || testFile
400
- })
401
- }
402
-
403
- function getTestFilepaths (ctx, testSpecifications) {
404
- const testFilepaths = getTestFilepathsFromSpecifications(testSpecifications)
405
- if (testFilepaths) {
406
- return testFilepaths
407
- }
408
-
409
- const getFilePaths = ctx.getTestFilepaths || ctx._globTestFilepaths
410
- return getFilePaths.call(ctx)
411
- }
412
-
413
- function wrapCoverageProvider (ctx) {
414
- const { coverageProvider } = ctx
415
- if (!coverageProvider?.generateCoverage || coverageWrappedProviders.has(coverageProvider)) {
416
- return
417
- }
418
- coverageWrappedProviders.add(coverageProvider)
419
-
420
- // Capture coverage root directory from config (default is 'coverage' in cwd)
421
- try {
422
- const coverageConfig = ctx.config?.coverage
423
- const reportsDirectory = coverageConfig?.reportsDirectory || 'coverage'
424
- const rootDir = ctx.config?.root || process.cwd()
425
- coverageRootDir = path.isAbsolute(reportsDirectory) ? reportsDirectory : path.join(rootDir, reportsDirectory)
426
- } catch {
427
- // Fallback to cwd if we can't get config
428
- coverageRootDir = process.cwd()
429
- }
430
-
431
- shimmer.wrap(coverageProvider, 'generateCoverage', generateCoverage => async function () {
432
- const totalCodeCoverage = await generateCoverage.apply(this, arguments)
433
-
434
- try {
435
- testCodeCoverageLinesTotal = totalCodeCoverage.getCoverageSummary().lines.pct
436
- } catch {
437
- // ignore errors
438
- }
439
- return totalCodeCoverage
440
- })
441
- }
442
-
443
- function wrapSessionFinish (ctx) {
444
- if (finishWrappedContexts.has(ctx)) {
445
- return
446
- }
447
- finishWrappedContexts.add(ctx)
448
-
449
- shimmer.wrap(ctx, 'exit', getFinishWrapper)
450
- shimmer.wrap(ctx, 'close', getFinishWrapper)
451
- }
452
-
453
- async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
454
- if (!testSessionFinishCh.hasSubscribers) {
455
- return
456
- }
457
-
458
- try {
459
- const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, frameworkVersion)
460
- if (!err) {
461
- isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
462
- flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
463
- isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
464
- earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
465
- earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
466
- isDiEnabled = libraryConfig.isDiEnabled
467
- isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
468
- isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
469
- testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
470
- isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
471
- }
472
- } catch {
473
- isFlakyTestRetriesEnabled = false
474
- isEarlyFlakeDetectionEnabled = false
475
- isDiEnabled = false
476
- isKnownTestsEnabled = false
477
- isImpactedTestsEnabled = false
478
- }
479
-
480
- if (testSessionConfigurationCh.hasSubscribers) {
481
- const { testSessionId, testModuleId, testCommand, repositoryRoot, codeOwnersEntries } = await getChannelPromise(
482
- testSessionConfigurationCh,
483
- frameworkVersion
484
- )
485
- setProvidedContext(ctx, {
486
- _ddTestSessionId: testSessionId,
487
- _ddTestModuleId: testModuleId,
488
- _ddTestCommand: testCommand,
489
- _ddRepositoryRoot: repositoryRoot,
490
- _ddCodeOwnersEntries: codeOwnersEntries,
491
- }, 'Could not send test session configuration to workers.')
492
- }
493
-
494
- const {
495
- knownTestsResponse,
496
- testManagementTestsResponse,
497
- } = await getTestOptimizationRequestResults({
498
- isKnownTestsEnabled,
499
- isTestManagementTestsEnabled,
500
- getKnownTests: () => getChannelPromise(knownTestsCh),
501
- getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
502
- })
503
-
504
- if (isFlakyTestRetriesEnabled && !ctx.config.retry && flakyTestRetriesCount > 0) {
505
- ctx.config.retry = flakyTestRetriesCount
506
- setProvidedContext(ctx, {
507
- _ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
508
- _ddFlakyTestRetriesCount: flakyTestRetriesCount,
509
- }, 'Could not send library configuration to workers.')
510
- }
511
-
512
- if (isKnownTestsEnabled) {
513
- const currentKnownTestsResponse = knownTestsResponse || await getChannelPromise(knownTestsCh)
514
- if (currentKnownTestsResponse.err) {
515
- isEarlyFlakeDetectionEnabled = false
516
- } else {
517
- const knownTests = currentKnownTestsResponse.knownTests
518
- const testFilepaths = await getTestFilepaths(ctx, testSpecifications)
519
-
520
- if (isValidKnownTests(knownTests)) {
521
- isEarlyFlakeDetectionFaultyCh.publish({
522
- knownTests: knownTests.vitest,
523
- testFilepaths,
524
- onDone: (isFaulty) => {
525
- isEarlyFlakeDetectionFaulty = isFaulty
526
- },
527
- })
528
- if (isEarlyFlakeDetectionFaulty) {
529
- isEarlyFlakeDetectionEnabled = false
530
- log.warn('New test detection is disabled because the number of new tests is too high.')
531
- } else {
532
- setProvidedContext(ctx, {
533
- _ddIsKnownTestsEnabled: isKnownTestsEnabled,
534
- _ddKnownTests: knownTests,
535
- _ddIsEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled,
536
- _ddEarlyFlakeDetectionNumRetries:
537
- getConfiguredEfdRetryCount(earlyFlakeDetectionSlowTestRetries, earlyFlakeDetectionNumRetries),
538
- _ddEarlyFlakeDetectionSlowTestRetries: earlyFlakeDetectionSlowTestRetries,
539
- }, 'Could not send known tests to workers so Early Flake Detection will not work.')
540
- }
541
- } else {
542
- isEarlyFlakeDetectionFaulty = true
543
- isEarlyFlakeDetectionEnabled = false
544
- }
545
- }
546
- }
547
-
548
- if (isDiEnabled) {
549
- setProvidedContext(ctx, {
550
- _ddIsDiEnabled: isDiEnabled,
551
- }, 'Could not send Dynamic Instrumentation configuration to workers.')
552
- }
553
-
554
- if (isTestManagementTestsEnabled) {
555
- const { err, testManagementTests: receivedTestManagementTests } =
556
- testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
557
- if (err) {
558
- isTestManagementTestsEnabled = false
559
- log.error('Could not get test management tests.')
560
- } else {
561
- setProvidedContext(ctx, {
562
- _ddIsTestManagementTestsEnabled: isTestManagementTestsEnabled,
563
- _ddTestManagementAttemptToFixRetries: testManagementAttemptToFixRetries,
564
- _ddTestManagementTests: receivedTestManagementTests,
565
- }, 'Could not send test management tests to workers so Test Management will not work.')
566
- }
567
- }
568
-
569
- if (isImpactedTestsEnabled) {
570
- const { err, modifiedFiles } = await getChannelPromise(modifiedFilesCh)
571
- if (err) {
572
- log.error('Could not get modified tests.')
573
- } else {
574
- setProvidedContext(ctx, {
575
- _ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
576
- _ddModifiedFiles: modifiedFiles,
577
- }, 'Could not send modified tests to workers so Impacted Tests will not work.')
578
- }
579
- }
580
-
581
- wrapCoverageProvider(ctx)
582
- wrapSessionFinish(ctx)
583
- }
584
-
585
- function ensureMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
586
- let setupPromise = mainProcessSetupPromises.get(ctx)
587
- if (!setupPromise) {
588
- setupPromise = runMainProcessSetup(ctx, frameworkVersion, testSpecifications)
589
- mainProcessSetupPromises.set(ctx, setupPromise)
590
- }
591
- return setupPromise
592
- }
593
-
594
- function getSortWrapper (sort, frameworkVersion) {
595
- return async function () {
596
- await ensureMainProcessSetup(this.ctx, frameworkVersion, arguments[0])
597
- return sort.apply(this, arguments)
598
- }
599
- }
600
-
601
- function getFinishWrapper (exitOrClose) {
602
- let isClosed = false
603
- return async function () {
604
- if (isClosed) { // needed because exit calls close
605
- return exitOrClose.apply(this, arguments)
606
- }
607
- isClosed = true
608
-
609
- if (!testSessionFinishCh.hasSubscribers) {
610
- return exitOrClose.apply(this, arguments)
611
- }
612
-
613
- let onFinish
614
-
615
- const flushPromise = new Promise(resolve => {
616
- onFinish = resolve
617
- })
618
- const failedSuites = this.state.getFailedFilepaths()
619
- let error
620
- if (failedSuites.length) {
621
- error = new Error(`Test suites failed: ${failedSuites.length}.`)
622
- }
623
-
624
- testSessionFinishCh.publish({
625
- status: getSessionStatus(this.state),
626
- testCodeCoverageLinesTotal,
627
- error,
628
- isEarlyFlakeDetectionEnabled,
629
- isEarlyFlakeDetectionFaulty,
630
- isTestManagementTestsEnabled,
631
- vitestPool,
632
- onFinish,
633
- })
634
-
635
- logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
636
- loggedAttemptToFixTests.clear()
637
-
638
- await flushPromise
639
-
640
- // If coverage was generated, publish coverage report channel for upload
641
- if (coverageRootDir && codeCoverageReportCh.hasSubscribers) {
642
- await new Promise((resolve) => {
643
- codeCoverageReportCh.publish({ rootDir: coverageRootDir, onDone: resolve })
644
- })
645
- }
646
-
647
- return exitOrClose.apply(this, arguments)
648
- }
649
- }
650
-
651
- function getCliOrStartVitestWrapper (frameworkVersion) {
652
- return function (oldCliOrStartVitest) {
653
- return function (...args) {
654
- if (!testSessionStartCh.hasSubscribers || isSessionStarted) {
655
- return oldCliOrStartVitest.apply(this, args)
656
- }
657
- isSessionStarted = true
658
- testSessionStartCh.publish({ command: getTestCommand(), frameworkVersion })
659
- return oldCliOrStartVitest.apply(this, args)
660
- }
661
- }
662
- }
663
-
664
- function isForkPool (pool) {
665
- return pool === 'forks' || pool === 'vmForks'
666
- }
667
-
668
- function isThreadPool (pool) {
669
- return pool === 'threads' || pool === 'vmThreads'
14
+ require('./vitest-worker')
15
+ } else {
16
+ require('./vitest-main')
670
17
  }
671
-
672
- function getTestSpecificationPool (testSpecification) {
673
- const project = Array.isArray(testSpecification) ? testSpecification[0] : testSpecification?.project
674
- return project?.config?.pool || project?.serializedConfig?.pool || project?.pool || testSpecification?.pool
675
- }
676
-
677
- function hasForkPoolTestSpecification (testSpecifications) {
678
- if (!Array.isArray(testSpecifications)) {
679
- return false
680
- }
681
-
682
- for (const testSpecification of testSpecifications) {
683
- if (isForkPool(getTestSpecificationPool(testSpecification))) {
684
- return true
685
- }
686
- }
687
-
688
- return false
689
- }
690
-
691
- function shouldMarkVitestWorkerEnv (pool, testSpecifications) {
692
- return isForkPool(pool) || hasForkPoolTestSpecification(testSpecifications) ||
693
- (!testSpecifications && !isThreadPool(pool))
694
- }
695
-
696
- function markVitestWorkerEnv (ctx, testSpecifications) {
697
- const config = ctx?.config
698
- if (!config || !shouldMarkVitestWorkerEnv(config.pool, testSpecifications)) {
699
- return
700
- }
701
- config.env = config.env || {}
702
- config.env.DD_VITEST_WORKER = '1'
703
- }
704
-
705
- function wrapVitestRunFiles (Vitest, frameworkVersion) {
706
- if (!Vitest?.prototype?.runFiles) {
707
- return
708
- }
709
-
710
- shimmer.wrap(Vitest.prototype, 'runFiles', runFiles => async function (testSpecifications) {
711
- markVitestWorkerEnv(this, testSpecifications)
712
- await ensureMainProcessSetup(this, frameworkVersion, testSpecifications)
713
- return runFiles.apply(this, arguments)
714
- })
715
-
716
- if (Vitest.prototype.collectTests) {
717
- shimmer.wrap(Vitest.prototype, 'collectTests', collectTests => function () {
718
- markVitestWorkerEnv(this)
719
- return collectTests.apply(this, arguments)
720
- })
721
- }
722
- }
723
-
724
- function getCreateCliWrapper (vitestPackage, frameworkVersion) {
725
- const createCliExport = findExportByName(vitestPackage, 'createCLI')
726
- if (!createCliExport) {
727
- return vitestPackage
728
- }
729
- shimmer.wrap(vitestPackage, createCliExport.key, getCliOrStartVitestWrapper(frameworkVersion))
730
-
731
- return vitestPackage
732
- }
733
-
734
- function threadHandler (thread) {
735
- const { runtime } = thread
736
- let workerProcess
737
- if (runtime === 'child_process') {
738
- vitestPool = 'child_process'
739
- workerProcess = thread.process
740
- } else if (runtime === 'worker_threads') {
741
- vitestPool = 'worker_threads'
742
- workerProcess = thread.thread
743
- } else {
744
- vitestPool = 'unknown'
745
- }
746
- if (!workerProcess) {
747
- log.error('Vitest error: could not get process or thread from TinyPool#run')
748
- return
749
- }
750
-
751
- if (workerProcesses.has(workerProcess)) {
752
- return
753
- }
754
- workerProcesses.add(workerProcess)
755
- workerProcess.on('message', (message) => {
756
- if (message.__tinypool_worker_message__ && message.data) {
757
- if (message.interprocessCode === VITEST_WORKER_TRACE_PAYLOAD_CODE) {
758
- collectTestOptimizationSummariesFromTraces(message.data, {
759
- newTestsWithDynamicNames,
760
- attemptToFixExecutions,
761
- })
762
- workerReportTraceCh.publish(message.data)
763
- } else if (message.interprocessCode === VITEST_WORKER_LOGS_PAYLOAD_CODE) {
764
- workerReportLogsCh.publish(message.data)
765
- }
766
- }
767
- })
768
- }
769
-
770
- function wrapTinyPoolRun (TinyPool) {
771
- shimmer.wrap(TinyPool.prototype, 'run', run => async function () {
772
- // We have to do this before and after because the threads list gets recycled, that is, the processes are re-created
773
- // eslint-disable-next-line unicorn/no-array-for-each
774
- this.threads.forEach(threadHandler)
775
- const runResult = await run.apply(this, arguments)
776
- // eslint-disable-next-line unicorn/no-array-for-each
777
- this.threads.forEach(threadHandler)
778
- return runResult
779
- })
780
- }
781
-
782
- addHook({
783
- name: 'tinypool',
784
- // version from tinypool@0.8 was used in vitest@1.6.0
785
- versions: ['>=0.8.0'],
786
- }, (TinyPool) => {
787
- wrapTinyPoolRun(TinyPool)
788
- return TinyPool
789
- })
790
-
791
- function getWrappedOn (on) {
792
- return function (event, callback) {
793
- if (event !== 'message') {
794
- return on.apply(this, arguments)
795
- }
796
- // `arguments[1]` is the callback function, which
797
- // we modify to intercept our messages to not interfere
798
- // with vitest's own messages
799
- arguments[1] = shimmer.wrapFunction(callback, callback => function (message) {
800
- if (message.type !== 'Buffer' && Array.isArray(message)) {
801
- const [interprocessCode, data] = message
802
- if (interprocessCode === VITEST_WORKER_TRACE_PAYLOAD_CODE) {
803
- collectTestOptimizationSummariesFromTraces(data, {
804
- newTestsWithDynamicNames,
805
- attemptToFixExecutions,
806
- })
807
- workerReportTraceCh.publish(data)
808
- } else if (interprocessCode === VITEST_WORKER_LOGS_PAYLOAD_CODE) {
809
- workerReportLogsCh.publish(data)
810
- }
811
- // If we execute the callback vitest crashes, as the message is not supported
812
- return
813
- }
814
- return callback.apply(this, arguments)
815
- })
816
- return on.apply(this, arguments)
817
- }
818
- }
819
-
820
- function getStartVitestWrapper (cliApiPackage, frameworkVersion) {
821
- if (!isCliApiPackage(cliApiPackage)) {
822
- return cliApiPackage
823
- }
824
- const startVitestExport = findExportByName(cliApiPackage, 'startVitest')
825
- shimmer.wrap(cliApiPackage, startVitestExport.key, getCliOrStartVitestWrapper(frameworkVersion))
826
-
827
- const vitest = getVitestExport(cliApiPackage)
828
- if (vitest) {
829
- wrapVitestRunFiles(vitest.value, frameworkVersion)
830
- }
831
-
832
- const forksPoolWorker = getForksPoolWorkerExport(cliApiPackage)
833
- if (forksPoolWorker) {
834
- // function is async
835
- shimmer.wrap(forksPoolWorker.value.prototype, 'start', start => function (...args) {
836
- vitestPool = 'child_process'
837
- this.env.DD_VITEST_WORKER = '1'
838
-
839
- return start.apply(this, args)
840
- })
841
- shimmer.wrap(forksPoolWorker.value.prototype, 'on', getWrappedOn)
842
- }
843
-
844
- const threadsPoolWorker = getThreadsPoolWorkerExport(cliApiPackage)
845
- if (threadsPoolWorker) {
846
- // function is async
847
- shimmer.wrap(threadsPoolWorker.value.prototype, 'start', start => function (...args) {
848
- vitestPool = 'worker_threads'
849
- this.env.DD_VITEST_WORKER = '1'
850
- return start.apply(this, args)
851
- })
852
- shimmer.wrap(threadsPoolWorker.value.prototype, 'on', getWrappedOn)
853
- }
854
- return cliApiPackage
855
- }
856
-
857
- function wrapVitestTestRunner (VitestTestRunner) {
858
- // `onBeforeRunTask` is run before any repetition or attempt is run
859
- // `onBeforeRunTask` is an async function
860
- shimmer.wrap(VitestTestRunner.prototype, 'onBeforeRunTask', onBeforeRunTask => function (task) {
861
- const testName = getTestName(task)
862
-
863
- const {
864
- knownTests,
865
- isEarlyFlakeDetectionEnabled,
866
- isKnownTestsEnabled,
867
- numRepeats,
868
- isTestManagementTestsEnabled,
869
- testManagementAttemptToFixRetries,
870
- testManagementTests,
871
- isImpactedTestsEnabled,
872
- modifiedFiles,
873
- } = getProvidedContext()
874
-
875
- if (isTestManagementTestsEnabled) {
876
- isAttemptToFixCh.publish({
877
- testManagementTests,
878
- testSuiteAbsolutePath: task.file.filepath,
879
- testName,
880
- onDone: (isAttemptToFix) => {
881
- if (isAttemptToFix) {
882
- isRetryReasonAttemptToFix = task.repeats !== testManagementAttemptToFixRetries
883
- disableFrameworkRetries(task)
884
- task.repeats = testManagementAttemptToFixRetries
885
- attemptToFixTasks.add(task)
886
- attemptToFixTaskToStatuses.set(task, [])
887
- }
888
- },
889
- })
890
- isDisabledCh.publish({
891
- testManagementTests,
892
- testSuiteAbsolutePath: task.file.filepath,
893
- testName,
894
- onDone: (isTestDisabled) => {
895
- if (isTestDisabled) {
896
- disabledTasks.add(task)
897
- if (!attemptToFixTasks.has(task)) {
898
- // we only actually skip if the test is not being attempted to be fixed
899
- task.mode = 'skip'
900
- }
901
- }
902
- },
903
- })
904
- }
905
-
906
- if (isImpactedTestsEnabled) {
907
- isModifiedCh.publish({
908
- modifiedFiles,
909
- testSuiteAbsolutePath: task.file.filepath,
910
- onDone: (isImpacted) => {
911
- if (isImpacted) {
912
- if (isEarlyFlakeDetectionEnabled) {
913
- isRetryReasonEfd = true
914
- disableFrameworkRetries(task)
915
- task.repeats = numRepeats
916
- }
917
- modifiedTasks.add(task)
918
- taskToStatuses.set(task, [])
919
- }
920
- },
921
- })
922
- }
923
-
924
- if (isKnownTestsEnabled) {
925
- isNewTestCh.publish({
926
- knownTests,
927
- testSuiteAbsolutePath: task.file.filepath,
928
- testName,
929
- onDone: (isNew) => {
930
- if (isNew && !attemptToFixTasks.has(task)) {
931
- if (isEarlyFlakeDetectionEnabled && !modifiedTasks.has(task)) {
932
- isRetryReasonEfd = true
933
- disableFrameworkRetries(task)
934
- task.repeats = numRepeats
935
- }
936
- newTasks.add(task)
937
- taskToStatuses.set(task, [])
938
- if (DYNAMIC_NAME_RE.test(testName)) {
939
- dynamicNameTasks.add(task)
940
- }
941
- }
942
- },
943
- })
944
- }
945
-
946
- return onBeforeRunTask.apply(this, arguments)
947
- })
948
-
949
- // `onAfterRunTask` is run after all repetitions or attempts are run
950
- // `onAfterRunTask` is an async function
951
- shimmer.wrap(VitestTestRunner.prototype, 'onAfterRunTask', onAfterRunTask => function (task) {
952
- const { isEarlyFlakeDetectionEnabled, isTestManagementTestsEnabled } = getProvidedContext()
953
-
954
- if (isTestManagementTestsEnabled) {
955
- const isAttemptingToFix = attemptToFixTasks.has(task)
956
- const isQuarantined = quarantinedTasks.has(task)
957
-
958
- if (isAttemptingToFix) {
959
- const statuses = attemptToFixTaskToStatuses.get(task)
960
- if (task.result.state === 'pass' && statuses?.includes('fail')) {
961
- switchedStatuses.add(task)
962
- task.result.state = 'fail'
963
- }
964
- }
965
-
966
- if (!isAttemptingToFix && isQuarantined) {
967
- if (task.result.state === 'fail') {
968
- switchedStatuses.add(task)
969
- }
970
- task.result.state = 'pass'
971
- }
972
- }
973
-
974
- if (isEarlyFlakeDetectionEnabled && taskToStatuses.has(task) && !attemptToFixTasks.has(task)) {
975
- const statuses = taskToStatuses.get(task)
976
- // If the test has passed at least once, we consider it passed
977
- if (statuses.includes('pass')) {
978
- if (task.result.state === 'fail') {
979
- switchedStatuses.add(task)
980
- }
981
- task.result.state = 'pass'
982
- }
983
- }
984
-
985
- return onAfterRunTask.apply(this, arguments)
986
- })
987
-
988
- // test start (only tests that are not marked as skip or todo)
989
- // `onBeforeTryTask` is run for every repetition and attempt of the test
990
- shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task, retryInfo) {
991
- if (!testPassCh.hasSubscribers && !testErrorCh.hasSubscribers && !testSkipCh.hasSubscribers) {
992
- return onBeforeTryTask.apply(this, arguments)
993
- }
994
- const testName = getTestName(task)
995
- let isNew = false
996
- let isQuarantined = false
997
-
998
- const {
999
- isKnownTestsEnabled,
1000
- isEarlyFlakeDetectionEnabled,
1001
- isDiEnabled,
1002
- isTestManagementTestsEnabled,
1003
- testManagementTests,
1004
- isFlakyTestRetriesEnabled,
1005
- slowTestRetries,
1006
- } = getProvidedContext()
1007
-
1008
- if (isKnownTestsEnabled) {
1009
- isNew = newTasks.has(task)
1010
- }
1011
-
1012
- if (isTestManagementTestsEnabled) {
1013
- isQuarantinedCh.publish({
1014
- testManagementTests,
1015
- testSuiteAbsolutePath: task.file.filepath,
1016
- testName,
1017
- onDone: (isTestQuarantined) => {
1018
- isQuarantined = isTestQuarantined
1019
- if (isTestQuarantined) {
1020
- quarantinedTasks.add(task)
1021
- }
1022
- },
1023
- })
1024
- }
1025
-
1026
- const { retry: numAttempt, repeats: numRepetition } = retryInfo
1027
- const isEfdManagedTask = isEarlyFlakeDetectionEnabled && taskToStatuses.has(task) && !attemptToFixTasks.has(task)
1028
-
1029
- if (isEfdManagedTask && numRepetition > 0 && !efdDeterminedRetries.has(task)) {
1030
- const previousExecutionStart = efdExecutionStartByTask.get(task)
1031
- const duration = previousExecutionStart === undefined
1032
- ? task.result?.duration ?? 0
1033
- : performance.now() - previousExecutionStart
1034
- const retryCount = getEfdRetryCount(duration, slowTestRetries)
1035
- efdDeterminedRetries.set(task, retryCount)
1036
- task.repeats = retryCount
1037
- if (retryCount === 0) {
1038
- efdSlowAbortedTasks.add(task)
1039
- }
1040
- }
1041
-
1042
- const efdRetryCount = efdDeterminedRetries.get(task)
1043
- if (isEfdManagedTask && efdRetryCount !== undefined && numRepetition > efdRetryCount) {
1044
- if (task.result) {
1045
- efdSkippedRetryResults.set(task, {
1046
- ...task.result,
1047
- errors: task.result.errors?.slice(),
1048
- })
1049
- }
1050
- if (vitestSetFn) {
1051
- const noop = function () {}
1052
- noop.__ddTraceWrapped = true
1053
- vitestSetFn(task, noop)
1054
- }
1055
- return onBeforeTryTask.apply(this, arguments)
1056
- }
1057
- if (isEfdManagedTask) {
1058
- efdExecutionStartByTask.set(task, performance.now())
1059
- }
1060
-
1061
- // We finish the previous test here because we know it has failed already
1062
- if (numAttempt > 0) {
1063
- const shouldWaitForHitProbe = isDiEnabled && numAttempt > 1
1064
- if (shouldWaitForHitProbe) {
1065
- await waitForHitProbe()
1066
- }
1067
-
1068
- const promises = {}
1069
- const shouldSetProbe = isDiEnabled && numAttempt === 1
1070
- const ctx = taskToCtx.get(task)
1071
- const testError = getCurrentAttemptTestError(task, task.result?.errors)
1072
- if (ctx) {
1073
- testErrorCh.publish({
1074
- error: testError,
1075
- shouldSetProbe,
1076
- promises,
1077
- ...ctx.currentStore,
1078
- })
1079
- // We wait for the probe to be set
1080
- if (promises.setProbePromise) {
1081
- await promises.setProbePromise
1082
- }
1083
- }
1084
- }
1085
-
1086
- const lastExecutionStatus = task.result.state
1087
- const isAtf = attemptToFixTasks.has(task)
1088
- const shouldTrackStatuses = isEarlyFlakeDetectionEnabled || isAtf
1089
- const shouldFlipStatus = isEarlyFlakeDetectionEnabled || isAtf
1090
- const statuses = isAtf ? attemptToFixTaskToStatuses.get(task) : taskToStatuses.get(task)
1091
-
1092
- // These clauses handle task.repeats, whether EFD is enabled or not
1093
- // The only thing that EFD does is to forcefully pass the test if it has passed at least once
1094
- if (numRepetition > 0 && numRepetition < task.repeats) { // it may or may have not failed
1095
- // Here we finish the earlier iteration,
1096
- // as long as it's not the _last_ iteration (which will be finished normally)
1097
-
1098
- const ctx = taskToCtx.get(task)
1099
- if (ctx) {
1100
- if (lastExecutionStatus === 'fail') {
1101
- const testError = getCurrentAttemptTestError(task, task.result?.errors)
1102
- testErrorCh.publish({ error: testError, ...ctx.currentStore })
1103
- } else {
1104
- testPassCh.publish({ task, ...ctx.currentStore })
1105
- }
1106
- if (shouldTrackStatuses && statuses) {
1107
- statuses.push(lastExecutionStatus)
1108
- }
1109
- if (shouldFlipStatus) {
1110
- // If we don't "reset" the result.state to "pass", once a repetition fails,
1111
- // vitest will always consider the test as failed, so we can't read the actual status
1112
- // This means that we change vitest's behavior:
1113
- // if the last attempt passes, vitest would consider the test as failed
1114
- // but after this change, it will consider the test as passed
1115
- task.result.state = 'pass'
1116
- }
1117
- }
1118
- } else if (numRepetition === task.repeats) {
1119
- if (shouldTrackStatuses && statuses) {
1120
- statuses.push(lastExecutionStatus)
1121
- }
1122
-
1123
- const ctx = taskToCtx.get(task)
1124
- if (lastExecutionStatus === 'fail') {
1125
- const testError = getCurrentAttemptTestError(task, task.result?.errors)
1126
- testErrorCh.publish({ error: testError, ...ctx.currentStore })
1127
- } else {
1128
- testPassCh.publish({ task, ...ctx.currentStore })
1129
- }
1130
- if (shouldFlipStatus) {
1131
- task.result.state = 'pass'
1132
- }
1133
- }
1134
-
1135
- const isRetryReasonAtr = numAttempt > 0 &&
1136
- isFlakyTestRetriesEnabled &&
1137
- !isRetryReasonAttemptToFix &&
1138
- !isRetryReasonEfd
1139
-
1140
- const ctx = {
1141
- testName,
1142
- testSuiteAbsolutePath: task.file.filepath,
1143
- isRetry: numAttempt > 0 || numRepetition > 0,
1144
- isRetryReasonEfd,
1145
- isRetryReasonAttemptToFix: isRetryReasonAttemptToFix && numRepetition > 0,
1146
- isNew,
1147
- hasDynamicName: dynamicNameTasks.has(task),
1148
- mightHitProbe: isDiEnabled && numAttempt > 0,
1149
- isAttemptToFix: attemptToFixTasks.has(task),
1150
- isDisabled: disabledTasks.has(task),
1151
- isQuarantined,
1152
- isRetryReasonAtr,
1153
- isModified: modifiedTasks.has(task),
1154
- }
1155
- taskToCtx.set(task, ctx)
1156
-
1157
- if (attemptToFixTasks.has(task)) {
1158
- logAttemptToFixTestExecution(
1159
- getTestSuitePath(task.file.filepath, process.cwd()),
1160
- testName,
1161
- loggedAttemptToFixTests
1162
- )
1163
- }
1164
-
1165
- testStartCh.runStores(ctx, () => {})
1166
-
1167
- // Wrap the test function so it runs inside the test span context.
1168
- // Without this, HTTP requests during test execution become orphaned root spans.
1169
- if (vitestGetFn && vitestSetFn) {
1170
- const originalFn = vitestGetFn(task)
1171
- if (originalFn && !originalFn.__ddTraceWrapped) {
1172
- const wrappedFn = wrapTestScopedFn(task, originalFn)
1173
- wrappedFn.__ddTraceWrapped = true
1174
- vitestSetFn(task, wrappedFn)
1175
- }
1176
- }
1177
-
1178
- // Wrap beforeEach/afterEach hooks so they also run inside the test span context.
1179
- // In vitest 4+, hooks are in a WeakMap accessed via getHooks(). In older versions, they're on suite.hooks.
1180
- let currentSuite = task.suite
1181
- while (currentSuite) {
1182
- const hooks = vitestGetHooks ? vitestGetHooks(currentSuite) : currentSuite.hooks
1183
- if (hooks) {
1184
- for (const hookType of ['beforeEach', 'afterEach']) {
1185
- const hookArray = hooks[hookType]
1186
- if (!hookArray) continue
1187
- for (let i = 0; i < hookArray.length; i++) {
1188
- const currentFn = hookArray[i]
1189
- const originalFn = originalHookFns.get(currentFn) || currentFn
1190
- const wrappedFn = shimmer.wrapFunction(originalFn, fn => function (...args) {
1191
- const result = testFnCh.runStores(taskToCtx.get(task), () => fn.apply(this, args))
1192
-
1193
- if (hookType === 'beforeEach') {
1194
- return wrapBeforeEachCleanupResult(task, result)
1195
- }
1196
-
1197
- return result
1198
- })
1199
- originalHookFns.set(wrappedFn, originalFn)
1200
- hookArray[i] = wrappedFn
1201
- }
1202
- }
1203
- }
1204
- currentSuite = currentSuite.suite
1205
- }
1206
-
1207
- return onBeforeTryTask.apply(this, arguments)
1208
- })
1209
-
1210
- // test finish (only passed tests)
1211
- shimmer.wrap(VitestTestRunner.prototype, 'onAfterTryTask', onAfterTryTask =>
1212
- async function (task, retryInfo) {
1213
- if (!testPassCh.hasSubscribers && !testErrorCh.hasSubscribers && !testSkipCh.hasSubscribers) {
1214
- return onAfterTryTask.apply(this, arguments)
1215
- }
1216
- const result = await onAfterTryTask.apply(this, arguments)
1217
-
1218
- const {
1219
- isEarlyFlakeDetectionEnabled,
1220
- testManagementAttemptToFixRetries,
1221
- slowTestRetries,
1222
- } = getProvidedContext()
1223
-
1224
- const status = getVitestTestStatus(task, retryInfo.retry)
1225
- const ctx = taskToCtx.get(task)
1226
-
1227
- const { isDiEnabled } = getProvidedContext()
1228
-
1229
- if (efdSkippedRetryResults.has(task)) {
1230
- task.result = efdSkippedRetryResults.get(task)
1231
- efdSkippedRetryResults.delete(task)
1232
- return result
1233
- }
1234
-
1235
- if (isDiEnabled && retryInfo.retry > 1) {
1236
- await waitForHitProbe()
1237
- }
1238
-
1239
- if (
1240
- isEarlyFlakeDetectionEnabled &&
1241
- (retryInfo.repeats ?? 0) === 0 &&
1242
- taskToStatuses.has(task) &&
1243
- !attemptToFixTasks.has(task) &&
1244
- !efdDeterminedRetries.has(task)
1245
- ) {
1246
- const executionStart = efdExecutionStartByTask.get(task)
1247
- const duration = executionStart === undefined ? task.result?.duration ?? 0 : performance.now() - executionStart
1248
- const retryCount = getEfdRetryCount(duration, slowTestRetries)
1249
- efdDeterminedRetries.set(task, retryCount)
1250
- task.repeats = retryCount
1251
- if (retryCount === 0) {
1252
- efdSlowAbortedTasks.add(task)
1253
- }
1254
- }
1255
-
1256
- let attemptToFixPassed = false
1257
- let attemptToFixFailed = false
1258
- if (attemptToFixTasks.has(task)) {
1259
- const statuses = attemptToFixTaskToStatuses.get(task)
1260
- if (statuses.length === testManagementAttemptToFixRetries) {
1261
- if (status === 'pass' && statuses.every(status => status === 'pass')) {
1262
- attemptToFixPassed = true
1263
- } else if (status === 'fail' || statuses.includes('fail')) {
1264
- attemptToFixFailed = true
1265
- }
1266
- }
1267
- }
1268
-
1269
- if (ctx) {
1270
- // We don't finish here because the test might fail in a later hook (afterEach)
1271
- ctx.status = status
1272
- ctx.task = task
1273
- ctx.attemptToFixPassed = attemptToFixPassed
1274
- ctx.attemptToFixFailed = attemptToFixFailed
1275
- testFinishTimeCh.runStores(ctx, () => {})
1276
- }
1277
-
1278
- return result
1279
- })
1280
- }
1281
-
1282
- function captureRunnerFunctions (pkg) {
1283
- if (vitestGetFn) return
1284
- const getFnExport = findExportByName(pkg, 'getFn')
1285
- const setFnExport = findExportByName(pkg, 'setFn')
1286
- if (getFnExport && setFnExport) {
1287
- vitestGetFn = getFnExport.value
1288
- vitestSetFn = setFnExport.value
1289
- }
1290
- const getHooksExport = findExportByName(pkg, 'getHooks')
1291
- if (getHooksExport) {
1292
- vitestGetHooks = getHooksExport.value
1293
- }
1294
- }
1295
-
1296
- addHook({
1297
- name: 'vitest',
1298
- versions: ['>=4.0.0'],
1299
- filePattern: 'dist/chunks/test.*',
1300
- }, (testPackage) => {
1301
- const testRunner = getTestRunnerExport(testPackage)
1302
- if (!testRunner) {
1303
- return testPackage
1304
- }
1305
-
1306
- captureRunnerFunctions(testPackage)
1307
- wrapVitestTestRunner(testRunner.value)
1308
-
1309
- return testPackage
1310
- })
1311
-
1312
- addHook({
1313
- name: '@vitest/runner',
1314
- versions: ['>=1.6.0'],
1315
- }, (runnerModule) => {
1316
- if (!vitestGetFn && runnerModule.getFn && runnerModule.setFn) {
1317
- vitestGetFn = runnerModule.getFn
1318
- vitestSetFn = runnerModule.setFn
1319
- }
1320
- if (!vitestGetHooks && runnerModule.getHooks) {
1321
- vitestGetHooks = runnerModule.getHooks
1322
- }
1323
- return runnerModule
1324
- })
1325
-
1326
- addHook({
1327
- name: 'vitest',
1328
- versions: ['>=1.6.0 <4.0.0'],
1329
- file: 'dist/runners.js',
1330
- }, (vitestPackage) => {
1331
- const { VitestTestRunner } = vitestPackage
1332
-
1333
- wrapVitestTestRunner(VitestTestRunner)
1334
-
1335
- return vitestPackage
1336
- })
1337
-
1338
- // There are multiple index* files across different versions of vitest,
1339
- // so we check for the existence of BaseSequencer to determine if we are in the right file
1340
- addHook({
1341
- name: 'vitest',
1342
- versions: ['>=1.6.0 <2.0.0'],
1343
- filePattern: 'dist/vendor/index.*',
1344
- }, (vitestPackage) => {
1345
- if (isReporterPackage(vitestPackage)) {
1346
- shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
1347
- }
1348
-
1349
- return vitestPackage
1350
- })
1351
-
1352
- addHook({
1353
- name: 'vitest',
1354
- versions: ['>=2.0.0 <2.0.5'],
1355
- filePattern: 'dist/vendor/index.*',
1356
- }, (vitestPackage) => {
1357
- if (isReporterPackageNew(vitestPackage)) {
1358
- shimmer.wrap(vitestPackage.e.prototype, 'sort', getSortWrapper)
1359
- }
1360
-
1361
- return vitestPackage
1362
- })
1363
-
1364
- addHook({
1365
- name: 'vitest',
1366
- versions: ['>=2.0.5 <2.1.0'],
1367
- filePattern: 'dist/chunks/index.*',
1368
- }, (vitestPackage) => {
1369
- if (isReporterPackageNewest(vitestPackage)) {
1370
- shimmer.wrap(vitestPackage.h.prototype, 'sort', getSortWrapper)
1371
- }
1372
-
1373
- return vitestPackage
1374
- })
1375
-
1376
- addHook({
1377
- name: 'vitest',
1378
- versions: ['>=2.1.0 <3.0.0'],
1379
- filePattern: 'dist/chunks/RandomSequencer.*',
1380
- }, (randomSequencerPackage) => {
1381
- shimmer.wrap(randomSequencerPackage.B.prototype, 'sort', getSortWrapper)
1382
- return randomSequencerPackage
1383
- })
1384
-
1385
- addHook({
1386
- name: 'vitest',
1387
- versions: ['>=3.0.9'],
1388
- filePattern: 'dist/chunks/coverage.*',
1389
- }, (coveragePackage) => {
1390
- const baseSequencer = getBaseSequencerExport(coveragePackage)
1391
- if (baseSequencer) {
1392
- shimmer.wrap(baseSequencer.value.prototype, 'sort', getSortWrapper)
1393
- }
1394
- return coveragePackage
1395
- })
1396
-
1397
- addHook({
1398
- name: 'vitest',
1399
- versions: ['>=3.0.0 <3.0.9'],
1400
- filePattern: 'dist/chunks/resolveConfig.*',
1401
- }, (resolveConfigPackage) => {
1402
- shimmer.wrap(resolveConfigPackage.B.prototype, 'sort', getSortWrapper)
1403
- return resolveConfigPackage
1404
- })
1405
-
1406
- // Can't specify file because compiled vitest includes hashes in their files
1407
- // Following 3 wrappers are for test session start
1408
- addHook({
1409
- name: 'vitest',
1410
- versions: ['>=1.6.0 <2.0.5'],
1411
- filePattern: 'dist/vendor/cac.*',
1412
- }, getCreateCliWrapper)
1413
-
1414
- addHook({
1415
- name: 'vitest',
1416
- versions: ['>=2.0.5'],
1417
- filePattern: 'dist/chunks/cac.*',
1418
- }, getCreateCliWrapper)
1419
-
1420
- addHook({
1421
- name: 'vitest',
1422
- versions: ['>=1.6.0 <2.0.5'],
1423
- filePattern: 'dist/vendor/cli-api.*',
1424
- }, getStartVitestWrapper)
1425
-
1426
- addHook({
1427
- name: 'vitest',
1428
- versions: ['>=2.0.5'],
1429
- filePattern: 'dist/chunks/cli-api.*',
1430
- }, getStartVitestWrapper)
1431
-
1432
- // test suite start and finish
1433
- // only relevant for workers
1434
- addHook({
1435
- name: '@vitest/runner',
1436
- versions: ['>=1.6.0'],
1437
- }, (vitestPackage, frameworkVersion) => {
1438
- shimmer.wrap(vitestPackage, 'startTests', startTests => async function (testPaths) {
1439
- let testSuiteError = null
1440
- if (!testSuiteFinishCh.hasSubscribers) {
1441
- return startTests.apply(this, arguments)
1442
- }
1443
- // From >=3.0.1, the first arguments changes from a string to an object containing the filepath
1444
- const testSuiteAbsolutePath = testPaths[0]?.filepath || testPaths[0]
1445
- const providedContext = getProvidedContext()
1446
-
1447
- const testSuiteCtx = {
1448
- testSuiteAbsolutePath,
1449
- frameworkVersion,
1450
- testSessionId: providedContext.testSessionId,
1451
- testModuleId: providedContext.testModuleId,
1452
- testCommand: providedContext.testCommand,
1453
- repositoryRoot: providedContext.repositoryRoot,
1454
- codeOwnersEntries: providedContext.codeOwnersEntries,
1455
- }
1456
- testSuiteStartCh.runStores(testSuiteCtx, () => {})
1457
- const startTestsResponse = await startTests.apply(this, arguments)
1458
-
1459
- let onFinish = null
1460
- const onFinishPromise = new Promise(resolve => {
1461
- onFinish = resolve
1462
- })
1463
-
1464
- const testTasks = getTypeTasks(startTestsResponse[0].tasks)
1465
-
1466
- // Only one test task per test, even if there are retries
1467
- for (const task of testTasks) {
1468
- const testCtx = taskToCtx.get(task)
1469
- const { result } = task
1470
- // We have to trick vitest into thinking that the test has passed
1471
- // but we want to report it as failed if it did fail
1472
- const isSwitchedStatus = switchedStatuses.has(task)
1473
-
1474
- if (result) {
1475
- const { state, duration, errors } = result
1476
- const testError = getCurrentAttemptTestError(task, errors)
1477
- if (attemptToFixTasks.has(task)) {
1478
- const status = getFinalAttemptToFixStatus(task, state, isSwitchedStatus, testCtx)
1479
- recordFinalAttemptToFixExecution(task, status, providedContext)
1480
- }
1481
-
1482
- if (state === 'skip') { // programmatic skip
1483
- testSkipCh.publish({
1484
- testName: getTestName(task),
1485
- testSuiteAbsolutePath: task.file.filepath,
1486
- isNew: newTasks.has(task),
1487
- isDisabled: disabledTasks.has(task),
1488
- })
1489
- } else if (state === 'pass' && !isSwitchedStatus) {
1490
- if (testCtx) {
1491
- const isSkippedByTestManagement =
1492
- !attemptToFixTasks.has(task) && (disabledTasks.has(task) || quarantinedTasks.has(task))
1493
- testPassCh.publish({
1494
- task,
1495
- finalStatus: isSkippedByTestManagement ? 'skip' : 'pass',
1496
- earlyFlakeAbortReason: efdSlowAbortedTasks.has(task) ? 'slow' : undefined,
1497
- ...testCtx.currentStore,
1498
- })
1499
- }
1500
- } else if (state === 'fail' || isSwitchedStatus) {
1501
- let hasFailedAllRetries = false
1502
- let attemptToFixFailed = false
1503
- if (attemptToFixTasks.has(task)) {
1504
- const statuses = attemptToFixTaskToStatuses.get(task)
1505
- if (statuses.includes('fail')) {
1506
- attemptToFixFailed = true
1507
- }
1508
- if (statuses.every(status => status === 'fail')) {
1509
- hasFailedAllRetries = true
1510
- }
1511
- }
1512
-
1513
- // Check if all EFD retries failed
1514
- const isEfdRetry =
1515
- providedContext.isEarlyFlakeDetectionEnabled && (newTasks.has(task) || modifiedTasks.has(task))
1516
- if (isEfdRetry) {
1517
- const statuses = taskToStatuses.get(task)
1518
- const efdRetryCount = efdDeterminedRetries.get(task) ?? providedContext.numRepeats
1519
- // statuses only includes repetitions (not the initial run), so we check against retry count (not +1)
1520
- if (efdRetryCount > 0 && statuses && statuses.length === efdRetryCount &&
1521
- statuses.every(status => status === 'fail')) {
1522
- hasFailedAllRetries = true
1523
- }
1524
- }
1525
-
1526
- // ATR: set hasFailedAllRetries when all auto test retries were exhausted and every attempt failed
1527
- const isAtrRetry = providedContext.isFlakyTestRetriesEnabled && !attemptToFixTasks.has(task) &&
1528
- !newTasks.has(task) && !modifiedTasks.has(task)
1529
- if (isAtrRetry) {
1530
- const maxRetries = providedContext.flakyTestRetriesCount ?? 0
1531
- if (maxRetries > 0 && task.result?.retryCount === maxRetries) {
1532
- hasFailedAllRetries = true
1533
- }
1534
- }
1535
-
1536
- if (testCtx) {
1537
- const isRetry = task.result?.retryCount > 0
1538
- // `duration` is the duration of all the retries, so it can't be used if there are retries
1539
-
1540
- let finalStatus
1541
- if (isSwitchedStatus) {
1542
- if (!attemptToFixTasks.has(task) && (disabledTasks.has(task) || quarantinedTasks.has(task))) {
1543
- finalStatus = 'skip'
1544
- } else if (isAtrRetry || isEfdRetry) {
1545
- finalStatus = hasFailedAllRetries ? 'fail' : 'pass'
1546
- } else if (attemptToFixTasks.has(task)) {
1547
- finalStatus = attemptToFixFailed ? 'fail' : 'pass'
1548
- } else {
1549
- finalStatus = undefined
1550
- }
1551
- } else {
1552
- finalStatus = 'fail'
1553
- }
1554
-
1555
- testErrorCh.publish({
1556
- duration: isRetry ? undefined : duration,
1557
- error: testError,
1558
- hasFailedAllRetries,
1559
- attemptToFixFailed,
1560
- finalStatus,
1561
- earlyFlakeAbortReason: efdSlowAbortedTasks.has(task) ? 'slow' : undefined,
1562
- ...testCtx.currentStore,
1563
- })
1564
- }
1565
- if (errors?.length) {
1566
- testSuiteError = testError // we store the error to bubble it up to the suite
1567
- }
1568
- }
1569
- } else { // test.skip or test.todo
1570
- testSkipCh.publish({
1571
- testName: getTestName(task),
1572
- testSuiteAbsolutePath: task.file.filepath,
1573
- isNew: newTasks.has(task),
1574
- isDisabled: disabledTasks.has(task),
1575
- })
1576
- }
1577
- }
1578
-
1579
- const testSuiteResult = startTestsResponse[0].result
1580
-
1581
- if (testSuiteResult.errors?.length) { // Errors from root level hooks
1582
- testSuiteError = testSuiteResult.errors[0]
1583
- } else if (testSuiteResult.state === 'fail') { // Errors from `describe` level hooks
1584
- const suiteTasks = getTypeTasks(startTestsResponse[0].tasks, 'suite')
1585
- const failedSuites = suiteTasks.filter(task => task.result?.state === 'fail')
1586
- if (failedSuites.length && failedSuites[0].result?.errors?.length) {
1587
- testSuiteError = failedSuites[0].result.errors[0]
1588
- }
1589
- }
1590
-
1591
- if (testSuiteError) {
1592
- testSuiteCtx.error = testSuiteError
1593
- testSuiteErrorCh.runStores(testSuiteCtx, () => {})
1594
- }
1595
-
1596
- testSuiteFinishCh.publish({ status: testSuiteResult.state, onFinish, ...testSuiteCtx.currentStore })
1597
-
1598
- await onFinishPromise
1599
-
1600
- return startTestsResponse
1601
- })
1602
-
1603
- return vitestPackage
1604
- })