dd-trace 6.8.0 → 6.10.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 (209) 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 +140 -42
  44. package/index.d.ts +140 -1
  45. package/init.js +1 -17
  46. package/initialize.mjs +11 -0
  47. package/loader-hook.mjs +6 -4
  48. package/package.json +8 -5
  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/confluentinc-kafka-javascript.js +111 -6
  55. package/packages/datadog-instrumentations/src/cucumber.js +124 -47
  56. package/packages/datadog-instrumentations/src/dns.js +21 -2
  57. package/packages/datadog-instrumentations/src/electron.js +1 -0
  58. package/packages/datadog-instrumentations/src/fastify.js +3 -1
  59. package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
  60. package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
  61. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
  62. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +629 -0
  63. package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
  64. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +199 -0
  65. package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
  66. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
  67. package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +62 -0
  68. package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +83 -1
  69. package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
  70. package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
  71. package/packages/datadog-instrumentations/src/jest.js +980 -234
  72. package/packages/datadog-instrumentations/src/mocha/main.js +108 -10
  73. package/packages/datadog-instrumentations/src/mocha/utils.js +66 -50
  74. package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
  75. package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
  76. package/packages/datadog-instrumentations/src/mysql.js +46 -16
  77. package/packages/datadog-instrumentations/src/mysql2.js +119 -18
  78. package/packages/datadog-instrumentations/src/next.js +155 -23
  79. package/packages/datadog-instrumentations/src/openai.js +14 -0
  80. package/packages/datadog-instrumentations/src/path-to-regexp.js +67 -1
  81. package/packages/datadog-instrumentations/src/pg.js +209 -11
  82. package/packages/datadog-instrumentations/src/playwright.js +173 -79
  83. package/packages/datadog-instrumentations/src/promise-js.js +4 -4
  84. package/packages/datadog-instrumentations/src/promise.js +3 -3
  85. package/packages/datadog-instrumentations/src/router.js +195 -19
  86. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +85 -13
  87. package/packages/datadog-instrumentations/src/vitest-main.js +528 -72
  88. package/packages/datadog-instrumentations/src/vitest-util.js +106 -12
  89. package/packages/datadog-instrumentations/src/vitest-worker.js +382 -40
  90. package/packages/datadog-instrumentations/src/webdriverio.js +212 -22
  91. package/packages/datadog-instrumentations/src/when.js +3 -3
  92. package/packages/datadog-instrumentations/src/ws.js +5 -1
  93. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/trace-checkpoint.js +12 -6
  94. package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
  95. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -5
  96. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -5
  97. package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
  98. package/packages/datadog-plugin-cucumber/src/index.js +5 -1
  99. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +51 -47
  100. package/packages/datadog-plugin-cypress/src/support.js +7 -4
  101. package/packages/datadog-plugin-electron/src/net.js +10 -4
  102. package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +31 -17
  103. package/packages/datadog-plugin-http/src/client.js +1 -1
  104. package/packages/datadog-plugin-http2/src/client.js +1 -1
  105. package/packages/datadog-plugin-jest/src/index.js +6 -22
  106. package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +2 -3
  107. package/packages/datadog-plugin-kafkajs/src/consumer.js +2 -3
  108. package/packages/datadog-plugin-kafkajs/src/producer.js +5 -3
  109. package/packages/datadog-plugin-kafkajs/src/utils.js +65 -7
  110. package/packages/datadog-plugin-mocha/src/index.js +832 -4
  111. package/packages/datadog-plugin-mysql/src/index.js +37 -0
  112. package/packages/datadog-plugin-next/src/index.js +51 -21
  113. package/packages/datadog-plugin-openai/src/tracing.js +4 -0
  114. package/packages/datadog-plugin-openai-agents/src/integration.js +278 -69
  115. package/packages/datadog-plugin-openai-agents/src/processor.js +16 -4
  116. package/packages/datadog-plugin-pg/src/index.js +66 -1
  117. package/packages/datadog-plugin-playwright/src/index.js +1 -1
  118. package/packages/datadog-plugin-vitest/src/index.js +103 -27
  119. package/packages/datadog-plugin-ws/src/util.js +2 -1
  120. package/packages/datadog-shimmer/src/shimmer.js +6 -2
  121. package/packages/dd-trace/src/aiguard/sdk.js +3 -0
  122. package/packages/dd-trace/src/appsec/api_security/normalized-route.js +453 -0
  123. package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
  124. package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
  125. package/packages/dd-trace/src/appsec/handlers/http-request.js +228 -0
  126. package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
  127. package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
  128. package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
  129. package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
  130. package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
  131. package/packages/dd-trace/src/appsec/index.js +48 -476
  132. package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
  133. package/packages/dd-trace/src/carrier.js +356 -0
  134. package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
  135. package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
  136. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
  137. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
  138. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
  139. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
  140. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
  141. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
  142. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
  143. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
  144. package/packages/dd-trace/src/config/generated-config-types.d.ts +5 -0
  145. package/packages/dd-trace/src/config/helper.js +2 -0
  146. package/packages/dd-trace/src/config/index.js +16 -5
  147. package/packages/dd-trace/src/config/remote_config.js +1 -1
  148. package/packages/dd-trace/src/config/supported-configurations.json +18 -0
  149. package/packages/dd-trace/src/datastreams/context.js +6 -1
  150. package/packages/dd-trace/src/datastreams/manager.js +5 -1
  151. package/packages/dd-trace/src/datastreams/pathway.js +23 -22
  152. package/packages/dd-trace/src/datastreams/processor.js +3 -2
  153. package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
  154. package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
  155. package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
  156. package/packages/dd-trace/src/guardrails/log.js +1 -10
  157. package/packages/dd-trace/src/llmobs/constants/tags.js +5 -0
  158. package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
  159. package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
  160. package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
  161. package/packages/dd-trace/src/llmobs/index.js +34 -2
  162. package/packages/dd-trace/src/llmobs/noop.js +2 -0
  163. package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
  164. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
  165. package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
  166. package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
  167. package/packages/dd-trace/src/llmobs/sdk.js +149 -68
  168. package/packages/dd-trace/src/llmobs/span_processor.js +16 -0
  169. package/packages/dd-trace/src/llmobs/tagger.js +96 -0
  170. package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
  171. package/packages/dd-trace/src/llmobs/util.js +78 -0
  172. package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
  173. package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
  174. package/packages/dd-trace/src/log/channels.js +1 -9
  175. package/packages/dd-trace/src/log/levels.js +12 -0
  176. package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
  177. package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
  178. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
  179. package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
  180. package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
  181. package/packages/dd-trace/src/opentelemetry/span.js +4 -0
  182. package/packages/dd-trace/src/opentracing/propagation/text_map.js +126 -120
  183. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +4 -8
  184. package/packages/dd-trace/src/plugin_manager.js +2 -0
  185. package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
  186. package/packages/dd-trace/src/plugins/index.js +1 -0
  187. package/packages/dd-trace/src/plugins/util/ci.js +6 -0
  188. package/packages/dd-trace/src/plugins/util/git.js +6 -4
  189. package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
  190. package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
  191. package/packages/{datadog-plugin-jest/src/util.js → dd-trace/src/plugins/util/jest.js} +3 -62
  192. package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
  193. package/packages/dd-trace/src/plugins/util/test.js +397 -74
  194. package/packages/dd-trace/src/plugins/util/url.js +1 -1
  195. package/packages/dd-trace/src/plugins/util/web.js +76 -3
  196. package/packages/dd-trace/src/profiler.js +1 -1
  197. package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
  198. package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
  199. package/packages/dd-trace/src/proxy.js +1 -0
  200. package/packages/dd-trace/src/ritm.js +5 -0
  201. package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
  202. package/packages/dd-trace/src/sampling_rule.js +4 -2
  203. package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
  204. package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
  205. package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
  206. package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
  207. package/vendor/dist/pprof-format/index.js +1 -1
  208. package/version.js +10 -8
  209. /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
@@ -10,12 +10,13 @@ const {
10
10
  } = require('../helpers/channel')
11
11
  const { addHook, channel } = require('../helpers/instrument')
12
12
  const shimmer = require('../../../datadog-shimmer')
13
- const { isMarkedAsUnskippable } = require('../../../datadog-plugin-jest/src/util')
13
+ const { EMPTY_EFD_RETRY_POLICY } = require('../../../dd-trace/src/ci-visibility/efd-retry-policy')
14
14
  const { writeCoverageBackfillToCache } = require('../../../dd-trace/src/ci-visibility/test-optimization-cache')
15
15
  const log = require('../../../dd-trace/src/log')
16
16
  const { getEnvironmentVariable } = require('../../../dd-trace/src/config/helper')
17
17
  const {
18
18
  getTestSuitePath,
19
+ MOCHA_WORKER_TELEMETRY_PAYLOAD_CODE,
19
20
  MOCHA_WORKER_TRACE_PAYLOAD_CODE,
20
21
  fromCoverageMapToCoverage,
21
22
  getCoveredFilesFromCoverage,
@@ -28,8 +29,10 @@ const {
28
29
  getTestCoverageLinesPercentage,
29
30
  collectTestOptimizationSummariesFromTraces,
30
31
  logTestOptimizationSummary,
32
+ TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE,
31
33
  getTestOptimizationRequestResults,
32
34
  isModifiedTest,
35
+ isMarkedAsUnskippable,
33
36
  } = require('../../../dd-trace/src/plugins/util/test')
34
37
 
35
38
  const {
@@ -66,12 +69,15 @@ let hasWarnedDeprecatedMochaVersion = false
66
69
  const unskippableSuites = []
67
70
  let suitesToSkip = []
68
71
  let isSuitesSkipped = false
72
+ let areAllSuitesSkipped = false
69
73
  let skippedSuites = []
70
74
  let skippableSuitesCoverage = {}
71
75
  let skippedSuitesCoverage = {}
72
76
  let itrCorrelationId = ''
73
77
  let isForcedToRun = false
74
- const config = {}
78
+ const config = {
79
+ earlyFlakeDetectionRetryPolicy: EMPTY_EFD_RETRY_POLICY,
80
+ }
75
81
 
76
82
  // We'll preserve the original coverage here
77
83
  const originalCoverageMap = createCoverageMap()
@@ -95,6 +101,7 @@ const mochaGlobalRunCh = channel('ci:mocha:global:run')
95
101
  const testManagementTestsCh = channel('ci:mocha:test-management-tests')
96
102
  const modifiedFilesCh = channel('ci:mocha:modified-files')
97
103
  const workerReportTraceCh = channel('ci:mocha:worker-report:trace')
104
+ const workerReportTelemetryCh = channel('ci:mocha:worker-report:telemetry')
98
105
  const testSessionStartCh = channel('ci:mocha:session:start')
99
106
  const testSessionFinishCh = channel('ci:mocha:session:finish')
100
107
  const itrSkippedSuitesCh = channel('ci:mocha:itr:skipped-suites')
@@ -227,6 +234,7 @@ function getMochaTestSessionCoverageFiles () {
227
234
 
228
235
  function resetSuiteSkippingRunState () {
229
236
  isSuitesSkipped = false
237
+ areAllSuitesSkipped = false
230
238
  skippedSuites = []
231
239
  skippableSuitesCoverage = {}
232
240
  skippedSuitesCoverage = {}
@@ -357,7 +365,11 @@ function getOnEndHandler (isParallel, onDone) {
357
365
  isParallel,
358
366
  }, onDone)
359
367
 
360
- logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
368
+ logTestOptimizationSummary({
369
+ attemptToFixExecutions,
370
+ newTestsWithDynamicNames,
371
+ extraSections: areAllSuitesSkipped ? [TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE] : [],
372
+ })
361
373
  loggedAttemptToFixTests.clear()
362
374
  }
363
375
  }
@@ -386,6 +398,57 @@ function isFailedTestReplayEnabled () {
386
398
  return config.isTestDynamicInstrumentationEnabled && config.isDiEnabled
387
399
  }
388
400
 
401
+ /**
402
+ * @typedef {object} MochaSuite
403
+ * @property {MochaSuite[]} suites
404
+ * @property {import('mocha').Test[]} tests
405
+ * @property {MochaSuite[]} _onlySuites
406
+ * @property {import('mocha').Test[]} _onlyTests
407
+ */
408
+
409
+ /**
410
+ * Mirrors Mocha 5's private exclusivity check.
411
+ *
412
+ * @param {MochaSuite} suite
413
+ * @returns {boolean}
414
+ */
415
+ function hasOnly (suite) {
416
+ if (suite._onlyTests.length || suite._onlySuites.length) return true
417
+
418
+ for (const childSuite of suite.suites) {
419
+ if (hasOnly(childSuite)) return true
420
+ }
421
+ return false
422
+ }
423
+
424
+ /**
425
+ * Mirrors Mocha 5's private exclusivity filter.
426
+ *
427
+ * @param {MochaSuite} suite
428
+ * @returns {boolean}
429
+ */
430
+ function filterOnly (suite) {
431
+ if (suite._onlyTests.length) {
432
+ suite.tests = suite._onlyTests
433
+ suite.suites = []
434
+ } else {
435
+ suite.tests = []
436
+
437
+ for (const onlySuite of suite._onlySuites) {
438
+ if (hasOnly(onlySuite)) filterOnly(onlySuite)
439
+ }
440
+
441
+ const filteredSuites = []
442
+ for (const childSuite of suite.suites) {
443
+ if (suite._onlySuites.includes(childSuite) || filterOnly(childSuite)) {
444
+ filteredSuites.push(childSuite)
445
+ }
446
+ }
447
+ suite.suites = filteredSuites
448
+ }
449
+ return suite.tests.length > 0 || suite.suites.length > 0
450
+ }
451
+
389
452
  function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFinishRequest, localSuites) {
390
453
  const ctx = {
391
454
  isParallel,
@@ -418,6 +481,13 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
418
481
  }
419
482
 
420
483
  // We remove the suites that we skip through ITR
484
+ // Mocha normally applies exclusivity after this asynchronous configuration step.
485
+ if (typeof runner.suite.hasOnly === 'function') {
486
+ if (runner.suite.hasOnly()) runner.suite.filterOnly()
487
+ } else if (hasOnly(runner.suite)) {
488
+ filterOnly(runner.suite)
489
+ }
490
+ const numTestsToRun = runner.grepTotal(runner.suite)
421
491
  const filteredSuites = getFilteredSuites(runner.suite.suites)
422
492
  const { suitesToRun, suitesToSkipForRun } = filteredSuites
423
493
 
@@ -426,6 +496,7 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
426
496
  log.debug('%d out of %d suites are going to run.', suitesToRun.length, runner.suite.suites.length)
427
497
 
428
498
  runner.suite.suites = suitesToRun
499
+ areAllSuitesSkipped = isSuitesSkipped && numTestsToRun > 0 && runner.grepTotal(runner.suite) === 0
429
500
 
430
501
  skippedSuites = [...filteredSuites.skippedSuites]
431
502
  suitesToSkip = suitesToSkipForRun
@@ -489,8 +560,8 @@ function getExecutionConfiguration (runner, isParallel, frameworkVersion, onFini
489
560
  }
490
561
  config.repositoryRoot = repositoryRoot
491
562
  config.isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
492
- config.earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
493
- config.earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
563
+ config.earlyFlakeDetectionRetryPolicy =
564
+ libraryConfig.earlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
494
565
  config.earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
495
566
  config.isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
496
567
  config.isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
@@ -978,6 +1049,8 @@ function onMessage (message) {
978
1049
  attemptToFixExecutions,
979
1050
  })
980
1051
  workerReportTraceCh.publish(payload)
1052
+ } else if (messageCode === MOCHA_WORKER_TELEMETRY_PAYLOAD_CODE) {
1053
+ workerReportTelemetryCh.publish(payload)
981
1054
  }
982
1055
  }
983
1056
  }
@@ -1042,7 +1115,7 @@ addHook({
1042
1115
  versions: ['>=8.0.0'],
1043
1116
  file: 'lib/nodejs/parallel-buffered-runner.js',
1044
1117
  }, (ParallelBufferedRunner, frameworkVersion) => {
1045
- shimmer.wrap(ParallelBufferedRunner.prototype, 'run', run => function (cb, { files }) {
1118
+ shimmer.wrap(ParallelBufferedRunner.prototype, 'run', run => function (cb, { files, options = {} }) {
1046
1119
  if (!testFinishCh.hasSubscribers) {
1047
1120
  return run.apply(this, arguments)
1048
1121
  }
@@ -1088,10 +1161,11 @@ addHook({
1088
1161
  }
1089
1162
  }
1090
1163
  isSuitesSkipped = skippedFiles.length > 0
1164
+ areAllSuitesSkipped = options.grep === undefined && files.length > 0 && filteredFiles.length === 0
1091
1165
  skippedSuites = skippedFiles
1092
1166
  skippedSuitesCoverage = getSkippedSuitesCoverageForRun()
1093
1167
  writeCoverageBackfillToCache(skippedSuitesCoverage, getCoverageRootDir())
1094
- run.apply(this, [onRunDone, { files: filteredFiles }])
1168
+ run.apply(this, [onRunDone, { files: filteredFiles, options }])
1095
1169
  } else {
1096
1170
  run.apply(this, arguments)
1097
1171
  }
@@ -1110,9 +1184,34 @@ addHook({
1110
1184
  name: 'mocha',
1111
1185
  versions: ['>=8.0.0'],
1112
1186
  file: 'lib/nodejs/buffered-worker-pool.js',
1113
- }, (BufferedWorkerPoolPackage) => {
1187
+ }, (BufferedWorkerPoolPackage, frameworkVersion) => {
1114
1188
  const { BufferedWorkerPool } = BufferedWorkerPoolPackage
1115
1189
 
1190
+ if (satisfies(frameworkVersion, '<9.2.0')) {
1191
+ // Shimmer is required because the worker environment must be changed before workerpool forks,
1192
+ // before any test lifecycle hook can run. Mocha added this worker ID itself in 9.2.0.
1193
+ shimmer.wrap(BufferedWorkerPool, 'create', create => function () {
1194
+ const pool = create.apply(this, arguments)
1195
+
1196
+ if (!testFinishCh.hasSubscribers) return pool
1197
+
1198
+ let workerId = 0
1199
+ shimmer.wrap(pool._pool, '_createWorkerHandler', createWorkerHandler => function () {
1200
+ this.forkOpts = {
1201
+ ...this.forkOpts,
1202
+ env: {
1203
+ // eslint-disable-next-line eslint-rules/eslint-process-env
1204
+ ...(this.forkOpts.env || process.env),
1205
+ MOCHA_WORKER_ID: String(workerId++),
1206
+ },
1207
+ }
1208
+ return createWorkerHandler.apply(this, arguments)
1209
+ })
1210
+
1211
+ return pool
1212
+ })
1213
+ }
1214
+
1116
1215
  shimmer.wrap(BufferedWorkerPool.prototype, 'run', run => async function (testSuiteAbsolutePath, workerArgs) {
1117
1216
  if (!testFinishCh.hasSubscribers ||
1118
1217
  (!config.isKnownTestsEnabled &&
@@ -1130,8 +1229,7 @@ addHook({
1130
1229
  if (config.isKnownTestsEnabled) {
1131
1230
  if (config.knownTests?.mocha) {
1132
1231
  const testSuiteKnownTests = config.knownTests.mocha[testPath] || []
1133
- newWorkerArgs._ddEfdNumRetries = config.earlyFlakeDetectionNumRetries
1134
- newWorkerArgs._ddEfdSlowTestRetries = config.earlyFlakeDetectionSlowTestRetries
1232
+ newWorkerArgs._ddEfdRetryPolicy = config.earlyFlakeDetectionRetryPolicy
1135
1233
  newWorkerArgs._ddIsEfdEnabled = config.isEarlyFlakeDetectionEnabled
1136
1234
  newWorkerArgs._ddIsKnownTestsEnabled = true
1137
1235
  newWorkerArgs._ddKnownTests = {
@@ -2,11 +2,17 @@
2
2
 
3
3
  const { performance } = require('node:perf_hooks')
4
4
 
5
+ const { getEnvironmentVariable } = require('../../../dd-trace/src/config/helper')
6
+ const {
7
+ getEfdRetryCountForDuration,
8
+ hasEfdRetries,
9
+ shouldSkipEfdRetry,
10
+ } = require('../../../dd-trace/src/ci-visibility/efd-retry-policy')
5
11
  const {
6
12
  getTestSuitePath,
7
13
  DYNAMIC_NAME_RE,
8
- getEfdRetryCount,
9
- getMaxEfdRetryCount,
14
+ getFailedTestReplayPromise,
15
+ recordTestManagementExecution,
10
16
  recordAttemptToFixExecution,
11
17
  logAttemptToFixTestExecution,
12
18
  } = require('../../../dd-trace/src/plugins/util/test')
@@ -26,6 +32,8 @@ const isModifiedCh = channel('ci:mocha:test:is-modified')
26
32
  // suite channels
27
33
  const testSuiteErrorCh = channel('ci:mocha:test-suite:error')
28
34
 
35
+ /** @typedef {{ length: number, [index: number]: unknown } & Iterable<unknown>} ArgumentsLike */
36
+
29
37
  const testToContext = new WeakMap()
30
38
  const originalFns = new WeakMap()
31
39
  const testToStartLine = new WeakMap()
@@ -40,6 +48,7 @@ const testsStatuses = new Map()
40
48
  const efdRetryCountByTestFullName = new Map()
41
49
  const efdSlowAbortedTests = new Set()
42
50
  const attemptToFixExecutions = new Map()
51
+ const isMochaWorker = !!getEnvironmentVariable('MOCHA_WORKER_ID')
43
52
 
44
53
  function waitForHitProbe () {
45
54
  const promises = {}
@@ -133,19 +142,28 @@ function isNewTest (test, knownTests) {
133
142
  return !testsForSuite.includes(testName)
134
143
  }
135
144
 
136
- function setEfdRetryCountForTest (test, duration, slowTestRetries) {
145
+ /**
146
+ * @param {import('mocha').Test} test
147
+ * @param {number} duration
148
+ * @param {import('../../../dd-trace/src/ci-visibility/efd-retry-policy').EfdRetryPolicy} retryPolicy
149
+ */
150
+ function setEfdRetryCountForTest (test, duration, retryPolicy) {
137
151
  const testName = getTestFullName(test)
138
152
  if (efdRetryCountByTestFullName.has(testName)) {
139
153
  return
140
154
  }
141
- const retryCount = getEfdRetryCount(duration, slowTestRetries || {})
155
+ const retryCount = getEfdRetryCountForDuration(duration, retryPolicy)
142
156
  efdRetryCountByTestFullName.set(testName, retryCount)
143
157
  if (retryCount === 0) {
144
158
  efdSlowAbortedTests.add(testName)
145
159
  }
146
160
  }
147
161
 
148
- function wrapOriginalEfdTest (test, slowTestRetries) {
162
+ /**
163
+ * @param {import('mocha').Test} test
164
+ * @param {import('../../../dd-trace/src/ci-visibility/efd-retry-policy').EfdRetryPolicy} retryPolicy
165
+ */
166
+ function wrapOriginalEfdTest (test, retryPolicy) {
149
167
  if (test._ddEfdDurationWrapped || typeof test.fn !== 'function') {
150
168
  return
151
169
  }
@@ -154,7 +172,7 @@ function wrapOriginalEfdTest (test, slowTestRetries) {
154
172
  test.fn = shimmer.wrapFunction(originalFn, originalFn => function () {
155
173
  const start = performance.now()
156
174
  const recordDuration = () => {
157
- setEfdRetryCountForTest(test, performance.now() - start, slowTestRetries)
175
+ setEfdRetryCountForTest(test, performance.now() - start, retryPolicy)
158
176
  }
159
177
 
160
178
  if (originalFn.length > 0) {
@@ -205,13 +223,14 @@ function disableMochaRetries (test) {
205
223
  * _ddIsModified?: boolean,
206
224
  * _ddIsNew?: boolean
207
225
  * }} test
208
- * @param {{ isEarlyFlakeDetectionEnabled?: boolean }} config
226
+ * @param {{
227
+ * isEarlyFlakeDetectionEnabled?: boolean,
228
+ * earlyFlakeDetectionRetryPolicy?: import('../../../dd-trace/src/ci-visibility/efd-retry-policy').EfdRetryPolicy
229
+ * }} config
209
230
  * @returns {boolean}
210
231
  */
211
232
  function isDatadogManagedRetryTest (test, config) {
212
- return test._ddIsAttemptToFix ||
213
- test._ddIsEfdRetry ||
214
- (config.isEarlyFlakeDetectionEnabled && (test._ddIsNew || test._ddIsModified))
233
+ return test._ddIsAttemptToFix || isEarlyFlakeDetectionTest(test, config)
215
234
  }
216
235
 
217
236
  /**
@@ -223,22 +242,32 @@ function isDatadogManagedRetryTest (test, config) {
223
242
  * _ddIsModified?: boolean,
224
243
  * _ddIsNew?: boolean
225
244
  * }} test
226
- * @param {{ isEarlyFlakeDetectionEnabled?: boolean }} config
245
+ * @param {{
246
+ * isEarlyFlakeDetectionEnabled?: boolean,
247
+ * earlyFlakeDetectionRetryPolicy?: import('../../../dd-trace/src/ci-visibility/efd-retry-policy').EfdRetryPolicy
248
+ * }} config
227
249
  * @returns {boolean}
228
250
  */
229
251
  function isEarlyFlakeDetectionTest (test, config) {
230
252
  return !test._ddIsAttemptToFix &&
231
253
  !test._ddIsDisabled &&
232
254
  config.isEarlyFlakeDetectionEnabled &&
255
+ hasEfdRetries(config.earlyFlakeDetectionRetryPolicy) &&
233
256
  (test._ddIsEfdRetry || test._ddIsNew || test._ddIsModified)
234
257
  }
235
258
 
236
- function retryTest (test, numRetries, tags, slowTestRetries) {
259
+ /**
260
+ * @param {import('mocha').Test} test
261
+ * @param {number} numRetries
262
+ * @param {string[]} tags
263
+ * @param {import('../../../dd-trace/src/ci-visibility/efd-retry-policy').EfdRetryPolicy} retryPolicy
264
+ */
265
+ function retryTest (test, numRetries, tags, retryPolicy) {
237
266
  const suite = test.parent
238
267
  const isEfdRetry = tags.includes('_ddIsEfdRetry')
239
268
  disableMochaRetries(test)
240
269
  if (isEfdRetry) {
241
- wrapOriginalEfdTest(test, slowTestRetries)
270
+ wrapOriginalEfdTest(test, retryPolicy)
242
271
  }
243
272
  for (let retryIndex = 0; retryIndex < numRetries; retryIndex++) {
244
273
  const clonedTest = test.clone()
@@ -250,7 +279,7 @@ function retryTest (test, numRetries, tags, slowTestRetries) {
250
279
  if (typeof originalFn === 'function') {
251
280
  clonedTest.fn = shimmer.wrapFunction(originalFn, originalFn => function (...args) {
252
281
  const efdRetryCount = efdRetryCountByTestFullName.get(getTestFullName(clonedTest))
253
- if (efdRetryCount !== undefined && clonedTest._ddEfdRetryIndex > efdRetryCount) {
282
+ if (shouldSkipEfdRetry(clonedTest._ddEfdRetryIndex, efdRetryCount)) {
254
283
  clonedTest._ddShouldSkipEfdRetry = true
255
284
  this.skip()
256
285
  }
@@ -266,14 +295,6 @@ function retryTest (test, numRetries, tags, slowTestRetries) {
266
295
  }
267
296
  }
268
297
 
269
- function getConfiguredEfdRetryCount (config) {
270
- const { earlyFlakeDetectionSlowTestRetries } = config
271
- if (!earlyFlakeDetectionSlowTestRetries || !Object.keys(earlyFlakeDetectionSlowTestRetries).length) {
272
- return config.earlyFlakeDetectionNumRetries
273
- }
274
- return getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
275
- }
276
-
277
298
  function getSuitesByTestFile (root) {
278
299
  const suitesByTestFile = {}
279
300
  function getSuites (suite) {
@@ -462,7 +483,7 @@ function getOnTestHandler (isMain) {
462
483
 
463
484
  if (isEfdRetry) {
464
485
  const efdRetryCount = efdRetryCountByTestFullName.get(getTestFullName(test))
465
- if (efdRetryCount !== undefined && test._ddEfdRetryIndex > efdRetryCount) {
486
+ if (shouldSkipEfdRetry(test._ddEfdRetryIndex, efdRetryCount)) {
466
487
  test.pending = true
467
488
  test._ddShouldSkipEfdRetry = true
468
489
  return
@@ -576,7 +597,7 @@ function getTestFinishInfo (test, status, config, error) {
576
597
  !efdRetryCountByTestFullName.has(testName)
577
598
  ) {
578
599
  const duration = test.duration > 0 ? test.duration : performance.now() - test._ddStartTime
579
- setEfdRetryCountForTest(test, duration, config.earlyFlakeDetectionSlowTestRetries)
600
+ setEfdRetryCountForTest(test, duration, config.earlyFlakeDetectionRetryPolicy)
580
601
  }
581
602
 
582
603
  if (testsStatuses.get(testName)) {
@@ -587,7 +608,8 @@ function getTestFinishInfo (test, status, config, error) {
587
608
  const testStatuses = testsStatuses.get(testName)
588
609
 
589
610
  const isLastAttempt = testStatuses.length === config.testManagementAttemptToFixRetries + 1
590
- const efdRetryCount = efdRetryCountByTestFullName.get(testName) ?? getConfiguredEfdRetryCount(config)
611
+ const efdRetryCount = efdRetryCountByTestFullName.get(testName) ??
612
+ config.earlyFlakeDetectionRetryPolicy.schedulingRetryCount
591
613
  const isLastEfdRetry = testStatuses.length === efdRetryCount + 1
592
614
  const isLastAtrAttempt = getIsLastRetry(test) || (config.isFlakyTestRetriesEnabled && status === 'pass')
593
615
 
@@ -642,6 +664,17 @@ function getTestFinishInfo (test, status, config, error) {
642
664
  isFinalAttempt,
643
665
  })
644
666
 
667
+ if (!isMochaWorker) {
668
+ recordTestManagementExecution({
669
+ testSuite: getTestSuitePath(test.file, process.cwd()),
670
+ testName: test.fullTitle(),
671
+ status,
672
+ isAttemptToFix: _ddIsAttemptToFix,
673
+ isDisabled: _ddIsDisabled,
674
+ isQuarantined: _ddIsQuarantined,
675
+ })
676
+ }
677
+
645
678
  if (_ddIsAttemptToFix) {
646
679
  recordAttemptToFixExecution(attemptToFixExecutions, {
647
680
  testSuite: getTestSuitePath(test.file, process.cwd()),
@@ -788,7 +821,7 @@ function finishDeferredHookEnd (test) {
788
821
  * @param {object} test - Mocha test currently owning the hook.
789
822
  * @param {Promise<void>|undefined} failedTestReplayPromise - Pending Failed Test Replay wait, if any.
790
823
  * @param {unknown} hookThis - Callback receiver.
791
- * @param {IArguments} args - Arguments passed by Mocha.
824
+ * @param {ArgumentsLike} args - Arguments passed by Mocha.
792
825
  * @returns {unknown}
793
826
  */
794
827
  function runFailedTestReplayHookUpCallback (fn, test, failedTestReplayPromise, hookThis, args) {
@@ -921,14 +954,7 @@ function getOnTestRetryHandler (config) {
921
954
  promises,
922
955
  ...ctx.currentStore,
923
956
  })
924
- if (promises.setProbePromise && promises.finishTestPromise) {
925
- test._ddFailedTestReplayPromise = Promise.all([
926
- promises.setProbePromise,
927
- promises.finishTestPromise,
928
- ]).then(() => {})
929
- } else if (promises.setProbePromise || promises.finishTestPromise) {
930
- test._ddFailedTestReplayPromise = promises.setProbePromise || promises.finishTestPromise
931
- }
957
+ test._ddFailedTestReplayPromise = getFailedTestReplayPromise(promises)
932
958
  }
933
959
  const key = getTestToContextKey(test)
934
960
  testToContext.delete(key)
@@ -1004,17 +1030,12 @@ function getRunTestsWrapper (runTests, config) {
1004
1030
  onDone: (isModified) => {
1005
1031
  if (isModified) {
1006
1032
  test._ddIsModified = true
1007
- if (
1008
- !test.isPending() &&
1009
- !test._ddIsDisabled &&
1010
- !test._ddIsAttemptToFix &&
1011
- config.isEarlyFlakeDetectionEnabled
1012
- ) {
1033
+ if (!test.isPending() && isEarlyFlakeDetectionTest(test, config)) {
1013
1034
  retryTest(
1014
1035
  test,
1015
- getConfiguredEfdRetryCount(config),
1036
+ config.earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
1016
1037
  ['_ddIsModified', '_ddIsEfdRetry'],
1017
- config.earlyFlakeDetectionSlowTestRetries
1038
+ config.earlyFlakeDetectionRetryPolicy
1018
1039
  )
1019
1040
  }
1020
1041
  }
@@ -1028,17 +1049,12 @@ function getRunTestsWrapper (runTests, config) {
1028
1049
  suite.tests.forEach((test) => {
1029
1050
  if (!test.isPending() && isNewTest(test, config.knownTests)) {
1030
1051
  test._ddIsNew = true
1031
- if (
1032
- config.isEarlyFlakeDetectionEnabled &&
1033
- !test._ddIsDisabled &&
1034
- !test._ddIsAttemptToFix &&
1035
- !test._ddIsModified
1036
- ) {
1052
+ if (!test._ddIsModified && isEarlyFlakeDetectionTest(test, config)) {
1037
1053
  retryTest(
1038
1054
  test,
1039
- getConfiguredEfdRetryCount(config),
1055
+ config.earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
1040
1056
  ['_ddIsNew', '_ddIsEfdRetry'],
1041
- config.earlyFlakeDetectionSlowTestRetries
1057
+ config.earlyFlakeDetectionRetryPolicy
1042
1058
  )
1043
1059
  }
1044
1060
  }
@@ -7,12 +7,37 @@ const {
7
7
  WEBDRIVERIO_WORKER_ORIGIN,
8
8
  } = require('../../../dd-trace/src/ci-visibility/exporters/test-worker/webdriverio')
9
9
 
10
+ /**
11
+ * Sends a message over WebdriverIO's worker IPC envelope.
12
+ *
13
+ * @param {object} message
14
+ * @param {(error?: Error) => void} [onError]
15
+ * @param {() => void} [onDone]
16
+ * @returns {void}
17
+ */
18
+ function sendWebdriverioWorkerMessage (message, onError, onDone) {
19
+ if (!process.send || !process.connected) {
20
+ onError?.()
21
+ onDone?.()
22
+ return
23
+ }
24
+
25
+ process.send(createWebdriverioWorkerMessage(message), (error) => {
26
+ if (error) {
27
+ onError?.(error)
28
+ }
29
+ onDone?.()
30
+ })
31
+ }
32
+
10
33
  module.exports = {
11
34
  CONFIGURATION_REQUEST: 'dd:test-optimization:webdriverio:configuration:request',
12
35
  CONFIGURATION_RESPONSE: 'dd:test-optimization:webdriverio:configuration:response',
13
36
  createWebdriverioWorkerMessage,
37
+ sendWebdriverioWorkerMessage,
14
38
  SUITE_FINISH: 'dd:test-optimization:webdriverio:test-suite:finish',
15
39
  WORKER_READY: 'dd:test-optimization:webdriverio:worker:ready',
40
+ WORKER_READY_RESPONSE: 'dd:test-optimization:webdriverio:worker:ready:response',
16
41
  WEBDRIVERIO_WORKER_ENV,
17
42
  WEBDRIVERIO_WORKER_EVENT,
18
43
  WEBDRIVERIO_WORKER_ORIGIN,
@@ -2,6 +2,7 @@
2
2
 
3
3
  const { addHook, channel } = require('../helpers/instrument')
4
4
  const shimmer = require('../../../datadog-shimmer')
5
+ const { EMPTY_EFD_RETRY_POLICY } = require('../../../dd-trace/src/ci-visibility/efd-retry-policy')
5
6
  const { getEnvironmentVariable } = require('../../../dd-trace/src/config/helper')
6
7
  const log = require('../../../dd-trace/src/log')
7
8
  const { DD_MAJOR } = require('../../../../version')
@@ -23,7 +24,7 @@ const {
23
24
  const {
24
25
  CONFIGURATION_REQUEST,
25
26
  CONFIGURATION_RESPONSE,
26
- createWebdriverioWorkerMessage,
27
+ sendWebdriverioWorkerMessage,
27
28
  SUITE_FINISH,
28
29
  WEBDRIVERIO_WORKER_ENV,
29
30
  WORKER_READY,
@@ -35,7 +36,9 @@ const MINIMUM_MOCHA_VERSION = DD_MAJOR >= 6 ? '>=8.0.0' : '>=5.2.0'
35
36
  const workerFinishCh = channel('ci:mocha:worker:finish')
36
37
  const workerConfigurationCh = channel('ci:mocha:worker:configuration')
37
38
 
38
- const config = {}
39
+ const config = {
40
+ earlyFlakeDetectionRetryPolicy: EMPTY_EFD_RETRY_POLICY,
41
+ }
39
42
  const runnerToFiles = new WeakMap()
40
43
  const runnerToFailedHooks = new WeakMap()
41
44
  const isWebdriverioWorker = !!getEnvironmentVariable(WEBDRIVERIO_WORKER_ENV)
@@ -50,19 +53,12 @@ let configurationRequestId = 0
50
53
  * @returns {void}
51
54
  */
52
55
  function sendWebdriverioMessage (message, onError, onDone) {
53
- if (!process.send || !process.connected) {
54
- onError?.()
55
- onDone?.()
56
- return
57
- }
58
-
59
- process.send(createWebdriverioWorkerMessage(message), (error) => {
56
+ sendWebdriverioWorkerMessage(message, error => {
60
57
  if (error) {
61
58
  log.error('WebdriverIO Test Optimization IPC error', error)
62
- onError?.()
63
59
  }
64
- onDone?.()
65
- })
60
+ onError?.()
61
+ }, onDone)
66
62
  }
67
63
 
68
64
  /**
@@ -76,12 +72,10 @@ function applyMochaOptions (options) {
76
72
  config.isKnownTestsEnabled = true
77
73
  config.isEarlyFlakeDetectionEnabled = options._ddIsEfdEnabled
78
74
  config.knownTests = options._ddKnownTests
79
- config.earlyFlakeDetectionNumRetries = options._ddEfdNumRetries
80
- config.earlyFlakeDetectionSlowTestRetries = options._ddEfdSlowTestRetries ?? {}
75
+ config.earlyFlakeDetectionRetryPolicy = options._ddEfdRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
81
76
  delete options._ddIsEfdEnabled
82
77
  delete options._ddKnownTests
83
- delete options._ddEfdNumRetries
84
- delete options._ddEfdSlowTestRetries
78
+ delete options._ddEfdRetryPolicy
85
79
  delete options._ddIsKnownTestsEnabled
86
80
  }
87
81
  if (options._ddIsImpactedTestsEnabled) {
@@ -245,7 +239,7 @@ function getWebdriverioHookTest (hook) {
245
239
  */
246
240
  function adjustWebdriverioHookFailures (runner) {
247
241
  let suppressedFailures = 0
248
- for (const { test } of runnerToFailedHooks.get(runner) || []) {
242
+ for (const { test } of runnerToFailedHooks.get(runner)) {
249
243
  if (isWebdriverioFailureSuppressed(test)) {
250
244
  suppressedFailures++
251
245
  }
@@ -287,7 +281,7 @@ function getWebdriverioSuiteResults (runner) {
287
281
  }
288
282
  })
289
283
 
290
- for (const { file, test } of runnerToFailedHooks.get(runner) || []) {
284
+ for (const { file, test } of runnerToFailedHooks.get(runner)) {
291
285
  const result = resultsByFile.get(file)
292
286
  if (result && !isWebdriverioFailureSuppressed(test)) {
293
287
  result.status = 'fail'