dd-trace 6.7.0 → 6.9.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 (272) 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 +14 -8
  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/playwright.js +5 -5
  20. package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
  21. package/ci/test-optimization-validation/generated-files.js +47 -37
  22. package/ci/test-optimization-validation/generated-test-contract.js +9 -7
  23. package/ci/test-optimization-validation/generated-verifier.js +24 -17
  24. package/ci/test-optimization-validation/literal-glob.js +52 -0
  25. package/ci/test-optimization-validation/manifest-loader.js +3 -0
  26. package/ci/test-optimization-validation/manifest-scaffold.js +301 -52
  27. package/ci/test-optimization-validation/manifest-schema.js +80 -17
  28. package/ci/test-optimization-validation/offline-fixtures.js +13 -4
  29. package/ci/test-optimization-validation/offline-output.js +6 -6
  30. package/ci/test-optimization-validation/package-check.js +94 -0
  31. package/ci/test-optimization-validation/plan-writer.js +183 -10
  32. package/ci/test-optimization-validation/preflight-runner.js +110 -31
  33. package/ci/test-optimization-validation/project-build-artifact.js +31 -0
  34. package/ci/test-optimization-validation/redaction.js +18 -27
  35. package/ci/test-optimization-validation/report-writer.js +182 -48
  36. package/ci/test-optimization-validation/result-semantics.js +20 -3
  37. package/ci/test-optimization-validation/runner-command.js +41 -21
  38. package/ci/test-optimization-validation/runner-contract.js +79 -43
  39. package/ci/test-optimization-validation/safe-files.js +4 -4
  40. package/ci/test-optimization-validation/scenarios/basic-reporting.js +60 -6
  41. package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
  42. package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
  43. package/ci/test-optimization-validation/scenarios/test-management.js +2 -2
  44. package/ci/test-optimization-validation/static-diagnosis.js +10 -3
  45. package/ci/test-optimization-validation/test-output.js +2 -1
  46. package/ci/test-optimization-validation/validation-blocker.js +21 -0
  47. package/ci/vitest-no-worker-init-setup.mjs +377 -84
  48. package/index.d.ts +175 -14
  49. package/init.js +1 -17
  50. package/initialize.mjs +11 -0
  51. package/loader-hook.mjs +6 -4
  52. package/package.json +12 -4
  53. package/packages/datadog-core/src/utils/src/kebabcase.js +8 -2
  54. package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
  55. package/packages/datadog-esbuild/index.js +4 -2
  56. package/packages/datadog-esbuild/src/utils.js +17 -2
  57. package/packages/datadog-instrumentations/src/ai.js +61 -34
  58. package/packages/datadog-instrumentations/src/bluebird.js +6 -6
  59. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +85 -9
  60. package/packages/datadog-instrumentations/src/cucumber.js +131 -51
  61. package/packages/datadog-instrumentations/src/dns.js +21 -2
  62. package/packages/datadog-instrumentations/src/electron.js +1 -0
  63. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  64. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  65. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  66. package/packages/datadog-instrumentations/src/helpers/extract-package-and-module-path.js +10 -0
  67. package/packages/datadog-instrumentations/src/helpers/hook.js +19 -2
  68. package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
  69. package/packages/datadog-instrumentations/src/helpers/register.js +9 -3
  70. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
  71. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
  72. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +175 -0
  73. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  74. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  75. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
  76. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +50 -18
  77. package/packages/datadog-instrumentations/src/helpers/router-helper.js +38 -7
  78. package/packages/datadog-instrumentations/src/helpers/shared-utils.js +39 -0
  79. package/packages/datadog-instrumentations/src/http/client.js +1 -3
  80. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  81. package/packages/datadog-instrumentations/src/jest.js +986 -246
  82. package/packages/datadog-instrumentations/src/mocha/common.js +1 -3
  83. package/packages/datadog-instrumentations/src/mocha/main.js +109 -51
  84. package/packages/datadog-instrumentations/src/mocha/utils.js +120 -32
  85. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +44 -0
  86. package/packages/datadog-instrumentations/src/mocha/worker.js +441 -43
  87. package/packages/datadog-instrumentations/src/moleculer/client.js +1 -1
  88. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  89. package/packages/datadog-instrumentations/src/next.js +155 -23
  90. package/packages/datadog-instrumentations/src/openai.js +14 -0
  91. package/packages/datadog-instrumentations/src/pg.js +180 -6
  92. package/packages/datadog-instrumentations/src/playwright.js +176 -81
  93. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  94. package/packages/datadog-instrumentations/src/promise.js +3 -3
  95. package/packages/datadog-instrumentations/src/rhea.js +1 -1
  96. package/packages/datadog-instrumentations/src/router.js +195 -19
  97. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +567 -65
  98. package/packages/datadog-instrumentations/src/vitest-main.js +438 -54
  99. package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
  100. package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
  101. package/packages/datadog-instrumentations/src/webdriverio.js +1090 -0
  102. package/packages/datadog-instrumentations/src/when.js +3 -3
  103. package/packages/datadog-instrumentations/src/ws.js +5 -1
  104. package/packages/datadog-plugin-aerospike/src/index.js +1 -3
  105. package/packages/datadog-plugin-amqplib/src/client.js +1 -1
  106. package/packages/datadog-plugin-amqplib/src/producer.js +1 -1
  107. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  108. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -2
  109. package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +2 -2
  110. package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -3
  111. package/packages/datadog-plugin-azure-service-bus/src/producer.js +5 -3
  112. package/packages/datadog-plugin-bullmq/src/producer.js +3 -3
  113. package/packages/datadog-plugin-child_process/src/index.js +3 -3
  114. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -11
  115. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  116. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
  117. package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -1
  118. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  119. package/packages/datadog-plugin-electron/src/net.js +10 -4
  120. package/packages/datadog-plugin-fastify/src/tracing.js +1 -1
  121. package/packages/datadog-plugin-fs/src/index.js +0 -4
  122. package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +14 -7
  123. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +19 -10
  124. package/packages/datadog-plugin-graphql/src/execute.js +1 -0
  125. package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -1
  126. package/packages/datadog-plugin-http/src/client.js +3 -2
  127. package/packages/datadog-plugin-http2/src/client.js +1 -1
  128. package/packages/datadog-plugin-jest/src/index.js +3 -21
  129. package/packages/datadog-plugin-jest/src/util.js +4 -1
  130. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
  131. package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
  132. package/packages/datadog-plugin-mocha/src/index.js +342 -6
  133. package/packages/datadog-plugin-next/src/index.js +57 -9
  134. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  135. package/packages/datadog-plugin-pg/src/index.js +65 -1
  136. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  137. package/packages/datadog-plugin-prisma/src/index.js +5 -1
  138. package/packages/datadog-plugin-rhea/src/consumer.js +16 -13
  139. package/packages/datadog-plugin-rhea/src/producer.js +1 -1
  140. package/packages/datadog-plugin-undici/src/index.js +2 -1
  141. package/packages/datadog-plugin-vitest/src/index.js +179 -18
  142. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  143. package/packages/datadog-webpack/index.js +2 -2
  144. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  145. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  146. package/packages/dd-trace/src/appsec/downstream_requests.js +1 -1
  147. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  148. package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
  149. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  150. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  151. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  152. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -1
  153. package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +1 -1
  154. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  155. package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
  156. package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -3
  157. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +2 -2
  158. package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +2 -2
  159. package/packages/dd-trace/src/appsec/index.js +48 -476
  160. package/packages/dd-trace/src/appsec/sdk/track_event.js +10 -1
  161. package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
  162. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  163. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  164. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +7 -6
  165. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -15
  166. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +19 -1
  167. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +25 -1
  168. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/webdriverio.js +26 -0
  169. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -1
  170. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  171. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  172. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -3
  173. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  174. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +2 -2
  175. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  176. package/packages/dd-trace/src/config/defaults.js +14 -6
  177. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -2
  178. package/packages/dd-trace/src/config/helper.js +2 -0
  179. package/packages/dd-trace/src/config/index.js +31 -5
  180. package/packages/dd-trace/src/config/parsers.js +19 -1
  181. package/packages/dd-trace/src/config/remote_config.js +1 -1
  182. package/packages/dd-trace/src/config/supported-configurations.json +13 -6
  183. package/packages/dd-trace/src/datastreams/encoding.js +3 -4
  184. package/packages/dd-trace/src/debugger/devtools_client/log.js +2 -1
  185. package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +1 -4
  186. package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -2
  187. package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +2 -2
  188. package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +16 -2
  189. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  190. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  191. package/packages/dd-trace/src/encode/tags-processors.js +2 -2
  192. package/packages/dd-trace/src/exporters/common/url.js +3 -5
  193. package/packages/dd-trace/src/exporters/electron/index.js +4 -1
  194. package/packages/dd-trace/src/external-logger/src/index.js +1 -2
  195. package/packages/dd-trace/src/flare/index.js +2 -2
  196. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  197. package/packages/dd-trace/src/guardrails/log.js +1 -10
  198. package/packages/dd-trace/src/id.js +1 -1
  199. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -1
  200. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  201. package/packages/dd-trace/src/llmobs/experiments/client.js +4 -2
  202. package/packages/dd-trace/src/llmobs/experiments/dataset.js +66 -14
  203. package/packages/dd-trace/src/llmobs/experiments/experiment.js +289 -95
  204. package/packages/dd-trace/src/llmobs/experiments/index.js +66 -19
  205. package/packages/dd-trace/src/llmobs/experiments/noop.js +95 -10
  206. package/packages/dd-trace/src/llmobs/experiments/result.js +13 -2
  207. package/packages/dd-trace/src/llmobs/experiments/util.js +220 -0
  208. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  209. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  210. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  211. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +1 -1
  212. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  213. package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +1 -1
  214. package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
  215. package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
  216. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  217. package/packages/dd-trace/src/llmobs/sdk.js +152 -78
  218. package/packages/dd-trace/src/llmobs/span_processor.js +9 -7
  219. package/packages/dd-trace/src/llmobs/tagger.js +1 -2
  220. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  221. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  222. package/packages/dd-trace/src/llmobs/writers/spans.js +12 -6
  223. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  224. package/packages/dd-trace/src/log/channels.js +1 -9
  225. package/packages/dd-trace/src/log/levels.js +12 -0
  226. package/packages/dd-trace/src/noop/proxy.js +2 -2
  227. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  228. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  229. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  230. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  231. package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
  232. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
  233. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  234. package/packages/dd-trace/src/opentracing/span.js +1 -1
  235. package/packages/dd-trace/src/opentracing/span_context.js +1 -1
  236. package/packages/dd-trace/src/plugins/ci_plugin.js +124 -23
  237. package/packages/dd-trace/src/plugins/index.js +2 -0
  238. package/packages/dd-trace/src/plugins/plugin.js +1 -1
  239. package/packages/dd-trace/src/plugins/tracing.js +1 -0
  240. package/packages/dd-trace/src/plugins/util/ci.js +13 -6
  241. package/packages/dd-trace/src/plugins/util/git.js +7 -5
  242. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +3 -3
  243. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  244. package/packages/dd-trace/src/plugins/util/llm.js +2 -2
  245. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  246. package/packages/dd-trace/src/plugins/util/tags.js +2 -0
  247. package/packages/dd-trace/src/plugins/util/test.js +356 -86
  248. package/packages/dd-trace/src/plugins/util/url.js +5 -5
  249. package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
  250. package/packages/dd-trace/src/plugins/util/web.js +24 -2
  251. package/packages/dd-trace/src/priority_sampler.js +6 -3
  252. package/packages/dd-trace/src/profiler.js +1 -1
  253. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  254. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  255. package/packages/dd-trace/src/profiling/profilers/poisson.js +1 -1
  256. package/packages/dd-trace/src/profiling/profilers/shared.js +2 -2
  257. package/packages/dd-trace/src/proxy.js +1 -0
  258. package/packages/dd-trace/src/ritm.js +10 -20
  259. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  260. package/packages/dd-trace/src/sampling_rule.js +4 -2
  261. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  262. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  263. package/packages/dd-trace/src/startup-log.js +3 -0
  264. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  265. package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -1
  266. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  267. package/packages/dd-trace/src/util.js +16 -0
  268. package/vendor/dist/@apm-js-collab/code-transformer/index.js +12 -12
  269. package/vendor/dist/pprof-format/index.js +1 -1
  270. package/vendor/dist/source-map/index.js +1 -1
  271. package/version.js +10 -8
  272. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -41,11 +41,9 @@ addHook({
41
41
  }, (Suite) => {
42
42
  shimmer.wrap(Suite.prototype, 'addTest', addTest => function (test) {
43
43
  const callSites = getCallSites()
44
- let startLine
45
44
  const testCallSite = callSites.find(site => site.getFileName() === test.file)
46
45
  if (testCallSite) {
47
- startLine = testCallSite.getLineNumber()
48
- testToStartLine.set(test, startLine)
46
+ testToStartLine.set(test, testCallSite.getLineNumber())
49
47
  }
50
48
  return addTest.apply(this, arguments)
51
49
  })
@@ -11,11 +11,13 @@ const {
11
11
  const { addHook, channel } = require('../helpers/instrument')
12
12
  const shimmer = require('../../../datadog-shimmer')
13
13
  const { isMarkedAsUnskippable } = require('../../../datadog-plugin-jest/src/util')
14
+ const { EMPTY_EFD_RETRY_POLICY } = require('../../../dd-trace/src/ci-visibility/efd-retry-policy')
14
15
  const { writeCoverageBackfillToCache } = require('../../../dd-trace/src/ci-visibility/test-optimization-cache')
15
16
  const log = require('../../../dd-trace/src/log')
16
17
  const { getEnvironmentVariable } = require('../../../dd-trace/src/config/helper')
17
18
  const {
18
19
  getTestSuitePath,
20
+ MOCHA_WORKER_TELEMETRY_PAYLOAD_CODE,
19
21
  MOCHA_WORKER_TRACE_PAYLOAD_CODE,
20
22
  fromCoverageMapToCoverage,
21
23
  getCoveredFilesFromCoverage,
@@ -28,6 +30,7 @@ const {
28
30
  getTestCoverageLinesPercentage,
29
31
  collectTestOptimizationSummariesFromTraces,
30
32
  logTestOptimizationSummary,
33
+ TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE,
31
34
  getTestOptimizationRequestResults,
32
35
  isModifiedTest,
33
36
  } = require('../../../dd-trace/src/plugins/util/test')
@@ -53,6 +56,7 @@ const {
53
56
  newTestsWithDynamicNames,
54
57
  attemptToFixExecutions,
55
58
  loggedAttemptToFixTests,
59
+ adjustRunnerFailuresForTestOptimization,
56
60
  } = require('./utils')
57
61
 
58
62
  require('./common')
@@ -65,12 +69,15 @@ let hasWarnedDeprecatedMochaVersion = false
65
69
  const unskippableSuites = []
66
70
  let suitesToSkip = []
67
71
  let isSuitesSkipped = false
72
+ let areAllSuitesSkipped = false
68
73
  let skippedSuites = []
69
74
  let skippableSuitesCoverage = {}
70
75
  let skippedSuitesCoverage = {}
71
76
  let itrCorrelationId = ''
72
77
  let isForcedToRun = false
73
- const config = {}
78
+ const config = {
79
+ earlyFlakeDetectionRetryPolicy: EMPTY_EFD_RETRY_POLICY,
80
+ }
74
81
 
75
82
  // We'll preserve the original coverage here
76
83
  const originalCoverageMap = createCoverageMap()
@@ -94,6 +101,7 @@ const mochaGlobalRunCh = channel('ci:mocha:global:run')
94
101
  const testManagementTestsCh = channel('ci:mocha:test-management-tests')
95
102
  const modifiedFilesCh = channel('ci:mocha:modified-files')
96
103
  const workerReportTraceCh = channel('ci:mocha:worker-report:trace')
104
+ const workerReportTelemetryCh = channel('ci:mocha:worker-report:telemetry')
97
105
  const testSessionStartCh = channel('ci:mocha:session:start')
98
106
  const testSessionFinishCh = channel('ci:mocha:session:finish')
99
107
  const itrSkippedSuitesCh = channel('ci:mocha:itr:skipped-suites')
@@ -114,17 +122,6 @@ function warnDeprecatedMochaVersion (frameworkVersion) {
114
122
  )
115
123
  }
116
124
 
117
- // Tests from workers do not come with `isFailed` method
118
- function isTestFailed (test) {
119
- if (test.isFailed) {
120
- return test.isFailed()
121
- }
122
- if (test.isPending) {
123
- return !test.isPending() && test.state === 'failed'
124
- }
125
- return false
126
- }
127
-
128
125
  function getRootSuiteStatus (rootTests) {
129
126
  let status = 'pass'
130
127
  if (rootTests.every(t => t.isPending())) {
@@ -237,6 +234,7 @@ function getMochaTestSessionCoverageFiles () {
237
234
 
238
235
  function resetSuiteSkippingRunState () {
239
236
  isSuitesSkipped = false
237
+ areAllSuitesSkipped = false
240
238
  skippedSuites = []
241
239
  skippableSuitesCoverage = {}
242
240
  skippedSuitesCoverage = {}
@@ -309,37 +307,7 @@ function getOnEndHandler (isParallel, onDone) {
309
307
  status = 'fail'
310
308
  }
311
309
 
312
- if (config.isEarlyFlakeDetectionEnabled) {
313
- /**
314
- * If Early Flake Detection (EFD) is enabled the logic is as follows:
315
- * - If all attempts for a test are failing, the test has failed and we will let the test process fail.
316
- * - If just a single attempt passes, we will prevent the test process from failing.
317
- * The rationale behind is the following: you may still be able to block your CI pipeline by gating
318
- * on flakiness (the test will be considered flaky), but you may choose to unblock the pipeline too.
319
- */
320
- for (const tests of Object.values(efdTests)) {
321
- const failingEfdTests = tests.filter(test => isTestFailed(test))
322
- const areAllEfdTestsFailing = failingEfdTests.length === tests.length
323
- const nonQuarantinedFailingEfdTests = failingEfdTests.filter(test => !testsQuarantined.has(test))
324
- if (nonQuarantinedFailingEfdTests.length && !areAllEfdTestsFailing) {
325
- this.stats.failures -= nonQuarantinedFailingEfdTests.length
326
- this.failures -= nonQuarantinedFailingEfdTests.length
327
- }
328
- }
329
- }
330
-
331
- // We subtract the errors from quarantined tests from the total number of failures.
332
- // Attempt-to-fix tests ignore quarantine/disabled suppression and keep their framework result.
333
- if (config.isTestManagementTestsEnabled) {
334
- let numFailedQuarantinedTests = 0
335
- for (const test of testsQuarantined) {
336
- if (isTestFailed(test)) {
337
- numFailedQuarantinedTests++
338
- }
339
- }
340
- this.stats.failures -= numFailedQuarantinedTests
341
- this.failures -= numFailedQuarantinedTests
342
- }
310
+ adjustRunnerFailuresForTestOptimization(this, config)
343
311
 
344
312
  // Recompute status after EFD and quarantine adjustments have reduced failure counts
345
313
  if (status === 'fail') {
@@ -397,7 +365,11 @@ function getOnEndHandler (isParallel, onDone) {
397
365
  isParallel,
398
366
  }, onDone)
399
367
 
400
- logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
368
+ logTestOptimizationSummary({
369
+ attemptToFixExecutions,
370
+ newTestsWithDynamicNames,
371
+ extraSections: areAllSuitesSkipped ? [TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE] : [],
372
+ })
401
373
  loggedAttemptToFixTests.clear()
402
374
  }
403
375
  }
@@ -426,6 +398,57 @@ function isFailedTestReplayEnabled () {
426
398
  return config.isTestDynamicInstrumentationEnabled && config.isDiEnabled
427
399
  }
428
400
 
401
+ /**
402
+ * @typedef {object} MochaSuite
403
+ * @property {MochaSuite[]} suites
404
+ * @property {import('mocha').Test[]} tests
405
+ * @property {MochaSuite[]} _onlySuites
406
+ * @property {import('mocha').Test[]} _onlyTests
407
+ */
408
+
409
+ /**
410
+ * Mirrors Mocha 5's private exclusivity check.
411
+ *
412
+ * @param {MochaSuite} suite
413
+ * @returns {boolean}
414
+ */
415
+ function hasOnly (suite) {
416
+ if (suite._onlyTests.length || suite._onlySuites.length) return true
417
+
418
+ for (const childSuite of suite.suites) {
419
+ if (hasOnly(childSuite)) return true
420
+ }
421
+ return false
422
+ }
423
+
424
+ /**
425
+ * Mirrors Mocha 5's private exclusivity filter.
426
+ *
427
+ * @param {MochaSuite} suite
428
+ * @returns {boolean}
429
+ */
430
+ function filterOnly (suite) {
431
+ if (suite._onlyTests.length) {
432
+ suite.tests = suite._onlyTests
433
+ suite.suites = []
434
+ } else {
435
+ suite.tests = []
436
+
437
+ for (const onlySuite of suite._onlySuites) {
438
+ if (hasOnly(onlySuite)) filterOnly(onlySuite)
439
+ }
440
+
441
+ const filteredSuites = []
442
+ for (const childSuite of suite.suites) {
443
+ if (suite._onlySuites.includes(childSuite) || filterOnly(childSuite)) {
444
+ filteredSuites.push(childSuite)
445
+ }
446
+ }
447
+ suite.suites = filteredSuites
448
+ }
449
+ return suite.tests.length > 0 || suite.suites.length > 0
450
+ }
451
+
429
452
  function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFinishRequest, localSuites) {
430
453
  const ctx = {
431
454
  isParallel,
@@ -458,6 +481,13 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
458
481
  }
459
482
 
460
483
  // We remove the suites that we skip through ITR
484
+ // Mocha normally applies exclusivity after this asynchronous configuration step.
485
+ if (typeof runner.suite.hasOnly === 'function') {
486
+ if (runner.suite.hasOnly()) runner.suite.filterOnly()
487
+ } else if (hasOnly(runner.suite)) {
488
+ filterOnly(runner.suite)
489
+ }
490
+ const numTestsToRun = runner.grepTotal(runner.suite)
461
491
  const filteredSuites = getFilteredSuites(runner.suite.suites)
462
492
  const { suitesToRun, suitesToSkipForRun } = filteredSuites
463
493
 
@@ -466,6 +496,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
466
496
  log.debug('%d out of %d suites are going to run.', suitesToRun.length, runner.suite.suites.length)
467
497
 
468
498
  runner.suite.suites = suitesToRun
499
+ areAllSuitesSkipped = isSuitesSkipped && numTestsToRun > 0 && runner.grepTotal(runner.suite) === 0
469
500
 
470
501
  skippedSuites = [...filteredSuites.skippedSuites]
471
502
  suitesToSkip = suitesToSkipForRun
@@ -529,8 +560,8 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
529
560
  }
530
561
  config.repositoryRoot = repositoryRoot
531
562
  config.isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
532
- config.earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
533
- config.earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
563
+ config.earlyFlakeDetectionRetryPolicy =
564
+ libraryConfig.earlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
534
565
  config.earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
535
566
  config.isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
536
567
  config.isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
@@ -1018,6 +1049,8 @@ function onMessage (message) {
1018
1049
  attemptToFixExecutions,
1019
1050
  })
1020
1051
  workerReportTraceCh.publish(payload)
1052
+ } else if (messageCode === MOCHA_WORKER_TELEMETRY_PAYLOAD_CODE) {
1053
+ workerReportTelemetryCh.publish(payload)
1021
1054
  }
1022
1055
  }
1023
1056
  }
@@ -1082,7 +1115,7 @@ addHook({
1082
1115
  versions: ['>=8.0.0'],
1083
1116
  file: 'lib/nodejs/parallel-buffered-runner.js',
1084
1117
  }, (ParallelBufferedRunner, frameworkVersion) => {
1085
- shimmer.wrap(ParallelBufferedRunner.prototype, 'run', run => function (cb, { files }) {
1118
+ shimmer.wrap(ParallelBufferedRunner.prototype, 'run', run => function (cb, { files, options = {} }) {
1086
1119
  if (!testFinishCh.hasSubscribers) {
1087
1120
  return run.apply(this, arguments)
1088
1121
  }
@@ -1128,10 +1161,11 @@ addHook({
1128
1161
  }
1129
1162
  }
1130
1163
  isSuitesSkipped = skippedFiles.length > 0
1164
+ areAllSuitesSkipped = options.grep === undefined && files.length > 0 && filteredFiles.length === 0
1131
1165
  skippedSuites = skippedFiles
1132
1166
  skippedSuitesCoverage = getSkippedSuitesCoverageForRun()
1133
1167
  writeCoverageBackfillToCache(skippedSuitesCoverage, getCoverageRootDir())
1134
- run.apply(this, [onRunDone, { files: filteredFiles }])
1168
+ run.apply(this, [onRunDone, { files: filteredFiles, options }])
1135
1169
  } else {
1136
1170
  run.apply(this, arguments)
1137
1171
  }
@@ -1150,9 +1184,34 @@ addHook({
1150
1184
  name: 'mocha',
1151
1185
  versions: ['>=8.0.0'],
1152
1186
  file: 'lib/nodejs/buffered-worker-pool.js',
1153
- }, (BufferedWorkerPoolPackage) => {
1187
+ }, (BufferedWorkerPoolPackage, frameworkVersion) => {
1154
1188
  const { BufferedWorkerPool } = BufferedWorkerPoolPackage
1155
1189
 
1190
+ if (satisfies(frameworkVersion, '<9.2.0')) {
1191
+ // Shimmer is required because the worker environment must be changed before workerpool forks,
1192
+ // before any test lifecycle hook can run. Mocha added this worker ID itself in 9.2.0.
1193
+ shimmer.wrap(BufferedWorkerPool, 'create', create => function () {
1194
+ const pool = create.apply(this, arguments)
1195
+
1196
+ if (!testFinishCh.hasSubscribers) return pool
1197
+
1198
+ let workerId = 0
1199
+ shimmer.wrap(pool._pool, '_createWorkerHandler', createWorkerHandler => function () {
1200
+ this.forkOpts = {
1201
+ ...this.forkOpts,
1202
+ env: {
1203
+ // eslint-disable-next-line eslint-rules/eslint-process-env
1204
+ ...(this.forkOpts.env || process.env),
1205
+ MOCHA_WORKER_ID: String(workerId++),
1206
+ },
1207
+ }
1208
+ return createWorkerHandler.apply(this, arguments)
1209
+ })
1210
+
1211
+ return pool
1212
+ })
1213
+ }
1214
+
1156
1215
  shimmer.wrap(BufferedWorkerPool.prototype, 'run', run => async function (testSuiteAbsolutePath, workerArgs) {
1157
1216
  if (!testFinishCh.hasSubscribers ||
1158
1217
  (!config.isKnownTestsEnabled &&
@@ -1170,8 +1229,7 @@ addHook({
1170
1229
  if (config.isKnownTestsEnabled) {
1171
1230
  if (config.knownTests?.mocha) {
1172
1231
  const testSuiteKnownTests = config.knownTests.mocha[testPath] || []
1173
- newWorkerArgs._ddEfdNumRetries = config.earlyFlakeDetectionNumRetries
1174
- newWorkerArgs._ddEfdSlowTestRetries = config.earlyFlakeDetectionSlowTestRetries
1232
+ newWorkerArgs._ddEfdRetryPolicy = config.earlyFlakeDetectionRetryPolicy
1175
1233
  newWorkerArgs._ddIsEfdEnabled = config.isEarlyFlakeDetectionEnabled
1176
1234
  newWorkerArgs._ddIsKnownTestsEnabled = true
1177
1235
  newWorkerArgs._ddKnownTests = {
@@ -2,11 +2,16 @@
2
2
 
3
3
  const { performance } = require('node:perf_hooks')
4
4
 
5
+ const { getEnvironmentVariable } = require('../../../dd-trace/src/config/helper')
6
+ const {
7
+ getEfdRetryCountForDuration,
8
+ hasEfdRetries,
9
+ shouldSkipEfdRetry,
10
+ } = require('../../../dd-trace/src/ci-visibility/efd-retry-policy')
5
11
  const {
6
12
  getTestSuitePath,
7
13
  DYNAMIC_NAME_RE,
8
- getEfdRetryCount,
9
- getMaxEfdRetryCount,
14
+ recordTestManagementExecution,
10
15
  recordAttemptToFixExecution,
11
16
  logAttemptToFixTestExecution,
12
17
  } = require('../../../dd-trace/src/plugins/util/test')
@@ -26,6 +31,8 @@ const isModifiedCh = channel('ci:mocha:test:is-modified')
26
31
  // suite channels
27
32
  const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
28
33
 
34
+ /** @typedef {{ length: number, [index: number]: unknown } & Iterable<unknown>} ArgumentsLike */
35
+
29
36
  const testToContext = new WeakMap()
30
37
  const originalFns = new WeakMap()
31
38
  const testToStartLine = new WeakMap()
@@ -40,6 +47,7 @@ const testsStatuses = new Map()
40
47
  const efdRetryCountByTestFullName = new Map()
41
48
  const efdSlowAbortedTests = new Set()
42
49
  const attemptToFixExecutions = new Map()
50
+ const isMochaWorker = !!getEnvironmentVariable('MOCHA_WORKER_ID')
43
51
 
44
52
  function waitForHitProbe () {
45
53
  const promises = {}
@@ -48,6 +56,59 @@ function waitForHitProbe () {
48
56
  }
49
57
  const loggedAttemptToFixTests = new Set()
50
58
 
59
+ /**
60
+ * Checks whether a Mocha test failed, including serialized tests from parallel workers.
61
+ *
62
+ * @param {object} test
63
+ * @returns {boolean}
64
+ */
65
+ function isTestFailed (test) {
66
+ if (test.isFailed) {
67
+ return test.isFailed()
68
+ }
69
+ if (test.isPending) {
70
+ return !test.isPending() && test.state === 'failed'
71
+ }
72
+ return false
73
+ }
74
+
75
+ /**
76
+ * Applies EFD and quarantine failure suppression to a completed Mocha runner.
77
+ *
78
+ * @param {object} runner
79
+ * @param {object} config
80
+ * @returns {void}
81
+ */
82
+ function adjustRunnerFailuresForTestOptimization (runner, config) {
83
+ if (config.isEarlyFlakeDetectionEnabled) {
84
+ for (const tests of Object.values(efdTests)) {
85
+ const executedEfdTests = tests.filter(test => !test.isPending())
86
+ const failingEfdTests = tests.filter(test => isTestFailed(test))
87
+ const areAllEfdTestsFailing = failingEfdTests.length === executedEfdTests.length
88
+ const nonQuarantinedFailingEfdTests = failingEfdTests.filter(test => !testsQuarantined.has(test))
89
+ if (nonQuarantinedFailingEfdTests.length && !areAllEfdTestsFailing) {
90
+ if (runner.stats) {
91
+ runner.stats.failures -= nonQuarantinedFailingEfdTests.length
92
+ }
93
+ runner.failures -= nonQuarantinedFailingEfdTests.length
94
+ }
95
+ }
96
+ }
97
+
98
+ if (config.isTestManagementTestsEnabled) {
99
+ let numFailedQuarantinedTests = 0
100
+ for (const test of testsQuarantined) {
101
+ if (isTestFailed(test)) {
102
+ numFailedQuarantinedTests++
103
+ }
104
+ }
105
+ if (runner.stats) {
106
+ runner.stats.failures -= numFailedQuarantinedTests
107
+ }
108
+ runner.failures -= numFailedQuarantinedTests
109
+ }
110
+ }
111
+
51
112
  function getAfterEachHooks (testOrHook) {
52
113
  const hooks = []
53
114
 
@@ -80,19 +141,28 @@ function isNewTest (test, knownTests) {
80
141
  return !testsForSuite.includes(testName)
81
142
  }
82
143
 
83
- function setEfdRetryCountForTest (test, duration, slowTestRetries) {
144
+ /**
145
+ * @param {import('mocha').Test} test
146
+ * @param {number} duration
147
+ * @param {import('../../../dd-trace/src/ci-visibility/efd-retry-policy').EfdRetryPolicy} retryPolicy
148
+ */
149
+ function setEfdRetryCountForTest (test, duration, retryPolicy) {
84
150
  const testName = getTestFullName(test)
85
151
  if (efdRetryCountByTestFullName.has(testName)) {
86
152
  return
87
153
  }
88
- const retryCount = getEfdRetryCount(duration, slowTestRetries || {})
154
+ const retryCount = getEfdRetryCountForDuration(duration, retryPolicy)
89
155
  efdRetryCountByTestFullName.set(testName, retryCount)
90
156
  if (retryCount === 0) {
91
157
  efdSlowAbortedTests.add(testName)
92
158
  }
93
159
  }
94
160
 
95
- function wrapOriginalEfdTest (test, slowTestRetries) {
161
+ /**
162
+ * @param {import('mocha').Test} test
163
+ * @param {import('../../../dd-trace/src/ci-visibility/efd-retry-policy').EfdRetryPolicy} retryPolicy
164
+ */
165
+ function wrapOriginalEfdTest (test, retryPolicy) {
96
166
  if (test._ddEfdDurationWrapped || typeof test.fn !== 'function') {
97
167
  return
98
168
  }
@@ -101,7 +171,7 @@ function wrapOriginalEfdTest (test, slowTestRetries) {
101
171
  test.fn = shimmer.wrapFunction(originalFn, originalFn => function () {
102
172
  const start = performance.now()
103
173
  const recordDuration = () => {
104
- setEfdRetryCountForTest(test, performance.now() - start, slowTestRetries)
174
+ setEfdRetryCountForTest(test, performance.now() - start, retryPolicy)
105
175
  }
106
176
 
107
177
  if (originalFn.length > 0) {
@@ -152,38 +222,51 @@ function disableMochaRetries (test) {
152
222
  * _ddIsModified?: boolean,
153
223
  * _ddIsNew?: boolean
154
224
  * }} test
155
- * @param {{ isEarlyFlakeDetectionEnabled?: boolean }} config
225
+ * @param {{
226
+ * isEarlyFlakeDetectionEnabled?: boolean,
227
+ * earlyFlakeDetectionRetryPolicy?: import('../../../dd-trace/src/ci-visibility/efd-retry-policy').EfdRetryPolicy
228
+ * }} config
156
229
  * @returns {boolean}
157
230
  */
158
231
  function isDatadogManagedRetryTest (test, config) {
159
- return test._ddIsAttemptToFix ||
160
- test._ddIsEfdRetry ||
161
- (config.isEarlyFlakeDetectionEnabled && (test._ddIsNew || test._ddIsModified))
232
+ return test._ddIsAttemptToFix || isEarlyFlakeDetectionTest(test, config)
162
233
  }
163
234
 
164
235
  /**
165
236
  * Checks whether a runnable belongs to an Early Flake Detection execution.
166
237
  * @param {{
167
238
  * _ddIsAttemptToFix?: boolean,
239
+ * _ddIsDisabled?: boolean,
168
240
  * _ddIsEfdRetry?: boolean,
169
241
  * _ddIsModified?: boolean,
170
242
  * _ddIsNew?: boolean
171
243
  * }} test
172
- * @param {{ isEarlyFlakeDetectionEnabled?: boolean }} config
244
+ * @param {{
245
+ * isEarlyFlakeDetectionEnabled?: boolean,
246
+ * earlyFlakeDetectionRetryPolicy?: import('../../../dd-trace/src/ci-visibility/efd-retry-policy').EfdRetryPolicy
247
+ * }} config
173
248
  * @returns {boolean}
174
249
  */
175
250
  function isEarlyFlakeDetectionTest (test, config) {
176
251
  return !test._ddIsAttemptToFix &&
252
+ !test._ddIsDisabled &&
177
253
  config.isEarlyFlakeDetectionEnabled &&
254
+ hasEfdRetries(config.earlyFlakeDetectionRetryPolicy) &&
178
255
  (test._ddIsEfdRetry || test._ddIsNew || test._ddIsModified)
179
256
  }
180
257
 
181
- function retryTest (test, numRetries, tags, slowTestRetries) {
258
+ /**
259
+ * @param {import('mocha').Test} test
260
+ * @param {number} numRetries
261
+ * @param {string[]} tags
262
+ * @param {import('../../../dd-trace/src/ci-visibility/efd-retry-policy').EfdRetryPolicy} retryPolicy
263
+ */
264
+ function retryTest (test, numRetries, tags, retryPolicy) {
182
265
  const suite = test.parent
183
266
  const isEfdRetry = tags.includes('_ddIsEfdRetry')
184
267
  disableMochaRetries(test)
185
268
  if (isEfdRetry) {
186
- wrapOriginalEfdTest(test, slowTestRetries)
269
+ wrapOriginalEfdTest(test, retryPolicy)
187
270
  }
188
271
  for (let retryIndex = 0; retryIndex < numRetries; retryIndex++) {
189
272
  const clonedTest = test.clone()
@@ -195,7 +278,7 @@ function retryTest (test, numRetries, tags, slowTestRetries) {
195
278
  if (typeof originalFn === 'function') {
196
279
  clonedTest.fn = shimmer.wrapFunction(originalFn, originalFn => function (...args) {
197
280
  const efdRetryCount = efdRetryCountByTestFullName.get(getTestFullName(clonedTest))
198
- if (efdRetryCount !== undefined && clonedTest._ddEfdRetryIndex > efdRetryCount) {
281
+ if (shouldSkipEfdRetry(clonedTest._ddEfdRetryIndex, efdRetryCount)) {
199
282
  clonedTest._ddShouldSkipEfdRetry = true
200
283
  this.skip()
201
284
  }
@@ -211,14 +294,6 @@ function retryTest (test, numRetries, tags, slowTestRetries) {
211
294
  }
212
295
  }
213
296
 
214
- function getConfiguredEfdRetryCount (config) {
215
- const { earlyFlakeDetectionSlowTestRetries } = config
216
- if (!earlyFlakeDetectionSlowTestRetries || !Object.keys(earlyFlakeDetectionSlowTestRetries).length) {
217
- return config.earlyFlakeDetectionNumRetries
218
- }
219
- return getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
220
- }
221
-
222
297
  function getSuitesByTestFile (root) {
223
298
  const suitesByTestFile = {}
224
299
  function getSuites (suite) {
@@ -407,7 +482,7 @@ function getOnTestHandler (isMain) {
407
482
 
408
483
  if (isEfdRetry) {
409
484
  const efdRetryCount = efdRetryCountByTestFullName.get(getTestFullName(test))
410
- if (efdRetryCount !== undefined && test._ddEfdRetryIndex > efdRetryCount) {
485
+ if (shouldSkipEfdRetry(test._ddEfdRetryIndex, efdRetryCount)) {
411
486
  test.pending = true
412
487
  test._ddShouldSkipEfdRetry = true
413
488
  return
@@ -521,7 +596,7 @@ function getTestFinishInfo (test, status, config, error) {
521
596
  !efdRetryCountByTestFullName.has(testName)
522
597
  ) {
523
598
  const duration = test.duration > 0 ? test.duration : performance.now() - test._ddStartTime
524
- setEfdRetryCountForTest(test, duration, config.earlyFlakeDetectionSlowTestRetries)
599
+ setEfdRetryCountForTest(test, duration, config.earlyFlakeDetectionRetryPolicy)
525
600
  }
526
601
 
527
602
  if (testsStatuses.get(testName)) {
@@ -532,7 +607,8 @@ function getTestFinishInfo (test, status, config, error) {
532
607
  const testStatuses = testsStatuses.get(testName)
533
608
 
534
609
  const isLastAttempt = testStatuses.length === config.testManagementAttemptToFixRetries + 1
535
- const efdRetryCount = efdRetryCountByTestFullName.get(testName) ?? getConfiguredEfdRetryCount(config)
610
+ const efdRetryCount = efdRetryCountByTestFullName.get(testName) ??
611
+ config.earlyFlakeDetectionRetryPolicy.schedulingRetryCount
536
612
  const isLastEfdRetry = testStatuses.length === efdRetryCount + 1
537
613
  const isLastAtrAttempt = getIsLastRetry(test) || (config.isFlakyTestRetriesEnabled && status === 'pass')
538
614
 
@@ -587,6 +663,17 @@ function getTestFinishInfo (test, status, config, error) {
587
663
  isFinalAttempt,
588
664
  })
589
665
 
666
+ if (!isMochaWorker) {
667
+ recordTestManagementExecution({
668
+ testSuite: getTestSuitePath(test.file, process.cwd()),
669
+ testName: test.fullTitle(),
670
+ status,
671
+ isAttemptToFix: _ddIsAttemptToFix,
672
+ isDisabled: _ddIsDisabled,
673
+ isQuarantined: _ddIsQuarantined,
674
+ })
675
+ }
676
+
590
677
  if (_ddIsAttemptToFix) {
591
678
  recordAttemptToFixExecution(attemptToFixExecutions, {
592
679
  testSuite: getTestSuitePath(test.file, process.cwd()),
@@ -733,7 +820,7 @@ function finishDeferredHookEnd (test) {
733
820
  * @param {object} test - Mocha test currently owning the hook.
734
821
  * @param {Promise<void>|undefined} failedTestReplayPromise - Pending Failed Test Replay wait, if any.
735
822
  * @param {unknown} hookThis - Callback receiver.
736
- * @param {IArguments} args - Arguments passed by Mocha.
823
+ * @param {ArgumentsLike} args - Arguments passed by Mocha.
737
824
  * @returns {unknown}
738
825
  */
739
826
  function runFailedTestReplayHookUpCallback (fn, test, failedTestReplayPromise, hookThis, args) {
@@ -949,12 +1036,12 @@ function getRunTestsWrapper (runTests, config) {
949
1036
  onDone: (isModified) => {
950
1037
  if (isModified) {
951
1038
  test._ddIsModified = true
952
- if (!test.isPending() && !test._ddIsAttemptToFix && config.isEarlyFlakeDetectionEnabled) {
1039
+ if (!test.isPending() && isEarlyFlakeDetectionTest(test, config)) {
953
1040
  retryTest(
954
1041
  test,
955
- getConfiguredEfdRetryCount(config),
1042
+ config.earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
956
1043
  ['_ddIsModified', '_ddIsEfdRetry'],
957
- config.earlyFlakeDetectionSlowTestRetries
1044
+ config.earlyFlakeDetectionRetryPolicy
958
1045
  )
959
1046
  }
960
1047
  }
@@ -968,12 +1055,12 @@ function getRunTestsWrapper (runTests, config) {
968
1055
  suite.tests.forEach((test) => {
969
1056
  if (!test.isPending() && isNewTest(test, config.knownTests)) {
970
1057
  test._ddIsNew = true
971
- if (config.isEarlyFlakeDetectionEnabled && !test._ddIsAttemptToFix && !test._ddIsModified) {
1058
+ if (!test._ddIsModified && isEarlyFlakeDetectionTest(test, config)) {
972
1059
  retryTest(
973
1060
  test,
974
- getConfiguredEfdRetryCount(config),
1061
+ config.earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
975
1062
  ['_ddIsNew', '_ddIsEfdRetry'],
976
- config.earlyFlakeDetectionSlowTestRetries
1063
+ config.earlyFlakeDetectionRetryPolicy
977
1064
  )
978
1065
  }
979
1066
  }
@@ -1015,4 +1102,5 @@ module.exports = {
1015
1102
  testsStatuses,
1016
1103
  attemptToFixExecutions,
1017
1104
  loggedAttemptToFixTests,
1105
+ adjustRunnerFailuresForTestOptimization,
1018
1106
  }
@@ -0,0 +1,44 @@
1
+ 'use strict'
2
+
3
+ const {
4
+ createWebdriverioWorkerMessage,
5
+ WEBDRIVERIO_WORKER_ENV,
6
+ WEBDRIVERIO_WORKER_EVENT,
7
+ WEBDRIVERIO_WORKER_ORIGIN,
8
+ } = require('../../../dd-trace/src/ci-visibility/exporters/test-worker/webdriverio')
9
+
10
+ /**
11
+ * Sends a message over WebdriverIO's worker IPC envelope.
12
+ *
13
+ * @param {object} message
14
+ * @param {(error?: Error) => void} [onError]
15
+ * @param {() => void} [onDone]
16
+ * @returns {void}
17
+ */
18
+ function sendWebdriverioWorkerMessage (message, onError, onDone) {
19
+ if (!process.send || !process.connected) {
20
+ onError?.()
21
+ onDone?.()
22
+ return
23
+ }
24
+
25
+ process.send(createWebdriverioWorkerMessage(message), (error) => {
26
+ if (error) {
27
+ onError?.(error)
28
+ }
29
+ onDone?.()
30
+ })
31
+ }
32
+
33
+ module.exports = {
34
+ CONFIGURATION_REQUEST: 'dd:test-optimization:webdriverio:configuration:request',
35
+ CONFIGURATION_RESPONSE: 'dd:test-optimization:webdriverio:configuration:response',
36
+ createWebdriverioWorkerMessage,
37
+ sendWebdriverioWorkerMessage,
38
+ SUITE_FINISH: 'dd:test-optimization:webdriverio:test-suite:finish',
39
+ WORKER_READY: 'dd:test-optimization:webdriverio:worker:ready',
40
+ WORKER_READY_RESPONSE: 'dd:test-optimization:webdriverio:worker:ready:response',
41
+ WEBDRIVERIO_WORKER_ENV,
42
+ WEBDRIVERIO_WORKER_EVENT,
43
+ WEBDRIVERIO_WORKER_ORIGIN,
44
+ }