dd-trace 6.10.0 → 6.12.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 (173) hide show
  1. package/LICENSE-3rdparty.csv +5 -0
  2. package/README.md +0 -17
  3. package/index.d.ts +128 -6
  4. package/initialize.mjs +28 -17
  5. package/openfeature.js +2 -2
  6. package/package.json +27 -23
  7. package/packages/datadog-esbuild/index.js +0 -34
  8. package/packages/datadog-instrumentations/src/anthropic.js +210 -15
  9. package/packages/datadog-instrumentations/src/aws-sdk.js +9 -1
  10. package/packages/datadog-instrumentations/src/browser-bunyan.js +11 -0
  11. package/packages/datadog-instrumentations/src/bunyan.js +3 -16
  12. package/packages/datadog-instrumentations/src/cucumber.js +30 -15
  13. package/packages/datadog-instrumentations/src/cypress-config.js +182 -16
  14. package/packages/datadog-instrumentations/src/cypress-legacy-finalizers.js +30 -0
  15. package/packages/datadog-instrumentations/src/cypress.js +2 -0
  16. package/packages/datadog-instrumentations/src/fastify.js +2 -0
  17. package/packages/datadog-instrumentations/src/helpers/bunyan.js +22 -0
  18. package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -1
  19. package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +9 -4
  20. package/packages/datadog-instrumentations/src/helpers/register.js +10 -8
  21. package/packages/datadog-instrumentations/src/jest.js +25 -17
  22. package/packages/datadog-instrumentations/src/mariadb.js +278 -21
  23. package/packages/datadog-instrumentations/src/mocha/main.js +539 -55
  24. package/packages/datadog-instrumentations/src/mocha/utils.js +135 -19
  25. package/packages/datadog-instrumentations/src/playwright-reporter.js +247 -0
  26. package/packages/datadog-instrumentations/src/playwright.js +182 -35
  27. package/packages/datadog-instrumentations/src/router.js +2 -0
  28. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +6 -8
  29. package/packages/datadog-instrumentations/src/vitest-main.js +11 -3
  30. package/packages/datadog-plugin-aws-sdk/src/base.js +1 -0
  31. package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +163 -44
  32. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +2 -8
  33. package/packages/datadog-plugin-azure-functions/src/index.js +1 -1
  34. package/packages/datadog-plugin-browser-bunyan/src/index.js +9 -0
  35. package/packages/datadog-plugin-bunyan/src/index.js +5 -8
  36. package/packages/datadog-plugin-cucumber/src/index.js +11 -0
  37. package/packages/datadog-plugin-cypress/src/after-run.js +5 -1
  38. package/packages/datadog-plugin-cypress/src/after-spec.js +5 -1
  39. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +81 -29
  40. package/packages/datadog-plugin-cypress/src/finalization.js +76 -0
  41. package/packages/datadog-plugin-cypress/src/index.js +12 -6
  42. package/packages/datadog-plugin-cypress/src/plugin.js +16 -3
  43. package/packages/datadog-plugin-dd-trace-api/src/index.js +4 -0
  44. package/packages/datadog-plugin-electron/src/index.js +2 -0
  45. package/packages/datadog-plugin-fs/src/index.js +1 -1
  46. package/packages/datadog-plugin-jest/src/index.js +6 -0
  47. package/packages/datadog-plugin-mocha/src/index.js +56 -1
  48. package/packages/datadog-plugin-nats/src/index.js +1 -1
  49. package/packages/datadog-plugin-openai/src/tracing.js +41 -30
  50. package/packages/datadog-plugin-openai-agents/src/integration.js +1 -0
  51. package/packages/datadog-plugin-playwright/src/index.js +46 -13
  52. package/packages/datadog-plugin-vitest/src/index.js +6 -0
  53. package/packages/datadog-webpack/index.js +0 -11
  54. package/packages/dd-trace/index.js +0 -1
  55. package/packages/dd-trace/src/agent/info.js +6 -5
  56. package/packages/dd-trace/src/aiguard/client.js +100 -1
  57. package/packages/dd-trace/src/aiguard/errors.js +41 -0
  58. package/packages/dd-trace/src/aiguard/evaluation.js +357 -0
  59. package/packages/dd-trace/src/aiguard/integrations/anthropic.js +70 -0
  60. package/packages/dd-trace/src/aiguard/integrations/index.js +4 -0
  61. package/packages/dd-trace/src/aiguard/messages/anthropic.js +423 -0
  62. package/packages/dd-trace/src/aiguard/messages/openai.js +1 -24
  63. package/packages/dd-trace/src/aiguard/messages/utils.js +28 -0
  64. package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +1 -11
  65. package/packages/dd-trace/src/aiguard/sdk.js +22 -278
  66. package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +9 -12
  67. package/packages/dd-trace/src/appsec/reporter.js +2 -0
  68. package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
  69. package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
  70. package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
  71. package/packages/dd-trace/src/carrier.js +107 -48
  72. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +1 -0
  73. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
  74. package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +29 -3
  75. package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +27 -9
  76. package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +21 -5
  77. package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +164 -0
  78. package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +26 -8
  79. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +365 -32
  80. package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
  81. package/packages/dd-trace/src/ci-visibility/exporters/request.js +201 -0
  82. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +5 -3
  83. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -5
  84. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -4
  85. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
  86. package/packages/dd-trace/src/ci-visibility/requests/rate-limit.js +39 -0
  87. package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -6
  88. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
  89. package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +10 -4
  90. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
  91. package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -0
  92. package/packages/dd-trace/src/config/index.js +13 -2
  93. package/packages/dd-trace/src/config/major-overrides.js +7 -0
  94. package/packages/dd-trace/src/config/supported-configurations.json +14 -0
  95. package/packages/dd-trace/src/datastreams/index.js +2 -0
  96. package/packages/dd-trace/src/datastreams/manager.js +9 -2
  97. package/packages/dd-trace/src/datastreams/processor.js +15 -13
  98. package/packages/dd-trace/src/datastreams/size.js +5 -0
  99. package/packages/dd-trace/src/debugger/constants.js +1 -0
  100. package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
  101. package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -11
  102. package/packages/dd-trace/src/debugger/index.js +4 -2
  103. package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
  104. package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
  105. package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
  106. package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
  107. package/packages/dd-trace/src/evp_proxy/path.js +22 -0
  108. package/packages/dd-trace/src/exporters/agent/writer.js +49 -9
  109. package/packages/dd-trace/src/exporters/common/buffering-exporter.js +6 -4
  110. package/packages/dd-trace/src/exporters/common/request.js +5 -2
  111. package/packages/dd-trace/src/exporters/common/writer.js +11 -6
  112. package/packages/dd-trace/src/llmobs/experiments/client.js +167 -5
  113. package/packages/dd-trace/src/llmobs/experiments/dataset.js +484 -92
  114. package/packages/dd-trace/src/llmobs/experiments/experiment.js +302 -29
  115. package/packages/dd-trace/src/llmobs/experiments/index.js +75 -64
  116. package/packages/dd-trace/src/llmobs/experiments/noop.js +135 -7
  117. package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
  118. package/packages/dd-trace/src/llmobs/experiments/util.js +101 -9
  119. package/packages/dd-trace/src/llmobs/plugins/ai/util.js +12 -3
  120. package/packages/dd-trace/src/llmobs/plugins/base.js +4 -2
  121. package/packages/dd-trace/src/noop/proxy.js +3 -4
  122. package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
  123. package/packages/dd-trace/src/openfeature/flagging_provider.js +18 -2
  124. package/packages/dd-trace/src/openfeature/index.js +7 -4
  125. package/packages/dd-trace/src/openfeature/remote_config.js +1 -1
  126. package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
  127. package/packages/dd-trace/src/openfeature/writers/exposures.js +62 -14
  128. package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
  129. package/packages/dd-trace/src/opentelemetry/context_manager.js +7 -7
  130. package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +2 -1
  131. package/packages/dd-trace/src/opentelemetry/logs/logger.js +67 -11
  132. package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +14 -3
  133. package/packages/dd-trace/src/opentelemetry/metrics/index.js +25 -12
  134. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +2 -5
  135. package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +96 -43
  136. package/packages/dd-trace/src/opentracing/propagation/text_map.js +22 -13
  137. package/packages/dd-trace/src/opentracing/tracer.js +5 -2
  138. package/packages/dd-trace/src/otel-thread-ctx.js +387 -0
  139. package/packages/dd-trace/src/plugin_manager.js +3 -3
  140. package/packages/dd-trace/src/plugins/ci_plugin.js +6 -1
  141. package/packages/dd-trace/src/plugins/index.js +1 -0
  142. package/packages/dd-trace/src/plugins/util/jest.js +4 -3
  143. package/packages/dd-trace/src/plugins/util/test.js +9 -9
  144. package/packages/dd-trace/src/plugins/util/web.js +3 -0
  145. package/packages/dd-trace/src/profiling/profiler.js +1 -1
  146. package/packages/dd-trace/src/profiling/profilers/space.js +1 -3
  147. package/packages/dd-trace/src/profiling/profilers/wall.js +37 -90
  148. package/packages/dd-trace/src/profiling/webspan-utils.js +45 -0
  149. package/packages/dd-trace/src/proxy.js +38 -29
  150. package/packages/dd-trace/src/ritm.js +9 -2
  151. package/packages/dd-trace/src/serverless.js +37 -0
  152. package/packages/dd-trace/src/span_format.js +1 -0
  153. package/packages/dd-trace/src/span_stats.js +30 -3
  154. package/packages/dd-trace/src/storage-channels.js +86 -0
  155. package/packages/dd-trace/src/telemetry/metrics.js +62 -8
  156. package/packages/dd-trace/src/telemetry/send-data.js +1 -0
  157. package/packages/dd-trace/src/tracer.js +9 -2
  158. package/packages/dd-trace/src/tracer_metadata.js +14 -1
  159. package/packages/dd-trace/src/web-tags-cache.js +132 -0
  160. package/vendor/dist/@datadog/openfeature-node-server/LICENSE +201 -0
  161. package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -0
  162. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  163. package/vendor/dist/https-proxy-agent/LICENSE +22 -0
  164. package/vendor/dist/https-proxy-agent/index.js +4 -0
  165. package/vendor/dist/protobufjs/index.js +1 -1
  166. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  167. package/vendor/dist/proxy-from-env/LICENSE +20 -0
  168. package/vendor/dist/proxy-from-env/index.js +1 -0
  169. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +0 -65
  170. package/packages/datadog-webpack/src/optional-peer-loader.js +0 -17
  171. package/packages/dd-trace/src/feature-registry.js +0 -29
  172. package/packages/dd-trace/src/openfeature/register.js +0 -35
  173. package/packages/dd-trace/src/openfeature/require-provider.js +0 -18
@@ -45,6 +45,9 @@ const testSkipCh = channel('ci:playwright:test:skip')
45
45
  const testSessionStartCh = channel('ci:playwright:session:start')
46
46
  const testSessionConfigurationCh = channel('ci:playwright:session:configuration')
47
47
  const testSessionFinishCh = channel('ci:playwright:session:finish')
48
+ const reporterErrorCh = channel('ci:playwright:reporter:error')
49
+ const reporterRunSummaryCh = channel('ci:playwright:reporter:run-summary')
50
+ const reporterSuiteHookErrorCh = channel('ci:playwright:reporter:suite-hook-error')
48
51
 
49
52
  const libraryConfigurationCh = channel('ci:playwright:library-configuration')
50
53
  const knownTestsCh = channel('ci:playwright:known-tests')
@@ -81,6 +84,8 @@ const isFailureScreenshotUploadEnabled =
81
84
  getValueFromEnvSources('DD_TEST_FAILURE_SCREENSHOTS_ENABLED') === true
82
85
 
83
86
  let applyRepeatEachIndex = null
87
+ let reporterError
88
+ let hasReporterError = false
84
89
 
85
90
  let startedSuites = []
86
91
 
@@ -108,7 +113,7 @@ let testManagementAttemptToFixRetries = 0
108
113
  let testManagementTests = {}
109
114
  let isImpactedTestsEnabled = false
110
115
  let modifiedFiles = {}
111
- let quarantinedButNotAttemptToFixFqns = new Set()
116
+ let playwrightRunSummary
112
117
  let recordedTestOptimizationExecutions = new Set()
113
118
  let testsReportedInGenerateSummary = new Set()
114
119
  const newTestsWithDynamicNames = new Set()
@@ -134,8 +139,10 @@ const DD_PROPERTIES_REQUEST = 'ddPropertiesRequest'
134
139
  const DD_PROPERTIES_RESPONSE = 'ddProperties'
135
140
  const kDdPlaywrightDisabledTestIds = Symbol('ddPlaywrightDisabledTestIds')
136
141
  const kDdPlaywrightFailureScreenshots = Symbol('ddPlaywrightFailureScreenshots')
142
+ const kDdPlaywrightReporterConfigured = Symbol('ddPlaywrightReporterConfigured')
137
143
  const kDdPlaywrightWorkerHostInstrumented = Symbol('ddPlaywrightWorkerHostInstrumented')
138
144
  const kDdPlaywrightWorkerInstrumented = Symbol('ddPlaywrightWorkerInstrumented')
145
+ const instrumentedPlaywrightReporters = new WeakSet()
139
146
  const PLAYWRIGHT_FAILURE_SCREENSHOT_PATH_RE = /(?:^|[\\/])test-failed-\d+\.png$/
140
147
  const automaticFailureScreenshotPaths = new Set()
141
148
 
@@ -780,6 +787,7 @@ function finishTestSuiteIfDone (testSuiteAbsolutePath, projects) {
780
787
  testSourceLine: test.location.line,
781
788
  browserName,
782
789
  isNew: test._ddIsNew,
790
+ isAttemptToFix: test._ddIsAttemptToFix,
783
791
  isDisabled: test._ddIsDisabled,
784
792
  isModified: test._ddIsModified,
785
793
  isQuarantined: test._ddIsQuarantined,
@@ -827,6 +835,7 @@ function testEndHandler ({
827
835
 
828
836
  if (hookError) {
829
837
  addErrorToTestSuite(testSuiteAbsolutePath, hookError)
838
+ reporterSuiteHookErrorCh.publish(testSuiteAbsolutePath)
830
839
  }
831
840
  return
832
841
  }
@@ -912,9 +921,7 @@ function testEndHandler ({
912
921
 
913
922
  // Check if all EFD retries failed
914
923
  const efdRetryCount = getEfdRetryCountForTest(test)
915
- if (efdRetryCount > 0 && testStatuses.length === efdRetryCount + 1 &&
916
- (test._ddIsNew || test._ddIsModified) &&
917
- isEarlyFlakeDetectionEnabled &&
924
+ if (isEfdManagedTest && efdRetryCount > 0 && testStatuses.length === efdRetryCount + 1 &&
918
925
  testStatuses.every(status => status === 'fail')) {
919
926
  test._ddHasFailedAllRetries = true
920
927
  }
@@ -1252,6 +1259,18 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
1252
1259
  function runAllTestsWrapper (runAllTests, playwrightVersion) {
1253
1260
  // Config parameter is only available from >=1.55.0
1254
1261
  return async function (config) {
1262
+ reporterError = undefined
1263
+ hasReporterError = false
1264
+ playwrightRunSummary = undefined
1265
+ let restoreReporterConsoleError
1266
+ if (satisfies(playwrightVersion, '>=1.60.0') && config?.config) {
1267
+ const DatadogPlaywrightReporter = require('./playwright-reporter')
1268
+ restoreReporterConsoleError = DatadogPlaywrightReporter.restoreConsoleError
1269
+ if (!config.config[kDdPlaywrightReporterConfigured]) {
1270
+ Object.defineProperty(config.config, kDdPlaywrightReporterConfigured, { value: true })
1271
+ config.config.reporter.unshift([require.resolve('./playwright-reporter')])
1272
+ }
1273
+ }
1255
1274
  rootDir = getRootDir(this, config)
1256
1275
  const projects = getProjectsFromRunner(this, config)
1257
1276
  const isFailureScreenshotEnabled = isFailureScreenshotCaptureEnabled(projects)
@@ -1367,7 +1386,27 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1367
1386
  }
1368
1387
  }
1369
1388
 
1370
- let runAllTestsReturn = await runAllTests.apply(this, arguments)
1389
+ let runAllTestsReturn
1390
+ try {
1391
+ runAllTestsReturn = await runAllTests.apply(this, arguments)
1392
+ } catch (error) {
1393
+ try {
1394
+ await getChannelPromise(testSessionFinishCh, {
1395
+ status: 'fail',
1396
+ error: hasReporterError ? reporterError : error,
1397
+ isEarlyFlakeDetectionEnabled,
1398
+ isEarlyFlakeDetectionFaulty,
1399
+ isTestManagementTestsEnabled,
1400
+ })
1401
+ } catch (finalizationError) {
1402
+ log.error('Playwright test session finalization error: %s', finalizationError)
1403
+ }
1404
+ reporterError = undefined
1405
+ hasReporterError = false
1406
+ throw error
1407
+ } finally {
1408
+ restoreReporterConsoleError?.()
1409
+ }
1371
1410
 
1372
1411
  // Tests that have only skipped tests may reach this point
1373
1412
  // Skipped tests may or may not go through `testBegin` or `testEnd`
@@ -1397,23 +1436,8 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1397
1436
  const sessionStatus = runAllTestsReturn.status || runAllTestsReturn
1398
1437
 
1399
1438
  if (isTestManagementTestsEnabled && sessionStatus === 'failed') {
1400
- let totalFailedTestCount = 0
1401
- let totalPureQuarantinedFailedTestCount = 0
1402
-
1403
- for (const [fqn, testStatuses] of testsToTestStatuses) {
1404
- // Only count as failed if the final status (after retries) is 'fail'
1405
- const lastStatus = testStatuses.at(-1)
1406
- if (lastStatus === 'fail') {
1407
- totalFailedTestCount += 1
1408
- if (quarantinedButNotAttemptToFixFqns.has(fqn)) {
1409
- totalPureQuarantinedFailedTestCount += 1
1410
- }
1411
- }
1412
- }
1413
-
1414
- const totalIgnorableFailures = totalPureQuarantinedFailedTestCount
1415
-
1416
- if (totalFailedTestCount > 0 && totalFailedTestCount === totalIgnorableFailures) {
1439
+ const { failureCount, quarantinedFailureCount, hasIncompleteTests } = playwrightRunSummary || {}
1440
+ if (!hasIncompleteTests && quarantinedFailureCount > 0 && quarantinedFailureCount === failureCount) {
1417
1441
  runAllTestsReturn = 'passed'
1418
1442
  preventedToFail = true
1419
1443
  }
@@ -1422,16 +1446,25 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1422
1446
  logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
1423
1447
  loggedAttemptToFixTests.clear()
1424
1448
 
1449
+ const finalizationError = reporterError
1450
+ const finalStatus = hasReporterError
1451
+ ? 'fail'
1452
+ : (preventedToFail ? 'pass' : STATUS_TO_TEST_STATUS[sessionStatus])
1425
1453
  await getChannelPromise(testSessionFinishCh, {
1426
- status: preventedToFail ? 'pass' : STATUS_TO_TEST_STATUS[sessionStatus],
1454
+ status: finalStatus,
1455
+ error: finalizationError,
1427
1456
  isEarlyFlakeDetectionEnabled,
1428
1457
  isEarlyFlakeDetectionFaulty,
1429
1458
  isTestManagementTestsEnabled,
1430
1459
  })
1460
+ if (hasReporterError) {
1461
+ if (typeof runAllTestsReturn === 'string') runAllTestsReturn = 'failed'
1462
+ else runAllTestsReturn.status = 'failed'
1463
+ }
1431
1464
 
1432
1465
  startedSuites = []
1433
1466
  remainingTestsByFile = {}
1434
- quarantinedButNotAttemptToFixFqns = new Set()
1467
+ playwrightRunSummary = undefined
1435
1468
  recordedTestOptimizationExecutions = new Set()
1436
1469
  testsReportedInGenerateSummary = new Set()
1437
1470
  efdManagedTestKeys.clear()
@@ -1444,6 +1477,8 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1444
1477
  ddPropertiesByTestId.clear()
1445
1478
  ddPropertiesRequestsByTestId.clear()
1446
1479
  disabledTestIds.clear()
1480
+ reporterError = undefined
1481
+ hasReporterError = false
1447
1482
 
1448
1483
  // TODO: we can trick playwright into thinking the session passed by returning
1449
1484
  // 'passed' here. We might be able to use this for both EFD and Test Management tests.
@@ -1451,6 +1486,70 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1451
1486
  }
1452
1487
  }
1453
1488
 
1489
+ /**
1490
+ * Records errors thrown or rejected by Playwright reporters before Playwright converts them into run status.
1491
+ *
1492
+ * @param {object} reportersPackage
1493
+ * @returns {object}
1494
+ */
1495
+ function reportersHook (reportersPackage) {
1496
+ return shimmer.wrap(reportersPackage, 'createReporters', createReporters => async function () {
1497
+ const reporters = await createReporters.apply(this, arguments)
1498
+ const DatadogPlaywrightReporter = require('./playwright-reporter')
1499
+ reporters.unshift(new DatadogPlaywrightReporter({ captureReporterErrors: false }))
1500
+ for (const reporter of reporters) {
1501
+ if (instrumentedPlaywrightReporters.has(reporter)) continue
1502
+
1503
+ const onEnd = reporter.onEnd
1504
+ const onExit = reporter.onExit
1505
+ try {
1506
+ if (typeof onEnd === 'function') {
1507
+ reporter.onEnd = async function () {
1508
+ try {
1509
+ return await onEnd.apply(this, arguments)
1510
+ } catch (error) {
1511
+ recordReporterError(error)
1512
+ throw error
1513
+ }
1514
+ }
1515
+ }
1516
+ if (typeof onExit === 'function') {
1517
+ reporter.onExit = async function () {
1518
+ try {
1519
+ return await onExit.apply(this, arguments)
1520
+ } catch (error) {
1521
+ recordReporterError(error)
1522
+ throw error
1523
+ }
1524
+ }
1525
+ }
1526
+ } catch {
1527
+ continue
1528
+ }
1529
+ if (reporter.onEnd !== onEnd || reporter.onExit !== onExit) instrumentedPlaywrightReporters.add(reporter)
1530
+ }
1531
+ return reporters
1532
+ }, { replaceGetter: true })
1533
+ }
1534
+
1535
+ /**
1536
+ * Records errors from the reporter multiplexer used by Playwright before 1.38.
1537
+ *
1538
+ * @param {object} reportersPackage
1539
+ * @returns {object}
1540
+ */
1541
+ function reporterMultiplexerHook (reportersPackage) {
1542
+ shimmer.wrap(reportersPackage.Multiplexer.prototype, 'onEnd', onEnd => async function () {
1543
+ try {
1544
+ return await onEnd.apply(this, arguments)
1545
+ } catch (error) {
1546
+ recordReporterError(error)
1547
+ throw error
1548
+ }
1549
+ })
1550
+ return reportersPackage
1551
+ }
1552
+
1454
1553
  function runnerHook (runnerExport, playwrightVersion) {
1455
1554
  shimmer.wrap(
1456
1555
  runnerExport.Runner.prototype,
@@ -1550,6 +1649,36 @@ pageGotoCh.subscribe({
1550
1649
  },
1551
1650
  })
1552
1651
 
1652
+ reporterErrorCh.subscribe((error) => {
1653
+ recordReporterError(error)
1654
+ })
1655
+
1656
+ reporterRunSummaryCh.subscribe((runSummary) => {
1657
+ playwrightRunSummary = runSummary
1658
+ })
1659
+
1660
+ /**
1661
+ * Records a reporter failure even when the reporter throws a falsy value.
1662
+ *
1663
+ * @param {unknown} error
1664
+ * @returns {void}
1665
+ */
1666
+ function recordReporterError (error) {
1667
+ if (hasReporterError) return
1668
+
1669
+ let normalizedError
1670
+ try {
1671
+ const errorObject = error && typeof error === 'object' ? error : undefined
1672
+ normalizedError = new Error(errorObject?.message || errorObject?.value || String(error))
1673
+ normalizedError.name = errorObject?.name || normalizedError.name
1674
+ normalizedError.stack = errorObject?.stack || normalizedError.stack
1675
+ } catch {
1676
+ normalizedError = new Error('Playwright reporter failed')
1677
+ }
1678
+ reporterError = normalizedError
1679
+ hasReporterError = true
1680
+ }
1681
+
1553
1682
  /**
1554
1683
  * Records a path created by Playwright's automatic screenshot recorder.
1555
1684
  *
@@ -1597,6 +1726,12 @@ if (DD_MAJOR < 6) { // <1.38.0 is only supported up to version 5
1597
1726
  file: 'lib/runner/runner.js',
1598
1727
  versions: ['>=1.31.0 <1.38.0'],
1599
1728
  }, runnerHook)
1729
+
1730
+ addHook({
1731
+ name: '@playwright/test',
1732
+ file: 'lib/reporters/multiplexer.js',
1733
+ versions: ['>=1.18.0 <1.38.0'],
1734
+ }, reporterMultiplexerHook)
1600
1735
  }
1601
1736
 
1602
1737
  addHook({
@@ -1617,6 +1752,12 @@ addHook({
1617
1752
  versions: ['>=1.38.0 <1.60.0'],
1618
1753
  }, runnerHook)
1619
1754
 
1755
+ addHook({
1756
+ name: 'playwright',
1757
+ file: 'lib/runner/reporters.js',
1758
+ versions: ['>=1.38.0 <1.60.0'],
1759
+ }, reportersHook)
1760
+
1620
1761
  addHook({
1621
1762
  name: 'playwright',
1622
1763
  file: 'lib/runner/testRunner.js',
@@ -1747,11 +1888,6 @@ function processRootSuite (createRootSuiteReturnValue) {
1747
1888
  }
1748
1889
  if (testProperties.quarantined) {
1749
1890
  test._ddIsQuarantined = true
1750
- if (!testProperties.attemptToFix) {
1751
- // Do not skip quarantined tests, let them run and overwrite results post-run if they fail
1752
- const testFqn = getTestFullyQualifiedName(test)
1753
- quarantinedButNotAttemptToFixFqns.add(testFqn)
1754
- }
1755
1891
  }
1756
1892
  if (testProperties.attemptToFix) {
1757
1893
  test._ddIsAttemptToFix = true
@@ -1791,7 +1927,10 @@ function processRootSuite (createRootSuiteReturnValue) {
1791
1927
  const fileSuitesWithImpactedTestsToProjects = new Map()
1792
1928
  for (const impactedTest of impactedTests) {
1793
1929
  impactedTest._ddIsModified = true
1794
- if (shouldRunEarlyFlakeDetection() && impactedTest.expectedStatus !== 'skipped') {
1930
+ if (shouldRunEarlyFlakeDetection() && impactedTest.expectedStatus !== 'skipped' &&
1931
+ !impactedTest._ddIsAttemptToFix) {
1932
+ // Prevent ATR or `--retries` from retrying tests that EFD manages.
1933
+ impactedTest.retries = 0
1795
1934
  markEfdManagedTest(impactedTest)
1796
1935
  const fileSuite = getSuiteType(impactedTest, 'file')
1797
1936
  if (!fileSuitesWithImpactedTestsToProjects.has(fileSuite)) {
@@ -1799,14 +1938,15 @@ function processRootSuite (createRootSuiteReturnValue) {
1799
1938
  }
1800
1939
  }
1801
1940
  }
1802
- // If something change in the file, all tests in the file are impacted, hence the () => true filter
1941
+ // If something changes in the file, all tests in the file are impacted, except attempt-to-fix tests managed above.
1803
1942
  applyRetriesToTests(
1804
1943
  fileSuitesWithImpactedTestsToProjects,
1805
- () => true,
1944
+ (test) => !test._ddIsAttemptToFix,
1806
1945
  [
1807
1946
  '_ddIsModified',
1808
1947
  '_ddIsEfdRetry',
1809
1948
  (test) => (isKnownTestsEnabled && isNewTest(test) ? '_ddIsNew' : null),
1949
+ (test) => test._ddIsQuarantined && '_ddIsQuarantined',
1810
1950
  ],
1811
1951
  earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
1812
1952
  (copiedTest, originalTest, retryIndex) => {
@@ -1834,7 +1974,8 @@ function processRootSuite (createRootSuiteReturnValue) {
1834
1974
  const fileSuitesWithNewTestsToProjects = new Map()
1835
1975
  for (const newTest of newTests) {
1836
1976
  newTest._ddIsNew = true
1837
- if (shouldRunEarlyFlakeDetection() && newTest.expectedStatus !== 'skipped' && !newTest._ddIsModified) {
1977
+ if (shouldRunEarlyFlakeDetection() && newTest.expectedStatus !== 'skipped' &&
1978
+ !newTest._ddIsModified && !newTest._ddIsAttemptToFix) {
1838
1979
  // Prevent ATR or `--retries` from retrying tests that EFD manages.
1839
1980
  newTest.retries = 0
1840
1981
  markEfdManagedTest(newTest)
@@ -1847,8 +1988,12 @@ function processRootSuite (createRootSuiteReturnValue) {
1847
1988
 
1848
1989
  applyRetriesToTests(
1849
1990
  fileSuitesWithNewTestsToProjects,
1850
- isNewTest,
1851
- ['_ddIsNew', '_ddIsEfdRetry'],
1991
+ (test) => !test._ddIsAttemptToFix && isNewTest(test),
1992
+ [
1993
+ '_ddIsNew',
1994
+ '_ddIsEfdRetry',
1995
+ (test) => test._ddIsQuarantined && '_ddIsQuarantined',
1996
+ ],
1852
1997
  earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
1853
1998
  (copiedTest, originalTest, retryIndex) => {
1854
1999
  markEfdRetryTest(copiedTest, retryIndex, originalTest)
@@ -2300,6 +2445,7 @@ function generateSummaryWrapper (generateSummary) {
2300
2445
  line: testSourceLine,
2301
2446
  },
2302
2447
  _ddIsNew: isNew,
2448
+ _ddIsAttemptToFix: isAttemptToFix,
2303
2449
  _ddIsDisabled: isDisabled,
2304
2450
  _ddIsModified: isModified,
2305
2451
  _ddIsQuarantined: isQuarantined,
@@ -2314,6 +2460,7 @@ function generateSummaryWrapper (generateSummary) {
2314
2460
  testSourceLine,
2315
2461
  browserName,
2316
2462
  isNew,
2463
+ isAttemptToFix,
2317
2464
  isDisabled,
2318
2465
  isModified,
2319
2466
  isQuarantined,
@@ -596,6 +596,7 @@ const visitedParams = new WeakSet()
596
596
  function wrapHandleRequest (original) {
597
597
  return function wrappedHandleRequest (...args) {
598
598
  const req = args[0]
599
+ // eslint-disable-next-line no-restricted-syntax -- arbitrary param names; publishing {} sets a WAF address
599
600
  if (routerParamStartCh.hasSubscribers && !visitedParams.has(req.params) && Object.keys(req.params).length) {
600
601
  visitedParams.add(req.params)
601
602
 
@@ -635,6 +636,7 @@ function wrapParam (original) {
635
636
  args[1] = shimmer.wrapFunction(args[1], (originalFn) => {
636
637
  return function wrappedFn (...fnArgs) {
637
638
  const req = fnArgs[0]
639
+ // eslint-disable-next-line no-restricted-syntax -- arbitrary param names; publishing {} sets a WAF address
638
640
  if (routerParamStartCh.hasSubscribers && Object.keys(req.params).length && !visitedParams.has(req.params)) {
639
641
  visitedParams.add(req.params)
640
642
 
@@ -1058,7 +1058,7 @@ function getRepeatedTestReport (task, testName, testSuiteAbsolutePath, testPrope
1058
1058
  const errors = browserEnvironment
1059
1059
  ? normalizeVitestBrowserErrors(result?.errors)
1060
1060
  : result?.errors || []
1061
- const finalAttemptStatus = finalStatus(statuses)
1061
+ const finalAttemptStatus = finalStatus(statuses, testProperties)
1062
1062
  const hasFailure = finalAttemptStatus === 'fail'
1063
1063
  const attempts = []
1064
1064
  const attemptCount = getRepeatedAttemptCount(task, statuses)
@@ -1095,8 +1095,7 @@ function getRepeatedTestReport (task, testName, testSuiteAbsolutePath, testPrope
1095
1095
  testProperties,
1096
1096
  type,
1097
1097
  statuses,
1098
- isFinalAttempt,
1099
- hasFailure
1098
+ isFinalAttempt
1100
1099
  ),
1101
1100
  isRetry: index > 0,
1102
1101
  status: attemptStatus,
@@ -1136,11 +1135,10 @@ function getRepeatedTestReport (task, testName, testSuiteAbsolutePath, testPrope
1136
1135
  * @param {'attempt_to_fix'|'early_flake_detection'|'external'} type
1137
1136
  * @param {string[]} statuses
1138
1137
  * @param {boolean} isFinalAttempt
1139
- * @param {boolean} hasFailure
1140
1138
  * @returns {boolean}
1141
1139
  */
1142
- function hasFailedAllManagedRetries (task, testProperties, type, statuses, isFinalAttempt, hasFailure) {
1143
- if (!isFinalAttempt || !hasFailure || statuses.length === 0 || !statuses.every(status => status === 'fail')) {
1140
+ function hasFailedAllManagedRetries (task, testProperties, type, statuses, isFinalAttempt) {
1141
+ if (!isFinalAttempt || statuses.length === 0 || !statuses.every(status => status === 'fail')) {
1144
1142
  return false
1145
1143
  }
1146
1144
 
@@ -1165,7 +1163,8 @@ function getAttemptToFixFinalStatus (statuses) {
1165
1163
  return statuses.includes('fail') ? 'fail' : 'pass'
1166
1164
  }
1167
1165
 
1168
- function getEarlyFlakeDetectionFinalStatus (statuses) {
1166
+ function getEarlyFlakeDetectionFinalStatus (statuses, testProperties) {
1167
+ if (testProperties.isDisabled || testProperties.isQuarantined) return 'skip'
1169
1168
  return statuses.includes('pass') ? 'pass' : 'fail'
1170
1169
  }
1171
1170
 
@@ -1274,7 +1273,6 @@ function reportFinalTestAttempt (testReport) {
1274
1273
  testProperties,
1275
1274
  'external',
1276
1275
  ['fail'],
1277
- true,
1278
1276
  true
1279
1277
  ),
1280
1278
  isRetry: (result?.retryCount || 0) > 0 || (result?.repeatCount || 0) > 0,
@@ -67,6 +67,7 @@ const coverageWrappedProviders = new WeakSet()
67
67
  const finishWrappedContexts = new WeakSet()
68
68
  const runFilesWrappedPrototypes = new WeakSet()
69
69
  const activeRunFilesContexts = new WeakSet()
70
+ const runErrorsByContext = new WeakMap()
70
71
  let isFlakyTestRetriesEnabled = false
71
72
  let flakyTestRetriesCount = 0
72
73
  let isEarlyFlakeDetectionEnabled = false
@@ -1195,15 +1196,18 @@ function getFinishWrapper (exitOrClose) {
1195
1196
  }
1196
1197
 
1197
1198
  const failedSuites = this.state.getFailedFilepaths()
1198
- let error
1199
- if (failedSuites.length) {
1199
+ const runError = runErrorsByContext.get(this)
1200
+ runErrorsByContext.delete(this)
1201
+ let error = runError
1202
+ if (!error && failedSuites.length) {
1200
1203
  error = new Error(`Test suites failed: ${failedSuites.length}.`)
1201
1204
  }
1202
1205
 
1203
1206
  const flushPromise = getChannelPromise(testSessionFinishCh, {
1204
- status: areAllSuitesSkipped ? 'skip' : getSessionStatus(this.state),
1207
+ status: runError ? 'fail' : (areAllSuitesSkipped ? 'skip' : getSessionStatus(this.state)),
1205
1208
  testCodeCoverageLinesTotal,
1206
1209
  error,
1210
+ isTestSessionFinalizationError: Boolean(runError),
1207
1211
  isEarlyFlakeDetectionEnabled,
1208
1212
  isEarlyFlakeDetectionFaulty,
1209
1213
  isTestManagementTestsEnabled,
@@ -1376,6 +1380,9 @@ function wrapVitestRunFiles (Vitest, frameworkVersion) {
1376
1380
  activeRunFilesContexts.add(this)
1377
1381
  try {
1378
1382
  return await runFiles.apply(this, arguments)
1383
+ } catch (error) {
1384
+ runErrorsByContext.set(this, error)
1385
+ throw error
1379
1386
  } finally {
1380
1387
  activeRunFilesContexts.delete(this)
1381
1388
  }
@@ -1677,6 +1684,7 @@ async function reportTypecheckFile (file, sessionConfiguration, frameworkVersion
1677
1684
  }
1678
1685
 
1679
1686
  await getChannelPromise(testSuiteFinishCh, {
1687
+ deferFlush: true,
1680
1688
  frameworkVersion,
1681
1689
  status: getTypecheckTaskStatus(file),
1682
1690
  ...testSuiteCtx.currentStore,
@@ -343,6 +343,7 @@ function getHostname (store, region) {
343
343
  if (!region) return
344
344
  const { awsParams, awsService } = store
345
345
  switch (awsService) {
346
+ case 'CloudWatchEvents':
346
347
  case 'EventBridge':
347
348
  return `events.${region}.amazonaws.com`
348
349
  case 'SQS':