dd-trace 6.3.0 → 6.5.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 (207) hide show
  1. package/README.electron.md +7 -0
  2. package/README.md +17 -0
  3. package/ci/diagnose.js +2100 -0
  4. package/ci/init.js +23 -2
  5. package/ci/runbook.md +198 -0
  6. package/ci/test-optimization-validation/approval-artifacts.js +143 -0
  7. package/ci/test-optimization-validation/approval.js +299 -0
  8. package/ci/test-optimization-validation/artifact-id.js +20 -0
  9. package/ci/test-optimization-validation/bounded-json.js +121 -0
  10. package/ci/test-optimization-validation/ci-command-candidate.js +83 -0
  11. package/ci/test-optimization-validation/ci-discovery.js +181 -0
  12. package/ci/test-optimization-validation/ci-remediation.js +210 -0
  13. package/ci/test-optimization-validation/cli.js +903 -0
  14. package/ci/test-optimization-validation/command-blocker.js +81 -0
  15. package/ci/test-optimization-validation/command-output-policy.js +206 -0
  16. package/ci/test-optimization-validation/command-runner.js +707 -0
  17. package/ci/test-optimization-validation/command-suitability.js +471 -0
  18. package/ci/test-optimization-validation/executable-approval.js +48 -0
  19. package/ci/test-optimization-validation/executable.js +480 -0
  20. package/ci/test-optimization-validation/generated-file-policy.js +63 -0
  21. package/ci/test-optimization-validation/generated-files.js +351 -0
  22. package/ci/test-optimization-validation/generated-verifier.js +196 -0
  23. package/ci/test-optimization-validation/init-probe-preload.js +163 -0
  24. package/ci/test-optimization-validation/init-probe.js +246 -0
  25. package/ci/test-optimization-validation/late-initialization.js +63 -0
  26. package/ci/test-optimization-validation/local-command.js +163 -0
  27. package/ci/test-optimization-validation/manifest-loader.js +133 -0
  28. package/ci/test-optimization-validation/manifest-scaffold.js +738 -0
  29. package/ci/test-optimization-validation/manifest-schema.js +862 -0
  30. package/ci/test-optimization-validation/offline-fixtures.js +327 -0
  31. package/ci/test-optimization-validation/offline-output.js +406 -0
  32. package/ci/test-optimization-validation/payload-normalizer.js +72 -0
  33. package/ci/test-optimization-validation/plan-writer.js +1120 -0
  34. package/ci/test-optimization-validation/preflight-runner.js +114 -0
  35. package/ci/test-optimization-validation/redaction.js +331 -0
  36. package/ci/test-optimization-validation/report-writer.js +1508 -0
  37. package/ci/test-optimization-validation/safe-files.js +203 -0
  38. package/ci/test-optimization-validation/scenarios/auto-test-retries.js +159 -0
  39. package/ci/test-optimization-validation/scenarios/basic-reporting.js +657 -0
  40. package/ci/test-optimization-validation/scenarios/ci-wiring.js +780 -0
  41. package/ci/test-optimization-validation/scenarios/early-flake-detection.js +141 -0
  42. package/ci/test-optimization-validation/scenarios/helpers.js +539 -0
  43. package/ci/test-optimization-validation/scenarios/test-management.js +218 -0
  44. package/ci/test-optimization-validation/setup-runner.js +97 -0
  45. package/ci/test-optimization-validation/static-diagnosis.js +159 -0
  46. package/ci/test-optimization-validation/test-output.js +129 -0
  47. package/ci/test-optimization-validation-manifest.schema.json +1 -0
  48. package/ci/validate-test-optimization.js +3 -0
  49. package/ext/exporters.js +1 -0
  50. package/index.d.ts +121 -8
  51. package/index.electron.js +3 -0
  52. package/init.js +18 -0
  53. package/initialize.mjs +1 -1
  54. package/loader-hook.mjs +28 -18
  55. package/openfeature.d.ts +1 -0
  56. package/openfeature.js +4 -0
  57. package/package.json +15 -8
  58. package/packages/datadog-instrumentations/src/ai.js +37 -26
  59. package/packages/datadog-instrumentations/src/child_process.js +1 -1
  60. package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
  61. package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +9 -3
  62. package/packages/datadog-instrumentations/src/cucumber.js +24 -10
  63. package/packages/datadog-instrumentations/src/cypress-config.js +398 -52
  64. package/packages/datadog-instrumentations/src/express.js +20 -2
  65. package/packages/datadog-instrumentations/src/fastify.js +7 -11
  66. package/packages/datadog-instrumentations/src/grpc/server.js +18 -0
  67. package/packages/datadog-instrumentations/src/helpers/hook.js +27 -6
  68. package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +8 -8
  69. package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +28 -2
  70. package/packages/datadog-instrumentations/src/helpers/router-helper.js +8 -8
  71. package/packages/datadog-instrumentations/src/http2/server.js +143 -17
  72. package/packages/datadog-instrumentations/src/jest.js +36 -4
  73. package/packages/datadog-instrumentations/src/mariadb.js +1 -1
  74. package/packages/datadog-instrumentations/src/mocha/main.js +0 -2
  75. package/packages/datadog-instrumentations/src/mocha/utils.js +0 -4
  76. package/packages/datadog-instrumentations/src/mongodb.js +3 -3
  77. package/packages/datadog-instrumentations/src/mongoose.js +3 -3
  78. package/packages/datadog-instrumentations/src/mquery.js +2 -2
  79. package/packages/datadog-instrumentations/src/mysql.js +1 -1
  80. package/packages/datadog-instrumentations/src/next.js +24 -0
  81. package/packages/datadog-instrumentations/src/nyc.js +0 -2
  82. package/packages/datadog-instrumentations/src/oracledb.js +2 -2
  83. package/packages/datadog-instrumentations/src/pg.js +2 -2
  84. package/packages/datadog-instrumentations/src/playwright.js +159 -22
  85. package/packages/datadog-instrumentations/src/process.js +3 -0
  86. package/packages/datadog-instrumentations/src/router.js +191 -106
  87. package/packages/datadog-instrumentations/src/selenium.js +52 -26
  88. package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +2 -2
  89. package/packages/datadog-instrumentations/src/vitest-main.js +0 -2
  90. package/packages/datadog-instrumentations/src/ws.js +2 -1
  91. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +2 -2
  92. package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +2 -1
  93. package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +41 -16
  94. package/packages/datadog-plugin-aws-sdk/src/services/sns.js +21 -21
  95. package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +14 -8
  96. package/packages/datadog-plugin-aws-sdk/src/util.js +2 -24
  97. package/packages/datadog-plugin-cucumber/src/index.js +1 -1
  98. package/packages/datadog-plugin-cypress/src/cypress-plugin.js +24 -62
  99. package/packages/datadog-plugin-cypress/src/index.js +6 -1
  100. package/packages/datadog-plugin-cypress/src/support.js +92 -26
  101. package/packages/datadog-plugin-electron/src/ipc.js +1 -1
  102. package/packages/datadog-plugin-graphql/src/execute.js +177 -59
  103. package/packages/datadog-plugin-graphql/src/parse.js +22 -1
  104. package/packages/datadog-plugin-graphql/src/request.js +32 -1
  105. package/packages/datadog-plugin-graphql/src/utils.js +42 -0
  106. package/packages/datadog-plugin-graphql/src/validate.js +15 -3
  107. package/packages/datadog-plugin-http2/src/client.js +1 -1
  108. package/packages/datadog-plugin-http2/src/server.js +38 -6
  109. package/packages/datadog-plugin-jest/src/util.js +21 -6
  110. package/packages/datadog-plugin-langchain/src/handlers/embedding.js +2 -1
  111. package/packages/datadog-plugin-langchain/src/handlers/language_models.js +2 -1
  112. package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
  113. package/packages/datadog-plugin-playwright/src/index.js +177 -64
  114. package/packages/datadog-plugin-router/src/index.js +11 -2
  115. package/packages/datadog-plugin-selenium/src/index.js +5 -36
  116. package/packages/datadog-plugin-vitest/src/index.js +4 -2
  117. package/packages/datadog-plugin-ws/src/close.js +2 -1
  118. package/packages/datadog-plugin-ws/src/producer.js +2 -1
  119. package/packages/datadog-plugin-ws/src/receiver.js +2 -1
  120. package/packages/datadog-plugin-ws/src/server.js +2 -1
  121. package/packages/dd-trace/index.electron.js +3 -0
  122. package/packages/dd-trace/index.js +2 -37
  123. package/packages/dd-trace/src/aiguard/sdk.js +1 -1
  124. package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
  125. package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +3 -1
  126. package/packages/dd-trace/src/azure_metadata.js +2 -1
  127. package/packages/dd-trace/src/bootstrap.js +39 -0
  128. package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +93 -28
  129. package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
  130. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +160 -0
  131. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +288 -0
  132. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/payload-projection.js +84 -0
  133. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +369 -0
  134. package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +60 -0
  135. package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +55 -5
  136. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +22 -6
  137. package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +5 -0
  138. package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +62 -7
  139. package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -2
  140. package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +6 -2
  141. package/packages/dd-trace/src/ci-visibility/rum.js +7 -0
  142. package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
  143. package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
  144. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +137 -9
  145. package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +173 -9
  146. package/packages/dd-trace/src/ci-visibility/test-screenshot.js +90 -0
  147. package/packages/dd-trace/src/config/generated-config-types.d.ts +20 -0
  148. package/packages/dd-trace/src/config/helper.js +1 -0
  149. package/packages/dd-trace/src/config/index.js +10 -0
  150. package/packages/dd-trace/src/config/supported-configurations.json +83 -0
  151. package/packages/dd-trace/src/constants.js +7 -0
  152. package/packages/dd-trace/src/datastreams/pathway.js +6 -4
  153. package/packages/dd-trace/src/exporter.js +2 -0
  154. package/packages/dd-trace/src/exporters/common/client-library-headers.js +21 -0
  155. package/packages/dd-trace/src/exporters/common/request.js +59 -30
  156. package/packages/dd-trace/src/exporters/common/url.js +15 -1
  157. package/packages/dd-trace/src/feature-registry.js +29 -0
  158. package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
  159. package/packages/dd-trace/src/llmobs/experiments/client.js +113 -0
  160. package/packages/dd-trace/src/llmobs/experiments/dataset.js +154 -0
  161. package/packages/dd-trace/src/llmobs/experiments/experiment.js +283 -0
  162. package/packages/dd-trace/src/llmobs/experiments/index.js +152 -0
  163. package/packages/dd-trace/src/llmobs/experiments/noop.js +30 -0
  164. package/packages/dd-trace/src/llmobs/experiments/result.js +34 -0
  165. package/packages/dd-trace/src/llmobs/index.js +10 -2
  166. package/packages/dd-trace/src/llmobs/noop.js +6 -0
  167. package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +20 -3
  168. package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +2 -2
  169. package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
  170. package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +8 -0
  171. package/packages/dd-trace/src/llmobs/plugins/openai/index.js +48 -14
  172. package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +43 -0
  173. package/packages/dd-trace/src/llmobs/sdk.js +17 -0
  174. package/packages/dd-trace/src/llmobs/tagger.js +79 -7
  175. package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
  176. package/packages/dd-trace/src/llmobs/util.js +32 -0
  177. package/packages/dd-trace/src/noop/proxy.js +5 -4
  178. package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +322 -0
  179. package/packages/dd-trace/src/openfeature/configuration_source.js +90 -0
  180. package/packages/dd-trace/src/openfeature/flagging_provider.js +14 -23
  181. package/packages/dd-trace/src/openfeature/index.js +0 -2
  182. package/packages/dd-trace/src/openfeature/noop.js +1 -28
  183. package/packages/dd-trace/src/openfeature/register.js +35 -0
  184. package/packages/dd-trace/src/openfeature/remote_config.js +15 -18
  185. package/packages/dd-trace/src/openfeature/require-provider.js +18 -0
  186. package/packages/dd-trace/src/opentelemetry/span-ending-hook.js +10 -0
  187. package/packages/dd-trace/src/opentelemetry/span.js +5 -0
  188. package/packages/dd-trace/src/opentracing/propagation/log.js +11 -2
  189. package/packages/dd-trace/src/opentracing/propagation/text_map.js +80 -14
  190. package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +10 -1
  191. package/packages/dd-trace/src/opentracing/tracer.js +7 -1
  192. package/packages/dd-trace/src/plugins/ci_plugin.js +20 -6
  193. package/packages/dd-trace/src/plugins/util/git.js +3 -2
  194. package/packages/dd-trace/src/plugins/util/inferred_proxy.js +29 -5
  195. package/packages/dd-trace/src/plugins/util/test.js +65 -8
  196. package/packages/dd-trace/src/plugins/util/web.js +12 -0
  197. package/packages/dd-trace/src/proxy.js +99 -10
  198. package/packages/dd-trace/src/span_processor.js +5 -0
  199. package/packages/dd-trace/src/standalone/index.js +0 -36
  200. package/packages/dd-trace/src/telemetry/send-data.js +2 -2
  201. package/packages/dd-trace/src/util.js +26 -0
  202. package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
  203. package/vendor/dist/@datadog/sketches-js/index.js +1 -1
  204. package/vendor/dist/protobufjs/index.js +1 -1
  205. package/vendor/dist/protobufjs/minimal/index.js +1 -1
  206. package/vendor/dist/shell-quote/index.js +1 -1
  207. package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +0 -17
@@ -144,8 +144,8 @@ addHook({ name: 'oracledb', versions: ['>=5'], file: 'lib/oracledb.js' }, oracle
144
144
  shimmer.wrap(oracledb.Pool.prototype, 'getConnection', getConnection => {
145
145
  return function wrappedGetConnection (...args) {
146
146
  let callback
147
- if (typeof args[args.length - 1] === 'function') {
148
- callback = args[args.length - 1]
147
+ if (typeof args.at(-1) === 'function') {
148
+ callback = args.at(-1)
149
149
  }
150
150
  if (callback) {
151
151
  args[args.length - 1] = shimmer.wrapFunction(callback, callback => (err, connection) => {
@@ -97,7 +97,7 @@ function wrapQuery (query) {
97
97
 
98
98
  // Based on: https://github.com/brianc/node-postgres/blob/54eb0fa216aaccd727765641e7d1cf5da2bc483d/packages/pg/lib/client.js#L510
99
99
  const reusingQuery = typeof pgQuery.submit === 'function'
100
- const callback = args[args.length - 1]
100
+ const callback = args.at(-1)
101
101
 
102
102
  finish(error)
103
103
 
@@ -197,7 +197,7 @@ function wrapPoolQuery (query) {
197
197
  const ctx = { query: pgQuery, abortController }
198
198
 
199
199
  return startPoolQueryCh.runStores(ctx, () => {
200
- const cb = args[args.length - 1]
200
+ const cb = args.at(-1)
201
201
 
202
202
  if (abortController.signal.aborted) {
203
203
  const error = abortController.signal.reason || new Error('Aborted')
@@ -25,6 +25,9 @@ const log = require('../../dd-trace/src/log')
25
25
  const {
26
26
  getValueFromEnvSources,
27
27
  } = require('../../dd-trace/src/config/helper')
28
+ const {
29
+ RUM_TEST_EXECUTION_ID_COOKIE_NAME: RUM_COOKIE_NAME,
30
+ } = require('../../dd-trace/src/ci-visibility/rum')
28
31
  const { DD_MAJOR } = require('../../../version')
29
32
  const { addHook, channel, tracingChannel } = require('./helpers/instrument')
30
33
 
@@ -33,6 +36,7 @@ const testFinishCh = channel('ci:playwright:test:finish')
33
36
  const testSkipCh = channel('ci:playwright:test:skip')
34
37
 
35
38
  const testSessionStartCh = channel('ci:playwright:session:start')
39
+ const testSessionConfigurationCh = channel('ci:playwright:session:configuration')
36
40
  const testSessionFinishCh = channel('ci:playwright:session:finish')
37
41
 
38
42
  const libraryConfigurationCh = channel('ci:playwright:library-configuration')
@@ -51,6 +55,9 @@ const dispatcherRunCh = tracingChannel('orchestrion:playwright:Dispatcher_run')
51
55
  const dispatcherCreateWorkerCh = tracingChannel('orchestrion:playwright:Dispatcher_createWorker')
52
56
  const processHostStartRunnerCh = tracingChannel('orchestrion:playwright:ProcessHost_startRunner')
53
57
  const createRootSuiteCh = tracingChannel('orchestrion:playwright:createRootSuite')
58
+ const artifactsRecorderScreenshotPathCh =
59
+ tracingChannel('orchestrion:playwright:ArtifactsRecorder_createScreenshotAttachmentPath')
60
+ const snapshotRecorderScreenshotPathCh = tracingChannel('orchestrion:playwright:SnapshotRecorder_createAttachmentPath')
54
61
  const pageGotoCh = tracingChannel('orchestrion:playwright-core:Page_goto')
55
62
 
56
63
  const testToCtx = new WeakMap()
@@ -61,6 +68,8 @@ const testsToTestStatuses = new Map()
61
68
 
62
69
  const RUM_FLUSH_WAIT_TIME = getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')
63
70
  const DD_PROPERTIES_TIMEOUT = 5000
71
+ const isFailureScreenshotUploadEnabled =
72
+ getValueFromEnvSources('DD_TEST_FAILURE_SCREENSHOTS_ENABLED') === true
64
73
 
65
74
  let applyRepeatEachIndex = null
66
75
 
@@ -115,8 +124,21 @@ const EFD_RETRY_COUNT_RESPONSE = 'ddEfdRetryCountResponse'
115
124
  const DD_PROPERTIES_REQUEST = 'ddPropertiesRequest'
116
125
  const DD_PROPERTIES_RESPONSE = 'ddProperties'
117
126
  const kDdPlaywrightDisabledTestIds = Symbol('ddPlaywrightDisabledTestIds')
127
+ const kDdPlaywrightFailureScreenshots = Symbol('ddPlaywrightFailureScreenshots')
118
128
  const kDdPlaywrightWorkerHostInstrumented = Symbol('ddPlaywrightWorkerHostInstrumented')
119
129
  const kDdPlaywrightWorkerInstrumented = Symbol('ddPlaywrightWorkerInstrumented')
130
+ const PLAYWRIGHT_FAILURE_SCREENSHOT_PATH_RE = /(?:^|[\\/])test-failed-\d+\.png$/
131
+ const automaticFailureScreenshotPaths = new Set()
132
+
133
+ /**
134
+ * Returns whether Playwright's internal screenshot recorder created an attachment.
135
+ *
136
+ * @param {object} attachment - Playwright attachment payload
137
+ * @returns {boolean}
138
+ */
139
+ function isAutomaticFailureScreenshotAttachment (attachment) {
140
+ return typeof attachment?.path === 'string' && automaticFailureScreenshotPaths.delete(attachment.path)
141
+ }
120
142
 
121
143
  function isValidKnownTests (receivedKnownTests) {
122
144
  return !!receivedKnownTests.playwright
@@ -501,6 +523,23 @@ function getProjectsFromRunner (runner, configArg) {
501
523
  })
502
524
  }
503
525
 
526
+ /**
527
+ * Returns whether at least one Playwright project captures automatic screenshots for failed tests.
528
+ *
529
+ * @param {Array<object>} projects - Playwright projects with resolved use options
530
+ * @returns {boolean} Whether failure screenshot capture is enabled
531
+ */
532
+ function isFailureScreenshotCaptureEnabled (projects) {
533
+ for (const project of projects) {
534
+ const screenshot = project.use?.screenshot
535
+ const mode = typeof screenshot === 'object' && screenshot !== null ? screenshot.mode : screenshot
536
+ if (mode === 'on' || mode === 'only-on-failure' || mode === 'on-first-failure') {
537
+ return true
538
+ }
539
+ }
540
+ return false
541
+ }
542
+
504
543
  function getProjectsFromDispatcher (dispatcher) {
505
544
  const bundledConfig = dispatcher._testRun?.config?.config?.projects
506
545
  if (bundledConfig) {
@@ -993,6 +1032,7 @@ function onDispatcherCreateWorker (dispatcher, worker) {
993
1032
 
994
1033
  const projects = getProjectsFromDispatcher(dispatcher)
995
1034
  sessionProjects = projects
1035
+ const automaticFailureScreenshotPathsByTestId = new Map()
996
1036
 
997
1037
  if (disabledTestIds.size && !worker[kDdPlaywrightWorkerHostInstrumented] &&
998
1038
  typeof worker.runTestGroup === 'function') {
@@ -1021,6 +1061,16 @@ function onDispatcherCreateWorker (dispatcher, worker) {
1021
1061
  const shouldCreateTestSpan = test.expectedStatus === 'skipped'
1022
1062
  testBeginHandler(test, browser, shouldCreateTestSpan)
1023
1063
  })
1064
+ worker.on('attach', ({ testId, path, _ddIsAutomaticFailureScreenshot }) => {
1065
+ if (!_ddIsAutomaticFailureScreenshot) return
1066
+
1067
+ let screenshotPaths = automaticFailureScreenshotPathsByTestId.get(testId)
1068
+ if (!screenshotPaths) {
1069
+ screenshotPaths = new Set()
1070
+ automaticFailureScreenshotPathsByTestId.set(testId, screenshotPaths)
1071
+ }
1072
+ screenshotPaths.add(path)
1073
+ })
1024
1074
  worker.on('testEnd', ({ testId, status, errors, annotations }) => {
1025
1075
  const test = getTestByTestId(dispatcher, testId)
1026
1076
  if (!test) return
@@ -1043,6 +1093,20 @@ function onDispatcherCreateWorker (dispatcher, worker) {
1043
1093
  }
1044
1094
  )
1045
1095
  const testResult = test.results.at(-1)
1096
+ const automaticFailureScreenshotPaths = automaticFailureScreenshotPathsByTestId.get(testId)
1097
+ automaticFailureScreenshotPathsByTestId.delete(testId)
1098
+ if (testStatus === 'fail' && automaticFailureScreenshotPaths?.size && testResult?.attachments?.length) {
1099
+ const screenshots = []
1100
+ for (const attachment of testResult.attachments) {
1101
+ if (automaticFailureScreenshotPaths.has(attachment.path)) {
1102
+ screenshots.push(attachment)
1103
+ }
1104
+ }
1105
+ if (screenshots.length) {
1106
+ worker[kDdPlaywrightFailureScreenshots] ??= []
1107
+ worker[kDdPlaywrightFailureScreenshots].push(screenshots)
1108
+ }
1109
+ }
1046
1110
  const isAtrRetry = testResult?.retry > 0 &&
1047
1111
  isFlakyTestRetriesEnabled &&
1048
1112
  !test._ddIsAttemptToFix &&
@@ -1145,9 +1209,16 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1145
1209
  let onDone
1146
1210
 
1147
1211
  rootDir = getRootDir(this, config)
1212
+ const projects = getProjectsFromRunner(this, config)
1213
+ const isFailureScreenshotEnabled = isFailureScreenshotCaptureEnabled(projects)
1148
1214
  const processArgv = process.argv.slice(2).join(' ')
1149
1215
  const command = `playwright ${processArgv}`
1150
- testSessionStartCh.publish({ command, frameworkVersion: playwrightVersion, rootDir })
1216
+ testSessionStartCh.publish({
1217
+ command,
1218
+ frameworkVersion: playwrightVersion,
1219
+ rootDir,
1220
+ isFailureScreenshotEnabled,
1221
+ })
1151
1222
 
1152
1223
  try {
1153
1224
  const { err, libraryConfig } = await getChannelPromise(
@@ -1174,6 +1245,8 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1174
1245
  log.error('Playwright session start error', e)
1175
1246
  }
1176
1247
 
1248
+ testSessionConfigurationCh.publish({ isFailureScreenshotEnabled })
1249
+
1177
1250
  const isTestOptimizationSupported = satisfies(playwrightVersion, MINIMUM_SUPPORTED_VERSION_RANGE_EFD)
1178
1251
  const shouldGetKnownTests = isKnownTestsEnabled && isTestOptimizationSupported
1179
1252
  const shouldGetTestManagementTests = isTestManagementTestsEnabled && isTestOptimizationSupported
@@ -1239,8 +1312,6 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1239
1312
  }
1240
1313
  }
1241
1314
 
1242
- const projects = getProjectsFromRunner(this, config)
1243
-
1244
1315
  // ATR and `--retries` are now compatible with Test Management.
1245
1316
  // Test Management tests have their retries set to 0 at the test level,
1246
1317
  // preventing them from being retried by ATR or `--retries`.
@@ -1288,7 +1359,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
1288
1359
 
1289
1360
  for (const [fqn, testStatuses] of testsToTestStatuses.entries()) {
1290
1361
  // Only count as failed if the final status (after retries) is 'fail'
1291
- const lastStatus = testStatuses[testStatuses.length - 1]
1362
+ const lastStatus = testStatuses.at(-1)
1292
1363
  if (lastStatus === 'fail') {
1293
1364
  totalFailedTestCount += 1
1294
1365
  if (quarantinedButNotAttemptToFixFqns.has(fqn)) {
@@ -1439,6 +1510,23 @@ pageGotoCh.subscribe({
1439
1510
  },
1440
1511
  })
1441
1512
 
1513
+ /**
1514
+ * Records a path created by Playwright's automatic screenshot recorder.
1515
+ *
1516
+ * @param {object} ctx - Orchestrion context
1517
+ * @returns {void}
1518
+ */
1519
+ function recordAutomaticFailureScreenshotPath (ctx) {
1520
+ if (isFailureScreenshotUploadEnabled &&
1521
+ typeof ctx.result === 'string' &&
1522
+ PLAYWRIGHT_FAILURE_SCREENSHOT_PATH_RE.test(ctx.result)) {
1523
+ automaticFailureScreenshotPaths.add(ctx.result)
1524
+ }
1525
+ }
1526
+
1527
+ artifactsRecorderScreenshotPathCh.subscribe({ end: recordAutomaticFailureScreenshotPath })
1528
+ snapshotRecorderScreenshotPathCh.subscribe({ end: recordAutomaticFailureScreenshotPath })
1529
+
1442
1530
  if (DD_MAJOR < 6) { // <1.38.0 is only supported up to version 5
1443
1531
  addHook({
1444
1532
  name: '@playwright/test',
@@ -1750,7 +1838,10 @@ function finishProcessHostStartRunner (processHost) {
1750
1838
  }
1751
1839
  // These messages are [code, payload]. The payload is test data
1752
1840
  if (Array.isArray(message) && message[0] === PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE) {
1753
- workerReportCh.publish(message[1])
1841
+ workerReportCh.publish({
1842
+ serializedTraces: message[1],
1843
+ screenshots: processHost[kDdPlaywrightFailureScreenshots]?.shift(),
1844
+ })
1754
1845
  }
1755
1846
  })
1756
1847
  }
@@ -1774,23 +1865,64 @@ addHook({
1774
1865
  })
1775
1866
 
1776
1867
  async function handlePageGoto (page) {
1868
+ if (!testPageGotoCh.hasSubscribers || !page || typeof page.evaluate !== 'function') {
1869
+ return
1870
+ }
1871
+
1872
+ let rumState
1777
1873
  try {
1778
- if (page && typeof page.evaluate === 'function') {
1779
- const { isRumInstrumented, isRumActive, rumSamplingRate } = await page.evaluate(detectRum)
1780
- if (isRumInstrumented && rumSamplingRate < 100 && !isRumActive) {
1781
- log.debug("RUM was detected on the page, but it isn't active because the sampling rate is below 100%")
1782
- }
1874
+ rumState = await page.evaluate(detectRum)
1875
+ } catch (error) {
1876
+ // Redirects and closed contexts can make page evaluation fail after a successful navigation.
1877
+ log.error('Playwright RUM detection error', error)
1878
+ return
1879
+ }
1783
1880
 
1784
- if (isRumActive) {
1785
- testPageGotoCh.publish({
1786
- isRumActive,
1787
- page,
1788
- })
1789
- }
1790
- }
1791
- } catch (e) {
1792
- // ignore errors such as redirects, context destroyed, etc
1793
- log.error('goto hook error', e)
1881
+ if (!rumState) {
1882
+ return
1883
+ }
1884
+
1885
+ const { isRumInstrumented, isRumActive, rumSamplingRate } = rumState
1886
+ if (isRumInstrumented && rumSamplingRate < 100 && !isRumActive) {
1887
+ log.debug("RUM was detected on the page, but it isn't active because the sampling rate is below 100%")
1888
+ }
1889
+ if (!isRumActive) {
1890
+ return
1891
+ }
1892
+
1893
+ let browserVersion
1894
+ try {
1895
+ browserVersion = page.context().browser()?.version()
1896
+ } catch (error) {
1897
+ log.error('Playwright browser metadata error', error)
1898
+ }
1899
+
1900
+ const context = {
1901
+ isRumActive,
1902
+ browserVersion,
1903
+ testExecutionId: undefined,
1904
+ }
1905
+ try {
1906
+ testPageGotoCh.publish(context)
1907
+ } catch (error) {
1908
+ log.error('Playwright RUM correlation channel error', error)
1909
+ return
1910
+ }
1911
+
1912
+ if (!context.testExecutionId) {
1913
+ return
1914
+ }
1915
+
1916
+ try {
1917
+ const domain = new URL(page.url()).hostname
1918
+ await page.context().addCookies([{
1919
+ name: RUM_COOKIE_NAME,
1920
+ value: context.testExecutionId,
1921
+ domain,
1922
+ path: '/',
1923
+ }])
1924
+ } catch (error) {
1925
+ log.error('Playwright RUM correlation cookie error', error)
1794
1926
  }
1795
1927
  }
1796
1928
 
@@ -1918,10 +2050,11 @@ function instrumentWorkerMainMethods (workerMain) {
1918
2050
  if (url) {
1919
2051
  const domain = new URL(url).hostname
1920
2052
  await page.context().addCookies([{
1921
- name: 'datadog-ci-visibility-test-execution-id',
2053
+ name: RUM_COOKIE_NAME,
1922
2054
  value: '',
1923
2055
  domain,
1924
2056
  path: '/',
2057
+ expires: 0,
1925
2058
  }])
1926
2059
  } else {
1927
2060
  log.error('RUM is active but page.url() is not available')
@@ -2042,7 +2175,9 @@ function instrumentWorkerMainMethods (workerMain) {
2042
2175
  // We reproduce what happens in `Dispatcher#_onStepBegin` and `Dispatcher#_onStepEnd`,
2043
2176
  // since `startTime` and `duration` are not available directly in the worker process
2044
2177
  shimmer.wrap(workerMain, 'dispatchEvent', dispatchEvent => function (event, payload) {
2045
- if (event === 'stepBegin') {
2178
+ if (event === 'testBegin' || event === 'testEnd') {
2179
+ automaticFailureScreenshotPaths.clear()
2180
+ } else if (event === 'stepBegin') {
2046
2181
  stepInfoByStepId[payload.stepId] = {
2047
2182
  startTime: payload.wallTime,
2048
2183
  title: payload.title,
@@ -2058,6 +2193,8 @@ function instrumentWorkerMainMethods (workerMain) {
2058
2193
  duration: payload.wallTime - stepInfo.startTime,
2059
2194
  error: payload.error,
2060
2195
  })
2196
+ } else if (event === 'attach' && isAutomaticFailureScreenshotAttachment(payload)) {
2197
+ payload._ddIsAutomaticFailureScreenshot = true
2061
2198
  }
2062
2199
  return dispatchEvent.apply(this, arguments)
2063
2200
  })
@@ -1,5 +1,7 @@
1
1
  'use strict'
2
2
 
3
+ const { syncBuiltinESMExports } = require('node:module')
4
+
3
5
  const { channel } = require('dc-polyfill')
4
6
  const shimmer = require('../../datadog-shimmer')
5
7
 
@@ -26,4 +28,5 @@ if (process.setUncaughtExceptionCaptureCallback) {
26
28
  return result
27
29
  }
28
30
  })
31
+ syncBuiltinESMExports()
29
32
  }