dd-trace 6.8.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 (179) hide show
  1. package/ci/diagnose.js +27 -25
  2. package/ci/init.js +6 -6
  3. package/ci/runbook.md +114 -20
  4. package/ci/test-optimization-validation/approval-artifacts.js +36 -3
  5. package/ci/test-optimization-validation/approval.js +78 -22
  6. package/ci/test-optimization-validation/blocker-category.js +24 -0
  7. package/ci/test-optimization-validation/ci-discovery.js +23 -17
  8. package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
  9. package/ci/test-optimization-validation/ci-remediation.js +1 -1
  10. package/ci/test-optimization-validation/cli.js +280 -52
  11. package/ci/test-optimization-validation/command-blocker.js +159 -20
  12. package/ci/test-optimization-validation/command-output-policy.js +9 -3
  13. package/ci/test-optimization-validation/command-runner.js +9 -7
  14. package/ci/test-optimization-validation/environment.js +4 -2
  15. package/ci/test-optimization-validation/executable.js +15 -13
  16. package/ci/test-optimization-validation/execution-lock.js +70 -0
  17. package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
  18. package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
  19. package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
  20. package/ci/test-optimization-validation/generated-files.js +38 -28
  21. package/ci/test-optimization-validation/generated-test-contract.js +8 -4
  22. package/ci/test-optimization-validation/generated-verifier.js +24 -17
  23. package/ci/test-optimization-validation/literal-glob.js +52 -0
  24. package/ci/test-optimization-validation/manifest-loader.js +3 -0
  25. package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
  26. package/ci/test-optimization-validation/manifest-schema.js +80 -17
  27. package/ci/test-optimization-validation/offline-fixtures.js +13 -4
  28. package/ci/test-optimization-validation/package-check.js +94 -0
  29. package/ci/test-optimization-validation/plan-writer.js +183 -10
  30. package/ci/test-optimization-validation/preflight-runner.js +110 -31
  31. package/ci/test-optimization-validation/project-build-artifact.js +31 -0
  32. package/ci/test-optimization-validation/redaction.js +18 -27
  33. package/ci/test-optimization-validation/report-writer.js +182 -48
  34. package/ci/test-optimization-validation/result-semantics.js +20 -3
  35. package/ci/test-optimization-validation/runner-command.js +40 -20
  36. package/ci/test-optimization-validation/runner-contract.js +79 -43
  37. package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
  38. package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
  39. package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
  40. package/ci/test-optimization-validation/static-diagnosis.js +10 -3
  41. package/ci/test-optimization-validation/test-output.js +2 -1
  42. package/ci/test-optimization-validation/validation-blocker.js +21 -0
  43. package/ci/vitest-no-worker-init-setup.mjs +30 -34
  44. package/index.d.ts +98 -1
  45. package/init.js +1 -17
  46. package/initialize.mjs +11 -0
  47. package/loader-hook.mjs +6 -4
  48. package/package.json +5 -3
  49. package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
  50. package/packages/datadog-esbuild/src/utils.js +16 -1
  51. package/packages/datadog-instrumentations/src/ai.js +61 -34
  52. package/packages/datadog-instrumentations/src/bluebird.js +6 -6
  53. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
  54. package/packages/datadog-instrumentations/src/cucumber.js +124 -47
  55. package/packages/datadog-instrumentations/src/dns.js +21 -2
  56. package/packages/datadog-instrumentations/src/electron.js +1 -0
  57. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  58. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  59. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  60. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  61. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +33 -9
  62. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +118 -0
  63. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  64. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  65. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
  66. package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
  67. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  68. package/packages/datadog-instrumentations/src/jest.js +979 -233
  69. package/packages/datadog-instrumentations/src/mocha/main.js +107 -9
  70. package/packages/datadog-instrumentations/src/mocha/utils.js +64 -42
  71. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
  72. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
  73. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  74. package/packages/datadog-instrumentations/src/next.js +155 -23
  75. package/packages/datadog-instrumentations/src/openai.js +14 -0
  76. package/packages/datadog-instrumentations/src/pg.js +180 -6
  77. package/packages/datadog-instrumentations/src/playwright.js +173 -79
  78. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  79. package/packages/datadog-instrumentations/src/promise.js +3 -3
  80. package/packages/datadog-instrumentations/src/router.js +195 -19
  81. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -10
  82. package/packages/datadog-instrumentations/src/vitest-main.js +360 -56
  83. package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
  84. package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
  85. package/packages/datadog-instrumentations/src/webdriverio.js +194 -18
  86. package/packages/datadog-instrumentations/src/when.js +3 -3
  87. package/packages/datadog-instrumentations/src/ws.js +5 -1
  88. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  89. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
  90. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  91. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
  92. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  93. package/packages/datadog-plugin-electron/src/net.js +10 -4
  94. package/packages/datadog-plugin-http/src/client.js +1 -1
  95. package/packages/datadog-plugin-http2/src/client.js +1 -1
  96. package/packages/datadog-plugin-jest/src/index.js +1 -18
  97. package/packages/datadog-plugin-jest/src/util.js +2 -0
  98. package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
  99. package/packages/datadog-plugin-mocha/src/index.js +318 -1
  100. package/packages/datadog-plugin-next/src/index.js +57 -9
  101. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  102. package/packages/datadog-plugin-pg/src/index.js +65 -1
  103. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  104. package/packages/datadog-plugin-vitest/src/index.js +95 -7
  105. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  106. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  107. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  108. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  109. package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
  110. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  111. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  112. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  113. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  114. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  115. package/packages/dd-trace/src/appsec/index.js +48 -476
  116. package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
  117. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  118. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  119. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
  120. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
  121. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
  122. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  123. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  124. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  125. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
  126. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  127. package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
  128. package/packages/dd-trace/src/config/helper.js +2 -0
  129. package/packages/dd-trace/src/config/index.js +16 -5
  130. package/packages/dd-trace/src/config/remote_config.js +1 -1
  131. package/packages/dd-trace/src/config/supported-configurations.json +8 -0
  132. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  133. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  134. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  135. package/packages/dd-trace/src/guardrails/log.js +1 -10
  136. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  137. package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
  138. package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
  139. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  140. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  141. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  142. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  143. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  144. package/packages/dd-trace/src/llmobs/sdk.js +149 -68
  145. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  146. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  147. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  148. package/packages/dd-trace/src/log/channels.js +1 -9
  149. package/packages/dd-trace/src/log/levels.js +12 -0
  150. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  151. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  152. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  153. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  154. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
  155. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  156. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
  157. package/packages/dd-trace/src/plugins/index.js +1 -0
  158. package/packages/dd-trace/src/plugins/util/ci.js +6 -0
  159. package/packages/dd-trace/src/plugins/util/git.js +6 -4
  160. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
  161. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  162. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  163. package/packages/dd-trace/src/plugins/util/test.js +319 -75
  164. package/packages/dd-trace/src/plugins/util/url.js +1 -1
  165. package/packages/dd-trace/src/plugins/util/web.js +23 -1
  166. package/packages/dd-trace/src/profiler.js +1 -1
  167. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  168. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  169. package/packages/dd-trace/src/proxy.js +1 -0
  170. package/packages/dd-trace/src/ritm.js +5 -0
  171. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  172. package/packages/dd-trace/src/sampling_rule.js +4 -2
  173. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  174. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  175. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  176. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  177. package/vendor/dist/pprof-format/index.js +1 -1
  178. package/version.js +10 -8
  179. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -3,6 +3,7 @@
3
3
  const { AsyncResource } = require('node:async_hooks')
4
4
  const { fileURLToPath } = require('node:url')
5
5
 
6
+ const { EMPTY_EFD_RETRY_POLICY } = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
6
7
  const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
7
8
  const log = require('../../dd-trace/src/log')
8
9
  const {
@@ -11,6 +12,7 @@ const {
11
12
  getTestSuitePath,
12
13
  logTestOptimizationSummary,
13
14
  MOCHA_WORKER_LOGS_PAYLOAD_CODE,
15
+ MOCHA_WORKER_TELEMETRY_PAYLOAD_CODE,
14
16
  MOCHA_WORKER_TRACE_PAYLOAD_CODE,
15
17
  TEST_SUITE_EXECUTION_ID,
16
18
  } = require('../../dd-trace/src/plugins/util/test')
@@ -18,31 +20,43 @@ const { addHook, channel, tracingChannel } = require('./helpers/instrument')
18
20
  const {
19
21
  CONFIGURATION_REQUEST,
20
22
  CONFIGURATION_RESPONSE,
23
+ sendWebdriverioWorkerMessage,
21
24
  SUITE_FINISH,
22
25
  WEBDRIVERIO_WORKER_ENV,
23
26
  WEBDRIVERIO_WORKER_EVENT,
24
27
  WEBDRIVERIO_WORKER_ORIGIN,
25
28
  WORKER_READY,
29
+ WORKER_READY_RESPONSE,
26
30
  } = require('./mocha/webdriverio-protocol')
27
31
 
32
+ const jasmineDoneCh = channel('ci:webdriverio:jasmine:done')
28
33
  const testFinishCh = channel('ci:mocha:test:finish')
29
34
  const testSessionStartCh = channel('ci:mocha:session:start')
30
35
  const testSessionFinishCh = channel('ci:mocha:session:finish')
36
+ const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
31
37
  const testSuiteStartCh = channel('ci:mocha:test-suite:start')
32
38
  const testSuiteFinishCh = channel('ci:mocha:test-suite:finish')
33
39
  const knownTestsCh = channel('ci:mocha:known-tests')
34
40
  const libraryConfigurationCh = channel('ci:mocha:library-configuration')
35
41
  const modifiedFilesCh = channel('ci:mocha:modified-files')
36
42
  const testManagementTestsCh = channel('ci:mocha:test-management-tests')
43
+ const workerConfigurationCh = channel('ci:mocha:worker:configuration')
37
44
  const workerReportLogsCh = channel('ci:mocha:worker-report:logs')
45
+ const workerReportTelemetryCh = channel('ci:mocha:worker-report:telemetry')
38
46
  const workerReportTraceCh = channel('ci:mocha:worker-report:trace')
39
47
 
48
+ const jasmineAdapterInitCh = tracingChannel('orchestrion:@wdio/jasmine-framework:JasmineAdapter_init')
40
49
  const launcherStartInstanceCh = tracingChannel('orchestrion:@wdio/cli:Launcher_startInstance')
41
50
  const localRunnerRunCh = tracingChannel('orchestrion:@wdio/local-runner:LocalRunner_run')
42
51
  const localRunnerShutdownCh = tracingChannel('orchestrion:@wdio/local-runner:LocalRunner_shutdown')
43
52
 
44
53
  const NODE_OPTIONS_SEPARATOR_RE = /\s/
54
+ const JASMINE_FRAMEWORK_ADAPTER = 'jasmine'
55
+ const MOCHA_FRAMEWORK_ADAPTER = 'mocha'
56
+ const SUPPORTED_FRAMEWORK_ADAPTERS = new Set([JASMINE_FRAMEWORK_ADAPTER, MOCHA_FRAMEWORK_ADAPTER])
45
57
  const TEST_FRAMEWORK = 'webdriverio'
58
+ const isWebdriverioWorker = !!getEnvironmentVariable(WEBDRIVERIO_WORKER_ENV)
59
+ let jasmineWorkerRequestId = 0
46
60
 
47
61
  const loadCh = channel('dd-trace:instrumentation:load')
48
62
  if (loadCh.hasSubscribers) {
@@ -66,7 +80,7 @@ addHook({
66
80
  * @typedef {object} WebdriverioRunnerConfig
67
81
  * @property {string} framework
68
82
  * @property {string|undefined} rootDir
69
- * @property {NodeJS.ProcessEnv|undefined} runnerEnv
83
+ * @property {typeof process.env|undefined} runnerEnv
70
84
  */
71
85
 
72
86
  /**
@@ -79,6 +93,7 @@ addHook({
79
93
  * @typedef {object} WorkerRecord
80
94
  * @property {object} worker
81
95
  * @property {string[]} specs
96
+ * @property {Map<string, WebdriverioSuiteResult>} reportedSuiteResults
82
97
  * @property {Map<string, WebdriverioSuiteContext>} suiteContexts
83
98
  * @property {string} testSuiteExecutionId
84
99
  * @property {boolean|undefined} hasTests
@@ -89,11 +104,18 @@ addHook({
89
104
  /**
90
105
  * @typedef {object} WebdriverioSuiteContext
91
106
  * @property {object|undefined} currentStore
107
+ * @property {Error|undefined} error
92
108
  * @property {string|undefined} status
93
109
  * @property {string} testSuiteAbsolutePath
94
110
  * @property {string} testSuiteExecutionId
95
111
  */
96
112
 
113
+ /**
114
+ * @typedef {object} WebdriverioSuiteResult
115
+ * @property {{message?: string, stack?: string}|undefined} error
116
+ * @property {string|undefined} status
117
+ */
118
+
97
119
  /**
98
120
  * @typedef {object} CoordinatorState
99
121
  * @property {WebdriverioLocalRunner} localRunner
@@ -112,21 +134,21 @@ addHook({
112
134
  * @property {Set<string>} newTestsWithDynamicNames
113
135
  * @property {number} nextWorkerId
114
136
  * @property {Set<string>} scheduledFiles
137
+ * @property {Map<string, object>} testManagementExecutions
115
138
  * @property {unknown} runError
116
139
  * @property {Set<WorkerRecord>} workers
117
140
  * @property {Map<object, string>} suiteStatuses
118
141
  */
119
142
 
120
143
  /**
121
- * Creates the configuration consumed by Mocha workers.
144
+ * Creates the configuration consumed by WebdriverIO workers.
122
145
  *
123
146
  * @returns {object}
124
147
  */
125
148
  function createWorkerConfiguration () {
126
149
  return {
127
150
  earlyFlakeDetectionFaultyThreshold: 30,
128
- earlyFlakeDetectionNumRetries: 0,
129
- earlyFlakeDetectionSlowTestRetries: {},
151
+ earlyFlakeDetectionRetryPolicy: EMPTY_EFD_RETRY_POLICY,
130
152
  flakyTestRetriesCount: 0,
131
153
  isCodeCoverageEnabled: false,
132
154
  isCoverageReportUploadEnabled: false,
@@ -148,6 +170,103 @@ function createWorkerConfiguration () {
148
170
  }
149
171
  }
150
172
 
173
+ /**
174
+ * Configures a Jasmine worker for reporting-only execution and notifies its coordinator.
175
+ *
176
+ * @param {{_jrunner?: {env?: {addReporter?: (reporter: object) => void}}, _specs?: string[]}} adapter
177
+ * @param {{
178
+ * resolveCallback?: (onDone: () => void) => void,
179
+ * rejectCallback?: (onDone: () => void) => void
180
+ * }} context
181
+ * @returns {void}
182
+ */
183
+ function initializeJasmineWorker (adapter, context) {
184
+ if (!isWebdriverioWorker) {
185
+ return
186
+ }
187
+
188
+ workerConfigurationCh.publish({
189
+ libraryConfig: createWorkerConfiguration(),
190
+ repositoryRoot: undefined,
191
+ specs: adapter?._specs || [],
192
+ testFramework: TEST_FRAMEWORK,
193
+ testFrameworkAdapter: JASMINE_FRAMEWORK_ADAPTER,
194
+ })
195
+
196
+ if (jasmineDoneCh.hasSubscribers) {
197
+ adapter?._jrunner?.env?.addReporter?.({
198
+ /**
199
+ * Publishes run-level failures that are absent from suiteDone.
200
+ *
201
+ * @param {object} result
202
+ * @returns {void}
203
+ */
204
+ jasmineDone (result) {
205
+ jasmineDoneCh.publish({ result })
206
+ },
207
+ })
208
+ }
209
+
210
+ /**
211
+ * Waits until the coordinator has started this worker's parent spans.
212
+ *
213
+ * @param {() => void} onDone
214
+ * @returns {void}
215
+ */
216
+ const waitForCoordinator = onDone => {
217
+ const requestId = `${process.pid}-${++jasmineWorkerRequestId}`
218
+ let finished = false
219
+
220
+ /**
221
+ * Releases Jasmine initialization exactly once.
222
+ *
223
+ * @returns {void}
224
+ */
225
+ function finish () {
226
+ if (finished) {
227
+ return
228
+ }
229
+ finished = true
230
+ clearTimeout(timeout)
231
+ process.off('message', onMessage)
232
+ process.off('disconnect', finish)
233
+ onDone()
234
+ }
235
+
236
+ /**
237
+ * Receives coordinator readiness for this worker.
238
+ *
239
+ * @param {object} message
240
+ * @returns {void}
241
+ */
242
+ function onMessage (message) {
243
+ if (message?.name === WORKER_READY_RESPONSE && message.content?.requestId === requestId) {
244
+ finish()
245
+ }
246
+ }
247
+
248
+ const timeout = setTimeout(finish, 30_000)
249
+ process.on('message', onMessage)
250
+ process.once('disconnect', finish)
251
+ sendWebdriverioWorkerMessage({
252
+ origin: 'datadog',
253
+ name: WORKER_READY,
254
+ content: {
255
+ requestId,
256
+ testFrameworkAdapter: JASMINE_FRAMEWORK_ADAPTER,
257
+ },
258
+ }, error => {
259
+ if (error) {
260
+ log.error('WebdriverIO Test Optimization IPC error', error)
261
+ }
262
+ finish()
263
+ })
264
+ }
265
+
266
+ context.resolveCallback = waitForCoordinator
267
+ context.rejectCallback = waitForCoordinator
268
+ }
269
+
151
270
  /**
152
271
  * Gets the public runner configuration, or the private equivalent used by older releases.
153
272
  *
@@ -187,6 +306,7 @@ function getCoordinatorState (localRunner) {
187
306
  newTestsWithDynamicNames: new Set(),
188
307
  nextWorkerId: 0,
189
308
  scheduledFiles: new Set(),
309
+ testManagementExecutions: new Map(),
190
310
  runError: undefined,
191
311
  workers: new Set(),
192
312
  suiteStatuses: new Map(),
@@ -247,7 +367,7 @@ function includesNodeOptions (workerNodeOptions, launcherNodeOptions) {
247
367
  }
248
368
 
249
369
  /**
250
- * Starts the single Mocha session owned by the WebdriverIO launcher.
370
+ * Starts the single test session owned by the WebdriverIO launcher.
251
371
  *
252
372
  * @param {CoordinatorState} state
253
373
  * @returns {void}
@@ -349,8 +469,7 @@ function configureCoordinator (state, response) {
349
469
  }
350
470
 
351
471
  configuration.earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
352
- configuration.earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
353
- configuration.earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
472
+ configuration.earlyFlakeDetectionRetryPolicy = libraryConfig.earlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
354
473
  configuration.flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
355
474
  configuration.isDiEnabled = libraryConfig.isDiEnabled
356
475
  configuration.isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
@@ -445,6 +564,7 @@ function initializeCoordinator (state, onDone) {
445
564
 
446
565
  try {
447
566
  libraryConfigurationCh.runStores({
567
+ basicReportingOnly: state.testFrameworkAdapter === JASMINE_FRAMEWORK_ADAPTER,
448
568
  disableTestImpactAnalysis: true,
449
569
  frameworkVersion: state.frameworkVersion,
450
570
  isParallel: state.maxActiveWorkers > 1,
@@ -476,7 +596,10 @@ function startWorkerSuites (workerRecord, files) {
476
596
  continue
477
597
  }
478
598
 
599
+ const reportedResult = workerRecord.reportedSuiteResults.get(file)
479
600
  const suiteContext = {
601
+ error: getWebdriverioSuiteError(reportedResult?.error),
602
+ status: reportedResult?.status,
480
603
  testSuiteAbsolutePath: file,
481
604
  testSuiteExecutionId: workerRecord.testSuiteExecutionId,
482
605
  }
@@ -502,6 +625,9 @@ function finishWorkerSuite (state, workerRecord, rawFile, status) {
502
625
  }
503
626
 
504
627
  state.suiteStatuses.set(suiteContext, status)
628
+ if (suiteContext.error) {
629
+ testSuiteErrorCh.runStores(suiteContext, () => {})
630
+ }
505
631
  testSuiteFinishCh.publish({ status, ...suiteContext.currentStore })
506
632
  }
507
633
 
@@ -552,7 +678,7 @@ function sendWorkerMessage (workerRecord, message) {
552
678
  }
553
679
 
554
680
  /**
555
- * Handles a worker request for its Mocha execution configuration.
681
+ * Handles a worker request for its execution configuration.
556
682
  *
557
683
  * @param {CoordinatorState} state
558
684
  * @param {WorkerRecord} workerRecord
@@ -608,7 +734,25 @@ function updateEarlyFlakeDetectionFaultyState (state, files) {
608
734
  }
609
735
 
610
736
  /**
611
- * Handles suite results reported by a Mocha worker.
737
+ * Reconstructs an Error received from a WebdriverIO worker.
738
+ *
739
+ * @param {{message?: string, stack?: string}|undefined} error
740
+ * @returns {Error|undefined}
741
+ */
742
+ function getWebdriverioSuiteError (error) {
743
+ if (!error?.message) {
744
+ return
745
+ }
746
+
747
+ const suiteError = new Error(error.message)
748
+ if (error.stack) {
749
+ suiteError.stack = error.stack
750
+ }
751
+ return suiteError
752
+ }
753
+
754
+ /**
755
+ * Handles suite results reported by a WebdriverIO worker.
612
756
  *
613
757
  * @param {WorkerRecord} workerRecord
614
758
  * @param {object} message
@@ -616,9 +760,12 @@ function updateEarlyFlakeDetectionFaultyState (state, files) {
616
760
  */
617
761
  function handleSuiteResults (workerRecord, message) {
618
762
  const { results = [] } = message.content || {}
619
- for (const { file, status } of results) {
620
- const suiteContext = workerRecord.suiteContexts.get(normalizeFile(file))
763
+ for (const { error, file, status } of results) {
764
+ const normalizedFile = normalizeFile(file)
765
+ workerRecord.reportedSuiteResults.set(normalizedFile, { error, status })
766
+ const suiteContext = workerRecord.suiteContexts.get(normalizedFile)
621
767
  if (suiteContext) {
768
+ suiteContext.error = getWebdriverioSuiteError(error)
622
769
  suiteContext.status = status
623
770
  }
624
771
  }
@@ -643,6 +790,7 @@ function handleWorkerMessage (state, workerRecord, message) {
643
790
  collectTestOptimizationSummariesFromTraces(payload, {
644
791
  attemptToFixExecutions: state.attemptToFixExecutions,
645
792
  newTestsWithDynamicNames: state.newTestsWithDynamicNames,
793
+ testManagementExecutions: state.testManagementExecutions,
646
794
  })
647
795
  workerReportTraceCh.publish({
648
796
  traces: payload,
@@ -650,6 +798,8 @@ function handleWorkerMessage (state, workerRecord, message) {
650
798
  })
651
799
  } else if (messageCode === MOCHA_WORKER_LOGS_PAYLOAD_CODE) {
652
800
  workerReportLogsCh.publish(payload)
801
+ } else if (messageCode === MOCHA_WORKER_TELEMETRY_PAYLOAD_CODE) {
802
+ workerReportTelemetryCh.publish(payload)
653
803
  }
654
804
  return
655
805
  }
@@ -659,7 +809,18 @@ function handleWorkerMessage (state, workerRecord, message) {
659
809
  }
660
810
 
661
811
  if (message.name === WORKER_READY) {
662
- initializeCoordinator(state)
812
+ initializeCoordinator(state, () => {
813
+ if (state.testFrameworkAdapter === JASMINE_FRAMEWORK_ADAPTER) {
814
+ startWorkerSuites(workerRecord, workerRecord.specs)
815
+ }
816
+ if (message.content?.requestId) {
817
+ sendWorkerMessage(workerRecord, {
818
+ origin: 'datadog',
819
+ name: WORKER_READY_RESPONSE,
820
+ content: { requestId: message.content.requestId },
821
+ })
822
+ }
823
+ })
663
824
  return
664
825
  }
665
826
  if (message.name === CONFIGURATION_REQUEST) {
@@ -721,6 +882,7 @@ function registerWorker (state, worker, specs) {
721
882
  const workerRecord = {
722
883
  worker,
723
884
  specs: normalizedSpecs,
885
+ reportedSuiteResults: new Map(),
724
886
  suiteContexts: new Map(),
725
887
  testSuiteExecutionId: String(++state.nextWorkerId),
726
888
  hasTests: undefined,
@@ -768,7 +930,7 @@ function getSessionStatus (state) {
768
930
  }
769
931
 
770
932
  /**
771
- * Finishes the single WebdriverIO-owned Mocha session.
933
+ * Finishes the single WebdriverIO-owned test session.
772
934
  *
773
935
  * @param {CoordinatorState} state
774
936
  * @param {unknown} error
@@ -801,6 +963,7 @@ function finishCoordinator (state, error, onDone) {
801
963
  logTestOptimizationSummary({
802
964
  attemptToFixExecutions: state.attemptToFixExecutions,
803
965
  newTestsWithDynamicNames: state.newTestsWithDynamicNames,
966
+ testManagementExecutions: state.testManagementExecutions,
804
967
  })
805
968
  onDone()
806
969
  return
@@ -819,24 +982,37 @@ function finishCoordinator (state, error, onDone) {
819
982
  logTestOptimizationSummary({
820
983
  attemptToFixExecutions: state.attemptToFixExecutions,
821
984
  newTestsWithDynamicNames: state.newTestsWithDynamicNames,
985
+ testManagementExecutions: state.testManagementExecutions,
822
986
  })
823
987
  }
824
988
 
989
+ // dc-polyfill supports partial tracing-channel subscribers, unlike the Node.js type definition.
990
+ // @ts-expect-error
991
+ jasmineAdapterInitCh.subscribe({
992
+ asyncEnd (context) {
993
+ initializeJasmineWorker(context.result || context.self, context)
994
+ },
995
+ })
996
+
825
997
  // dc-polyfill supports partial tracing-channel subscribers, unlike the Node.js type definition.
826
998
  // @ts-expect-error
827
999
  launcherStartInstanceCh.subscribe({
828
1000
  start (context) {
829
1001
  const localRunner = context.self?.runner
830
1002
  const runnerConfiguration = localRunner && getRunnerConfiguration(localRunner)
831
- if (!testFinishCh.hasSubscribers || runnerConfiguration?.framework !== 'mocha') {
1003
+ if (!testFinishCh.hasSubscribers || !SUPPORTED_FRAMEWORK_ADAPTERS.has(runnerConfiguration?.framework)) {
832
1004
  return
833
1005
  }
834
1006
 
835
1007
  const state = getCoordinatorState(localRunner)
836
1008
  addScheduledFiles(state, context.arguments?.[0] || [])
837
- for (const schedule of context.self._schedule || []) {
838
- for (const { files } of schedule.specs || []) {
839
- addScheduledFiles(state, files)
1009
+ if (context.self._schedule) {
1010
+ for (const schedule of context.self._schedule) {
1011
+ if (schedule.specs) {
1012
+ for (const { files } of schedule.specs) {
1013
+ addScheduledFiles(state, files)
1014
+ }
1015
+ }
840
1016
  }
841
1017
  }
842
1018
  },
@@ -847,7 +1023,7 @@ launcherStartInstanceCh.subscribe({
847
1023
  localRunnerRunCh.subscribe({
848
1024
  start (context) {
849
1025
  const runnerConfiguration = getRunnerConfiguration(context.self)
850
- if (!testFinishCh.hasSubscribers || runnerConfiguration?.framework !== 'mocha') {
1026
+ if (!testFinishCh.hasSubscribers || !SUPPORTED_FRAMEWORK_ADAPTERS.has(runnerConfiguration?.framework)) {
851
1027
  return
852
1028
  }
853
1029
 
@@ -8,7 +8,7 @@ addHook({
8
8
  name: 'when',
9
9
  file: 'lib/Promise.js',
10
10
  versions: ['>=3'],
11
- }, Promise => {
12
- shimmer.wrap(Promise.prototype, 'then', wrapThen)
13
- return Promise
11
+ }, LibraryPromise => {
12
+ shimmer.wrap(LibraryPromise.prototype, 'then', wrapThen)
13
+ return LibraryPromise
14
14
  })
@@ -49,7 +49,11 @@ const setSocketCh = channel('tracing:ws:server:connect:setSocket')
49
49
  */
50
50
 
51
51
  /**
52
- * @typedef {string | Buffer | ArrayBuffer | ArrayBufferView | Blob | Buffer[]} WebSocketMessageData
52
+ * @typedef {{ buffer: ArrayBuffer | SharedArrayBuffer, byteLength: number, byteOffset: number }} BufferView
53
+ */
54
+
55
+ /**
56
+ * @typedef {string | Buffer | ArrayBuffer | BufferView | Blob | Buffer[]} WebSocketMessageData
53
57
  */
54
58
 
55
59
  /**
@@ -487,16 +487,18 @@ function extractMessagesFromConverseContent (role, contentBlocks) {
487
487
  const toolCalls = []
488
488
  const toolResults = []
489
489
 
490
- for (const block of contentBlocks || []) {
491
- if (block == null || typeof block !== 'object') continue
492
- if (typeof block.text === 'string') {
493
- content += block.text
494
- } else if (block.toolUse) {
495
- toolCalls.push(buildToolCall(block.toolUse))
496
- } else if (block.toolResult) {
497
- toolResults.push(buildToolResult(block.toolResult))
498
- } else {
499
- content += `[Unsupported content type: ${getContentBlockType(block)}]`
490
+ if (contentBlocks) {
491
+ for (const block of contentBlocks) {
492
+ if (block == null || typeof block !== 'object') continue
493
+ if (typeof block.text === 'string') {
494
+ content += block.text
495
+ } else if (block.toolUse) {
496
+ toolCalls.push(buildToolCall(block.toolUse))
497
+ } else if (block.toolResult) {
498
+ toolResults.push(buildToolResult(block.toolResult))
499
+ } else {
500
+ content += `[Unsupported content type: ${getContentBlockType(block)}]`
501
+ }
500
502
  }
501
503
  }
502
504
 
@@ -572,14 +574,17 @@ function buildUsage (usage = {}) {
572
574
  */
573
575
  function extractConverseToolDefinitions (params) {
574
576
  const toolDefinitions = []
575
- for (const tool of params.toolConfig?.tools || []) {
576
- const toolSpec = tool?.toolSpec
577
- if (!toolSpec?.name) continue
578
- toolDefinitions.push({
579
- name: toolSpec.name,
580
- description: toolSpec.description ?? '',
581
- schema: toolSpec.inputSchema ?? {},
582
- })
577
+ const tools = params.toolConfig?.tools
578
+ if (tools) {
579
+ for (const tool of tools) {
580
+ const toolSpec = tool?.toolSpec
581
+ if (!toolSpec?.name) continue
582
+ toolDefinitions.push({
583
+ name: toolSpec.name,
584
+ description: toolSpec.description ?? '',
585
+ schema: toolSpec.inputSchema ?? {},
586
+ })
587
+ }
583
588
  }
584
589
  return toolDefinitions
585
590
  }
@@ -593,13 +598,17 @@ function extractConverseToolDefinitions (params) {
593
598
  */
594
599
  function extractRequestParamsConverse (params) {
595
600
  const prompt = []
596
- for (const block of params.system || []) {
597
- if (typeof block?.text === 'string') prompt.push({ content: block.text, role: 'system' })
601
+ if (params.system) {
602
+ for (const block of params.system) {
603
+ if (typeof block?.text === 'string') prompt.push({ content: block.text, role: 'system' })
604
+ }
598
605
  }
599
- for (const msg of params.messages || []) {
600
- if (msg == null || typeof msg !== 'object') continue
601
- const message = extractMessagesFromConverseContent(msg.role || 'user', msg.content)
602
- if (message) prompt.push(message)
606
+ if (params.messages) {
607
+ for (const msg of params.messages) {
608
+ if (msg == null || typeof msg !== 'object') continue
609
+ const message = extractMessagesFromConverseContent(msg.role || 'user', msg.content)
610
+ if (message) prompt.push(message)
611
+ }
603
612
  }
604
613
 
605
614
  const { temperature, topP, maxTokens, stopSequences } = params.inferenceConfig || {}
@@ -632,7 +641,7 @@ function extractTextAndResponseReasonConverse (response) {
632
641
  * response, spread across start/delta chunks. We reassemble those chunks
633
642
  * into a normalized content-block array and reuse the non-stream extractor.
634
643
  *
635
- * @param {Array<object>} chunks - Ordered ConverseStreamOutput events.
644
+ * @param {Array<object> | undefined} chunks - Ordered ConverseStreamOutput events.
636
645
  * @returns {Generation}
637
646
  */
638
647
  function extractTextAndResponseReasonConverseFromStream (chunks) {
@@ -641,29 +650,31 @@ function extractTextAndResponseReasonConverseFromStream (chunks) {
641
650
  let usage = {}
642
651
  const blocksByIdx = new Map()
643
652
 
644
- for (const chunk of chunks || []) {
645
- if (chunk.messageStart?.role) {
646
- role = chunk.messageStart.role
647
- } else if (chunk.messageStop?.stopReason) {
648
- stopReason = chunk.messageStop.stopReason
649
- } else if (chunk.metadata?.usage) {
650
- usage = chunk.metadata.usage
651
- } else if (chunk.contentBlockStart?.start?.toolUse) {
652
- const { contentBlockIndex, start: { toolUse } } = chunk.contentBlockStart
653
- blocksByIdx.set(contentBlockIndex, {
654
- toolUse: { toolUseId: toolUse.toolUseId, name: toolUse.name, inputStr: '' },
655
- })
656
- } else if (chunk.contentBlockDelta) {
657
- const { contentBlockIndex, delta } = chunk.contentBlockDelta
658
- if (typeof delta?.text === 'string') {
659
- const block = blocksByIdx.get(contentBlockIndex) ?? {}
660
- block.text = (block.text ?? '') + delta.text
661
- blocksByIdx.set(contentBlockIndex, block)
662
- } else if (typeof delta?.toolUse?.input === 'string') {
663
- const block = blocksByIdx.get(contentBlockIndex) ?? { toolUse: { inputStr: '' } }
664
- block.toolUse ??= { inputStr: '' }
665
- block.toolUse.inputStr += delta.toolUse.input
666
- blocksByIdx.set(contentBlockIndex, block)
653
+ if (chunks) {
654
+ for (const chunk of chunks) {
655
+ if (chunk.messageStart?.role) {
656
+ role = chunk.messageStart.role
657
+ } else if (chunk.messageStop?.stopReason) {
658
+ stopReason = chunk.messageStop.stopReason
659
+ } else if (chunk.metadata?.usage) {
660
+ usage = chunk.metadata.usage
661
+ } else if (chunk.contentBlockStart?.start?.toolUse) {
662
+ const { contentBlockIndex, start: { toolUse } } = chunk.contentBlockStart
663
+ blocksByIdx.set(contentBlockIndex, {
664
+ toolUse: { toolUseId: toolUse.toolUseId, name: toolUse.name, inputStr: '' },
665
+ })
666
+ } else if (chunk.contentBlockDelta) {
667
+ const { contentBlockIndex, delta } = chunk.contentBlockDelta
668
+ if (typeof delta?.text === 'string') {
669
+ const block = blocksByIdx.get(contentBlockIndex) ?? {}
670
+ block.text = (block.text ?? '') + delta.text
671
+ blocksByIdx.set(contentBlockIndex, block)
672
+ } else if (typeof delta?.toolUse?.input === 'string') {
673
+ const block = blocksByIdx.get(contentBlockIndex) ?? { toolUse: { inputStr: '' } }
674
+ block.toolUse ??= { inputStr: '' }
675
+ block.toolUse.inputStr += delta.toolUse.input
676
+ blocksByIdx.set(contentBlockIndex, block)
677
+ }
667
678
  }
668
679
  }
669
680
  }
@@ -58,9 +58,7 @@ function scrubChildProcessCmd (expression) {
58
58
  for (let index = 0; index < expressionTokens.length; index++) {
59
59
  const token = expressionTokens[index]
60
60
 
61
- if (token === null) {
62
- continue
63
- } else if (typeof token === 'object') {
61
+ if (token !== null && typeof token === 'object') {
64
62
  if (token.pattern) {
65
63
  result.push(token.pattern)
66
64
  } else if (token.op) {
@@ -308,6 +308,7 @@ class CucumberPlugin extends CiPlugin {
308
308
  isNew,
309
309
  isEfdRetry,
310
310
  isFlakyRetry,
311
+ isExternalRetry,
311
312
  isAttemptToFix,
312
313
  isAttemptToFixRetry,
313
314
  hasFailedAllRetries,
@@ -348,9 +349,12 @@ class CucumberPlugin extends CiPlugin {
348
349
  span.setTag(ERROR_MESSAGE, errorMessage)
349
350
  }
350
351
 
351
- if (isFlakyRetry > 0) {
352
+ if (isFlakyRetry) {
352
353
  span.setTag(TEST_IS_RETRY, 'true')
353
354
  span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.atr)
355
+ } else if (isExternalRetry) {
356
+ span.setTag(TEST_IS_RETRY, 'true')
357
+ span.setTag(TEST_RETRY_REASON, TEST_RETRY_REASON_TYPES.ext)
354
358
  }
355
359
 
356
360
  if (hasFailedAllRetries) {