dd-trace 6.8.0 → 6.10.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 (209) hide show
  1. package/ci/diagnose.js +27 -25
  2. package/ci/init.js +6 -6
  3. package/ci/runbook.md +114 -20
  4. package/ci/test-optimization-validation/approval-artifacts.js +36 -3
  5. package/ci/test-optimization-validation/approval.js +78 -22
  6. package/ci/test-optimization-validation/blocker-category.js +24 -0
  7. package/ci/test-optimization-validation/ci-discovery.js +23 -17
  8. package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
  9. package/ci/test-optimization-validation/ci-remediation.js +1 -1
  10. package/ci/test-optimization-validation/cli.js +280 -52
  11. package/ci/test-optimization-validation/command-blocker.js +159 -20
  12. package/ci/test-optimization-validation/command-output-policy.js +9 -3
  13. package/ci/test-optimization-validation/command-runner.js +9 -7
  14. package/ci/test-optimization-validation/environment.js +4 -2
  15. package/ci/test-optimization-validation/executable.js +15 -13
  16. package/ci/test-optimization-validation/execution-lock.js +70 -0
  17. package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
  18. package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
  19. package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
  20. package/ci/test-optimization-validation/generated-files.js +38 -28
  21. package/ci/test-optimization-validation/generated-test-contract.js +8 -4
  22. package/ci/test-optimization-validation/generated-verifier.js +24 -17
  23. package/ci/test-optimization-validation/literal-glob.js +52 -0
  24. package/ci/test-optimization-validation/manifest-loader.js +3 -0
  25. package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
  26. package/ci/test-optimization-validation/manifest-schema.js +80 -17
  27. package/ci/test-optimization-validation/offline-fixtures.js +13 -4
  28. package/ci/test-optimization-validation/package-check.js +94 -0
  29. package/ci/test-optimization-validation/plan-writer.js +183 -10
  30. package/ci/test-optimization-validation/preflight-runner.js +110 -31
  31. package/ci/test-optimization-validation/project-build-artifact.js +31 -0
  32. package/ci/test-optimization-validation/redaction.js +18 -27
  33. package/ci/test-optimization-validation/report-writer.js +182 -48
  34. package/ci/test-optimization-validation/result-semantics.js +20 -3
  35. package/ci/test-optimization-validation/runner-command.js +40 -20
  36. package/ci/test-optimization-validation/runner-contract.js +79 -43
  37. package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
  38. package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
  39. package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
  40. package/ci/test-optimization-validation/static-diagnosis.js +10 -3
  41. package/ci/test-optimization-validation/test-output.js +2 -1
  42. package/ci/test-optimization-validation/validation-blocker.js +21 -0
  43. package/ci/vitest-no-worker-init-setup.mjs +140 -42
  44. package/index.d.ts +140 -1
  45. package/init.js +1 -17
  46. package/initialize.mjs +11 -0
  47. package/loader-hook.mjs +6 -4
  48. package/package.json +8 -5
  49. package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
  50. package/packages/datadog-esbuild/src/utils.js +16 -1
  51. package/packages/datadog-instrumentations/src/ai.js +61 -34
  52. package/packages/datadog-instrumentations/src/bluebird.js +6 -6
  53. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
  54. package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +111 -6
  55. package/packages/datadog-instrumentations/src/cucumber.js +124 -47
  56. package/packages/datadog-instrumentations/src/dns.js +21 -2
  57. package/packages/datadog-instrumentations/src/electron.js +1 -0
  58. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  59. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  60. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  61. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  62. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +629 -0
  63. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
  64. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +199 -0
  65. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  66. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  67. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +62 -0
  68. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
  69. package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
  70. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  71. package/packages/datadog-instrumentations/src/jest.js +980 -234
  72. package/packages/datadog-instrumentations/src/mocha/main.js +108 -10
  73. package/packages/datadog-instrumentations/src/mocha/utils.js +66 -50
  74. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
  75. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
  76. package/packages/datadog-instrumentations/src/mysql.js +46 -16
  77. package/packages/datadog-instrumentations/src/mysql2.js +119 -18
  78. package/packages/datadog-instrumentations/src/next.js +155 -23
  79. package/packages/datadog-instrumentations/src/openai.js +14 -0
  80. package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
  81. package/packages/datadog-instrumentations/src/pg.js +209 -11
  82. package/packages/datadog-instrumentations/src/playwright.js +173 -79
  83. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  84. package/packages/datadog-instrumentations/src/promise.js +3 -3
  85. package/packages/datadog-instrumentations/src/router.js +195 -19
  86. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +85 -13
  87. package/packages/datadog-instrumentations/src/vitest-main.js +528 -72
  88. package/packages/datadog-instrumentations/src/vitest-util.js +106 -12
  89. package/packages/datadog-instrumentations/src/vitest-worker.js +382 -40
  90. package/packages/datadog-instrumentations/src/webdriverio.js +212 -22
  91. package/packages/datadog-instrumentations/src/when.js +3 -3
  92. package/packages/datadog-instrumentations/src/ws.js +5 -1
  93. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
  94. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  95. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -5
  96. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
  97. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
  98. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  99. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +51 -47
  100. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  101. package/packages/datadog-plugin-electron/src/net.js +10 -4
  102. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
  103. package/packages/datadog-plugin-http/src/client.js +1 -1
  104. package/packages/datadog-plugin-http2/src/client.js +1 -1
  105. package/packages/datadog-plugin-jest/src/index.js +6 -22
  106. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
  107. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
  108. package/packages/datadog-plugin-kafkajs/src/producer.js +5 -3
  109. package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
  110. package/packages/datadog-plugin-mocha/src/index.js +832 -4
  111. package/packages/datadog-plugin-mysql/src/index.js +37 -0
  112. package/packages/datadog-plugin-next/src/index.js +51 -21
  113. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  114. package/packages/datadog-plugin-openai-agents/src/integration.js +278 -69
  115. package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
  116. package/packages/datadog-plugin-pg/src/index.js +66 -1
  117. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  118. package/packages/datadog-plugin-vitest/src/index.js +103 -27
  119. package/packages/datadog-plugin-ws/src/util.js +2 -1
  120. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  121. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  122. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
  123. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  124. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  125. package/packages/dd-trace/src/appsec/handlers/http-request.js +228 -0
  126. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  127. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  128. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  129. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  130. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  131. package/packages/dd-trace/src/appsec/index.js +48 -476
  132. package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
  133. package/packages/dd-trace/src/carrier.js +356 -0
  134. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  135. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  136. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
  137. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
  138. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
  139. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  140. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  141. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  142. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
  143. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  144. package/packages/dd-trace/src/config/generated-config-types.d.ts +5 -0
  145. package/packages/dd-trace/src/config/helper.js +2 -0
  146. package/packages/dd-trace/src/config/index.js +16 -5
  147. package/packages/dd-trace/src/config/remote_config.js +1 -1
  148. package/packages/dd-trace/src/config/supported-configurations.json +18 -0
  149. package/packages/dd-trace/src/datastreams/context.js +6 -1
  150. package/packages/dd-trace/src/datastreams/manager.js +5 -1
  151. package/packages/dd-trace/src/datastreams/pathway.js +23 -22
  152. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  153. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  154. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  155. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  156. package/packages/dd-trace/src/guardrails/log.js +1 -10
  157. package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
  158. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  159. package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
  160. package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
  161. package/packages/dd-trace/src/llmobs/index.js +34 -2
  162. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  163. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  164. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  165. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  166. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  167. package/packages/dd-trace/src/llmobs/sdk.js +149 -68
  168. package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
  169. package/packages/dd-trace/src/llmobs/tagger.js +96 -0
  170. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  171. package/packages/dd-trace/src/llmobs/util.js +78 -0
  172. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  173. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  174. package/packages/dd-trace/src/log/channels.js +1 -9
  175. package/packages/dd-trace/src/log/levels.js +12 -0
  176. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  177. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  178. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  179. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  180. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
  181. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  182. package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
  183. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
  184. package/packages/dd-trace/src/plugin_manager.js +2 -0
  185. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
  186. package/packages/dd-trace/src/plugins/index.js +1 -0
  187. package/packages/dd-trace/src/plugins/util/ci.js +6 -0
  188. package/packages/dd-trace/src/plugins/util/git.js +6 -4
  189. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
  190. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  191. package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +3 -62
  192. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  193. package/packages/dd-trace/src/plugins/util/test.js +397 -74
  194. package/packages/dd-trace/src/plugins/util/url.js +1 -1
  195. package/packages/dd-trace/src/plugins/util/web.js +76 -3
  196. package/packages/dd-trace/src/profiler.js +1 -1
  197. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  198. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  199. package/packages/dd-trace/src/proxy.js +1 -0
  200. package/packages/dd-trace/src/ritm.js +5 -0
  201. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  202. package/packages/dd-trace/src/sampling_rule.js +4 -2
  203. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  204. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  205. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  206. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  207. package/vendor/dist/pprof-format/index.js +1 -1
  208. package/version.js +10 -8
  209. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -1,24 +1,34 @@
1
1
  'use strict'
2
2
 
3
- const fs = require('node:fs')
4
3
  const path = require('node:path')
5
4
  const { fileURLToPath } = require('node:url')
6
5
  const { MessagePort } = require('node:worker_threads')
7
6
 
7
+ const satisfies = require('../../../vendor/dist/semifies')
8
+
8
9
  const shimmer = require('../../datadog-shimmer')
9
10
  const log = require('../../dd-trace/src/log')
11
+ const { EMPTY_EFD_RETRY_POLICY } = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
10
12
  const {
11
13
  VITEST_WORKER_TRACE_PAYLOAD_CODE,
14
+ VITEST_WORKER_COVERAGE_PAYLOAD_CODE,
12
15
  VITEST_WORKER_LOGS_PAYLOAD_CODE,
13
- getMaxEfdRetryCount,
16
+ VITEST_WORKER_TELEMETRY_PAYLOAD_CODE,
17
+ VITEST_WORKER_EFD_SUITE_ADMISSION_REQUEST_CODE,
18
+ VITEST_WORKER_EFD_SUITE_ADMISSION_RESPONSE_CODE,
14
19
  collectTestOptimizationSummariesFromTraces,
20
+ getIsFaultyEarlyFlakeDetection,
15
21
  logTestOptimizationSummary,
22
+ TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE,
16
23
  getTestOptimizationRequestResults,
17
24
  getTestSuitePath,
18
25
  isModifiedTest,
26
+ isMarkedAsUnskippable,
27
+ recordTestManagementExecution,
28
+ recordAttemptToFixExecution,
19
29
  } = require('../../dd-trace/src/plugins/util/test')
20
30
  const { getChannelPromise } = require('./helpers/channel')
21
- const { addHook } = require('./helpers/instrument')
31
+ const { addHook, channel } = require('./helpers/instrument')
22
32
  const noWorkerInit = require('./vitest-main-no-worker-init')
23
33
  const {
24
34
  testStartCh,
@@ -33,15 +43,18 @@ const {
33
43
  testSessionConfigurationCh,
34
44
  libraryConfigurationCh,
35
45
  knownTestsCh,
36
- isEarlyFlakeDetectionFaultyCh,
37
46
  testManagementTestsCh,
38
47
  modifiedFilesCh,
39
48
  workerReportTraceCh,
49
+ workerReportCoverageCh,
40
50
  workerReportLogsCh,
51
+ workerReportTelemetryCh,
41
52
  codeCoverageReportCh,
53
+ realpath,
42
54
  findExportByName,
43
55
  getTypeTasks,
44
56
  getWorkspaceProject,
57
+ parseProvidedContextValue,
45
58
  setProvidedContext,
46
59
  getVitestTestProperties,
47
60
  } = require('./vitest-util')
@@ -52,33 +65,51 @@ const workerProcesses = new WeakSet()
52
65
  const mainProcessSetupStates = new WeakMap()
53
66
  const coverageWrappedProviders = new WeakSet()
54
67
  const finishWrappedContexts = new WeakSet()
68
+ const runFilesWrappedPrototypes = new WeakSet()
69
+ const activeRunFilesContexts = new WeakSet()
55
70
  let isFlakyTestRetriesEnabled = false
56
71
  let flakyTestRetriesCount = 0
57
72
  let isEarlyFlakeDetectionEnabled = false
58
- let earlyFlakeDetectionNumRetries = 0
59
- let earlyFlakeDetectionSlowTestRetries = {}
73
+ let earlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
74
+ let earlyFlakeDetectionFaultyThreshold = 0
60
75
  let isEarlyFlakeDetectionFaulty = false
61
76
  let isKnownTestsEnabled = false
62
77
  let isTestManagementTestsEnabled = false
63
78
  let isImpactedTestsEnabled = false
79
+ let isCodeCoverageEnabled = false
80
+ let isSuitesSkippingEnabled = false
81
+ let isSessionCodeCoverageEnabled = false
82
+ let isSessionSuitesSkippingEnabled = false
64
83
  let testManagementAttemptToFixRetries = 0
65
84
  let isDiEnabled = false
66
85
  let testCodeCoverageLinesTotal
67
86
  let coverageRootDir
68
87
  let requestErrorTags = {}
69
88
  let isSessionStarted = false
89
+ let isTestImpactAnalysisDisabled = false
70
90
  let isVitestNoWorkerInitActive = false
71
91
  let isVitestBrowserModeActive = false
72
92
  let vitestPool = null
73
93
  let isMessagePortWrapped = false
94
+ let skippableSuites = []
95
+ let skippedSuites = []
96
+ let unskippableSuites = {}
97
+ let forcedToRunSuites = {}
98
+ let hasUnskippableSuites = false
99
+ let hasForcedToRunSuites = false
100
+ let areAllSuitesSkipped = false
101
+ let hasLoggedAllTestsSkippedMessage = false
102
+ let hasRunnableSuites = false
103
+ let hasSelectedSuites = false
104
+ let itrCorrelationId
105
+ let tiaRepositoryRoot = process.cwd()
106
+ let activeNoWorkerInitState
107
+ let isEfdSuiteAdmissionEnabled = false
108
+ let maximumSuitesWithNewTests = 0
109
+ const suitesWithNewTests = new Set()
74
110
  const tinyPoolClassWrappers = new WeakMap()
75
-
76
- function getConfiguredEfdRetryCount (slowTestRetries, fallbackRetryCount) {
77
- if (!slowTestRetries || !Object.keys(slowTestRetries).length) {
78
- return fallbackRetryCount
79
- }
80
- return getMaxEfdRetryCount(slowTestRetries)
81
- }
111
+ const itrSkippedSuitesCh = channel('ci:vitest:itr:skipped-suites')
112
+ const skippableSuitesCh = channel('ci:vitest:test-suite:skippable')
82
113
 
83
114
  function getTestCommand () {
84
115
  return `vitest ${process.argv.slice(2).join(' ')}`
@@ -203,17 +234,261 @@ function getNormalizedTestSuitePath (testFilepath, repositoryRoot) {
203
234
  }
204
235
 
205
236
  /**
206
- * Resolves a path without failing Test Optimization when the path is unavailable.
237
+ * Resets suite-level EFD admission state between Vitest runs.
207
238
  *
208
- * @param {string} filepath
209
- * @returns {string}
239
+ * @returns {void}
210
240
  */
211
- function realpath (filepath) {
212
- try {
213
- return fs.realpathSync(filepath)
214
- } catch {
215
- return filepath
241
+ function resetEfdSuiteTracker () {
242
+ activeNoWorkerInitState = undefined
243
+ isEfdSuiteAdmissionEnabled = false
244
+ maximumSuitesWithNewTests = 0
245
+ suitesWithNewTests.clear()
246
+ }
247
+
248
+ /**
249
+ * Returns whether a Vitest pool has a transport for runtime EFD suite admission.
250
+ *
251
+ * @param {string|undefined} pool
252
+ * @returns {boolean}
253
+ */
254
+ function isEfdSuiteAdmissionPool (pool) {
255
+ return pool === undefined || pool === 'forks' || pool === 'threads' || pool === 'browser'
256
+ }
257
+
258
+ /**
259
+ * Returns whether Vitest exposes the worker transports used for runtime EFD suite admission.
260
+ *
261
+ * @param {string} frameworkVersion
262
+ * @param {object[]|undefined} testSpecifications
263
+ * @param {object} ctx
264
+ * @returns {boolean}
265
+ */
266
+ function supportsEfdSuiteAdmission (frameworkVersion, testSpecifications, ctx) {
267
+ if (!satisfies(frameworkVersion, '>=4.0.0')) return false
268
+ const defaultPool = ctx?.config?.pool
269
+ if (!Array.isArray(testSpecifications) || testSpecifications.length === 0) {
270
+ return isEfdSuiteAdmissionPool(defaultPool)
271
+ }
272
+
273
+ for (const testSpecification of testSpecifications) {
274
+ const pool = getTestSpecificationPool(testSpecification) || defaultPool
275
+ if (!isEfdSuiteAdmissionPool(pool)) return false
276
+ }
277
+ return true
278
+ }
279
+
280
+ /**
281
+ * Configures the EFD threshold against the unique suites selected by Vitest.
282
+ *
283
+ * @param {string[]} testFilepaths
284
+ * @param {string} repositoryRoot
285
+ * @returns {void}
286
+ */
287
+ function configureEfdSuiteTracker (testFilepaths, repositoryRoot) {
288
+ const testSuites = new Set()
289
+ for (const testFilepath of testFilepaths) {
290
+ testSuites.add(getNormalizedTestSuitePath(testFilepath, repositoryRoot))
291
+ }
292
+
293
+ maximumSuitesWithNewTests = Math.floor(Math.max(
294
+ earlyFlakeDetectionFaultyThreshold,
295
+ testSuites.size * earlyFlakeDetectionFaultyThreshold / 100
296
+ ))
297
+ isEfdSuiteAdmissionEnabled = true
298
+ }
299
+
300
+ /**
301
+ * Returns whether a suite may schedule EFD retries and records runnable suites with new tests.
302
+ *
303
+ * @param {string} testSuite
304
+ * @param {boolean} hasNewTest
305
+ * @returns {boolean}
306
+ */
307
+ function reserveEarlyFlakeDetectionSuite (testSuite, hasNewTest) {
308
+ if (!isEfdSuiteAdmissionEnabled || typeof testSuite !== 'string') return false
309
+ if (isEarlyFlakeDetectionFaulty) return false
310
+
311
+ if (hasNewTest) {
312
+ suitesWithNewTests.add(testSuite)
313
+ if (suitesWithNewTests.size > maximumSuitesWithNewTests) {
314
+ isEarlyFlakeDetectionEnabled = false
315
+ isEarlyFlakeDetectionFaulty = true
316
+ if (activeNoWorkerInitState) {
317
+ activeNoWorkerInitState.isEarlyFlakeDetectionEnabled = false
318
+ activeNoWorkerInitState.isEarlyFlakeDetectionFaulty = true
319
+ }
320
+ log.warn(
321
+ 'Early Flake Detection retries are disabled because the number of suites with new tests is too high.'
322
+ )
323
+ return false
324
+ }
216
325
  }
326
+
327
+ return true
328
+ }
329
+
330
+ function resetSuiteSkippingRunState () {
331
+ skippableSuites = []
332
+ resetAppliedSuiteSkippingState()
333
+ itrCorrelationId = undefined
334
+ }
335
+
336
+ function resetAppliedSuiteSkippingState () {
337
+ unskippableSuites = {}
338
+ forcedToRunSuites = {}
339
+ }
340
+
341
+ function resetSessionSuiteSkippingState () {
342
+ skippedSuites = []
343
+ hasUnskippableSuites = false
344
+ hasForcedToRunSuites = false
345
+ areAllSuitesSkipped = false
346
+ hasLoggedAllTestsSkippedMessage = false
347
+ hasRunnableSuites = false
348
+ hasSelectedSuites = false
349
+ isSessionCodeCoverageEnabled = false
350
+ isSessionSuitesSkippingEnabled = false
351
+ isTestImpactAnalysisDisabled = false
352
+ }
353
+
354
+ /**
355
+ * Returns the file path from Vitest's version-dependent test specification shape.
356
+ *
357
+ * @param {unknown} testSpecification
358
+ * @returns {string|undefined}
359
+ */
360
+ function getTestSpecificationFilepath (testSpecification) {
361
+ const testFile = Array.isArray(testSpecification) ? testSpecification[1] : testSpecification
362
+ return testFile?.moduleId || testFile?.filepath || (typeof testFile === 'string' ? testFile : undefined)
363
+ }
364
+
365
+ /**
366
+ * Returns the normalized suite path and absolute path for a Vitest test specification.
367
+ *
368
+ * @param {unknown} testSpecification
369
+ * @returns {{ testSuite: string, testSuiteAbsolutePath: string }|undefined}
370
+ */
371
+ function getTestSpecificationSuite (testSpecification) {
372
+ const testFilepath = getTestSpecificationFilepath(testSpecification)
373
+ if (!testFilepath) return
374
+
375
+ const testSuiteAbsolutePath = path.isAbsolute(testFilepath)
376
+ ? realpath(testFilepath)
377
+ : realpath(path.join(tiaRepositoryRoot, testFilepath))
378
+
379
+ return {
380
+ testSuite: getTestSuitePath(testSuiteAbsolutePath, tiaRepositoryRoot),
381
+ testSuiteAbsolutePath,
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Returns suites that Vitest selected for typechecking.
387
+ *
388
+ * @param {unknown[]} testSpecifications
389
+ * @returns {Set<string>}
390
+ */
391
+ function getTypecheckTestSuites (testSpecifications) {
392
+ const typecheckTestSuites = new Set()
393
+
394
+ for (const testSpecification of testSpecifications) {
395
+ if (getTestSpecificationPool(testSpecification) !== 'typescript') continue
396
+
397
+ const testSuite = getTestSpecificationSuite(testSpecification)?.testSuite
398
+ if (testSuite) {
399
+ typecheckTestSuites.add(testSuite)
400
+ }
401
+ }
402
+
403
+ return typecheckTestSuites
404
+ }
405
+
406
+ /**
407
+ * Removes suites selected by TIA and propagates suite metadata to Vitest workers.
408
+ *
409
+ * @param {object} ctx
410
+ * @param {unknown[]} testSpecifications
411
+ * @param {string} frameworkVersion
412
+ * @returns {unknown[]}
413
+ */
414
+ function applySuiteSkipping (ctx, testSpecifications, frameworkVersion) {
415
+ if (!Array.isArray(testSpecifications)) {
416
+ setProvidedContext(ctx, {
417
+ _ddIsCodeCoverageEnabled: isCodeCoverageEnabled,
418
+ }, 'Could not send TIA configuration to workers.')
419
+ return testSpecifications
420
+ }
421
+
422
+ if (testSpecifications.length > 0) {
423
+ hasSelectedSuites = true
424
+ }
425
+ if (!isSuitesSkippingEnabled) {
426
+ if (testSpecifications.length > 0) {
427
+ hasRunnableSuites = true
428
+ }
429
+ areAllSuitesSkipped = hasSelectedSuites && !hasRunnableSuites
430
+ setProvidedContext(ctx, {
431
+ _ddIsCodeCoverageEnabled: isCodeCoverageEnabled,
432
+ }, 'Could not send TIA configuration to workers.')
433
+ return testSpecifications
434
+ }
435
+
436
+ resetAppliedSuiteSkippingState()
437
+ const skippableSuiteSet = new Set(skippableSuites.map(testSuite => testSuite.replaceAll('\\', '/')))
438
+ const typecheckTestSuites = getTypecheckTestSuites(testSpecifications)
439
+ const currentSkippedSuites = []
440
+ const testSpecificationsToRun = []
441
+
442
+ for (const testSpecification of testSpecifications) {
443
+ const testSpecificationSuite = getTestSpecificationSuite(testSpecification)
444
+ if (!testSpecificationSuite) {
445
+ testSpecificationsToRun.push(testSpecification)
446
+ continue
447
+ }
448
+
449
+ const { testSuite, testSuiteAbsolutePath } = testSpecificationSuite
450
+ const shouldSkip = !typecheckTestSuites.has(testSuite) && skippableSuiteSet.has(testSuite)
451
+ const isUnskippable = isMarkedAsUnskippable({ path: testSuiteAbsolutePath })
452
+
453
+ if (isUnskippable) {
454
+ unskippableSuites[testSuite] = true
455
+ hasUnskippableSuites = true
456
+ if (shouldSkip) {
457
+ forcedToRunSuites[testSuite] = true
458
+ hasForcedToRunSuites = true
459
+ }
460
+ }
461
+
462
+ if (shouldSkip && !isUnskippable) {
463
+ skippedSuites.push(testSuite)
464
+ currentSkippedSuites.push(testSuite)
465
+ } else {
466
+ testSpecificationsToRun.push(testSpecification)
467
+ }
468
+ }
469
+
470
+ setProvidedContext(ctx, {
471
+ _ddIsCodeCoverageEnabled: isCodeCoverageEnabled,
472
+ _ddItrCorrelationId: itrCorrelationId,
473
+ _ddUnskippableSuites: unskippableSuites,
474
+ _ddForcedToRunSuites: forcedToRunSuites,
475
+ }, 'Could not send TIA configuration to workers.')
476
+
477
+ if (currentSkippedSuites.length) {
478
+ itrSkippedSuitesCh.publish({ skippedSuites: currentSkippedSuites, frameworkVersion })
479
+ }
480
+ if (testSpecificationsToRun.length > 0) {
481
+ hasRunnableSuites = true
482
+ }
483
+ areAllSuitesSkipped = hasSelectedSuites && !hasRunnableSuites
484
+ if (testSpecifications.length > 0 && testSpecificationsToRun.length === 0) {
485
+ const config = safeConfig(ctx)
486
+ if (config) {
487
+ config.passWithNoTests = true
488
+ }
489
+ }
490
+
491
+ return testSpecificationsToRun
217
492
  }
218
493
 
219
494
  /**
@@ -371,13 +646,15 @@ function resetLibraryConfig () {
371
646
  isFlakyTestRetriesEnabled = false
372
647
  flakyTestRetriesCount = 0
373
648
  isEarlyFlakeDetectionEnabled = false
374
- earlyFlakeDetectionNumRetries = 0
375
- earlyFlakeDetectionSlowTestRetries = {}
649
+ earlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
650
+ earlyFlakeDetectionFaultyThreshold = 0
376
651
  isEarlyFlakeDetectionFaulty = false
377
652
  isDiEnabled = false
378
653
  isKnownTestsEnabled = false
379
654
  isTestManagementTestsEnabled = false
380
655
  isImpactedTestsEnabled = false
656
+ isCodeCoverageEnabled = false
657
+ isSuitesSkippingEnabled = false
381
658
  testManagementAttemptToFixRetries = 0
382
659
  }
383
660
 
@@ -385,22 +662,24 @@ function applyLibraryConfig (libraryConfig) {
385
662
  isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
386
663
  flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
387
664
  isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
388
- earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
389
- earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
665
+ earlyFlakeDetectionRetryPolicy = libraryConfig.earlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
666
+ earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold ?? 0
390
667
  isEarlyFlakeDetectionFaulty = false
391
668
  isDiEnabled = libraryConfig.isDiEnabled
392
669
  isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
393
670
  isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
394
671
  testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
395
672
  isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
673
+ isCodeCoverageEnabled = libraryConfig.isItrEnabled && libraryConfig.isCodeCoverageEnabled
674
+ isSuitesSkippingEnabled = libraryConfig.isItrEnabled && libraryConfig.isSuitesSkippingEnabled
396
675
  }
397
676
 
398
677
  function resetMainProcessProvidedContext (ctx) {
399
678
  setProvidedContext(ctx, {
400
679
  _ddIsDiEnabled: false,
401
680
  _ddIsEarlyFlakeDetectionEnabled: false,
402
- _ddEarlyFlakeDetectionNumRetries: 0,
403
- _ddEarlyFlakeDetectionSlowTestRetries: {},
681
+ _ddEarlyFlakeDetectionRetryPolicy: EMPTY_EFD_RETRY_POLICY,
682
+ _ddIsEfdSuiteAdmissionEnabled: false,
404
683
  _ddIsFlakyTestRetriesEnabled: false,
405
684
  _ddFlakyTestRetriesCount: 0,
406
685
  _ddFlakyTestRetriesIncludesUnnamedProject: false,
@@ -410,6 +689,10 @@ function resetMainProcessProvidedContext (ctx) {
410
689
  _ddIsTestManagementTestsEnabled: false,
411
690
  _ddTestManagementAttemptToFixRetries: 0,
412
691
  _ddTestPropertiesByFilepath: {},
692
+ _ddIsCodeCoverageEnabled: false,
693
+ _ddItrCorrelationId: undefined,
694
+ _ddUnskippableSuites: {},
695
+ _ddForcedToRunSuites: {},
413
696
  }, 'Could not reset Test Optimization context for workers.')
414
697
  }
415
698
 
@@ -432,13 +715,18 @@ async function runMainProcessSetup (
432
715
  frameworkVersion,
433
716
  testSpecifications,
434
717
  shouldInstallNoWorkerInit,
435
- shouldInstallBrowserReporter
718
+ shouldInstallBrowserReporter,
719
+ disableTestImpactAnalysis
436
720
  ) {
437
721
  if (!testSessionFinishCh.hasSubscribers) {
438
722
  return
439
723
  }
440
724
 
725
+ resetSuiteSkippingRunState()
726
+ resetEfdSuiteTracker()
441
727
  isVitestBrowserModeActive ||= shouldInstallBrowserReporter
728
+ isTestImpactAnalysisDisabled =
729
+ disableTestImpactAnalysis || shouldInstallNoWorkerInit || isVitestBrowserModeActive
442
730
  let repositoryRoot = process.cwd()
443
731
  let testSessionConfiguration
444
732
  let testFilepaths
@@ -464,6 +752,7 @@ async function runMainProcessSetup (
464
752
  requestErrorTags: receivedRequestErrorTags = {},
465
753
  } = await getChannelPromise(libraryConfigurationCh, {
466
754
  frameworkVersion,
755
+ disableTestImpactAnalysis: isTestImpactAnalysisDisabled,
467
756
  isVitestNoWorkerInitActive: shouldInstallNoWorkerInit || isVitestBrowserModeActive,
468
757
  })
469
758
  requestErrorTags = receivedRequestErrorTags
@@ -476,6 +765,8 @@ async function runMainProcessSetup (
476
765
  requestErrorTags = {}
477
766
  resetLibraryConfig()
478
767
  }
768
+ isSessionCodeCoverageEnabled ||= isCodeCoverageEnabled
769
+ isSessionSuitesSkippingEnabled ||= isSuitesSkippingEnabled
479
770
 
480
771
  resetMainProcessProvidedContext(ctx)
481
772
 
@@ -503,19 +794,33 @@ async function runMainProcessSetup (
503
794
  }, 'Could not send test session configuration to workers.')
504
795
  }
505
796
  }
797
+ tiaRepositoryRoot = realpath(repositoryRoot)
506
798
 
507
799
  const {
508
800
  knownTestsResponse,
801
+ skippableSuitesResponse,
509
802
  testManagementTestsResponse,
510
803
  } = await getTestOptimizationRequestResults({
511
804
  isKnownTestsEnabled,
805
+ isSuitesSkippingEnabled,
512
806
  isTestManagementTestsEnabled,
513
807
  getKnownTests: () => getChannelPromise(knownTestsCh),
808
+ getSkippableSuites: () => getChannelPromise(skippableSuitesCh),
514
809
  getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
515
810
  })
516
811
  mergeRequestErrorTags(knownTestsResponse)
812
+ mergeRequestErrorTags(skippableSuitesResponse)
517
813
  mergeRequestErrorTags(testManagementTestsResponse)
518
814
 
815
+ if (isSuitesSkippingEnabled) {
816
+ const currentSkippableSuitesResponse = skippableSuitesResponse ||
817
+ await getChannelPromise(skippableSuitesCh)
818
+ if (!currentSkippableSuitesResponse?.err) {
819
+ skippableSuites = currentSkippableSuitesResponse.skippableSuites || []
820
+ itrCorrelationId = currentSkippableSuitesResponse.itrCorrelationId
821
+ }
822
+ }
823
+
519
824
  const flakyTestRetriesConfiguration = configureFlakyTestRetries(ctx, testSpecifications)
520
825
  if (flakyTestRetriesConfiguration) {
521
826
  setProvidedContext(ctx, {
@@ -535,26 +840,30 @@ async function runMainProcessSetup (
535
840
  const currentTestFilepaths = await getCurrentTestFilepaths()
536
841
 
537
842
  if (isValidKnownTests(knownTests)) {
538
- isEarlyFlakeDetectionFaultyCh.publish({
539
- knownTests: knownTests.vitest,
540
- testFilepaths: currentTestFilepaths,
541
- onDone: (isFaulty) => {
542
- isEarlyFlakeDetectionFaulty = isFaulty
543
- },
544
- })
545
- if (isEarlyFlakeDetectionFaulty) {
546
- isEarlyFlakeDetectionEnabled = false
547
- log.warn('New test detection is disabled because the number of new tests is too high.')
843
+ if (!shouldInstallNoWorkerInit && supportsEfdSuiteAdmission(frameworkVersion, testSpecifications, ctx)) {
844
+ configureEfdSuiteTracker(currentTestFilepaths, repositoryRoot)
548
845
  } else {
846
+ const projectSuites = currentTestFilepaths.map(testFilepath => getTestSuitePath(testFilepath, repositoryRoot))
847
+ isEarlyFlakeDetectionFaulty = getIsFaultyEarlyFlakeDetection(
848
+ projectSuites,
849
+ knownTests.vitest,
850
+ earlyFlakeDetectionFaultyThreshold
851
+ )
852
+ if (isEarlyFlakeDetectionFaulty) {
853
+ isEarlyFlakeDetectionEnabled = false
854
+ log.warn('New test detection is disabled because the number of new tests is too high.')
855
+ }
856
+ }
857
+
858
+ if (!isEarlyFlakeDetectionFaulty) {
549
859
  knownTestsBySuite = knownTests.vitest
550
860
  shouldSendTestProperties = true
551
861
  if (!shouldInstallNoWorkerInit) {
552
862
  setProvidedContext(ctx, {
863
+ _ddIsEfdSuiteAdmissionEnabled: isEfdSuiteAdmissionEnabled,
553
864
  _ddIsKnownTestsEnabled: isKnownTestsEnabled,
554
865
  _ddIsEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled,
555
- _ddEarlyFlakeDetectionNumRetries:
556
- getConfiguredEfdRetryCount(earlyFlakeDetectionSlowTestRetries, earlyFlakeDetectionNumRetries),
557
- _ddEarlyFlakeDetectionSlowTestRetries: earlyFlakeDetectionSlowTestRetries,
866
+ _ddEarlyFlakeDetectionRetryPolicy: earlyFlakeDetectionRetryPolicy,
558
867
  }, 'Could not send known tests to workers so Early Flake Detection will not work.')
559
868
  }
560
869
  }
@@ -626,6 +935,7 @@ async function runMainProcessSetup (
626
935
  const reporterTestSpecifications = shouldInstallNoWorkerInit
627
936
  ? testSpecifications
628
937
  : getBrowserTestSpecifications(testSpecifications)
938
+ activeNoWorkerInitState = getNoWorkerInitState()
629
939
  noWorkerInit.configure(ctx, frameworkVersion, reporterTestSpecifications, {
630
940
  knownTests,
631
941
  knownTestsBySuite,
@@ -637,9 +947,9 @@ async function runMainProcessSetup (
637
947
  testPropertiesByFilepath: testPropertiesByFilepath || {},
638
948
  testSessionConfiguration,
639
949
  }, {
640
- getConfiguredEfdRetryCount,
950
+ reserveEarlyFlakeDetectionSuite,
641
951
  shouldReportTestModule: shouldInstallBrowserReporter ? isBrowserTestModule : undefined,
642
- state: getNoWorkerInitState(),
952
+ state: activeNoWorkerInitState,
643
953
  })
644
954
  }
645
955
 
@@ -651,8 +961,8 @@ async function runMainProcessSetup (
651
961
  function getNoWorkerInitState () {
652
962
  return {
653
963
  attemptToFixExecutions,
654
- earlyFlakeDetectionNumRetries,
655
- earlyFlakeDetectionSlowTestRetries,
964
+ earlyFlakeDetectionRetryPolicy,
965
+ isEfdSuiteAdmissionEnabled,
656
966
  isEarlyFlakeDetectionEnabled,
657
967
  isEarlyFlakeDetectionFaulty,
658
968
  isFlakyTestRetriesEnabled,
@@ -663,10 +973,20 @@ function getNoWorkerInitState () {
663
973
  }
664
974
  }
665
975
 
666
- function ensureMainProcessSetup (ctx, frameworkVersion, testSpecifications, shouldDeactivateOnFallback = false) {
976
+ function ensureMainProcessSetup (
977
+ ctx,
978
+ frameworkVersion,
979
+ testSpecifications,
980
+ shouldDeactivateOnFallback = false,
981
+ forceDisableTestImpactAnalysis = false
982
+ ) {
667
983
  const shouldInstallNoWorkerInit = shouldUseNoWorkerInit(ctx, frameworkVersion, testSpecifications)
668
984
  const shouldInstallBrowserReporter = shouldUseBrowserReporter(frameworkVersion, testSpecifications)
669
985
  const shouldInstallMainReporter = shouldInstallNoWorkerInit || shouldInstallBrowserReporter
986
+ const disableTestImpactAnalysis =
987
+ forceDisableTestImpactAnalysis ||
988
+ safeConfig(ctx)?.watch === true ||
989
+ hasOnlyTypecheckTestSpecifications(testSpecifications)
670
990
  const specificationsKey = getTestSpecificationsKey(testSpecifications)
671
991
  let setupState = mainProcessSetupStates.get(ctx)
672
992
  if (shouldDeactivateOnFallback && setupState?.shouldInstallMainReporter && !shouldInstallMainReporter) {
@@ -676,7 +996,8 @@ function ensureMainProcessSetup (ctx, frameworkVersion, testSpecifications, shou
676
996
  !setupState ||
677
997
  setupState.specificationsKey !== specificationsKey ||
678
998
  setupState.shouldInstallNoWorkerInit !== shouldInstallNoWorkerInit ||
679
- setupState.shouldInstallBrowserReporter !== shouldInstallBrowserReporter
999
+ setupState.shouldInstallBrowserReporter !== shouldInstallBrowserReporter ||
1000
+ setupState.disableTestImpactAnalysis !== disableTestImpactAnalysis
680
1001
  ) {
681
1002
  setupState = {
682
1003
  setupPromise: runMainProcessSetup(
@@ -684,8 +1005,10 @@ function ensureMainProcessSetup (ctx, frameworkVersion, testSpecifications, shou
684
1005
  frameworkVersion,
685
1006
  testSpecifications,
686
1007
  shouldInstallNoWorkerInit,
687
- shouldInstallBrowserReporter
1008
+ shouldInstallBrowserReporter,
1009
+ disableTestImpactAnalysis
688
1010
  ),
1011
+ disableTestImpactAnalysis,
689
1012
  shouldInstallBrowserReporter,
690
1013
  shouldInstallMainReporter,
691
1014
  shouldInstallNoWorkerInit,
@@ -850,7 +1173,11 @@ function safeWorkspaceProject (ctx) {
850
1173
 
851
1174
  function getSortWrapper (sort, frameworkVersion) {
852
1175
  return async function () {
853
- await ensureMainProcessSetup(this.ctx, frameworkVersion, arguments[0])
1176
+ if (!activeRunFilesContexts.has(this.ctx)) {
1177
+ const testSpecifications = arguments[0]
1178
+ await ensureMainProcessSetup(this.ctx, frameworkVersion, testSpecifications)
1179
+ arguments[0] = applySuiteSkipping(this.ctx, testSpecifications, frameworkVersion)
1180
+ }
854
1181
  return sort.apply(this, arguments)
855
1182
  }
856
1183
  }
@@ -874,18 +1201,30 @@ function getFinishWrapper (exitOrClose) {
874
1201
  }
875
1202
 
876
1203
  const flushPromise = getChannelPromise(testSessionFinishCh, {
877
- status: getSessionStatus(this.state),
1204
+ status: areAllSuitesSkipped ? 'skip' : getSessionStatus(this.state),
878
1205
  testCodeCoverageLinesTotal,
879
1206
  error,
880
1207
  isEarlyFlakeDetectionEnabled,
881
1208
  isEarlyFlakeDetectionFaulty,
882
1209
  isTestManagementTestsEnabled,
1210
+ isCodeCoverageEnabled: isSessionCodeCoverageEnabled,
1211
+ isSuitesSkippingEnabled: isSessionSuitesSkippingEnabled,
1212
+ isSuitesSkipped: skippedSuites.length > 0,
1213
+ numSkippedSuites: skippedSuites.length,
1214
+ hasUnskippableSuites,
1215
+ hasForcedToRunSuites,
883
1216
  requestErrorTags,
884
1217
  vitestPool,
885
1218
  isVitestNoWorkerInitActive: isVitestNoWorkerInitActive || isVitestBrowserModeActive,
886
1219
  })
887
1220
 
888
- logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
1221
+ const shouldLogAllTestsSkippedMessage = areAllSuitesSkipped && !hasLoggedAllTestsSkippedMessage
1222
+ hasLoggedAllTestsSkippedMessage ||= shouldLogAllTestsSkippedMessage
1223
+ logTestOptimizationSummary({
1224
+ attemptToFixExecutions,
1225
+ newTestsWithDynamicNames,
1226
+ extraSections: shouldLogAllTestsSkippedMessage ? [TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE] : [],
1227
+ })
889
1228
 
890
1229
  await flushPromise
891
1230
 
@@ -905,6 +1244,7 @@ function getCliOrStartVitestWrapper (frameworkVersion) {
905
1244
  return oldCliOrStartVitest.apply(this, args)
906
1245
  }
907
1246
  isSessionStarted = true
1247
+ resetSessionSuiteSkippingState()
908
1248
  testSessionStartCh.publish({ command: getTestCommand(), frameworkVersion })
909
1249
  return oldCliOrStartVitest.apply(this, args)
910
1250
  }
@@ -949,8 +1289,24 @@ function getTestSpecificationOptions (testSpecification) {
949
1289
  function getTestSpecificationPool (testSpecification) {
950
1290
  const options = getTestSpecificationOptions(testSpecification)
951
1291
  const project = getTestSpecificationProject(testSpecification)
952
- return options?.pool || project?.config?.pool || project?.serializedConfig?.pool || project?.pool ||
953
- testSpecification?.pool
1292
+ const filePool = Array.isArray(testSpecification) ? testSpecification[1]?.pool : undefined
1293
+ return options?.pool || filePool || project?.config?.pool || project?.serializedConfig?.pool ||
1294
+ project?.pool || testSpecification?.pool
1295
+ }
1296
+
1297
+ /**
1298
+ * Detect whether Vitest selected only TypeScript typecheck specifications.
1299
+ *
1300
+ * @param {unknown} testSpecifications
1301
+ * @returns {boolean}
1302
+ */
1303
+ function hasOnlyTypecheckTestSpecifications (testSpecifications) {
1304
+ if (!Array.isArray(testSpecifications) || testSpecifications.length === 0) return false
1305
+
1306
+ for (const testSpecification of testSpecifications) {
1307
+ if (getTestSpecificationPool(testSpecification) !== 'typescript') return false
1308
+ }
1309
+ return true
954
1310
  }
955
1311
 
956
1312
  function isBrowserTestSpecification (testSpecification) {
@@ -1003,14 +1359,26 @@ function markVitestWorkerEnv (ctx, testSpecifications, shouldSkipWorkerInit = fa
1003
1359
  }
1004
1360
 
1005
1361
  function wrapVitestRunFiles (Vitest, frameworkVersion) {
1006
- if (!Vitest?.prototype?.runFiles) {
1362
+ if (!Vitest?.prototype?.runFiles || runFilesWrappedPrototypes.has(Vitest.prototype)) {
1007
1363
  return
1008
1364
  }
1365
+ runFilesWrappedPrototypes.add(Vitest.prototype)
1009
1366
 
1010
1367
  shimmer.wrap(Vitest.prototype, 'runFiles', runFiles => async function (testSpecifications) {
1368
+ if (activeRunFilesContexts.has(this)) {
1369
+ return runFiles.apply(this, arguments)
1370
+ }
1371
+
1011
1372
  const shouldSkipWorkerInit = await ensureMainProcessSetup(this, frameworkVersion, testSpecifications, true)
1012
- markVitestWorkerEnv(this, testSpecifications, shouldSkipWorkerInit)
1013
- return runFiles.apply(this, arguments)
1373
+ const testSpecificationsToRun = applySuiteSkipping(this, testSpecifications, frameworkVersion)
1374
+ arguments[0] = testSpecificationsToRun
1375
+ markVitestWorkerEnv(this, testSpecificationsToRun, shouldSkipWorkerInit)
1376
+ activeRunFilesContexts.add(this)
1377
+ try {
1378
+ return await runFiles.apply(this, arguments)
1379
+ } finally {
1380
+ activeRunFilesContexts.delete(this)
1381
+ }
1014
1382
  })
1015
1383
 
1016
1384
  if (Vitest.prototype.collectTests) {
@@ -1078,7 +1446,7 @@ function getMainProcessProvidedContext (ctx) {
1078
1446
  const providedContext = workspaceProject.getProvidedContext?.() || workspaceProject._provided || {}
1079
1447
 
1080
1448
  return {
1081
- testPropertiesByFilepath: providedContext._ddTestPropertiesByFilepath || {},
1449
+ testPropertiesByFilepath: parseProvidedContextValue(providedContext._ddTestPropertiesByFilepath) || {},
1082
1450
  }
1083
1451
  } catch {
1084
1452
  return {
@@ -1219,13 +1587,34 @@ function reportTypecheckTest (task, testSuiteAbsolutePath, providedContext) {
1219
1587
  const isModified = testProperties.isModified === true
1220
1588
  const isSkippedByTestManagement = !isAttemptToFix && isDisabled
1221
1589
  const status = getTypecheckTaskStatus(task)
1590
+ const summaryStatus = isSkippedByTestManagement ? 'skip' : status
1591
+
1592
+ recordTestManagementExecution({
1593
+ testSuite: testProperties.testSuite,
1594
+ testName,
1595
+ status: summaryStatus,
1596
+ isAttemptToFix,
1597
+ isDisabled,
1598
+ isQuarantined,
1599
+ })
1600
+ if (isAttemptToFix) {
1601
+ recordAttemptToFixExecution(attemptToFixExecutions, {
1602
+ testSuite: testProperties.testSuite,
1603
+ testName,
1604
+ status: summaryStatus,
1605
+ isDisabled,
1606
+ isQuarantined,
1607
+ })
1608
+ }
1222
1609
 
1223
1610
  if (status === 'skip' || isSkippedByTestManagement) {
1224
1611
  testSkipCh.publish({
1225
1612
  testName,
1226
1613
  testSuiteAbsolutePath,
1227
1614
  isNew: testProperties.isNew,
1615
+ isAttemptToFix,
1228
1616
  isDisabled,
1617
+ isQuarantined,
1229
1618
  })
1230
1619
  updateTypecheckTaskResultForTestManagement(task, status, { isAttemptToFix, isDisabled, isQuarantined })
1231
1620
  return
@@ -1272,6 +1661,7 @@ async function reportTypecheckFile (file, sessionConfiguration, frameworkVersion
1272
1661
  testCommand: sessionConfiguration.testCommand,
1273
1662
  repositoryRoot: sessionConfiguration.repositoryRoot,
1274
1663
  codeOwnersEntries: sessionConfiguration.codeOwnersEntries,
1664
+ disableTestImpactAnalysis: isTestImpactAnalysisDisabled,
1275
1665
  }
1276
1666
  testSuiteStartCh.runStores(testSuiteCtx, () => {})
1277
1667
 
@@ -1297,8 +1687,18 @@ async function reportTypecheckResults (result, frameworkVersion, ctx, typechecke
1297
1687
  if (!testSuiteFinishCh.hasSubscribers) return
1298
1688
  if (!Array.isArray(result?.files)) return
1299
1689
 
1300
- if (ctx) {
1301
- await ensureMainProcessSetup(ctx, frameworkVersion, result.files)
1690
+ const setupState = ctx && mainProcessSetupStates.get(ctx)
1691
+ if (
1692
+ ctx &&
1693
+ (!setupState || setupState.shouldInstallMainReporter || setupState.disableTestImpactAnalysis)
1694
+ ) {
1695
+ await ensureMainProcessSetup(
1696
+ ctx,
1697
+ frameworkVersion,
1698
+ result.files,
1699
+ false,
1700
+ !setupState || setupState.disableTestImpactAnalysis
1701
+ )
1302
1702
  }
1303
1703
  const providedContext = getMainProcessProvidedContext(ctx)
1304
1704
  const sessionConfiguration = testSessionConfigurationCh.hasSubscribers
@@ -1468,7 +1868,10 @@ function getWrappedOn (on) {
1468
1868
  arguments[1] = shimmer.wrapFunction(callback, callback => function (message) {
1469
1869
  if (message.type !== 'Buffer' && Array.isArray(message)) {
1470
1870
  const [interprocessCode, data] = message
1471
- if (handleWorkerReport(interprocessCode, data)) {
1871
+ if (
1872
+ handleEfdAdmissionMessage(this, interprocessCode, data) ||
1873
+ handleWorkerReport(interprocessCode, data)
1874
+ ) {
1472
1875
  // If we execute the callback vitest crashes, as the message is not supported
1473
1876
  return
1474
1877
  }
@@ -1479,6 +1882,37 @@ function getWrappedOn (on) {
1479
1882
  }
1480
1883
  }
1481
1884
 
1885
+ /**
1886
+ * Handles an EFD suite admission request sent through a Vitest worker transport.
1887
+ *
1888
+ * @param {object} workerProcess
1889
+ * @param {number} interprocessCode
1890
+ * @param {object} data
1891
+ * @returns {boolean}
1892
+ */
1893
+ function handleEfdAdmissionMessage (workerProcess, interprocessCode, data) {
1894
+ if (interprocessCode !== VITEST_WORKER_EFD_SUITE_ADMISSION_REQUEST_CODE) return false
1895
+
1896
+ const response = [VITEST_WORKER_EFD_SUITE_ADMISSION_RESPONSE_CODE, {
1897
+ allowed: reserveEarlyFlakeDetectionSuite(data?.testSuite, data?.hasNewTest === true),
1898
+ requestId: data?.requestId,
1899
+ }]
1900
+ try {
1901
+ if (typeof workerProcess.send === 'function') {
1902
+ workerProcess.send(response, (error) => {
1903
+ if (error) {
1904
+ log.error('Could not send Vitest EFD suite admission response: %s', error.message)
1905
+ }
1906
+ })
1907
+ } else {
1908
+ workerProcess.postMessage(response)
1909
+ }
1910
+ } catch (error) {
1911
+ log.error('Could not send Vitest EFD suite admission response: %s', error?.message)
1912
+ }
1913
+ return true
1914
+ }
1915
+
1482
1916
  function handleWorkerReport (interprocessCode, data) {
1483
1917
  if (interprocessCode === VITEST_WORKER_TRACE_PAYLOAD_CODE) {
1484
1918
  collectTestOptimizationSummariesFromTraces(data, {
@@ -1489,11 +1923,21 @@ function handleWorkerReport (interprocessCode, data) {
1489
1923
  return true
1490
1924
  }
1491
1925
 
1926
+ if (interprocessCode === VITEST_WORKER_COVERAGE_PAYLOAD_CODE) {
1927
+ workerReportCoverageCh.publish(data)
1928
+ return true
1929
+ }
1930
+
1492
1931
  if (interprocessCode === VITEST_WORKER_LOGS_PAYLOAD_CODE) {
1493
1932
  workerReportLogsCh.publish(data)
1494
1933
  return true
1495
1934
  }
1496
1935
 
1936
+ if (interprocessCode === VITEST_WORKER_TELEMETRY_PAYLOAD_CODE) {
1937
+ workerReportTelemetryCh.publish(data)
1938
+ return true
1939
+ }
1940
+
1497
1941
  return false
1498
1942
  }
1499
1943
 
@@ -1557,9 +2001,9 @@ addHook({
1557
2001
  name: 'vitest',
1558
2002
  versions: ['>=1.6.0 <2.0.0'],
1559
2003
  filePattern: 'dist/vendor/index.*',
1560
- }, (vitestPackage) => {
2004
+ }, (vitestPackage, frameworkVersion) => {
1561
2005
  if (isReporterPackage(vitestPackage)) {
1562
- shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
2006
+ shimmer.wrap(vitestPackage.B.prototype, 'sort', sort => getSortWrapper(sort, frameworkVersion))
1563
2007
  }
1564
2008
 
1565
2009
  return vitestPackage
@@ -1569,9 +2013,9 @@ addHook({
1569
2013
  name: 'vitest',
1570
2014
  versions: ['>=2.0.0 <2.0.5'],
1571
2015
  filePattern: 'dist/vendor/index.*',
1572
- }, (vitestPackage) => {
2016
+ }, (vitestPackage, frameworkVersion) => {
1573
2017
  if (isReporterPackageNew(vitestPackage)) {
1574
- shimmer.wrap(vitestPackage.e.prototype, 'sort', getSortWrapper)
2018
+ shimmer.wrap(vitestPackage.e.prototype, 'sort', sort => getSortWrapper(sort, frameworkVersion))
1575
2019
  }
1576
2020
 
1577
2021
  return vitestPackage
@@ -1581,9 +2025,9 @@ addHook({
1581
2025
  name: 'vitest',
1582
2026
  versions: ['>=2.0.5 <2.1.0'],
1583
2027
  filePattern: 'dist/chunks/index.*',
1584
- }, (vitestPackage) => {
2028
+ }, (vitestPackage, frameworkVersion) => {
1585
2029
  if (isReporterPackageNewest(vitestPackage)) {
1586
- shimmer.wrap(vitestPackage.h.prototype, 'sort', getSortWrapper)
2030
+ shimmer.wrap(vitestPackage.h.prototype, 'sort', sort => getSortWrapper(sort, frameworkVersion))
1587
2031
  }
1588
2032
 
1589
2033
  return vitestPackage
@@ -1605,8 +2049,12 @@ addHook({
1605
2049
  name: 'vitest',
1606
2050
  versions: ['>=2.1.0 <3.0.0'],
1607
2051
  filePattern: 'dist/chunks/RandomSequencer.*',
1608
- }, (randomSequencerPackage) => {
1609
- shimmer.wrap(randomSequencerPackage.B.prototype, 'sort', getSortWrapper)
2052
+ }, (randomSequencerPackage, frameworkVersion) => {
2053
+ shimmer.wrap(
2054
+ randomSequencerPackage.B.prototype,
2055
+ 'sort',
2056
+ sort => getSortWrapper(sort, frameworkVersion)
2057
+ )
1610
2058
  return randomSequencerPackage
1611
2059
  })
1612
2060
 
@@ -1614,10 +2062,14 @@ addHook({
1614
2062
  name: 'vitest',
1615
2063
  versions: ['>=3.0.9'],
1616
2064
  filePattern: 'dist/chunks/coverage.*',
1617
- }, (coveragePackage) => {
2065
+ }, (coveragePackage, frameworkVersion) => {
1618
2066
  const baseSequencer = getBaseSequencerExport(coveragePackage)
1619
2067
  if (baseSequencer) {
1620
- shimmer.wrap(baseSequencer.value.prototype, 'sort', getSortWrapper)
2068
+ shimmer.wrap(
2069
+ baseSequencer.value.prototype,
2070
+ 'sort',
2071
+ sort => getSortWrapper(sort, frameworkVersion)
2072
+ )
1621
2073
  }
1622
2074
  return coveragePackage
1623
2075
  })
@@ -1626,8 +2078,12 @@ addHook({
1626
2078
  name: 'vitest',
1627
2079
  versions: ['>=3.0.0 <3.0.9'],
1628
2080
  filePattern: 'dist/chunks/resolveConfig.*',
1629
- }, (resolveConfigPackage) => {
1630
- shimmer.wrap(resolveConfigPackage.B.prototype, 'sort', getSortWrapper)
2081
+ }, (resolveConfigPackage, frameworkVersion) => {
2082
+ shimmer.wrap(
2083
+ resolveConfigPackage.B.prototype,
2084
+ 'sort',
2085
+ sort => getSortWrapper(sort, frameworkVersion)
2086
+ )
1631
2087
  return resolveConfigPackage
1632
2088
  })
1633
2089