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
@@ -116,6 +116,66 @@ function wrapModelWithLifecycle (model) {
116
116
  }
117
117
  }
118
118
 
119
+ /**
120
+ * Wraps an OTel span without changing its method receivers.
121
+ *
122
+ * OTel spans may use private fields, so methods must run against the original
123
+ * span. A per-invocation wrapper also preserves `ctx` without mutating the AI
124
+ * SDK's shared no-op span.
125
+ *
126
+ * @param {import('@opentelemetry/api').Span} span
127
+ * @param {object} ctx
128
+ * @returns {import('@opentelemetry/api').Span}
129
+ */
130
+ function createDelegatingSpan (span, ctx) {
131
+ return {
132
+ spanContext () {
133
+ return span.spanContext.apply(span, arguments)
134
+ },
135
+ setAttribute () {
136
+ span.setAttribute.apply(span, arguments)
137
+ return this
138
+ },
139
+ setAttributes (attributes) {
140
+ vercelAiSpanSetAttributesChannel.publish({ ctx, attributes })
141
+ span.setAttributes.apply(span, arguments)
142
+ return this
143
+ },
144
+ addEvent () {
145
+ span.addEvent.apply(span, arguments)
146
+ return this
147
+ },
148
+ addLink () {
149
+ span.addLink.apply(span, arguments)
150
+ return this
151
+ },
152
+ addLinks () {
153
+ span.addLinks.apply(span, arguments)
154
+ return this
155
+ },
156
+ setStatus () {
157
+ span.setStatus.apply(span, arguments)
158
+ return this
159
+ },
160
+ updateName () {
161
+ span.updateName.apply(span, arguments)
162
+ return this
163
+ },
164
+ isRecording () {
165
+ return span.isRecording.apply(span, arguments)
166
+ },
167
+ recordException (exception) {
168
+ ctx.error = exception
169
+ vercelAiTracingChannel.error.publish(ctx)
170
+ return span.recordException.apply(span, arguments)
171
+ },
172
+ end () {
173
+ vercelAiTracingChannel.asyncEnd.publish(ctx)
174
+ return span.end.apply(span, arguments)
175
+ },
176
+ }
177
+ }
178
+
119
179
  function wrapTracer (tracer) {
120
180
  if (tracers.has(tracer)) {
121
181
  return
@@ -136,40 +196,7 @@ function wrapTracer (tracer) {
136
196
 
137
197
  args[args.length - 1] = shimmer.wrapFunction(cb, function (originalCb) {
138
198
  return function (span) {
139
- // A plain delegating wrapper is used instead of Object.create(span) because
140
- // Object.create (and Proxy) cannot cross private-field brand checks — any span
141
- // method that reads a private field (e.g. BridgeSpanBase#statusCode) would throw
142
- // "Cannot read private member" on a prototype clone. Every method here calls
143
- // through to the real span instance so private fields always resolve correctly.
144
- const freshSpan = {
145
- spanContext () { return span.spanContext() },
146
- setAttribute (key, value) { span.setAttribute(key, value); return freshSpan },
147
- setAttributes (attributes) {
148
- vercelAiSpanSetAttributesChannel.publish({ ctx, attributes })
149
- span.setAttributes(attributes)
150
- return freshSpan
151
- },
152
- addEvent (name, attributesOrStartTime, startTime) {
153
- span.addEvent(name, attributesOrStartTime, startTime)
154
- return freshSpan
155
- },
156
- addLink (link, attrs) { span.addLink(link, attrs); return freshSpan },
157
- addLinks (links) { span.addLinks(links); return freshSpan },
158
- setStatus (status) { span.setStatus(status); return freshSpan },
159
- updateName (spanName) { span.updateName(spanName); return freshSpan },
160
- isRecording () { return span.isRecording() },
161
- recordException (exception, timeInput) {
162
- ctx.error = exception
163
- vercelAiTracingChannel.error.publish(ctx)
164
- span.recordException(exception, timeInput)
165
- },
166
- end (...endArgs) {
167
- vercelAiTracingChannel.asyncEnd.publish(ctx)
168
- span.end(...endArgs)
169
- },
170
- }
171
-
172
- return originalCb.call(this, freshSpan)
199
+ return originalCb.call(this, createDelegatingSpan(span, ctx))
173
200
  }
174
201
  })
175
202
 
@@ -15,12 +15,12 @@ function createGetNewLibraryCopyWrap (originalLib) {
15
15
  }
16
16
  }
17
17
 
18
- addHook({ name: 'bluebird', versions: ['>=2.0.2'] }, Promise => {
19
- shimmer.wrap(Promise.prototype, '_then', wrapThen)
20
- return Promise
18
+ addHook({ name: 'bluebird', versions: ['>=2.0.2'] }, LibraryPromise => {
19
+ shimmer.wrap(LibraryPromise.prototype, '_then', wrapThen)
20
+ return LibraryPromise
21
21
  })
22
22
 
23
- addHook({ name: 'bluebird', versions: ['^2.11.0', '^3.4.1'] }, Promise => {
24
- shimmer.wrap(Promise, 'getNewLibraryCopy', createGetNewLibraryCopyWrap(Promise))
25
- return Promise
23
+ addHook({ name: 'bluebird', versions: ['^2.11.0', '^3.4.1'] }, LibraryPromise => {
24
+ shimmer.wrap(LibraryPromise, 'getNewLibraryCopy', createGetNewLibraryCopyWrap(LibraryPromise))
25
+ return LibraryPromise
26
26
  })
@@ -13,6 +13,66 @@ const LOCAL_LIFECYCLE_LOOKAHEAD = 4
13
13
 
14
14
  const chunkEmitTimes = new WeakMap()
15
15
 
16
+ /**
17
+ * @typedef {{ session_id?: string } & Record<string, unknown>} Chunk
18
+ */
19
+
20
+ /**
21
+ * @typedef {{
22
+ * id: string,
23
+ * name?: string,
24
+ * input?: unknown,
25
+ * output?: unknown,
26
+ * error?: unknown,
27
+ * isInterrupt?: boolean,
28
+ * sessionId?: string,
29
+ * hookStartTime?: number,
30
+ * hookFinishTime?: number,
31
+ * }} ToolState
32
+ */
33
+
34
+ /**
35
+ * @typedef {{ taskStartedChunk?: Chunk, toolResultIndex?: number }} ToolLifecycle
36
+ */
37
+
38
+ /**
39
+ * @typedef {(startIndex: number, toolUseId: string) => ToolLifecycle} GetLifecycle
40
+ */
41
+
42
+ /**
43
+ * @typedef {{
44
+ * tools: Map<string, ToolState>,
45
+ * subagents: Map<string, Record<string, unknown>>,
46
+ * prompt?: string,
47
+ * model?: string,
48
+ * resume?: string,
49
+ * maxTurns?: number,
50
+ * permissionMode?: string,
51
+ * sessionId?: string,
52
+ * source?: string,
53
+ * cwd?: string,
54
+ * transcriptPath?: string,
55
+ * agentType?: string,
56
+ * endReason?: string,
57
+ * stopReason?: string,
58
+ * lastAssistantMessage?: string,
59
+ * }} SessionContext
60
+ */
61
+
62
+ /**
63
+ * @typedef {{
64
+ * stepIndex: number,
65
+ * startTime?: number,
66
+ * finishTime?: number,
67
+ * parentToolUseId?: string,
68
+ * sessionId?: string,
69
+ * chunks?: Chunk[],
70
+ * llmStartIdx?: number,
71
+ * llmEndIdx?: number,
72
+ * toolOutputs?: unknown[],
73
+ * }} StepContext
74
+ */
75
+
16
76
  function hasDownstreamSubscribers () {
17
77
  return queryChannel.asyncEnd.hasSubscribers ||
18
78
  queryChannel.error.hasSubscribers ||
@@ -287,10 +347,12 @@ function getToolResultContent (chunk, toolUseId) {
287
347
  }
288
348
 
289
349
  /**
290
- *
291
- * @param {Array<Record<string, unknown>>} chunks
350
+ * @param {Chunk[]} chunks
292
351
  * @param {number} startIndex
293
352
  * @param {string} toolUseId
353
+ * @param {SessionContext | null | undefined} sessionCtx
354
+ * @param {GetLifecycle} getLifecycle
355
+ * @param {ToolLifecycle} lifecycle
294
356
  * @returns {number} index in chunks where the next step should start iteration
295
357
  */
296
358
  function processTool (chunks, startIndex, toolUseId, sessionCtx, getLifecycle, lifecycle) {
@@ -342,9 +404,14 @@ function processTool (chunks, startIndex, toolUseId, sessionCtx, getLifecycle, l
342
404
  }
343
405
 
344
406
  /**
345
- *
346
- * @param {Array<Record<string, unknown>>} chunks
407
+ * @param {Chunk[]} chunks
347
408
  * @param {number} startIndex
409
+ * @param {number | undefined} stepStartTime
410
+ * @param {string | null | undefined} parentToolUseId
411
+ * @param {string | undefined} initialPrompt
412
+ * @param {StepContext | null | undefined} stepCtx
413
+ * @param {SessionContext | null | undefined} sessionCtx
414
+ * @param {GetLifecycle} getLifecycle
348
415
  * @returns {number} index in chunks where the next step should start iteration
349
416
  */
350
417
  function processStep (
@@ -447,7 +514,16 @@ function processStep (
447
514
  }
448
515
 
449
516
  /**
450
- * @param {Array<Record<string, unknown>>} chunks
517
+ * @param {Chunk[]} chunks
518
+ * @param {{
519
+ * sessionCtx?: SessionContext,
520
+ * session_id?: string,
521
+ * cwd?: string,
522
+ * permissionMode?: string,
523
+ * output?: unknown,
524
+ * arguments?: Array<{ prompt?: string }>,
525
+ * runInContext?: (fn: () => number) => number,
526
+ * }} agentCtx
451
527
  */
452
528
  function processChunks (chunks, agentCtx) {
453
529
  let chunkIndex = 0
@@ -7,6 +7,11 @@ const shimmer = require('../../datadog-shimmer')
7
7
  const log = require('../../dd-trace/src/log')
8
8
  const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
9
9
  const { getSegment } = require('../../dd-trace/src/util')
10
+ const {
11
+ EMPTY_EFD_RETRY_POLICY,
12
+ getEfdRetryCountForDuration,
13
+ hasEfdRetries,
14
+ } = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
10
15
  const {
11
16
  getCoveredFilesFromCoverage,
12
17
  getExecutableFilesFromCoverage,
@@ -16,15 +21,16 @@ const {
16
21
  getTestSuitePath,
17
22
  getRelativeCoverageFiles,
18
23
  CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
24
+ CUCUMBER_WORKER_TELEMETRY_PAYLOAD_CODE,
19
25
  getIsFaultyEarlyFlakeDetection,
20
- getEfdRetryCount,
21
- getMaxEfdRetryCount,
22
26
  applySkippedCoverageToCoverage,
23
27
  getTestCoverageLinesPercentage,
28
+ recordTestManagementExecution,
24
29
  recordAttemptToFixExecution,
25
- collectAttemptToFixExecutionsFromTraces,
30
+ collectTestOptimizationSummariesFromTraces,
26
31
  logAttemptToFixTestExecution,
27
32
  logTestOptimizationSummary,
33
+ TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE,
28
34
  getTestOptimizationRequestResults,
29
35
  } = require('../../dd-trace/src/plugins/util/test')
30
36
  const { writeCoverageBackfillToCache } = require('../../dd-trace/src/ci-visibility/test-optimization-cache')
@@ -57,12 +63,25 @@ const modifiedFilesCh = channel('ci:cucumber:modified-files')
57
63
  const isModifiedCh = channel('ci:cucumber:is-modified-test')
58
64
 
59
65
  const workerReportTraceCh = channel('ci:cucumber:worker-report:trace')
66
+ const workerReportTelemetryCh = channel('ci:cucumber:worker-report:telemetry')
60
67
 
61
68
  const itrSkippedSuitesCh = channel('ci:cucumber:itr:skipped-suites')
62
69
 
63
70
  const getCodeCoverageCh = channel('ci:nyc:get-coverage')
64
71
 
65
72
  const DD_EFD_RETRY_COUNT_MESSAGE = '_ddEfdRetryCount'
73
+ const CUCUMBER_RETRY_NAME_SUFFIX = / ?\(attempt \d+(?:, retried)?\) ?$/
74
+
75
+ /**
76
+ * Removes Cucumber's generated retry suffix without changing literal scenario names.
77
+ *
78
+ * @param {string} testName
79
+ * @param {boolean} isRetry
80
+ * @returns {string}
81
+ */
82
+ function getCucumberTestName (testName, isRetry) {
83
+ return isRetry ? testName.replace(CUCUMBER_RETRY_NAME_SUFFIX, '') : testName
84
+ }
66
85
 
67
86
  const isMarkedAsUnskippable = (pickle) => {
68
87
  return pickle.tags.some(tag => tag.name === '@datadog:unskippable')
@@ -106,8 +125,7 @@ let isItrEnabled = false
106
125
  let isSuitesSkippingEnabled = false
107
126
  let isCoverageReportUploadEnabled = false
108
127
  let isEarlyFlakeDetectionEnabled = false
109
- let earlyFlakeDetectionNumRetries = 0
110
- let earlyFlakeDetectionSlowTestRetries = {}
128
+ let earlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
111
129
  let earlyFlakeDetectionFaultyThreshold = 0
112
130
  let isEarlyFlakeDetectionFaulty = false
113
131
  let isFlakyTestRetriesEnabled = false
@@ -121,8 +139,16 @@ let numTestRetries = 0
121
139
  let knownTests = {}
122
140
  let skippedSuites = []
123
141
  let isSuitesSkipped = false
142
+ let areAllSuitesSkipped = false
124
143
  let repositoryRoot
125
144
 
145
+ /**
146
+ * @returns {boolean}
147
+ */
148
+ function shouldRunEarlyFlakeDetection () {
149
+ return isEarlyFlakeDetectionEnabled && hasEfdRetries(earlyFlakeDetectionRetryPolicy)
150
+ }
151
+
126
152
  function isValidKnownTests (receivedKnownTests) {
127
153
  return !!receivedKnownTests.cucumber
128
154
  }
@@ -166,6 +192,7 @@ function resetSuiteSkippingRunState () {
166
192
  skippedSuitesCoverage = {}
167
193
  skippedSuites = []
168
194
  isSuitesSkipped = false
195
+ areAllSuitesSkipped = false
169
196
  repositoryRoot = undefined
170
197
  writeCoverageBackfillToCache({})
171
198
  }
@@ -180,11 +207,6 @@ function getSuiteStatusFromTestStatuses (testStatuses) {
180
207
  return 'pass'
181
208
  }
182
209
 
183
- function getConfiguredEfdRetryCount () {
184
- const maxSlowTestRetryCount = getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
185
- return maxSlowTestRetryCount ?? earlyFlakeDetectionNumRetries
186
- }
187
-
188
210
  function publishWorkerEfdRetryCount (pickle, retryCount) {
189
211
  const message = {
190
212
  [DD_EFD_RETRY_COUNT_MESSAGE]: {
@@ -221,15 +243,13 @@ function configureParallelWorkerWorldParameters (options) {
221
243
  options.worldParameters._ddIsKnownTestsEnabled = true
222
244
  options.worldParameters._ddIsEarlyFlakeDetectionEnabled = isEarlyFlakeDetectionEnabled
223
245
  options.worldParameters._ddKnownTests = knownTests
224
- options.worldParameters._ddEarlyFlakeDetectionNumRetries = earlyFlakeDetectionNumRetries
225
- options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries = earlyFlakeDetectionSlowTestRetries
246
+ options.worldParameters._ddEarlyFlakeDetectionRetryPolicy = earlyFlakeDetectionRetryPolicy
226
247
  } else {
227
248
  isEarlyFlakeDetectionEnabled = false
228
249
  isKnownTestsEnabled = false
229
250
  options.worldParameters._ddIsEarlyFlakeDetectionEnabled = false
230
251
  options.worldParameters._ddIsKnownTestsEnabled = false
231
- options.worldParameters._ddEarlyFlakeDetectionNumRetries = 0
232
- options.worldParameters._ddEarlyFlakeDetectionSlowTestRetries = {}
252
+ options.worldParameters._ddEarlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
233
253
  }
234
254
 
235
255
  if (isImpactedTestsEnabled) {
@@ -261,10 +281,9 @@ function readParallelWorkerWorldParameters (options) {
261
281
  }
262
282
  }
263
283
  isEarlyFlakeDetectionEnabled = !!worldParameters._ddIsEarlyFlakeDetectionEnabled
264
- if (isEarlyFlakeDetectionEnabled) {
265
- earlyFlakeDetectionNumRetries = worldParameters._ddEarlyFlakeDetectionNumRetries
266
- earlyFlakeDetectionSlowTestRetries = worldParameters._ddEarlyFlakeDetectionSlowTestRetries ?? {}
267
- }
284
+ earlyFlakeDetectionRetryPolicy = isEarlyFlakeDetectionEnabled
285
+ ? worldParameters._ddEarlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
286
+ : EMPTY_EFD_RETRY_POLICY
268
287
  isImpactedTestsEnabled = !!worldParameters._ddImpactedTestsEnabled
269
288
  if (isImpactedTestsEnabled) {
270
289
  modifiedFiles = worldParameters._ddModifiedFiles
@@ -282,10 +301,14 @@ function handleDdWorkerMessage (message) {
282
301
  if (Array.isArray(message)) {
283
302
  const [messageCode, payload] = message
284
303
  if (messageCode === CUCUMBER_WORKER_TRACE_PAYLOAD_CODE) {
285
- collectAttemptToFixExecutionsFromTraces(payload, attemptToFixExecutions)
304
+ collectTestOptimizationSummariesFromTraces(payload, { attemptToFixExecutions })
286
305
  workerReportTraceCh.publish(payload)
287
306
  return true
288
307
  }
308
+ if (messageCode === CUCUMBER_WORKER_TELEMETRY_PAYLOAD_CODE) {
309
+ workerReportTelemetryCh.publish(payload)
310
+ return true
311
+ }
289
312
  }
290
313
 
291
314
  if (message?.[DD_EFD_RETRY_COUNT_MESSAGE]) {
@@ -334,8 +357,10 @@ function maybeStartParallelSuite (pickle) {
334
357
  })
335
358
  }
336
359
 
337
- function handleParallelTestCaseFinished (pickle, worstTestStepResult) {
338
- const { status } = getStatusFromResultLatest(worstTestStepResult)
360
+ function handleParallelTestCaseFinished (pickle, worstTestStepResult, usesNumericStatus = false) {
361
+ const { status } = usesNumericStatus
362
+ ? getStatusFromResult(worstTestStepResult)
363
+ : getStatusFromResultLatest(worstTestStepResult)
339
364
  let isNew = false
340
365
 
341
366
  if (isKnownTestsEnabled) {
@@ -345,7 +370,7 @@ function handleParallelTestCaseFinished (pickle, worstTestStepResult) {
345
370
  const testFileAbsolutePath = pickle.uri
346
371
  const finished = (pickleResultByFile[testFileAbsolutePath] ||= [])
347
372
 
348
- if (isEarlyFlakeDetectionEnabled && isNew) {
373
+ if (shouldRunEarlyFlakeDetection() && isNew) {
349
374
  const testFullname = `${pickle.uri}:${pickle.name}`
350
375
  let testStatuses = newTestsByTestFullname.get(testFullname)
351
376
  if (testStatuses) {
@@ -358,7 +383,7 @@ function handleParallelTestCaseFinished (pickle, worstTestStepResult) {
358
383
  if (efdRetryCount === undefined) {
359
384
  efdRetryCount = status === 'skip'
360
385
  ? 0
361
- : getConfiguredEfdRetryCount()
386
+ : earlyFlakeDetectionRetryPolicy.schedulingRetryCount
362
387
  efdRetryCountByPickleId.set(pickle.id, efdRetryCount)
363
388
  if (efdRetryCount === 0 && status !== 'skip') {
364
389
  efdSlowAbortedPickleIds.add(pickle.id)
@@ -696,7 +721,7 @@ function publishRetriedAttempt (runner, state) {
696
721
 
697
722
  // ATR: record this attempt as failed so when run().finally runs (after retry) we have all statuses
698
723
  if (isFlakyTestRetriesEnabled) {
699
- const nameForKey = runner.pickle.name.replace(/ ?\(attempt \d+(?:, retried)?\) ?$/, '')
724
+ const nameForKey = getCucumberTestName(runner.pickle.name, currentAttempt > 0)
700
725
  const atrKey = `${runner.pickle.uri}:${nameForKey}`
701
726
  if (atrStatusesByScenarioKey.has(atrKey)) {
702
727
  atrStatusesByScenarioKey.get(atrKey).push('fail')
@@ -802,6 +827,7 @@ function wrapRun (pl, isLatestVersion, version) {
802
827
  const { status, skipReason } = isLatestVersion
803
828
  ? getStatusFromResultLatest(result)
804
829
  : getStatusFromResult(result)
830
+ const testName = getCucumberTestName(this.pickle.name, state.numAttempt > 0)
805
831
 
806
832
  if (lastStatusByPickleId.has(this.pickle.id)) {
807
833
  lastStatusByPickleId.get(this.pickle.id).push(status)
@@ -821,7 +847,7 @@ function wrapRun (pl, isLatestVersion, version) {
821
847
 
822
848
  if (isTestManagementTestsEnabled) {
823
849
  const testSuitePath = getTestSuitePath(testFileAbsolutePath, process.cwd())
824
- const testProperties = getTestProperties(testSuitePath, this.pickle.name)
850
+ const testProperties = getTestProperties(testSuitePath, testName)
825
851
  const numRetries = numRetriesByPickleId.get(this.pickle.id)
826
852
  isAttemptToFix = testProperties.attemptToFix
827
853
  isAttemptToFixRetry = isAttemptToFix && numRetries > 0
@@ -857,20 +883,24 @@ function wrapRun (pl, isLatestVersion, version) {
857
883
  }
858
884
 
859
885
  if (
860
- isEarlyFlakeDetectionEnabled &&
886
+ shouldRunEarlyFlakeDetection() &&
861
887
  status !== 'skip' &&
862
888
  (isNew || isModified) &&
863
889
  !isEfdRetry &&
864
890
  !efdRetryCountByPickleId.has(this.pickle.id)
865
891
  ) {
866
- const retryCount = getEfdRetryCount(performance.now() - executionStart, earlyFlakeDetectionSlowTestRetries)
892
+ const retryCount = getEfdRetryCountForDuration(
893
+ performance.now() - executionStart,
894
+ earlyFlakeDetectionRetryPolicy
895
+ )
867
896
  efdRetryCountByPickleId.set(this.pickle.id, retryCount)
868
897
  if (retryCount === 0) {
869
898
  efdSlowAbortedPickleIds.add(this.pickle.id)
870
899
  }
871
900
  }
872
901
 
873
- const efdRetryCount = efdRetryCountByPickleId.get(this.pickle.id) ?? earlyFlakeDetectionNumRetries
902
+ const efdRetryCount = efdRetryCountByPickleId.get(this.pickle.id) ??
903
+ earlyFlakeDetectionRetryPolicy.schedulingRetryCount
874
904
 
875
905
  // Check if all EFD retries failed
876
906
  if (isEfdRetry && (isNew || isModified)) {
@@ -889,8 +919,7 @@ function wrapRun (pl, isLatestVersion, version) {
889
919
  // ATR: accumulate statuses by stable scenario key (uri:name) so retries are grouped.
890
920
  // Cucumber appends " (attempt N)" or " (attempt N, retried)" to the scenario name; normalize for keying.
891
921
  if (isFlakyTestRetriesEnabled && !isAttemptToFix && !isEfdRetry && numTestRetries > 0) {
892
- const nameForKey = this.pickle.name.replace(/ ?\(attempt \d+(?:, retried)?\) ?$/, '')
893
- const atrKey = `${this.pickle.uri}:${nameForKey}`
922
+ const atrKey = `${this.pickle.uri}:${testName}`
894
923
  if (atrStatusesByScenarioKey.has(atrKey)) {
895
924
  atrStatusesByScenarioKey.get(atrKey).push(status)
896
925
  } else {
@@ -909,10 +938,21 @@ function wrapRun (pl, isLatestVersion, version) {
909
938
 
910
939
  const error = getErrorFromCucumberResult(result)
911
940
 
941
+ if (!testStartPayload.isParallel) {
942
+ recordTestManagementExecution({
943
+ testSuite: testSuitePath,
944
+ testName,
945
+ status,
946
+ isAttemptToFix,
947
+ isDisabled,
948
+ isQuarantined,
949
+ })
950
+ }
951
+
912
952
  if (isAttemptToFix) {
913
953
  recordAttemptToFixExecution(attemptToFixExecutions, {
914
954
  testSuite: testSuitePath,
915
- testName: this.pickle.name,
955
+ testName,
916
956
  status,
917
957
  isDisabled,
918
958
  isQuarantined,
@@ -953,7 +993,8 @@ function wrapRun (pl, isLatestVersion, version) {
953
993
  error,
954
994
  isNew,
955
995
  isEfdRetry,
956
- isFlakyRetry: state.numAttempt > 0,
996
+ isFlakyRetry: state.numAttempt > 0 && isFlakyTestRetriesEnabled,
997
+ isExternalRetry: state.numAttempt > 0 && !isFlakyTestRetriesEnabled,
957
998
  isAttemptToFix,
958
999
  isAttemptToFixRetry,
959
1000
  hasFailedAllRetries,
@@ -1059,8 +1100,8 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
1059
1100
  repositoryRoot = configurationResponse.repositoryRoot
1060
1101
  isItrEnabled = configurationResponse.libraryConfig?.isItrEnabled
1061
1102
  isEarlyFlakeDetectionEnabled = configurationResponse.libraryConfig?.isEarlyFlakeDetectionEnabled
1062
- earlyFlakeDetectionNumRetries = configurationResponse.libraryConfig?.earlyFlakeDetectionNumRetries
1063
- earlyFlakeDetectionSlowTestRetries = configurationResponse.libraryConfig?.earlyFlakeDetectionSlowTestRetries ?? {}
1103
+ earlyFlakeDetectionRetryPolicy = configurationResponse.libraryConfig?.earlyFlakeDetectionRetryPolicy ??
1104
+ EMPTY_EFD_RETRY_POLICY
1064
1105
  earlyFlakeDetectionFaultyThreshold = configurationResponse.libraryConfig?.earlyFlakeDetectionFaultyThreshold
1065
1106
  isSuitesSkippingEnabled = isItrEnabled && configurationResponse.libraryConfig?.isSuitesSkippingEnabled
1066
1107
  isCoverageReportUploadEnabled = configurationResponse.libraryConfig?.isCoverageReportUploadEnabled
@@ -1113,6 +1154,7 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
1113
1154
  const oldPickles = isCoordinator ? this.sourcedPickles : this.pickleIds
1114
1155
 
1115
1156
  isSuitesSkipped = picklesToRun.length !== oldPickles.length
1157
+ areAllSuitesSkipped = oldPickles.length > 0 && picklesToRun.length === 0
1116
1158
 
1117
1159
  log.debug('%s out of %s suites are going to run.', picklesToRun.length, oldPickles.length)
1118
1160
 
@@ -1229,7 +1271,10 @@ function getWrappedStart (start, frameworkVersion, isParallel = false, isCoordin
1229
1271
  isParallel,
1230
1272
  })
1231
1273
 
1232
- logTestOptimizationSummary({ attemptToFixExecutions })
1274
+ logTestOptimizationSummary({
1275
+ attemptToFixExecutions,
1276
+ extraSections: areAllSuitesSkipped ? [TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE] : [],
1277
+ })
1233
1278
  loggedAttemptToFixTests.clear()
1234
1279
  eventDataCollector = null
1235
1280
  await flushPromise
@@ -1317,7 +1362,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1317
1362
  }
1318
1363
  }
1319
1364
  const originalRetry = this.options.retry
1320
- const isManagedRetry = isAttemptToFix || (isEarlyFlakeDetectionEnabled && (isNew || isModified))
1365
+ const isManagedRetry = isAttemptToFix || (shouldRunEarlyFlakeDetection() && (isNew || isModified))
1321
1366
  if (isManagedRetry) {
1322
1367
  this.options.retry = 0
1323
1368
  }
@@ -1342,10 +1387,10 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1342
1387
  }
1343
1388
 
1344
1389
  // If it's a new test and it hasn't been skipped, we run it again
1345
- if (isEarlyFlakeDetectionEnabled && lastTestStatus !== 'skip' && (isNew || isModified)) {
1390
+ if (shouldRunEarlyFlakeDetection() && lastTestStatus !== 'skip' && (isNew || isModified)) {
1346
1391
  let efdRetryCount = efdRetryCountByPickleId.get(pickle.id)
1347
1392
  if (efdRetryCount === undefined) {
1348
- efdRetryCount = getEfdRetryCount(firstExecutionDurationMs, earlyFlakeDetectionSlowTestRetries)
1393
+ efdRetryCount = getEfdRetryCountForDuration(firstExecutionDurationMs, earlyFlakeDetectionRetryPolicy)
1349
1394
  efdRetryCountByPickleId.set(pickle.id, efdRetryCount)
1350
1395
  if (efdRetryCount === 0) {
1351
1396
  efdSlowAbortedPickleIds.add(pickle.id)
@@ -1364,7 +1409,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1364
1409
  let shouldBePassedByEFD = false
1365
1410
  let shouldBePassedByTestManagement = false
1366
1411
  let shouldBeFailedByAttemptToFix = false
1367
- if ((isNew || isModified) && isEarlyFlakeDetectionEnabled) {
1412
+ if ((isNew || isModified) && shouldRunEarlyFlakeDetection()) {
1368
1413
  /**
1369
1414
  * If Early Flake Detection (EFD) is enabled the logic is as follows:
1370
1415
  * - If all attempts for a test are failing, the test has failed and we will let the test process fail.
@@ -1422,7 +1467,7 @@ function getWrappedRunTestCase (runTestCaseFunction, isNewerCucumberVersion = fa
1422
1467
 
1423
1468
  this.options.retry = originalRetry
1424
1469
 
1425
- if (isNewerCucumberVersion && isEarlyFlakeDetectionEnabled && (isNew || isModified)) {
1470
+ if (isNewerCucumberVersion && shouldRunEarlyFlakeDetection() && (isNew || isModified)) {
1426
1471
  return shouldBePassedByEFD
1427
1472
  }
1428
1473
 
@@ -1451,7 +1496,7 @@ function patchCucumberWorkerRunTestCase (runtimeExecutorPackage, isWorker) {
1451
1496
  )
1452
1497
  }
1453
1498
 
1454
- function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion) {
1499
+ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion, usesNumericStatus = false) {
1455
1500
  return function (worker, message) {
1456
1501
  if (!testSuiteFinishCh.hasSubscribers) {
1457
1502
  return parseWorkerMessageFunction.apply(this, arguments)
@@ -1502,13 +1547,31 @@ function getWrappedParseWorkerMessage (parseWorkerMessageFunction, isNewVersion)
1502
1547
  pickle = testCase.pickle
1503
1548
  }
1504
1549
 
1505
- handleParallelTestCaseFinished(pickle, worstTestStepResult)
1550
+ handleParallelTestCaseFinished(pickle, worstTestStepResult, usesNumericStatus)
1506
1551
  }
1507
1552
 
1508
1553
  return parseWorkerResponse
1509
1554
  }
1510
1555
  }
1511
1556
 
1557
+ /**
1558
+ * Adapts Cucumber 7's callback-based parallel coordinator to the Promise contract used by getWrappedStart.
1559
+ *
1560
+ * @param {Function} run
1561
+ * @param {string} frameworkVersion
1562
+ * @returns {Function}
1563
+ */
1564
+ function getWrappedCoordinatorRun (run, frameworkVersion) {
1565
+ const runAsPromise = function (numberOfWorkers) {
1566
+ return new Promise(resolve => run.call(this, numberOfWorkers, resolve))
1567
+ }
1568
+ const wrappedStart = getWrappedStart(runAsPromise, frameworkVersion, true)
1569
+
1570
+ return function (numberOfWorkers, done) {
1571
+ return wrappedStart.call(this, numberOfWorkers).then(done)
1572
+ }
1573
+ }
1574
+
1512
1575
  module.exports.patchCucumberWorkerRunTestCase = patchCucumberWorkerRunTestCase
1513
1576
 
1514
1577
  // Test start / finish for older versions. The only hook executed in workers when in parallel mode
@@ -1556,19 +1619,33 @@ addHook({
1556
1619
  return runtimePackage
1557
1620
  })
1558
1621
 
1559
- // Only executed in parallel mode.
1560
- // `getWrappedStart` generates session start and finish events
1622
+ // Only executed in parallel mode in Cucumber 7 through 10.
1623
+ // `getWrappedCoordinatorRun` or `getWrappedStart` generates session start and finish events
1561
1624
  // `getWrappedParseWorkerMessage` generates suite start and finish events
1625
+ // Shimmer is required because the coordinator must be changed before it starts workers and exposes no lifecycle hook.
1562
1626
  addHook({
1563
1627
  name: '@cucumber/cucumber',
1564
- versions: ['>=8.0.0 <11.0.0'],
1628
+ versions: ['>=7.0.0 <11.0.0'],
1565
1629
  file: 'lib/runtime/parallel/coordinator.js',
1566
1630
  }, (coordinatorPackage, frameworkVersion) => {
1567
- shimmer.wrap(coordinatorPackage.default.prototype, 'start', start => getWrappedStart(start, frameworkVersion, true))
1631
+ const isCucumber7 = satisfies(frameworkVersion, '<8.0.0')
1632
+ if (isCucumber7) {
1633
+ shimmer.wrap(
1634
+ coordinatorPackage.default.prototype,
1635
+ 'run',
1636
+ run => getWrappedCoordinatorRun(run, frameworkVersion)
1637
+ )
1638
+ } else {
1639
+ shimmer.wrap(
1640
+ coordinatorPackage.default.prototype,
1641
+ 'start',
1642
+ start => getWrappedStart(start, frameworkVersion, true)
1643
+ )
1644
+ }
1568
1645
  shimmer.wrap(
1569
1646
  coordinatorPackage.default.prototype,
1570
1647
  'parseWorkerMessage',
1571
- parseWorkerMessage => getWrappedParseWorkerMessage(parseWorkerMessage)
1648
+ parseWorkerMessage => getWrappedParseWorkerMessage(parseWorkerMessage, false, isCucumber7)
1572
1649
  )
1573
1650
  return coordinatorPackage
1574
1651
  })