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
@@ -5,6 +5,8 @@ const path = require('node:path')
5
5
 
6
6
  const log = require('../log')
7
7
 
8
+ const BIGINT_LSTAT_OPTIONS = { bigint: true, throwIfNoEntry: false }
9
+
8
10
  /**
9
11
  * Coverage report file patterns to search for
10
12
  * Each entry contains the relative path from root and the format identifier
@@ -38,7 +40,7 @@ const COVERAGE_REPORT_PATTERNS = [
38
40
  /**
39
41
  * Discovers code coverage report files in the given root directory
40
42
  * @param {string} rootDir - The root directory to search for coverage reports
41
- * @returns {Array<{filePath: string, format: string}>} Array of discovered coverage reports
43
+ * @returns {Array<{filePath: string, fileDevice: bigint, fileInode: bigint, format: string}>}
42
44
  */
43
45
  function discoverCoverageReports (rootDir) {
44
46
  if (!rootDir) {
@@ -46,27 +48,57 @@ function discoverCoverageReports (rootDir) {
46
48
  return []
47
49
  }
48
50
 
51
+ let resolvedRoot
52
+ try {
53
+ const unresolvedRoot = path.resolve(rootDir)
54
+ const rootStats = fs.lstatSync(unresolvedRoot, BIGINT_LSTAT_OPTIONS)
55
+ if (rootStats?.isSymbolicLink() || !rootStats?.isDirectory()) {
56
+ log.debug('Coverage report root is not a regular directory: %s', unresolvedRoot)
57
+ return []
58
+ }
59
+ resolvedRoot = fs.realpathSync(unresolvedRoot)
60
+ const resolvedRootStats = fs.lstatSync(resolvedRoot, BIGINT_LSTAT_OPTIONS)
61
+ if (!resolvedRootStats || resolvedRootStats.dev !== rootStats.dev || resolvedRootStats.ino !== rootStats.ino) {
62
+ log.debug('Coverage report root changed while resolving it: %s', unresolvedRoot)
63
+ return []
64
+ }
65
+ } catch (error) {
66
+ log.debug('Error checking coverage report root %s: %s', rootDir, error.message)
67
+ return []
68
+ }
69
+
49
70
  const discoveredReports = []
50
71
 
51
72
  for (const pattern of COVERAGE_REPORT_PATTERNS) {
52
- const fullPath = path.join(rootDir, pattern.path)
73
+ let currentPath = resolvedRoot
53
74
 
54
75
  try {
55
- if (fs.existsSync(fullPath)) {
56
- const stats = fs.statSync(fullPath)
57
-
58
- // Only include regular files, not directories or symlinks
59
- if (stats.isFile()) {
60
- discoveredReports.push({
61
- filePath: fullPath,
62
- format: pattern.format,
63
- })
64
- log.debug('Found coverage report: %s (format: %s)', fullPath, pattern.format)
76
+ let stats
77
+ for (const pathSegment of pattern.path.split('/')) {
78
+ currentPath = path.join(currentPath, pathSegment)
79
+ stats = fs.lstatSync(currentPath, BIGINT_LSTAT_OPTIONS)
80
+ if (!stats || stats.isSymbolicLink()) break
81
+ }
82
+
83
+ // A hard link can name a file outside the root without changing its real path.
84
+ if (stats?.isFile() && stats.nlink === 1n) {
85
+ const resolvedPath = fs.realpathSync(currentPath)
86
+ const pathFromRoot = path.relative(resolvedRoot, resolvedPath)
87
+ if (pathFromRoot === '..' || pathFromRoot.startsWith(`..${path.sep}`) || path.isAbsolute(pathFromRoot)) {
88
+ log.debug('Coverage report resolved outside the root directory: %s', currentPath)
89
+ continue
65
90
  }
91
+ discoveredReports.push({
92
+ filePath: resolvedPath,
93
+ fileDevice: stats.dev,
94
+ fileInode: stats.ino,
95
+ format: pattern.format,
96
+ })
97
+ log.debug('Found coverage report: %s (format: %s)', resolvedPath, pattern.format)
66
98
  }
67
- } catch (err) {
99
+ } catch (error) {
68
100
  // Log but don't fail if we can't access a file
69
- log.debug('Error checking coverage report path %s: %s', fullPath, err.message)
101
+ log.debug('Error checking coverage report path %s: %s', currentPath, error.message)
70
102
  }
71
103
  }
72
104
 
@@ -0,0 +1,89 @@
1
+ 'use strict'
2
+
3
+ const EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS = [
4
+ { limitMs: 5000, key: '5s' },
5
+ { limitMs: 10_000, key: '10s' },
6
+ { limitMs: 30_000, key: '30s' },
7
+ { limitMs: 300_000, key: '5m' },
8
+ ]
9
+
10
+ // A bucket the settings validator accepts but this module ignores silently loses its retries.
11
+ const EARLY_FLAKE_DETECTION_RETRY_BUCKETS =
12
+ Object.freeze(EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS.map(({ key }) => key))
13
+
14
+ /**
15
+ * @typedef {object} EfdDurationRetryCount
16
+ * @property {number} durationLimitMs
17
+ * @property {number} retryCount
18
+ */
19
+
20
+ /**
21
+ * @typedef {object} EfdRetryPolicy
22
+ * @property {readonly EfdDurationRetryCount[]} durationRetryCounts
23
+ * @property {number} schedulingRetryCount
24
+ */
25
+
26
+ /**
27
+ * @param {number} durationMs
28
+ * @param {EfdRetryPolicy} retryPolicy
29
+ * @returns {number}
30
+ */
31
+ function getEfdRetryCountForDuration (durationMs, retryPolicy) {
32
+ for (const { durationLimitMs, retryCount } of retryPolicy.durationRetryCounts) {
33
+ if (durationMs < durationLimitMs) {
34
+ return retryCount
35
+ }
36
+ }
37
+ return 0
38
+ }
39
+
40
+ /**
41
+ * @param {Record<string, unknown> | undefined} retriesByDuration
42
+ * @returns {EfdRetryPolicy}
43
+ */
44
+ function createEfdRetryPolicy (retriesByDuration = {}) {
45
+ const durationRetryCounts = []
46
+ let schedulingRetryCount = 0
47
+ for (const { limitMs: durationLimitMs, key } of EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS) {
48
+ const configuredRetryCount = retriesByDuration[key]
49
+ const retryCount = Number.isSafeInteger(configuredRetryCount) && configuredRetryCount >= 0
50
+ ? configuredRetryCount
51
+ : 0
52
+ durationRetryCounts.push(Object.freeze({ durationLimitMs, retryCount }))
53
+ if (retryCount > schedulingRetryCount) {
54
+ schedulingRetryCount = retryCount
55
+ }
56
+ }
57
+ return Object.freeze({
58
+ durationRetryCounts: Object.freeze(durationRetryCounts),
59
+ schedulingRetryCount,
60
+ })
61
+ }
62
+
63
+ /**
64
+ * @param {EfdRetryPolicy | undefined} retryPolicy
65
+ * @returns {boolean}
66
+ */
67
+ function hasEfdRetries (retryPolicy) {
68
+ return (retryPolicy?.schedulingRetryCount ?? 0) > 0
69
+ }
70
+
71
+ /**
72
+ * @param {number} retryIndex
73
+ * @param {number | undefined} retryCount
74
+ * @returns {boolean}
75
+ */
76
+ function shouldSkipEfdRetry (retryIndex, retryCount) {
77
+ return retryCount !== undefined && retryIndex > retryCount
78
+ }
79
+
80
+ const EMPTY_EFD_RETRY_POLICY = createEfdRetryPolicy()
81
+
82
+ module.exports = {
83
+ EARLY_FLAKE_DETECTION_RETRY_BUCKETS,
84
+ EMPTY_EFD_RETRY_POLICY,
85
+ createEfdRetryPolicy,
86
+ getEfdRetryCountForDuration,
87
+ hasEfdRetries,
88
+ shouldSkipEfdRetry,
89
+ }
@@ -303,10 +303,11 @@ function writeNewFile (filename, payload) {
303
303
  *
304
304
  * @param {string} directory directory path
305
305
  * @param {string} label directory label
306
- * @returns {{dev: number, ino: number}} stable directory identity
306
+ * @returns {{dev: bigint, ino: bigint}} stable directory identity
307
307
  */
308
308
  function captureDirectory (directory, label) {
309
- const stat = fs.lstatSync(directory)
309
+ // Windows file reference numbers exceed 2^53, where distinct directories round to one number.
310
+ const stat = fs.lstatSync(directory, { bigint: true })
310
311
  if (!stat.isDirectory() || stat.isSymbolicLink()) {
311
312
  throw new Error(`Offline Test Optimization validation ${label} must be a regular directory.`)
312
313
  }
@@ -317,10 +318,10 @@ function captureDirectory (directory, label) {
317
318
  * Creates or validates one child directory without accepting symbolic links.
318
319
  *
319
320
  * @param {string} parent parent directory path
320
- * @param {{dev: number, ino: number}} parentIdentity expected parent identity
321
+ * @param {{dev: bigint, ino: bigint}} parentIdentity expected parent identity
321
322
  * @param {string} directory child directory path
322
323
  * @param {string} label directory label
323
- * @returns {{dev: number, ino: number}} stable child identity
324
+ * @returns {{dev: bigint, ino: bigint}} stable child identity
324
325
  */
325
326
  function createDirectory (parent, parentIdentity, directory, label) {
326
327
  assertDirectoryUnchanged(parent, parentIdentity, 'parent output')
@@ -336,7 +337,7 @@ function createDirectory (parent, parentIdentity, directory, label) {
336
337
  * Rejects a directory that changed after sink construction.
337
338
  *
338
339
  * @param {string} directory directory path
339
- * @param {{dev: number, ino: number}} identity expected directory identity
340
+ * @param {{dev: bigint, ino: bigint}} identity expected directory identity
340
341
  * @param {string} label directory label
341
342
  */
342
343
  function assertDirectoryUnchanged (directory, identity, label) {
@@ -351,7 +352,7 @@ function assertDirectoryUnchanged (directory, identity, label) {
351
352
  *
352
353
  * @param {string} filename partial payload path
353
354
  * @param {string} directory expected parent directory
354
- * @param {{dev: number, ino: number}} identity expected parent identity
355
+ * @param {{dev: bigint, ino: bigint}} identity expected parent identity
355
356
  */
356
357
  function removePartialFile (filename, directory, identity) {
357
358
  try {
@@ -4,6 +4,7 @@ const { hostname: getHostname } = require('node:os')
4
4
  const URL = require('url').URL
5
5
 
6
6
  const { version: tracerVersion } = require('../../../../../package.json')
7
+ const { EMPTY_EFD_RETRY_POLICY, createEfdRetryPolicy } = require('../efd-retry-policy')
7
8
  const { getLibraryConfiguration: getLibraryConfigurationRequest } = require('../requests/get-library-configuration')
8
9
  const { getSkippableSuites: getSkippableSuitesRequest } = require('../intelligent-test-runner/get-skippable-suites')
9
10
  const { getKnownTests: getKnownTestsRequest } = require('../early-flake-detection/get-known-tests')
@@ -15,6 +16,7 @@ const { uploadCoverageReport: uploadCoverageReportRequest } = require('../reques
15
16
  const { uploadTestScreenshot: uploadTestScreenshotRequest } = require('../requests/upload-test-screenshot')
16
17
  const { parsers } = require('../../config/parsers')
17
18
  const log = require('../../log')
19
+ const { getSegment } = require('../../util')
18
20
  const BufferingExporter = require('../../exporters/common/buffering-exporter')
19
21
  const { GIT_REPOSITORY_URL, GIT_COMMIT_SHA } = require('../../plugins/util/tags')
20
22
  const { sendGitMetadata: sendGitMetadataRequest } = require('./git/git_metadata')
@@ -22,14 +24,25 @@ const { sendGitMetadata: sendGitMetadataRequest } = require('./git/git_metadata'
22
24
  const hostname = getHostname()
23
25
  const EMPTY_SETTINGS = Object.freeze({})
24
26
 
27
+ /**
28
+ * Test session identity sent with every request. Fields are optional because the CI provider,
29
+ * the git repository or the runtime may not expose them.
30
+ *
31
+ * @typedef {{
32
+ * repositoryUrl?: string, sha?: string, branch?: string, tag?: string, testLevel?: string,
33
+ * osVersion?: string, osPlatform?: string, osArchitecture?: string,
34
+ * runtimeName?: string, runtimeVersion?: string, commitMessage?: string,
35
+ * pullRequestBaseSha?: string, commitHeadSha?: string, commitHeadMessage?: string,
36
+ * }} TestConfiguration
37
+ */
38
+
25
39
  function getTestConfigurationTags (tags) {
26
40
  if (!tags) {
27
41
  return {}
28
42
  }
29
43
  return Object.keys(tags).reduce((acc, key) => {
30
44
  if (key.startsWith('test.configuration.')) {
31
- const [, configKey] = key.split('test.configuration.')
32
- acc[configKey] = tags[key]
45
+ acc[getSegment(key, 'test.configuration.', 1)] = tags[key]
33
46
  }
34
47
  return acc
35
48
  }, {})
@@ -190,6 +203,7 @@ class CiVisibilityExporter extends BufferingExporter {
190
203
  const cachedSkippableSuites = this._testOptimizationHttpCache.readSkippableSuites({
191
204
  testLevel: requestConfiguration.testLevel,
192
205
  isCoverageReportUploadEnabled: requestConfiguration.isCoverageReportUploadEnabled,
206
+ isLineCoverageSupported: requestConfiguration.isLineCoverageSupported,
193
207
  })
194
208
  if (cachedSkippableSuites !== CACHE_MISS) {
195
209
  const { skippableSuites, correlationId, coverage } = cachedSkippableSuites
@@ -238,6 +252,10 @@ class CiVisibilityExporter extends BufferingExporter {
238
252
  /**
239
253
  * We can't request library configuration until we know whether we can use the
240
254
  * CI Visibility Protocol, hence the this._canUseCiVisProtocol promise.
255
+ *
256
+ * @param {TestConfiguration} testConfiguration
257
+ * @param {(error: Error | null, libraryConfig?: Readonly<Record<string, unknown>>) => void} callback
258
+ * @returns {void}
241
259
  */
242
260
  getLibraryConfiguration (testConfiguration, callback) {
243
261
  const { repositoryUrl } = testConfiguration
@@ -321,21 +339,14 @@ class CiVisibilityExporter extends BufferingExporter {
321
339
  DD_TEST_MANAGEMENT_ATTEMPT_TO_FIX_RETRIES: configuredAttemptToFixRetries = 0,
322
340
  DD_TEST_MANAGEMENT_ENABLED: isTestManagementAllowed,
323
341
  } = testOptimization
324
- const hasEarlyFlakeDetectionRetryCount = earlyFlakeDetectionRetryCount !== undefined
325
- const configuredSlowTestRetries = remoteConfiguration.earlyFlakeDetectionSlowTestRetries ?? EMPTY_SETTINGS
326
- const earlyFlakeDetectionSlowTestRetries = hasEarlyFlakeDetectionRetryCount
327
- ? Object.freeze({
342
+ const earlyFlakeDetectionRetryPolicy = earlyFlakeDetectionRetryCount === undefined
343
+ ? remoteConfiguration.earlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
344
+ : createEfdRetryPolicy({
328
345
  '5s': earlyFlakeDetectionRetryCount,
329
346
  '10s': earlyFlakeDetectionRetryCount,
330
347
  '30s': earlyFlakeDetectionRetryCount,
331
348
  '5m': earlyFlakeDetectionRetryCount,
332
349
  })
333
- : Object.isFrozen(configuredSlowTestRetries)
334
- ? configuredSlowTestRetries
335
- : Object.freeze({ ...configuredSlowTestRetries })
336
- const earlyFlakeDetectionNumRetries = hasEarlyFlakeDetectionRetryCount
337
- ? earlyFlakeDetectionRetryCount
338
- : remoteConfiguration.earlyFlakeDetectionNumRetries ?? 0
339
350
  const testManagementAttemptToFixRetries =
340
351
  remoteConfiguration.testManagementAttemptToFixRetries ?? configuredAttemptToFixRetries
341
352
 
@@ -346,8 +357,7 @@ class CiVisibilityExporter extends BufferingExporter {
346
357
  requireGit: remoteConfiguration.requireGit === true,
347
358
  isEarlyFlakeDetectionEnabled:
348
359
  remoteConfiguration.isEarlyFlakeDetectionEnabled === true && isEarlyFlakeDetectionAllowed === true,
349
- earlyFlakeDetectionNumRetries,
350
- earlyFlakeDetectionSlowTestRetries,
360
+ earlyFlakeDetectionRetryPolicy,
351
361
  earlyFlakeDetectionFaultyThreshold: remoteConfiguration.earlyFlakeDetectionFaultyThreshold ?? 30,
352
362
  isFlakyTestRetriesEnabled:
353
363
  remoteConfiguration.isFlakyTestRetriesEnabled === true && isFlakyTestRetriesAllowed === true,
@@ -521,17 +531,21 @@ class CiVisibilityExporter extends BufferingExporter {
521
531
  * Uploads a single coverage report to the CI intake.
522
532
  * @param {object} options - Upload options
523
533
  * @param {string} options.filePath - Path to the coverage report file
534
+ * @param {bigint} options.fileDevice - Device containing the discovered report
535
+ * @param {bigint} options.fileInode - Inode of the discovered report
524
536
  * @param {string} options.format - Format of the coverage report
525
537
  * @param {object} options.testEnvironmentMetadata - Test environment metadata containing git/CI tags
526
538
  * @param {(error: Error|null) => void} callback - Callback function
527
539
  */
528
- uploadCoverageReport ({ filePath, format, testEnvironmentMetadata }, callback) {
540
+ uploadCoverageReport ({ filePath, fileDevice, fileInode, format, testEnvironmentMetadata }, callback) {
529
541
  if (!this._codeCoverageReportUrl) {
530
542
  return callback(new Error('Coverage report upload URL not configured'))
531
543
  }
532
544
 
533
545
  uploadCoverageReportRequest({
534
546
  filePath,
547
+ fileDevice,
548
+ fileInode,
535
549
  format,
536
550
  flags: this._coverageReportFlags,
537
551
  testEnvironmentMetadata,
@@ -8,6 +8,7 @@ const FormData = require('../../../exporters/common/form-data')
8
8
  const request = require('../../../exporters/common/request')
9
9
 
10
10
  const log = require('../../../log')
11
+ const { getSegment } = require('../../../util')
11
12
  const {
12
13
  getLatestCommits,
13
14
  getRepositoryUrl,
@@ -31,6 +32,9 @@ const {
31
32
  TELEMETRY_GIT_REQUESTS_OBJECT_PACKFILES_BYTES,
32
33
  } = require('../../../ci-visibility/telemetry')
33
34
 
35
+ /** @typedef {{ isEvpProxy: boolean, evpProxyPrefix: string }} EvpProxyConfiguration */
36
+ /** @typedef {EvpProxyConfiguration & { url: URL, repositoryUrl: string }} GitUploadTarget */
37
+
34
38
  const isValidSha1 = (sha) => /^[0-9a-f]{40}$/.test(sha)
35
39
  const isValidSha256 = (sha) => /^[0-9a-f]{64}$/.test(sha)
36
40
 
@@ -61,6 +65,10 @@ function getCommonRequestOptions (url) {
61
65
  * This function posts the SHAs of the commits of the last month
62
66
  * The response are the commits for which the backend already has information
63
67
  * This response is used to know which commits can be ignored from there on
68
+ *
69
+ * @param {GitUploadTarget & { latestCommits: string[] }} options
70
+ * @param {(error: Error | null, commitsToUpload?: string[]) => void} callback
71
+ * @returns {void}
64
72
  */
65
73
  function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy, evpProxyPrefix }, callback) {
66
74
  const commonOptions = getCommonRequestOptions(url)
@@ -126,6 +134,10 @@ function getCommitsToUpload ({ url, repositoryUrl, latestCommits, isEvpProxy, ev
126
134
 
127
135
  /**
128
136
  * This function uploads a git packfile
137
+ *
138
+ * @param {GitUploadTarget & { packFileToUpload: string, headCommit: string }} options
139
+ * @param {(error: Error | null, uploadSize?: number) => void} callback
140
+ * @returns {void}
129
141
  */
130
142
  function uploadPackFile ({ url, isEvpProxy, evpProxyPrefix, packFileToUpload, repositoryUrl, headCommit }, callback) {
131
143
  const form = new FormData()
@@ -145,7 +157,7 @@ function uploadPackFile ({ url, isEvpProxy, evpProxyPrefix, packFileToUpload, re
145
157
  try {
146
158
  const packFileContent = fs.readFileSync(packFileToUpload)
147
159
  // The original filename includes a random prefix, so we remove it here
148
- const [, filename] = path.basename(packFileToUpload).split('-')
160
+ const filename = getSegment(path.basename(packFileToUpload), '-', 1)
149
161
  form.append('packfile', packFileContent, {
150
162
  filename,
151
163
  contentType: 'application/octet-stream',
@@ -244,6 +256,12 @@ function generateAndUploadPackFiles ({
244
256
 
245
257
  /**
246
258
  * This function uploads git metadata to CI Visibility's backend.
259
+ *
260
+ * @param {URL} url
261
+ * @param {EvpProxyConfiguration} evpProxyConfiguration
262
+ * @param {string | undefined} configRepositoryUrl repository URL from the configuration, if set
263
+ * @param {(error?: Error | null) => void} callback
264
+ * @returns {void}
247
265
  */
248
266
  function sendGitMetadata (url, { isEvpProxy, evpProxyPrefix }, configRepositoryUrl, callback) {
249
267
  if (!isGitAvailable()) {
@@ -5,11 +5,17 @@ const {
5
5
  JEST_WORKER_TRACE_PAYLOAD_CODE,
6
6
  JEST_WORKER_TELEMETRY_PAYLOAD_CODE,
7
7
  CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
8
+ CUCUMBER_WORKER_TELEMETRY_PAYLOAD_CODE,
9
+ MOCHA_WORKER_LOGS_PAYLOAD_CODE,
10
+ MOCHA_WORKER_TELEMETRY_PAYLOAD_CODE,
8
11
  MOCHA_WORKER_TRACE_PAYLOAD_CODE,
9
12
  JEST_WORKER_LOGS_PAYLOAD_CODE,
13
+ PLAYWRIGHT_WORKER_TELEMETRY_PAYLOAD_CODE,
10
14
  PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE,
11
15
  VITEST_WORKER_TRACE_PAYLOAD_CODE,
16
+ VITEST_WORKER_COVERAGE_PAYLOAD_CODE,
12
17
  VITEST_WORKER_LOGS_PAYLOAD_CODE,
18
+ VITEST_WORKER_TELEMETRY_PAYLOAD_CODE,
13
19
  } = require('../../../plugins/util/test')
14
20
  const getConfig = require('../../../config')
15
21
  const { getEnvironmentVariable } = require('../../../config/helper')
@@ -43,6 +49,9 @@ function getInterprocessCoverageCode () {
43
49
  if (getEnvironmentVariable('JEST_WORKER_ID')) {
44
50
  return JEST_WORKER_COVERAGE_PAYLOAD_CODE
45
51
  }
52
+ if (getEnvironmentVariable('TINYPOOL_WORKER_ID') || getConfig().DD_VITEST_WORKER) {
53
+ return VITEST_WORKER_COVERAGE_PAYLOAD_CODE
54
+ }
46
55
  return null
47
56
  }
48
57
 
@@ -50,6 +59,9 @@ function getInterprocessLogsCode () {
50
59
  if (getEnvironmentVariable('JEST_WORKER_ID')) {
51
60
  return JEST_WORKER_LOGS_PAYLOAD_CODE
52
61
  }
62
+ if (getEnvironmentVariable('MOCHA_WORKER_ID') === 'webdriverio') {
63
+ return MOCHA_WORKER_LOGS_PAYLOAD_CODE
64
+ }
53
65
  if (getEnvironmentVariable('TINYPOOL_WORKER_ID')) {
54
66
  return VITEST_WORKER_LOGS_PAYLOAD_CODE
55
67
  }
@@ -63,6 +75,19 @@ function getInterprocessTelemetryCode () {
63
75
  if (getEnvironmentVariable('JEST_WORKER_ID')) {
64
76
  return JEST_WORKER_TELEMETRY_PAYLOAD_CODE
65
77
  }
78
+ if (getEnvironmentVariable('CUCUMBER_WORKER_ID')) {
79
+ return CUCUMBER_WORKER_TELEMETRY_PAYLOAD_CODE
80
+ }
81
+ if (getEnvironmentVariable('MOCHA_WORKER_ID')) {
82
+ return MOCHA_WORKER_TELEMETRY_PAYLOAD_CODE
83
+ }
84
+ const { DD_PLAYWRIGHT_WORKER, DD_VITEST_WORKER } = getConfig()
85
+ if (DD_PLAYWRIGHT_WORKER) {
86
+ return PLAYWRIGHT_WORKER_TELEMETRY_PAYLOAD_CODE
87
+ }
88
+ if (getEnvironmentVariable('TINYPOOL_WORKER_ID') || DD_VITEST_WORKER) {
89
+ return VITEST_WORKER_TELEMETRY_PAYLOAD_CODE
90
+ }
66
91
  return null
67
92
  }
68
93
 
@@ -81,7 +106,6 @@ class TestWorkerCiVisibilityExporter {
81
106
  this._writer = new Writer(interprocessTraceCode)
82
107
  this._coverageWriter = new Writer(interprocessCoverageCode)
83
108
  this._logsWriter = new Writer(interprocessLogsCode)
84
- // TODO: add support for test workers other than Jest
85
109
  if (interprocessTelemetryCode) {
86
110
  this._telemetryWriter = new Writer(interprocessTelemetryCode)
87
111
  this.exportTelemetry = function (telemetryEvent) {
@@ -0,0 +1,26 @@
1
+ 'use strict'
2
+
3
+ const WEBDRIVERIO_WORKER_ENV = '_DD_TEST_OPTIMIZATION_WEBDRIVERIO_WORKER'
4
+ const WEBDRIVERIO_WORKER_EVENT = 'workerEvent'
5
+ const WEBDRIVERIO_WORKER_ORIGIN = 'datadog'
6
+
7
+ /**
8
+ * Wraps an internal worker payload in the event WebdriverIO reserves for worker messages.
9
+ *
10
+ * @param {unknown} args
11
+ * @returns {{args: unknown, name: string, origin: string}}
12
+ */
13
+ function createWebdriverioWorkerMessage (args) {
14
+ return {
15
+ origin: WEBDRIVERIO_WORKER_ORIGIN,
16
+ name: WEBDRIVERIO_WORKER_EVENT,
17
+ args,
18
+ }
19
+ }
20
+
21
+ module.exports = {
22
+ createWebdriverioWorkerMessage,
23
+ WEBDRIVERIO_WORKER_ENV,
24
+ WEBDRIVERIO_WORKER_EVENT,
25
+ WEBDRIVERIO_WORKER_ORIGIN,
26
+ }
@@ -2,6 +2,10 @@
2
2
  const { JSONEncoder } = require('../../encode/json-encoder')
3
3
  const { getEnvironmentVariable } = require('../../../config/helper')
4
4
  const log = require('../../../log')
5
+ const {
6
+ createWebdriverioWorkerMessage,
7
+ WEBDRIVERIO_WORKER_ENV,
8
+ } = require('./webdriverio')
5
9
 
6
10
  function getVitestWorkerPort () {
7
11
  const port = globalThis.__vitest_worker__?.ctx?.port
@@ -13,6 +17,7 @@ class Writer {
13
17
  this._encoder = new JSONEncoder()
14
18
  // Code used to identify the type of payload being sent to the main process
15
19
  this._interprocessCode = interprocessCode
20
+ this._isWebdriverioWorker = !!getEnvironmentVariable(WEBDRIVERIO_WORKER_ENV)
16
21
  }
17
22
 
18
23
  /**
@@ -43,9 +48,12 @@ class Writer {
43
48
  // Old because vitest@>=4 uses `DD_VITEST_WORKER` and reports arrays just like other frameworks
44
49
  // Before vitest@>=4, we need the `__tinypool_worker_message__` property, or tinypool will crash
45
50
  const isVitestWorkerOld = !!getEnvironmentVariable('TINYPOOL_WORKER_ID')
46
- const payload = isVitestWorkerOld
51
+ let payload = isVitestWorkerOld
47
52
  ? { __tinypool_worker_message__: true, interprocessCode: this._interprocessCode, data }
48
53
  : [this._interprocessCode, data]
54
+ if (this._isWebdriverioWorker) {
55
+ payload = createWebdriverioWorkerMessage(payload)
56
+ }
49
57
 
50
58
  const vitestWorkerPort = getVitestWorkerPort()
51
59
  if (vitestWorkerPort) {
@@ -25,6 +25,7 @@ function parseSkippableSuitesResponse (
25
25
  {
26
26
  testLevel = 'suite',
27
27
  isCoverageReportUploadEnabled = false,
28
+ isLineCoverageSupported = true,
28
29
  validateRequiredFields = false,
29
30
  validationMode = false,
30
31
  } = {}
@@ -34,8 +35,11 @@ function parseSkippableSuitesResponse (
34
35
  validateSkippableTestsResponse(parsedResponse, { validationMode })
35
36
  }
36
37
  const coverage = {}
37
- for (const [filename, bitmap] of Object.entries(parsedResponse.meta?.coverage || {})) {
38
- coverage[filename.replaceAll('\\', '/')] = bitmap
38
+ const coverageByFilename = parsedResponse.meta?.coverage
39
+ if (coverageByFilename) {
40
+ for (const [filename, bitmap] of Object.entries(coverageByFilename)) {
41
+ coverage[filename.replaceAll('\\', '/')] = bitmap
42
+ }
39
43
  }
40
44
 
41
45
  const skippableItems = parsedResponse
@@ -51,7 +55,7 @@ function parseSkippableSuitesResponse (
51
55
  },
52
56
  } of skippableItems) {
53
57
  // Only reject candidates without backend line coverage when we need that coverage to backfill reports.
54
- if (isCoverageReportUploadEnabled && isMissingLineCodeCoverage) {
58
+ if (isCoverageReportUploadEnabled && isLineCoverageSupported && isMissingLineCodeCoverage) {
55
59
  numExcludedByMissingLineCoverage++
56
60
  continue
57
61
  }
@@ -78,10 +82,11 @@ function parseSkippableSuitesResponse (
78
82
  * numExcludedByMissingLineCoverage?: number
79
83
  * }} result - Parsed skippable response.
80
84
  * @param {'suite'|'test'} testLevel - Test optimization skipping level.
85
+ * @param {boolean} shouldConsiderLineCoverage - Whether line coverage can affect suite skipping.
81
86
  * @returns {void}
82
87
  */
83
- function logSkippableSuitesResponse (result, testLevel) {
84
- if (result.numReceivedSkippableItems === undefined) {
88
+ function logSkippableSuitesResponse (result, testLevel, shouldConsiderLineCoverage) {
89
+ if (!shouldConsiderLineCoverage || result.numReceivedSkippableItems === undefined) {
85
90
  log.debug('Number of received skippable %ss: %d', testLevel, result.skippableSuites.length)
86
91
  return
87
92
  }
@@ -122,10 +127,12 @@ function getSkippableSuites ({
122
127
  custom,
123
128
  testLevel = 'suite',
124
129
  isCoverageReportUploadEnabled = false,
130
+ isLineCoverageSupported = true,
125
131
  }, done) {
132
+ const shouldConsiderLineCoverage = isCoverageReportUploadEnabled && isLineCoverageSupported
126
133
  const cacheKey = buildCacheKey('skippable', [
127
134
  sha, service, env, repositoryUrl, osPlatform, osVersion, osArchitecture,
128
- runtimeName, runtimeVersion, testLevel, custom, isCoverageReportUploadEnabled,
135
+ runtimeName, runtimeVersion, testLevel, custom, shouldConsiderLineCoverage,
129
136
  ])
130
137
 
131
138
  withCache(cacheKey, (activeCacheKey, cb) => {
@@ -146,11 +153,16 @@ function getSkippableSuites ({
146
153
  custom,
147
154
  testLevel,
148
155
  isCoverageReportUploadEnabled,
156
+ isLineCoverageSupported,
149
157
  cacheKey: activeCacheKey,
150
158
  }, cb)
151
159
  }, (err, data) => {
152
160
  if (err) return done(err)
153
- logSkippableSuitesResponse(data, testLevel)
161
+ logSkippableSuitesResponse(
162
+ data,
163
+ testLevel,
164
+ shouldConsiderLineCoverage
165
+ )
154
166
  done(null, data.skippableSuites, data.correlationId, data.coverage)
155
167
  })
156
168
  }
@@ -175,6 +187,7 @@ function getSkippableSuites ({
175
187
  * @param {object} [params.custom]
176
188
  * @param {string} [params.testLevel]
177
189
  * @param {boolean} [params.isCoverageReportUploadEnabled]
190
+ * @param {boolean} [params.isLineCoverageSupported]
178
191
  * @param {string | null} params.cacheKey
179
192
  * @param {Function} done
180
193
  */
@@ -195,6 +208,7 @@ function fetchFromApi ({
195
208
  custom,
196
209
  testLevel,
197
210
  isCoverageReportUploadEnabled,
211
+ isLineCoverageSupported,
198
212
  cacheKey,
199
213
  }, done) {
200
214
  const options = {
@@ -258,6 +272,7 @@ function fetchFromApi ({
258
272
  const result = parseSkippableSuitesResponse(parsedResponse, {
259
273
  testLevel,
260
274
  isCoverageReportUploadEnabled,
275
+ isLineCoverageSupported,
261
276
  validateRequiredFields: true,
262
277
  })
263
278
  const skippableItems = parsedResponse.data.filter(({ type }) => type === testLevel)