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
@@ -27,7 +27,7 @@ class FastifyTracingPlugin extends RouterPlugin {
27
27
  }
28
28
 
29
29
  function getParentStore (ctx) {
30
- ctx.parentStore = ctx.parentStore ?? storage('legacy').getStore()
30
+ ctx.parentStore ??= storage('legacy').getStore()
31
31
  return ctx.parentStore
32
32
  }
33
33
 
@@ -7,10 +7,6 @@ class FsPlugin extends TracingPlugin {
7
7
  static operation = 'operation'
8
8
  static experimental = true
9
9
 
10
- configure (...args) {
11
- return super.configure(...args)
12
- }
13
-
14
10
  bindStart (ctx) {
15
11
  if (!this.activeSpan) return { noop: true }
16
12
 
@@ -96,6 +96,20 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
96
96
  })
97
97
  }
98
98
 
99
+ start (ctx) {
100
+ if (!this.config.dsmEnabled) return
101
+ const { message } = ctx
102
+ if (!message?.attributes) return
103
+
104
+ const { span } = ctx.currentStore
105
+ const subscription = message._subscriber._subscription
106
+ const topic = subscription?.metadata?.topic || message.attributes?.['pubsub.topic']
107
+ const payloadSize = getMessageSize(message)
108
+ this.tracer.decodeDataStreamsContext(message.attributes)
109
+ this.tracer
110
+ .setCheckpoint(['direction:in', `topic:${topic}`, 'type:google-pubsub'], span, payloadSize)
111
+ }
112
+
99
113
  bindStart (ctx) {
100
114
  const { message } = ctx
101
115
  const subscription = message._subscriber._subscription
@@ -181,13 +195,6 @@ class GoogleCloudPubsubConsumerPlugin extends ConsumerPlugin {
181
195
  }
182
196
  }
183
197
 
184
- if (this.config.dsmEnabled && message?.attributes) {
185
- const payloadSize = getMessageSize(message)
186
- this.tracer.decodeDataStreamsContext(message.attributes)
187
- this.tracer
188
- .setCheckpoint(['direction:in', `topic:${topic}`, 'type:google-pubsub'], span, payloadSize)
189
- }
190
-
191
198
  return ctx.currentStore
192
199
  }
193
200
 
@@ -36,6 +36,25 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
36
36
  if (topicName) attributes['pubsub.topic'] = topicName
37
37
  }
38
38
 
39
+ start (ctx) {
40
+ if (!this.config.dsmEnabled) return
41
+ if (ctx.api !== 'publish' || !ctx.currentStore) return
42
+ const { request } = ctx
43
+ const messages = request.messages || []
44
+ const topic = request.topic
45
+ const { span } = ctx.currentStore
46
+
47
+ for (const msg of messages) {
48
+ const attributes = msg.attributes ??= {}
49
+ const dataStreamsContext = this.tracer.setCheckpoint(
50
+ ['direction:out', `topic:${topic}`, 'type:google-pubsub'],
51
+ span,
52
+ getHeadersSize(msg)
53
+ )
54
+ DsmPathwayCodec.encode(dataStreamsContext, attributes)
55
+ }
56
+ }
57
+
39
58
  bindStart (ctx) {
40
59
  const { request, api, projectId } = ctx
41
60
  if (api !== 'publish') return
@@ -113,7 +132,6 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
113
132
 
114
133
  const messageCountStr = String(messageCount)
115
134
  const startTimeStr = String(Math.floor(batchSpan._startTime))
116
- const dsmEnabled = this.config.dsmEnabled
117
135
 
118
136
  for (let i = 0; i < messageCount; i++) {
119
137
  const msg = messages[i]
@@ -137,15 +155,6 @@ class GoogleCloudPubsubProducerPlugin extends ProducerPlugin {
137
155
  if (batchTraceIdUpper) {
138
156
  attributes['_dd.pubsub_request.p.tid'] = batchTraceIdUpper
139
157
  }
140
-
141
- if (dsmEnabled) {
142
- const dataStreamsContext = this.tracer.setCheckpoint(
143
- ['direction:out', `topic:${topic}`, 'type:google-pubsub'],
144
- batchSpan,
145
- getHeadersSize(msg)
146
- )
147
- DsmPathwayCodec.encode(dataStreamsContext, attributes)
148
- }
149
158
  }
150
159
 
151
160
  ctx.batchSpan = batchSpan
@@ -495,6 +495,7 @@ function wrapResolve (resolve) {
495
495
  // run in the parent store, not field.currentStore: the first sibling's
496
496
  // synchronous resolver already finished the shared graphql.resolve span, so
497
497
  // re-entering its store would parent user spans to a closed span.
498
+ // eslint-disable-next-line unicorn/prefer-else-if -- Keep sibling early return outside first-field setup.
498
499
  if (!isFirst) {
499
500
  return callInAsyncScope(resolve, this, arguments, rootCtx.abortController, field.parentStore, (err) => {
500
501
  if (updateFieldCh.hasSubscribers) {
@@ -11,7 +11,7 @@ const transforms = require('./transforms')
11
11
  const cache = new WeakMap()
12
12
 
13
13
  function defaultEngineReportingSignature (ast, operationName) {
14
- const key = operationName == null ? '' : operationName
14
+ const key = operationName ?? ''
15
15
  let inner = cache.get(ast)
16
16
  if (inner !== undefined) {
17
17
  const cached = inner.get(key)
@@ -10,6 +10,7 @@ const HTTP_HEADERS = formats.HTTP_HEADERS
10
10
  const urlFilter = require('../../dd-trace/src/plugins/util/urlfilter')
11
11
  const { buildClientHttpUrl } = require('../../dd-trace/src/plugins/util/url')
12
12
  const log = require('../../dd-trace/src/log')
13
+ const { stripQueryAndFragment } = require('../../dd-trace/src/util')
13
14
  const { CLIENT_PORT_KEY, COMPONENT, ERROR_MESSAGE, ERROR_TYPE, ERROR_STACK } = require('../../dd-trace/src/constants')
14
15
 
15
16
  const HTTP_STATUS_CODE = tags.HTTP_STATUS_CODE
@@ -32,7 +33,7 @@ class HttpClientPlugin extends ClientPlugin {
32
33
  // A URL object (e.g. from the fetch integration) carries the query in
33
34
  // `options.search`, not `options.path`; keep it so url.full retains the query.
34
35
  const pathname = options.path || `${options.pathname || ''}${options.search || ''}`
35
- const path = pathname ? pathname.split(/[?#]/)[0] : '/'
36
+ const path = pathname ? stripQueryAndFragment(pathname) : '/'
36
37
  const uri = `${base}${path}`
37
38
 
38
39
  const allowed = this.config.filter(uri)
@@ -55,7 +56,7 @@ class HttpClientPlugin extends ClientPlugin {
55
56
  'out.host': hostname,
56
57
  },
57
58
  metrics: {
58
- [CLIENT_PORT_KEY]: Number.parseInt(options.port),
59
+ [CLIENT_PORT_KEY]: Number.parseInt(options.port, 10),
59
60
  },
60
61
  }, false)
61
62
 
@@ -55,7 +55,7 @@ class Http2ClientPlugin extends ClientPlugin {
55
55
  'out.host': sessionDetails.host,
56
56
  },
57
57
  metrics: {
58
- [CLIENT_PORT_KEY]: Number.parseInt(sessionDetails.port),
58
+ [CLIENT_PORT_KEY]: Number.parseInt(sessionDetails.port, 10),
59
59
  },
60
60
  }, false)
61
61
 
@@ -197,7 +197,7 @@ class JestPlugin extends CiPlugin {
197
197
  config._ddRequestErrorTags = this.getSessionRequestErrorTags()
198
198
  config._ddItrCorrelationId = this.itrCorrelationId
199
199
  config._ddIsEarlyFlakeDetectionEnabled = !!this.libraryConfig?.isEarlyFlakeDetectionEnabled
200
- config._ddEarlyFlakeDetectionSlowTestRetries = this.libraryConfig?.earlyFlakeDetectionSlowTestRetries ?? {}
200
+ config._ddEarlyFlakeDetectionRetryPolicy = this.libraryConfig?.earlyFlakeDetectionRetryPolicy
201
201
  config._ddRepositoryRoot = this.repositoryRoot
202
202
  config._ddIsFlakyTestRetriesEnabled = this.libraryConfig?.isFlakyTestRetriesEnabled ?? false
203
203
  config._ddIsTestManagementTestsEnabled = this.libraryConfig?.isTestManagementEnabled ?? false
@@ -237,9 +237,8 @@ class JestPlugin extends CiPlugin {
237
237
 
238
238
  const testSuiteMetadata = {
239
239
  ...getTestSuiteCommonTags(testCommand, frameworkVersion, testSuite, 'jest'),
240
- // requestErrorTags from test env options may be undefined
241
- ...(requestErrorTags !== undefined && requestErrorTags !== null ? requestErrorTags : {}),
242
- ...(itrSkippingEnabledTags !== undefined && itrSkippingEnabledTags !== null ? itrSkippingEnabledTags : {}),
240
+ ...requestErrorTags,
241
+ ...itrSkippingEnabledTags,
243
242
  }
244
243
 
245
244
  if (_ddUnskippable) {
@@ -300,23 +299,6 @@ class JestPlugin extends CiPlugin {
300
299
  }
301
300
  })
302
301
 
303
- this.addSub('ci:jest:worker-report:telemetry', data => {
304
- const telemetryEvents = JSON.parse(data)
305
- for (const event of telemetryEvents) {
306
- if (event.type === 'ciVisEvent') {
307
- this.telemetry.ciVisEvent(event.name, event.testLevel, {
308
- ...event.tags,
309
- testFramework: event.testFramework,
310
- isUnsupportedCIProvider: event.isUnsupportedCIProvider,
311
- })
312
- } else if (event.type === 'count') {
313
- this.telemetry.count(event.name, event.tags, event.value)
314
- } else if (event.type === 'distribution') {
315
- this.telemetry.distribution(event.name, event.tags, event.measure)
316
- }
317
- }
318
- })
319
-
320
302
  this.addSub('ci:jest:test-suite:finish', ({
321
303
  status,
322
304
  errorMessage,
@@ -15,6 +15,8 @@ const log = require('../../dd-trace/src/log')
15
15
  * [[1, 2, 3], [2, 3, 5]]
16
16
  * 2. An array of objects, e.g.
17
17
  * [{ a: 1, b: 2, expected: 3 }, { a: 2, b: 3, expected: 5}]
18
+ *
19
+ * @param {unknown[]} testParameters `test.each` arguments
18
20
  */
19
21
  function getFormattedJestTestParameters (testParameters) {
20
22
  if (!testParameters || !testParameters.length) {
@@ -43,7 +45,8 @@ function getFormattedJestTestParameters (testParameters) {
43
45
 
44
46
  // @fast-check/jest appends a random seed to the reported test name. A test name that keeps changing
45
47
  // breaks some Test Optimization features, so normalize this narrow suffix regardless of import style.
46
- const SEED_SUFFIX_RE = /\s*\(with seed=-?\d+\)\s*$/i
48
+ // fast-check emits exactly one space before the suffix.
49
+ const SEED_SUFFIX_RE = / ?\(with seed=-?\d+\) ?$/i
47
50
 
48
51
  function removeSeedSuffixFromTestName (testName) {
49
52
  return testName.replace(SEED_SUFFIX_RE, '')
@@ -29,12 +29,13 @@ class KafkajsProducerPlugin extends ProducerPlugin {
29
29
  /**
30
30
  *
31
31
  * @typedef {object} ProducerResponseItem
32
- * @property {string} topic
32
+ * @property {string} topicName
33
33
  * @property {number} partition
34
34
  * @property {import('kafkajs/utils/long').Long} [offset]
35
35
  * @property {import('kafkajs/utils/long').Long} [baseOffset]
36
36
  *
37
37
  * @param {ProducerResponseItem} response
38
+ * @param {string} [clusterId]
38
39
  * @returns {ProducerBacklog}
39
40
  */
40
41
  transformProduceResponse (response, clusterId) {
@@ -9,7 +9,7 @@ function getTokensFromLlmOutput (result) {
9
9
  const { llmOutput } = result
10
10
  if (!llmOutput) return tokens
11
11
 
12
- const tokenUsage = llmOutput.tokenUsage || llmOutput.usage_metadata || llmOutput.usage_metadata
12
+ const tokenUsage = llmOutput.tokenUsage || llmOutput.usage_metadata
13
13
  if (!tokenUsage) return tokens
14
14
 
15
15
  for (const tokenNames of [['input', 'prompt'], ['output', 'completion'], ['total']]) {
@@ -25,7 +25,7 @@ function getTokensFromLlmOutput (result) {
25
25
  }
26
26
 
27
27
  // assign total_tokens again in case it was improperly set the first time, or was not on tokenUsage
28
- tokens.total = tokens.total || tokens.input + tokens.output
28
+ tokens.total ||= tokens.input + tokens.output
29
29
 
30
30
  return tokens
31
31
  }
@@ -1,7 +1,16 @@
1
1
  'use strict'
2
2
 
3
+ const { fileURLToPath } = require('node:url')
4
+
5
+ const { channel } = require('dc-polyfill')
6
+
3
7
  const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
4
8
  const { storage } = require('../../datadog-core')
9
+ const log = require('../../dd-trace/src/log')
10
+ const {
11
+ sendWebdriverioWorkerMessage,
12
+ SUITE_FINISH,
13
+ } = require('../../datadog-instrumentations/src/mocha/webdriverio-protocol')
5
14
 
6
15
  const {
7
16
  TEST_STATUS,
@@ -34,6 +43,8 @@ const {
34
43
  TEST_IS_MODIFIED,
35
44
  TEST_FINAL_STATUS,
36
45
  TEST_HAS_DYNAMIC_NAME,
46
+ TEST_FRAMEWORK_ADAPTER,
47
+ getTestSuiteExecutionKey,
37
48
  isModifiedTest,
38
49
  } = require('../../dd-trace/src/plugins/util/test')
39
50
  const { COMPONENT } = require('../../dd-trace/src/constants')
@@ -49,6 +60,94 @@ const {
49
60
  TELEMETRY_TEST_SESSION,
50
61
  } = require('../../dd-trace/src/ci-visibility/telemetry')
51
62
 
63
+ const jasmineAdapterRunAsyncEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineAdapter_run:asyncEnd'
64
+ const jasmineDoneCh = 'ci:webdriverio:jasmine:done'
65
+ const jasmineReporterSpecDoneEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_specDone:end'
66
+ const jasmineReporterSpecStartedEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_specStarted:end'
67
+ const jasmineReporterSuiteDoneEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_suiteDone:end'
68
+ const jasmineReporterSuiteStartedEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineReporter_suiteStarted:end'
69
+ const jasmineTestFunctionStartCh = 'tracing:orchestrion:@wdio/utils:testFrameworkFnWrapper:start'
70
+ const testFinishCh = channel('ci:mocha:test:finish')
71
+ const WEBDRIVERIO_JASMINE_ADAPTER = 'jasmine'
72
+ const workerFinishCh = channel('ci:mocha:worker:finish')
73
+
74
+ /**
75
+ * @typedef {object} WebdriverioJasmineResult
76
+ * @property {string|undefined} id
77
+ * @property {string|undefined} description
78
+ * @property {Array<{message?: string, stack?: string}>|undefined} failedExpectations
79
+ * @property {string|undefined} file
80
+ * @property {string|undefined} filename
81
+ * @property {string|undefined} fullName
82
+ * @property {string|undefined} parentSuiteId
83
+ * @property {string|undefined} status
84
+ */
85
+
86
+ /**
87
+ * Normalizes a WebdriverIO Jasmine spec identifier to a filesystem path.
88
+ *
89
+ * @param {string|undefined} file
90
+ * @returns {string|undefined}
91
+ */
92
+ function normalizeJasmineFile (file) {
93
+ return file?.startsWith('file://') ? fileURLToPath(file) : file
94
+ }
95
+
96
+ /**
97
+ * Maps a Jasmine result status to the Test Optimization status vocabulary.
98
+ *
99
+ * @param {string|undefined} status
100
+ * @returns {'pass'|'fail'|'skip'}
101
+ */
102
+ function getJasmineStatus (status) {
103
+ if (status === 'passed') return 'pass'
104
+ if (status === 'failed') return 'fail'
105
+ return 'skip'
106
+ }
107
+
108
+ /**
109
+ * Converts Jasmine's failed-expectation shape to an Error.
110
+ *
111
+ * @param {WebdriverioJasmineResult} result
112
+ * @returns {Error|undefined}
113
+ */
114
+ function getJasmineError (result) {
115
+ const failedExpectation = result.failedExpectations?.[0]
116
+ if (!failedExpectation) return
117
+
118
+ const error = new Error(failedExpectation.message)
119
+ if (failedExpectation.stack) {
120
+ error.stack = failedExpectation.stack
121
+ }
122
+ return error
123
+ }
124
+
125
+ /**
126
+ * Resolves the spec file responsible for a run-level Jasmine failure.
127
+ *
128
+ * @param {WebdriverioJasmineResult|undefined} result
129
+ * @param {string[]} specs
130
+ * @returns {string|undefined}
131
+ */
132
+ function getJasmineFailureFile (result, specs) {
133
+ const resultFile = normalizeJasmineFile(result?.file || result?.filename)
134
+ if (resultFile) {
135
+ return resultFile
136
+ }
137
+
138
+ const stack = result?.failedExpectations?.[0]?.stack
139
+ if (stack) {
140
+ for (const spec of specs) {
141
+ const file = normalizeJasmineFile(spec)
142
+ if (file && stack.includes(file)) {
143
+ return file
144
+ }
145
+ }
146
+ }
147
+
148
+ return specs.length === 1 ? normalizeJasmineFile(specs[0]) : undefined
149
+ }
150
+
52
151
  class MochaPlugin extends CiPlugin {
53
152
  static id = 'mocha'
54
153
 
@@ -58,6 +157,100 @@ class MochaPlugin extends CiPlugin {
58
157
  this._testTitleToParams = {}
59
158
  this.sourceRoot = process.cwd()
60
159
 
160
+ this.addSub('ci:mocha:worker:configuration', ({
161
+ libraryConfig,
162
+ repositoryRoot,
163
+ specs,
164
+ testFramework,
165
+ testFrameworkAdapter,
166
+ }) => {
167
+ this.libraryConfig = libraryConfig
168
+ this.testFramework = testFramework
169
+ this.testFrameworkAdapter = testFrameworkAdapter
170
+ this._setRepositoryRoot(repositoryRoot)
171
+ if (testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
172
+ this._webdriverioJasmineState = {
173
+ currentResult: undefined,
174
+ specs: specs || [],
175
+ suiteErrors: new Map(),
176
+ suiteFiles: new Map(),
177
+ suiteStatuses: new Map(),
178
+ tests: new Map(),
179
+ }
180
+ }
181
+ })
182
+
183
+ this.addBind(jasmineTestFunctionStartCh, (ctx) => {
184
+ if (this.testFrameworkAdapter !== WEBDRIVERIO_JASMINE_ADAPTER) {
185
+ return storage('legacy').getStore()
186
+ }
187
+
188
+ const result = this._webdriverioJasmineState?.currentResult
189
+ const type = ctx.arguments?.[1]
190
+ if (type === 'Test' || (type === 'Hook' && result)) {
191
+ return this.#startWebdriverioJasmineTest(result)
192
+ }
193
+ return storage('legacy').getStore()
194
+ })
195
+
196
+ this.addSub(jasmineReporterSuiteStartedEndCh, (ctx) => {
197
+ if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
198
+ const suite = ctx.arguments?.[0]
199
+ const file = normalizeJasmineFile(suite?.filename)
200
+ if (suite?.id && file) {
201
+ this._webdriverioJasmineState.suiteFiles.set(suite.id, file)
202
+ }
203
+ }
204
+ })
205
+
206
+ this.addSub(jasmineReporterSuiteDoneEndCh, (ctx) => {
207
+ if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
208
+ const state = this._webdriverioJasmineState
209
+ const suite = ctx.arguments?.[0]
210
+ const error = suite && getJasmineError(suite)
211
+ const file = normalizeJasmineFile(
212
+ suite?.filename ||
213
+ state.suiteFiles.get(suite?.id) ||
214
+ (state.specs.length === 1 ? state.specs[0] : undefined)
215
+ )
216
+ if (error && file) {
217
+ state.suiteErrors.set(file, error)
218
+ state.suiteStatuses.set(file, 'fail')
219
+ }
220
+ }
221
+ })
222
+
223
+ this.addSub(jasmineDoneCh, ({ result } = {}) => {
224
+ if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
225
+ const state = this._webdriverioJasmineState
226
+ const error = result && getJasmineError(result)
227
+ const file = getJasmineFailureFile(result, state.specs)
228
+ if (error && file) {
229
+ state.suiteErrors.set(file, error)
230
+ state.suiteStatuses.set(file, 'fail')
231
+ }
232
+ }
233
+ })
234
+
235
+ this.addSub(jasmineReporterSpecStartedEndCh, (ctx) => {
236
+ if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
237
+ this._webdriverioJasmineState.currentResult = ctx.arguments?.[0]
238
+ this.#startWebdriverioJasmineTest(ctx.arguments?.[0], ctx.self?._specs, ctx.self?.startedSuite)
239
+ }
240
+ })
241
+
242
+ this.addSub(jasmineReporterSpecDoneEndCh, (ctx) => {
243
+ if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
244
+ this.#finishWebdriverioJasmineTest(ctx.arguments?.[0], ctx.self?._specs)
245
+ }
246
+ })
247
+
248
+ this.addSub(jasmineAdapterRunAsyncEndCh, (ctx) => {
249
+ if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
250
+ this.#finishWebdriverioJasmineWorker(ctx)
251
+ }
252
+ })
253
+
61
254
  this.addSub('ci:mocha:test-suite:code-coverage', ({ coverageFiles, suiteFile }) => {
62
255
  if (!this.libraryConfig?.isCodeCoverageEnabled) {
63
256
  return
@@ -88,23 +281,33 @@ class MochaPlugin extends CiPlugin {
88
281
  })
89
282
 
90
283
  this.addBind('ci:mocha:test-suite:start', (ctx) => {
91
- const { testSuiteAbsolutePath, isUnskippable, isForcedToRun, itrCorrelationId } = ctx
284
+ const {
285
+ testSuiteAbsolutePath,
286
+ testSuiteExecutionId,
287
+ isUnskippable,
288
+ isForcedToRun,
289
+ itrCorrelationId,
290
+ } = ctx
92
291
 
93
292
  // If the test module span is undefined, the plugin has not been initialized correctly and we bail out
94
293
  if (!this.testModuleSpan) {
95
294
  return
96
295
  }
97
296
  const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
297
+ const testFramework = this.testFramework || this.constructor.id
98
298
  const testSuiteMetadata = {
99
299
  ...getTestSuiteCommonTags(
100
300
  this.command,
101
301
  this.frameworkVersion,
102
302
  testSuite,
103
- 'mocha'
303
+ testFramework
104
304
  ),
105
305
  ...this.getSessionRequestErrorTags(),
106
306
  ...this.getSessionItrSkippingEnabledTags(),
107
307
  }
308
+ if (this.testFrameworkAdapter) {
309
+ testSuiteMetadata[TEST_FRAMEWORK_ADAPTER] = this.testFrameworkAdapter
310
+ }
108
311
  if (isUnskippable) {
109
312
  testSuiteMetadata[TEST_ITR_UNSKIPPABLE] = 'true'
110
313
  this.telemetry.count(TELEMETRY_ITR_UNSKIPPABLE, { testLevel: 'suite' })
@@ -144,8 +347,9 @@ class MochaPlugin extends CiPlugin {
144
347
  const store = storage('legacy').getStore()
145
348
  ctx.parentStore = store
146
349
  ctx.currentStore = { ...store, testSuiteSpan }
147
- this._testSuiteSpansByTestSuite.set(testSuite, testSuiteSpan)
148
- this._exportPendingWorkerTracesForTestSuite(testSuite)
350
+ const testSuiteKey = getTestSuiteExecutionKey(testSuite, testSuiteExecutionId)
351
+ this._testSuiteSpansByTestSuite.set(testSuiteKey, testSuiteSpan)
352
+ this._exportPendingWorkerTracesForTestSuite(testSuiteKey)
149
353
  })
150
354
 
151
355
  this.addSub('ci:mocha:test-suite:finish', ({ testSuiteSpan, status }) => {
@@ -203,8 +407,8 @@ class MochaPlugin extends CiPlugin {
203
407
  return ctx.currentStore
204
408
  })
205
409
 
206
- this.addSub('ci:mocha:worker:finish', () => {
207
- this.tracer._exporter.flush()
410
+ this.addSub('ci:mocha:worker:finish', ({ onDone } = {}) => {
411
+ this.tracer._exporter.flush(onDone)
208
412
  })
209
413
 
210
414
  this.addSub('ci:mocha:test:finish', ({
@@ -465,6 +669,138 @@ class MochaPlugin extends CiPlugin {
465
669
  })
466
670
  }
467
671
 
672
+ /**
673
+ * Starts a Jasmine test span around WebdriverIO's test-function wrapper.
674
+ *
675
+ * @param {WebdriverioJasmineResult|undefined} result
676
+ * @param {string[]} [specs]
677
+ * @param {{filename?: string}|undefined} currentSuite
678
+ * @returns {object|undefined}
679
+ */
680
+ #startWebdriverioJasmineTest (result, specs, currentSuite) {
681
+ const state = this._webdriverioJasmineState
682
+ const currentStore = storage('legacy').getStore()
683
+ if (!state || !result?.id) {
684
+ return currentStore
685
+ }
686
+
687
+ const existingTest = state.tests.get(result.id)
688
+ if (existingTest) {
689
+ return existingTest.currentStore
690
+ }
691
+
692
+ const candidateSpecs = specs || state.specs
693
+ const testSuiteAbsolutePath = normalizeJasmineFile(
694
+ state.suiteFiles.get(result.parentSuiteId) ||
695
+ currentSuite?.filename ||
696
+ (candidateSpecs.length === 1 ? candidateSpecs[0] : undefined) ||
697
+ result.file ||
698
+ result.filename ||
699
+ candidateSpecs[0]
700
+ )
701
+ if (!testSuiteAbsolutePath) {
702
+ return currentStore
703
+ }
704
+
705
+ const span = this.startTestSpan({
706
+ testName: result.fullName || result.description,
707
+ testSuiteAbsolutePath,
708
+ title: result.description,
709
+ })
710
+ const testStore = { ...currentStore, span }
711
+ state.tests.set(result.id, {
712
+ currentStore: testStore,
713
+ span,
714
+ testSuiteAbsolutePath,
715
+ })
716
+ this.activeTestSpan = span
717
+
718
+ return testStore
719
+ }
720
+
721
+ /**
722
+ * Finishes a Jasmine test after its reporter receives the final result.
723
+ *
724
+ * @param {WebdriverioJasmineResult|undefined} result
725
+ * @param {string[]} [specs]
726
+ * @returns {void}
727
+ */
728
+ #finishWebdriverioJasmineTest (result, specs) {
729
+ const state = this._webdriverioJasmineState
730
+ if (!state || !result?.id) {
731
+ return
732
+ }
733
+
734
+ this.#startWebdriverioJasmineTest(result, specs)
735
+ const test = state.tests.get(result.id)
736
+ if (!test) {
737
+ return
738
+ }
739
+
740
+ const status = getJasmineStatus(result.status)
741
+ const error = getJasmineError(result)
742
+ if (error) {
743
+ test.span.setTag('error', error)
744
+ }
745
+ testFinishCh.publish({ span: test.span, status })
746
+ state.tests.delete(result.id)
747
+ if (state.currentResult?.id === result.id) {
748
+ state.currentResult = undefined
749
+ }
750
+
751
+ const previousStatus = state.suiteStatuses.get(test.testSuiteAbsolutePath)
752
+ if (status === 'fail' || !previousStatus || previousStatus === 'skip') {
753
+ state.suiteStatuses.set(test.testSuiteAbsolutePath, status)
754
+ }
755
+ }
756
+
757
+ /**
758
+ * Reports Jasmine suite statuses and flushes worker traces before its run settles.
759
+ *
760
+ * @param {{
761
+ * resolveCallback?: (onDone: () => void) => void,
762
+ * rejectCallback?: (onDone: () => void) => void
763
+ * }} context
764
+ * @returns {void}
765
+ */
766
+ #finishWebdriverioJasmineWorker (context) {
767
+ const state = this._webdriverioJasmineState
768
+ const results = []
769
+ const reportedFiles = new Set()
770
+ for (const [file, status] of state.suiteStatuses) {
771
+ const error = state.suiteErrors.get(file)
772
+ const result = { file, status }
773
+ if (error) {
774
+ result.error = {
775
+ message: error.message,
776
+ stack: error.stack,
777
+ }
778
+ }
779
+ results.push(result)
780
+ reportedFiles.add(file)
781
+ }
782
+ for (const spec of state.specs) {
783
+ const file = normalizeJasmineFile(spec)
784
+ if (!reportedFiles.has(file)) {
785
+ results.push({ file, status: 'skip' })
786
+ }
787
+ }
788
+
789
+ const waitForWorker = onDone => {
790
+ sendWebdriverioWorkerMessage({
791
+ origin: 'datadog',
792
+ name: SUITE_FINISH,
793
+ content: { results },
794
+ }, error => {
795
+ if (error) {
796
+ log.error('WebdriverIO Test Optimization IPC error', error)
797
+ }
798
+ }, () => workerFinishCh.publish({ onDone }))
799
+ }
800
+ context.resolveCallback = waitForWorker
801
+ context.rejectCallback = waitForWorker
802
+ }
803
+
468
804
  startTestSpan (testInfo) {
469
805
  const {
470
806
  testName,