dd-trace 6.4.0 → 6.6.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.
- package/README.electron.md +7 -0
- package/README.md +17 -0
- package/ci/diagnose.js +2100 -0
- package/ci/init.js +23 -2
- package/ci/runbook.md +198 -0
- package/ci/test-optimization-validation/approval-artifacts.js +143 -0
- package/ci/test-optimization-validation/approval.js +299 -0
- package/ci/test-optimization-validation/artifact-id.js +20 -0
- package/ci/test-optimization-validation/bounded-json.js +121 -0
- package/ci/test-optimization-validation/ci-command-candidate.js +83 -0
- package/ci/test-optimization-validation/ci-discovery.js +181 -0
- package/ci/test-optimization-validation/ci-remediation.js +210 -0
- package/ci/test-optimization-validation/cli.js +903 -0
- package/ci/test-optimization-validation/command-blocker.js +81 -0
- package/ci/test-optimization-validation/command-output-policy.js +206 -0
- package/ci/test-optimization-validation/command-runner.js +707 -0
- package/ci/test-optimization-validation/command-suitability.js +471 -0
- package/ci/test-optimization-validation/executable-approval.js +48 -0
- package/ci/test-optimization-validation/executable.js +480 -0
- package/ci/test-optimization-validation/generated-file-policy.js +63 -0
- package/ci/test-optimization-validation/generated-files.js +351 -0
- package/ci/test-optimization-validation/generated-verifier.js +196 -0
- package/ci/test-optimization-validation/init-probe-preload.js +163 -0
- package/ci/test-optimization-validation/init-probe.js +246 -0
- package/ci/test-optimization-validation/late-initialization.js +63 -0
- package/ci/test-optimization-validation/local-command.js +163 -0
- package/ci/test-optimization-validation/manifest-loader.js +133 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +738 -0
- package/ci/test-optimization-validation/manifest-schema.js +862 -0
- package/ci/test-optimization-validation/offline-fixtures.js +327 -0
- package/ci/test-optimization-validation/offline-output.js +406 -0
- package/ci/test-optimization-validation/payload-normalizer.js +72 -0
- package/ci/test-optimization-validation/plan-writer.js +1120 -0
- package/ci/test-optimization-validation/preflight-runner.js +114 -0
- package/ci/test-optimization-validation/redaction.js +331 -0
- package/ci/test-optimization-validation/report-writer.js +1508 -0
- package/ci/test-optimization-validation/safe-files.js +203 -0
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +159 -0
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +657 -0
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +780 -0
- package/ci/test-optimization-validation/scenarios/early-flake-detection.js +141 -0
- package/ci/test-optimization-validation/scenarios/helpers.js +539 -0
- package/ci/test-optimization-validation/scenarios/test-management.js +218 -0
- package/ci/test-optimization-validation/setup-runner.js +97 -0
- package/ci/test-optimization-validation/static-diagnosis.js +159 -0
- package/ci/test-optimization-validation/test-output.js +129 -0
- package/ci/test-optimization-validation-manifest.schema.json +1 -0
- package/ci/validate-test-optimization.js +3 -0
- package/ext/exporters.js +1 -0
- package/index.d.ts +121 -8
- package/init.js +18 -0
- package/initialize.mjs +1 -1
- package/loader-hook.mjs +28 -18
- package/openfeature.d.ts +1 -0
- package/openfeature.js +4 -0
- package/package.json +11 -8
- package/packages/datadog-instrumentations/src/ai.js +8 -2
- package/packages/datadog-instrumentations/src/child_process.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +1 -1
- package/packages/datadog-instrumentations/src/cucumber-worker-threads.js +9 -3
- package/packages/datadog-instrumentations/src/cucumber.js +31 -19
- package/packages/datadog-instrumentations/src/cypress-config.js +398 -52
- package/packages/datadog-instrumentations/src/fastify.js +7 -11
- package/packages/datadog-instrumentations/src/helpers/channel.js +74 -0
- package/packages/datadog-instrumentations/src/helpers/hook.js +27 -6
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +8 -8
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +28 -2
- package/packages/datadog-instrumentations/src/jest.js +44 -17
- package/packages/datadog-instrumentations/src/mariadb.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +79 -32
- package/packages/datadog-instrumentations/src/mocha/utils.js +0 -4
- package/packages/datadog-instrumentations/src/mongodb.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/mquery.js +2 -2
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +24 -0
- package/packages/datadog-instrumentations/src/nyc.js +0 -2
- package/packages/datadog-instrumentations/src/oracledb.js +2 -2
- package/packages/datadog-instrumentations/src/pg.js +2 -2
- package/packages/datadog-instrumentations/src/playwright.js +162 -46
- package/packages/datadog-instrumentations/src/process.js +3 -0
- package/packages/datadog-instrumentations/src/router.js +18 -8
- package/packages/datadog-instrumentations/src/selenium.js +52 -26
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +3 -3
- package/packages/datadog-instrumentations/src/vitest-main.js +14 -28
- package/packages/datadog-instrumentations/src/vitest-util.js +0 -7
- package/packages/datadog-instrumentations/src/vitest-worker.js +5 -8
- package/packages/datadog-instrumentations/src/ws.js +2 -1
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +2 -1
- package/packages/datadog-plugin-aws-sdk/src/util.js +2 -2
- package/packages/datadog-plugin-cucumber/src/index.js +3 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -57
- package/packages/datadog-plugin-cypress/src/index.js +6 -1
- package/packages/datadog-plugin-cypress/src/support.js +92 -26
- package/packages/datadog-plugin-electron/src/ipc.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +87 -45
- package/packages/datadog-plugin-graphql/src/validate.js +2 -2
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-langchain/src/handlers/embedding.js +2 -1
- package/packages/datadog-plugin-langchain/src/handlers/language_models.js +2 -1
- package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +2 -1
- package/packages/datadog-plugin-playwright/src/index.js +181 -65
- package/packages/datadog-plugin-router/src/index.js +11 -2
- package/packages/datadog-plugin-selenium/src/index.js +5 -36
- package/packages/datadog-plugin-vitest/src/index.js +5 -5
- package/packages/datadog-plugin-ws/src/close.js +2 -1
- package/packages/datadog-plugin-ws/src/producer.js +2 -1
- package/packages/datadog-plugin-ws/src/receiver.js +2 -1
- package/packages/datadog-plugin-ws/src/server.js +2 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +3 -1
- package/packages/dd-trace/src/azure_metadata.js +2 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +93 -28
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/index.js +160 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +288 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/payload-projection.js +84 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +369 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +60 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +41 -3
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +7 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +1 -1
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +6 -2
- package/packages/dd-trace/src/ci-visibility/rum.js +7 -0
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +4 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +137 -9
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +168 -8
- package/packages/dd-trace/src/ci-visibility/test-screenshot.js +90 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +16 -0
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +10 -0
- package/packages/dd-trace/src/config/supported-configurations.json +66 -0
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/common/client-library-headers.js +21 -0
- package/packages/dd-trace/src/exporters/common/request.js +59 -30
- package/packages/dd-trace/src/exporters/common/url.js +15 -1
- package/packages/dd-trace/src/feature-registry.js +10 -3
- package/packages/dd-trace/src/llmobs/experiments/client.js +113 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +154 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +283 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +152 -0
- package/packages/dd-trace/src/llmobs/experiments/noop.js +30 -0
- package/packages/dd-trace/src/llmobs/experiments/result.js +34 -0
- package/packages/dd-trace/src/llmobs/noop.js +6 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +20 -3
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +2 -2
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/constants.js +8 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +48 -14
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +43 -0
- package/packages/dd-trace/src/llmobs/sdk.js +17 -0
- package/packages/dd-trace/src/llmobs/tagger.js +68 -0
- package/packages/dd-trace/src/llmobs/telemetry.js +2 -1
- package/packages/dd-trace/src/llmobs/util.js +32 -0
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +322 -0
- package/packages/dd-trace/src/openfeature/configuration_source.js +90 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +14 -23
- package/packages/dd-trace/src/openfeature/index.js +0 -2
- package/packages/dd-trace/src/openfeature/noop.js +1 -28
- package/packages/dd-trace/src/openfeature/register.js +16 -27
- package/packages/dd-trace/src/openfeature/remote_config.js +15 -18
- package/packages/dd-trace/src/openfeature/require-provider.js +18 -0
- package/packages/dd-trace/src/opentelemetry/span-ending-hook.js +10 -0
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +20 -6
- package/packages/dd-trace/src/plugins/util/git.js +3 -2
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +29 -5
- package/packages/dd-trace/src/plugins/util/test.js +56 -4
- package/packages/dd-trace/src/profiling/profilers/space.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -0
- package/packages/dd-trace/src/proxy.js +84 -7
- package/packages/dd-trace/src/span_processor.js +5 -0
- package/packages/dd-trace/src/standalone/index.js +0 -14
- package/packages/dd-trace/src/telemetry/send-data.js +2 -2
- package/packages/dd-trace/src/util.js +26 -0
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/require-optional-peer.js +0 -17
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const shimmer = require('../../datadog-shimmer')
|
|
4
|
+
const nomenclature = require('../../dd-trace/src/service-naming')
|
|
5
|
+
const spanEndingHook = require('../../dd-trace/src/opentelemetry/span-ending-hook')
|
|
6
|
+
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
4
7
|
const { channel, addHook } = require('./helpers/instrument')
|
|
5
8
|
|
|
6
9
|
const startChannel = channel('apm:next:request:start')
|
|
@@ -21,6 +24,27 @@ const NEXT_REQUEST_META = Symbol.for('NextInternalRequestMeta')
|
|
|
21
24
|
const META_IS_MIDDLEWARE = 'middlewareInvoke'
|
|
22
25
|
const encounteredMiddleware = new WeakSet()
|
|
23
26
|
|
|
27
|
+
// `next.span_type` value Next.js sets on its own OTel root request span; the whole detection surface.
|
|
28
|
+
const NEXT_BASE_SERVER_HANDLE_REQUEST = 'BaseServer.handleRequest'
|
|
29
|
+
|
|
30
|
+
// In OTel-bridge mode (`plugins: false` + `new tracer.TracerProvider().register()`) Next emits its
|
|
31
|
+
// own OTel spans and renames the root request span to `${method} ${route}` at finish, which the
|
|
32
|
+
// bridge routes into the DD operation name and leaves the resource as the bare method — the reverse
|
|
33
|
+
// of Datadog's contract. Correct it via the bridge's pre-finish hook. See span-ending-hook.js.
|
|
34
|
+
spanEndingHook.hook = (ddSpan) => {
|
|
35
|
+
const tags = ddSpan.context().getTags()
|
|
36
|
+
if (tags['next.span_type'] !== NEXT_BASE_SERVER_HANDLE_REQUEST) return
|
|
37
|
+
|
|
38
|
+
const method = tags['http.method']
|
|
39
|
+
const route = tags['next.route'] ?? tags['http.route']
|
|
40
|
+
// Next already wrote the RSC-aware `${method} ${route}` into `next.span_name`; prefer it so we
|
|
41
|
+
// mirror Next's own naming, and only construct the resource when it is absent.
|
|
42
|
+
const resource = tags['next.span_name'] ?? (route ? `${method} ${route}` : method)
|
|
43
|
+
|
|
44
|
+
ddSpan.setOperationName(nomenclature.opName('web', 'server', 'next'))
|
|
45
|
+
ddSpan.setTag(RESOURCE_NAME, resource)
|
|
46
|
+
}
|
|
47
|
+
|
|
24
48
|
function wrapHandleRequest (handleRequest) {
|
|
25
49
|
return function (req, res, pathname, query) {
|
|
26
50
|
return instrument(req, res, () => handleRequest.apply(this, arguments))
|
|
@@ -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
|
|
148
|
-
callback = args
|
|
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
|
|
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
|
|
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,7 +25,11 @@ 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')
|
|
32
|
+
const { getChannelPromise } = require('./helpers/channel')
|
|
29
33
|
const { addHook, channel, tracingChannel } = require('./helpers/instrument')
|
|
30
34
|
|
|
31
35
|
const testStartCh = channel('ci:playwright:test:start')
|
|
@@ -33,6 +37,7 @@ const testFinishCh = channel('ci:playwright:test:finish')
|
|
|
33
37
|
const testSkipCh = channel('ci:playwright:test:skip')
|
|
34
38
|
|
|
35
39
|
const testSessionStartCh = channel('ci:playwright:session:start')
|
|
40
|
+
const testSessionConfigurationCh = channel('ci:playwright:session:configuration')
|
|
36
41
|
const testSessionFinishCh = channel('ci:playwright:session:finish')
|
|
37
42
|
|
|
38
43
|
const libraryConfigurationCh = channel('ci:playwright:library-configuration')
|
|
@@ -51,6 +56,9 @@ const dispatcherRunCh = tracingChannel('orchestrion:playwright:Dispatcher_run')
|
|
|
51
56
|
const dispatcherCreateWorkerCh = tracingChannel('orchestrion:playwright:Dispatcher_createWorker')
|
|
52
57
|
const processHostStartRunnerCh = tracingChannel('orchestrion:playwright:ProcessHost_startRunner')
|
|
53
58
|
const createRootSuiteCh = tracingChannel('orchestrion:playwright:createRootSuite')
|
|
59
|
+
const artifactsRecorderScreenshotPathCh =
|
|
60
|
+
tracingChannel('orchestrion:playwright:ArtifactsRecorder_createScreenshotAttachmentPath')
|
|
61
|
+
const snapshotRecorderScreenshotPathCh = tracingChannel('orchestrion:playwright:SnapshotRecorder_createAttachmentPath')
|
|
54
62
|
const pageGotoCh = tracingChannel('orchestrion:playwright-core:Page_goto')
|
|
55
63
|
|
|
56
64
|
const testToCtx = new WeakMap()
|
|
@@ -61,6 +69,8 @@ const testsToTestStatuses = new Map()
|
|
|
61
69
|
|
|
62
70
|
const RUM_FLUSH_WAIT_TIME = getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')
|
|
63
71
|
const DD_PROPERTIES_TIMEOUT = 5000
|
|
72
|
+
const isFailureScreenshotUploadEnabled =
|
|
73
|
+
getValueFromEnvSources('DD_TEST_FAILURE_SCREENSHOTS_ENABLED') === true
|
|
64
74
|
|
|
65
75
|
let applyRepeatEachIndex = null
|
|
66
76
|
|
|
@@ -115,8 +125,21 @@ const EFD_RETRY_COUNT_RESPONSE = 'ddEfdRetryCountResponse'
|
|
|
115
125
|
const DD_PROPERTIES_REQUEST = 'ddPropertiesRequest'
|
|
116
126
|
const DD_PROPERTIES_RESPONSE = 'ddProperties'
|
|
117
127
|
const kDdPlaywrightDisabledTestIds = Symbol('ddPlaywrightDisabledTestIds')
|
|
128
|
+
const kDdPlaywrightFailureScreenshots = Symbol('ddPlaywrightFailureScreenshots')
|
|
118
129
|
const kDdPlaywrightWorkerHostInstrumented = Symbol('ddPlaywrightWorkerHostInstrumented')
|
|
119
130
|
const kDdPlaywrightWorkerInstrumented = Symbol('ddPlaywrightWorkerInstrumented')
|
|
131
|
+
const PLAYWRIGHT_FAILURE_SCREENSHOT_PATH_RE = /(?:^|[\\/])test-failed-\d+\.png$/
|
|
132
|
+
const automaticFailureScreenshotPaths = new Set()
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Returns whether Playwright's internal screenshot recorder created an attachment.
|
|
136
|
+
*
|
|
137
|
+
* @param {object} attachment - Playwright attachment payload
|
|
138
|
+
* @returns {boolean}
|
|
139
|
+
*/
|
|
140
|
+
function isAutomaticFailureScreenshotAttachment (attachment) {
|
|
141
|
+
return typeof attachment?.path === 'string' && automaticFailureScreenshotPaths.delete(attachment.path)
|
|
142
|
+
}
|
|
120
143
|
|
|
121
144
|
function isValidKnownTests (receivedKnownTests) {
|
|
122
145
|
return !!receivedKnownTests.playwright
|
|
@@ -501,6 +524,23 @@ function getProjectsFromRunner (runner, configArg) {
|
|
|
501
524
|
})
|
|
502
525
|
}
|
|
503
526
|
|
|
527
|
+
/**
|
|
528
|
+
* Returns whether at least one Playwright project captures automatic screenshots for failed tests.
|
|
529
|
+
*
|
|
530
|
+
* @param {Array<object>} projects - Playwright projects with resolved use options
|
|
531
|
+
* @returns {boolean} Whether failure screenshot capture is enabled
|
|
532
|
+
*/
|
|
533
|
+
function isFailureScreenshotCaptureEnabled (projects) {
|
|
534
|
+
for (const project of projects) {
|
|
535
|
+
const screenshot = project.use?.screenshot
|
|
536
|
+
const mode = typeof screenshot === 'object' && screenshot !== null ? screenshot.mode : screenshot
|
|
537
|
+
if (mode === 'on' || mode === 'only-on-failure' || mode === 'on-first-failure') {
|
|
538
|
+
return true
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return false
|
|
542
|
+
}
|
|
543
|
+
|
|
504
544
|
function getProjectsFromDispatcher (dispatcher) {
|
|
505
545
|
const bundledConfig = dispatcher._testRun?.config?.config?.projects
|
|
506
546
|
if (bundledConfig) {
|
|
@@ -575,12 +615,6 @@ function getTestByTestId (dispatcher, testId) {
|
|
|
575
615
|
}
|
|
576
616
|
}
|
|
577
617
|
|
|
578
|
-
function getChannelPromise (channelToPublishTo, params) {
|
|
579
|
-
return new Promise(resolve => {
|
|
580
|
-
channelToPublishTo.publish({ onDone: resolve, ...params })
|
|
581
|
-
})
|
|
582
|
-
}
|
|
583
|
-
|
|
584
618
|
// Inspired by https://github.com/microsoft/playwright/blob/2b77ed4d7aafa85a600caa0b0d101b72c8437eeb/packages/playwright/src/reporters/base.ts#L293
|
|
585
619
|
// We can't use test.outcome() directly because it's set on follow up handlers:
|
|
586
620
|
// our `testEndHandler` is called before the outcome is set.
|
|
@@ -993,6 +1027,7 @@ function onDispatcherCreateWorker (dispatcher, worker) {
|
|
|
993
1027
|
|
|
994
1028
|
const projects = getProjectsFromDispatcher(dispatcher)
|
|
995
1029
|
sessionProjects = projects
|
|
1030
|
+
const automaticFailureScreenshotPathsByTestId = new Map()
|
|
996
1031
|
|
|
997
1032
|
if (disabledTestIds.size && !worker[kDdPlaywrightWorkerHostInstrumented] &&
|
|
998
1033
|
typeof worker.runTestGroup === 'function') {
|
|
@@ -1021,6 +1056,16 @@ function onDispatcherCreateWorker (dispatcher, worker) {
|
|
|
1021
1056
|
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
|
|
1022
1057
|
testBeginHandler(test, browser, shouldCreateTestSpan)
|
|
1023
1058
|
})
|
|
1059
|
+
worker.on('attach', ({ testId, path, _ddIsAutomaticFailureScreenshot }) => {
|
|
1060
|
+
if (!_ddIsAutomaticFailureScreenshot) return
|
|
1061
|
+
|
|
1062
|
+
let screenshotPaths = automaticFailureScreenshotPathsByTestId.get(testId)
|
|
1063
|
+
if (!screenshotPaths) {
|
|
1064
|
+
screenshotPaths = new Set()
|
|
1065
|
+
automaticFailureScreenshotPathsByTestId.set(testId, screenshotPaths)
|
|
1066
|
+
}
|
|
1067
|
+
screenshotPaths.add(path)
|
|
1068
|
+
})
|
|
1024
1069
|
worker.on('testEnd', ({ testId, status, errors, annotations }) => {
|
|
1025
1070
|
const test = getTestByTestId(dispatcher, testId)
|
|
1026
1071
|
if (!test) return
|
|
@@ -1043,6 +1088,20 @@ function onDispatcherCreateWorker (dispatcher, worker) {
|
|
|
1043
1088
|
}
|
|
1044
1089
|
)
|
|
1045
1090
|
const testResult = test.results.at(-1)
|
|
1091
|
+
const automaticFailureScreenshotPaths = automaticFailureScreenshotPathsByTestId.get(testId)
|
|
1092
|
+
automaticFailureScreenshotPathsByTestId.delete(testId)
|
|
1093
|
+
if (testStatus === 'fail' && automaticFailureScreenshotPaths?.size && testResult?.attachments?.length) {
|
|
1094
|
+
const screenshots = []
|
|
1095
|
+
for (const attachment of testResult.attachments) {
|
|
1096
|
+
if (automaticFailureScreenshotPaths.has(attachment.path)) {
|
|
1097
|
+
screenshots.push(attachment)
|
|
1098
|
+
}
|
|
1099
|
+
}
|
|
1100
|
+
if (screenshots.length) {
|
|
1101
|
+
worker[kDdPlaywrightFailureScreenshots] ??= []
|
|
1102
|
+
worker[kDdPlaywrightFailureScreenshots].push(screenshots)
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1046
1105
|
const isAtrRetry = testResult?.retry > 0 &&
|
|
1047
1106
|
isFlakyTestRetriesEnabled &&
|
|
1048
1107
|
!test._ddIsAttemptToFix &&
|
|
@@ -1142,12 +1201,17 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
1142
1201
|
function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
1143
1202
|
// Config parameter is only available from >=1.55.0
|
|
1144
1203
|
return async function (config) {
|
|
1145
|
-
let onDone
|
|
1146
|
-
|
|
1147
1204
|
rootDir = getRootDir(this, config)
|
|
1205
|
+
const projects = getProjectsFromRunner(this, config)
|
|
1206
|
+
const isFailureScreenshotEnabled = isFailureScreenshotCaptureEnabled(projects)
|
|
1148
1207
|
const processArgv = process.argv.slice(2).join(' ')
|
|
1149
1208
|
const command = `playwright ${processArgv}`
|
|
1150
|
-
testSessionStartCh.publish({
|
|
1209
|
+
testSessionStartCh.publish({
|
|
1210
|
+
command,
|
|
1211
|
+
frameworkVersion: playwrightVersion,
|
|
1212
|
+
rootDir,
|
|
1213
|
+
isFailureScreenshotEnabled,
|
|
1214
|
+
})
|
|
1151
1215
|
|
|
1152
1216
|
try {
|
|
1153
1217
|
const { err, libraryConfig } = await getChannelPromise(
|
|
@@ -1174,6 +1238,8 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1174
1238
|
log.error('Playwright session start error', e)
|
|
1175
1239
|
}
|
|
1176
1240
|
|
|
1241
|
+
testSessionConfigurationCh.publish({ isFailureScreenshotEnabled })
|
|
1242
|
+
|
|
1177
1243
|
const isTestOptimizationSupported = satisfies(playwrightVersion, MINIMUM_SUPPORTED_VERSION_RANGE_EFD)
|
|
1178
1244
|
const shouldGetKnownTests = isKnownTestsEnabled && isTestOptimizationSupported
|
|
1179
1245
|
const shouldGetTestManagementTests = isTestManagementTestsEnabled && isTestOptimizationSupported
|
|
@@ -1239,8 +1305,6 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1239
1305
|
}
|
|
1240
1306
|
}
|
|
1241
1307
|
|
|
1242
|
-
const projects = getProjectsFromRunner(this, config)
|
|
1243
|
-
|
|
1244
1308
|
// ATR and `--retries` are now compatible with Test Management.
|
|
1245
1309
|
// Test Management tests have their retries set to 0 at the test level,
|
|
1246
1310
|
// preventing them from being retried by ATR or `--retries`.
|
|
@@ -1288,7 +1352,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1288
1352
|
|
|
1289
1353
|
for (const [fqn, testStatuses] of testsToTestStatuses.entries()) {
|
|
1290
1354
|
// Only count as failed if the final status (after retries) is 'fail'
|
|
1291
|
-
const lastStatus = testStatuses
|
|
1355
|
+
const lastStatus = testStatuses.at(-1)
|
|
1292
1356
|
if (lastStatus === 'fail') {
|
|
1293
1357
|
totalFailedTestCount += 1
|
|
1294
1358
|
if (quarantinedButNotAttemptToFixFqns.has(fqn)) {
|
|
@@ -1308,17 +1372,12 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1308
1372
|
logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
|
|
1309
1373
|
loggedAttemptToFixTests.clear()
|
|
1310
1374
|
|
|
1311
|
-
|
|
1312
|
-
onDone = resolve
|
|
1313
|
-
})
|
|
1314
|
-
testSessionFinishCh.publish({
|
|
1375
|
+
await getChannelPromise(testSessionFinishCh, {
|
|
1315
1376
|
status: preventedToFail ? 'pass' : STATUS_TO_TEST_STATUS[sessionStatus],
|
|
1316
1377
|
isEarlyFlakeDetectionEnabled,
|
|
1317
1378
|
isEarlyFlakeDetectionFaulty,
|
|
1318
1379
|
isTestManagementTestsEnabled,
|
|
1319
|
-
onDone,
|
|
1320
1380
|
})
|
|
1321
|
-
await flushWait
|
|
1322
1381
|
|
|
1323
1382
|
startedSuites = []
|
|
1324
1383
|
remainingTestsByFile = {}
|
|
@@ -1439,6 +1498,23 @@ pageGotoCh.subscribe({
|
|
|
1439
1498
|
},
|
|
1440
1499
|
})
|
|
1441
1500
|
|
|
1501
|
+
/**
|
|
1502
|
+
* Records a path created by Playwright's automatic screenshot recorder.
|
|
1503
|
+
*
|
|
1504
|
+
* @param {object} ctx - Orchestrion context
|
|
1505
|
+
* @returns {void}
|
|
1506
|
+
*/
|
|
1507
|
+
function recordAutomaticFailureScreenshotPath (ctx) {
|
|
1508
|
+
if (isFailureScreenshotUploadEnabled &&
|
|
1509
|
+
typeof ctx.result === 'string' &&
|
|
1510
|
+
PLAYWRIGHT_FAILURE_SCREENSHOT_PATH_RE.test(ctx.result)) {
|
|
1511
|
+
automaticFailureScreenshotPaths.add(ctx.result)
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1515
|
+
artifactsRecorderScreenshotPathCh.subscribe({ end: recordAutomaticFailureScreenshotPath })
|
|
1516
|
+
snapshotRecorderScreenshotPathCh.subscribe({ end: recordAutomaticFailureScreenshotPath })
|
|
1517
|
+
|
|
1442
1518
|
if (DD_MAJOR < 6) { // <1.38.0 is only supported up to version 5
|
|
1443
1519
|
addHook({
|
|
1444
1520
|
name: '@playwright/test',
|
|
@@ -1750,7 +1826,10 @@ function finishProcessHostStartRunner (processHost) {
|
|
|
1750
1826
|
}
|
|
1751
1827
|
// These messages are [code, payload]. The payload is test data
|
|
1752
1828
|
if (Array.isArray(message) && message[0] === PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE) {
|
|
1753
|
-
workerReportCh.publish(
|
|
1829
|
+
workerReportCh.publish({
|
|
1830
|
+
serializedTraces: message[1],
|
|
1831
|
+
screenshots: processHost[kDdPlaywrightFailureScreenshots]?.shift(),
|
|
1832
|
+
})
|
|
1754
1833
|
}
|
|
1755
1834
|
})
|
|
1756
1835
|
}
|
|
@@ -1774,23 +1853,64 @@ addHook({
|
|
|
1774
1853
|
})
|
|
1775
1854
|
|
|
1776
1855
|
async function handlePageGoto (page) {
|
|
1856
|
+
if (!testPageGotoCh.hasSubscribers || !page || typeof page.evaluate !== 'function') {
|
|
1857
|
+
return
|
|
1858
|
+
}
|
|
1859
|
+
|
|
1860
|
+
let rumState
|
|
1777
1861
|
try {
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1862
|
+
rumState = await page.evaluate(detectRum)
|
|
1863
|
+
} catch (error) {
|
|
1864
|
+
// Redirects and closed contexts can make page evaluation fail after a successful navigation.
|
|
1865
|
+
log.error('Playwright RUM detection error', error)
|
|
1866
|
+
return
|
|
1867
|
+
}
|
|
1783
1868
|
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
}
|
|
1792
|
-
|
|
1793
|
-
|
|
1869
|
+
if (!rumState) {
|
|
1870
|
+
return
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
const { isRumInstrumented, isRumActive, rumSamplingRate } = rumState
|
|
1874
|
+
if (isRumInstrumented && rumSamplingRate < 100 && !isRumActive) {
|
|
1875
|
+
log.debug("RUM was detected on the page, but it isn't active because the sampling rate is below 100%")
|
|
1876
|
+
}
|
|
1877
|
+
if (!isRumActive) {
|
|
1878
|
+
return
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
let browserVersion
|
|
1882
|
+
try {
|
|
1883
|
+
browserVersion = page.context().browser()?.version()
|
|
1884
|
+
} catch (error) {
|
|
1885
|
+
log.error('Playwright browser metadata error', error)
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
const context = {
|
|
1889
|
+
isRumActive,
|
|
1890
|
+
browserVersion,
|
|
1891
|
+
testExecutionId: undefined,
|
|
1892
|
+
}
|
|
1893
|
+
try {
|
|
1894
|
+
testPageGotoCh.publish(context)
|
|
1895
|
+
} catch (error) {
|
|
1896
|
+
log.error('Playwright RUM correlation channel error', error)
|
|
1897
|
+
return
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
if (!context.testExecutionId) {
|
|
1901
|
+
return
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
try {
|
|
1905
|
+
const domain = new URL(page.url()).hostname
|
|
1906
|
+
await page.context().addCookies([{
|
|
1907
|
+
name: RUM_COOKIE_NAME,
|
|
1908
|
+
value: context.testExecutionId,
|
|
1909
|
+
domain,
|
|
1910
|
+
path: '/',
|
|
1911
|
+
}])
|
|
1912
|
+
} catch (error) {
|
|
1913
|
+
log.error('Playwright RUM correlation cookie error', error)
|
|
1794
1914
|
}
|
|
1795
1915
|
}
|
|
1796
1916
|
|
|
@@ -1918,10 +2038,11 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
1918
2038
|
if (url) {
|
|
1919
2039
|
const domain = new URL(url).hostname
|
|
1920
2040
|
await page.context().addCookies([{
|
|
1921
|
-
name:
|
|
2041
|
+
name: RUM_COOKIE_NAME,
|
|
1922
2042
|
value: '',
|
|
1923
2043
|
domain,
|
|
1924
2044
|
path: '/',
|
|
2045
|
+
expires: 0,
|
|
1925
2046
|
}])
|
|
1926
2047
|
} else {
|
|
1927
2048
|
log.error('RUM is active but page.url() is not available')
|
|
@@ -1979,12 +2100,6 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
1979
2100
|
annotationTags = parseAnnotations(annotations)
|
|
1980
2101
|
}
|
|
1981
2102
|
|
|
1982
|
-
let onDone
|
|
1983
|
-
|
|
1984
|
-
const flushPromise = new Promise(resolve => {
|
|
1985
|
-
onDone = resolve
|
|
1986
|
-
})
|
|
1987
|
-
|
|
1988
2103
|
// Wait for the properties to be received, but do not block the worker forever if IPC fails.
|
|
1989
2104
|
const ddPropertiesTimeoutPromise = new Promise(resolve => {
|
|
1990
2105
|
const ddPropertiesTimeout = realSetTimeout(() => {
|
|
@@ -2010,7 +2125,7 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2010
2125
|
testStatus: STATUS_TO_TEST_STATUS[status],
|
|
2011
2126
|
})
|
|
2012
2127
|
|
|
2013
|
-
testFinishCh
|
|
2128
|
+
await getChannelPromise(testFinishCh, {
|
|
2014
2129
|
testStatus: STATUS_TO_TEST_STATUS[status],
|
|
2015
2130
|
steps: steps.filter(step => step.testId === testId),
|
|
2016
2131
|
error,
|
|
@@ -2028,21 +2143,20 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2028
2143
|
hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
|
|
2029
2144
|
isAtrRetry: test._ddIsAtrRetry,
|
|
2030
2145
|
isModified: test._ddIsModified,
|
|
2031
|
-
onDone,
|
|
2032
2146
|
finalStatus,
|
|
2033
2147
|
earlyFlakeAbortReason: test._ddEarlyFlakeAbortReason,
|
|
2034
2148
|
...testCtx.currentStore,
|
|
2035
2149
|
})
|
|
2036
2150
|
|
|
2037
|
-
await flushPromise
|
|
2038
|
-
|
|
2039
2151
|
return res
|
|
2040
2152
|
})
|
|
2041
2153
|
|
|
2042
2154
|
// We reproduce what happens in `Dispatcher#_onStepBegin` and `Dispatcher#_onStepEnd`,
|
|
2043
2155
|
// since `startTime` and `duration` are not available directly in the worker process
|
|
2044
2156
|
shimmer.wrap(workerMain, 'dispatchEvent', dispatchEvent => function (event, payload) {
|
|
2045
|
-
if (event === '
|
|
2157
|
+
if (event === 'testBegin' || event === 'testEnd') {
|
|
2158
|
+
automaticFailureScreenshotPaths.clear()
|
|
2159
|
+
} else if (event === 'stepBegin') {
|
|
2046
2160
|
stepInfoByStepId[payload.stepId] = {
|
|
2047
2161
|
startTime: payload.wallTime,
|
|
2048
2162
|
title: payload.title,
|
|
@@ -2058,6 +2172,8 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2058
2172
|
duration: payload.wallTime - stepInfo.startTime,
|
|
2059
2173
|
error: payload.error,
|
|
2060
2174
|
})
|
|
2175
|
+
} else if (event === 'attach' && isAutomaticFailureScreenshotAttachment(payload)) {
|
|
2176
|
+
payload._ddIsAutomaticFailureScreenshot = true
|
|
2061
2177
|
}
|
|
2062
2178
|
return dispatchEvent.apply(this, arguments)
|
|
2063
2179
|
})
|
|
@@ -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
|
}
|
|
@@ -86,26 +86,36 @@ function createLayerDispatchWrappers (name) {
|
|
|
86
86
|
const finishChannel = channel(`apm:${name}:middleware:finish`)
|
|
87
87
|
const errorChannel = channel(`apm:${name}:middleware:error`)
|
|
88
88
|
const nextChannel = channel(`apm:${name}:middleware:next`)
|
|
89
|
+
const repeatChannel = channel(`apm:${name}:middleware:repeat`)
|
|
89
90
|
// Bound per name so express and a bare router keep independent guards.
|
|
90
91
|
const publishError = createErrorPublisher(errorChannel)
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
/**
|
|
94
|
+
* @param {import('node:http').IncomingMessage} req
|
|
95
|
+
* @param {string | undefined} layerName
|
|
96
|
+
* @param {(error?: unknown) => void} originalNext
|
|
97
|
+
*/
|
|
98
|
+
function wrapNext (req, layerName, originalNext) {
|
|
93
99
|
// Per layer dispatch, N per request. Named `next`/arity-1 mirrors the
|
|
94
100
|
// router continuation so wrapCallback skips its name/length rewrite.
|
|
95
|
-
let
|
|
101
|
+
let calls = 0
|
|
96
102
|
return shimmer.wrapCallback(originalNext, original => function next (error) {
|
|
97
103
|
// A handler that calls `next()` and then rejects (`next(); await bg()`)
|
|
98
104
|
// makes the host call this continuation twice. Publish once so the second
|
|
99
105
|
// pass cannot tag the already-finished span's parent with a late error.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
106
|
+
calls++
|
|
107
|
+
if (calls === 1) {
|
|
103
108
|
if (error && error !== 'route' && error !== 'router') {
|
|
104
109
|
publishError({ req, error })
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
nextChannel.publish({ req })
|
|
108
113
|
finishChannel.publish({ req })
|
|
114
|
+
} else if (calls === 2) {
|
|
115
|
+
// Surface the repeat as a diagnostic on the still-live request span. The
|
|
116
|
+
// host cannot tell a legitimate `next(); await bg()` from a buggy double
|
|
117
|
+
// `next()`, so this only records that it happened, not that it is wrong.
|
|
118
|
+
repeatChannel.publish({ req, name: layerName, error })
|
|
109
119
|
}
|
|
110
120
|
|
|
111
121
|
original.apply(this, arguments)
|
|
@@ -125,7 +135,7 @@ function createLayerDispatchWrappers (name) {
|
|
|
125
135
|
const meta = getLayerMeta(this)
|
|
126
136
|
if (meta === undefined || this.handle.length > 3) return originalRequest.call(this, req, res, next)
|
|
127
137
|
|
|
128
|
-
const wrappedNext = typeof next === 'function' ? wrapNext(req, next) : next
|
|
138
|
+
const wrappedNext = typeof next === 'function' ? wrapNext(req, meta.name, next) : next
|
|
129
139
|
enterChannel.publish({ name: meta.name, req, route: resolveLayerRoute(meta, this), layer: this })
|
|
130
140
|
|
|
131
141
|
try {
|
|
@@ -143,7 +153,7 @@ function createLayerDispatchWrappers (name) {
|
|
|
143
153
|
const meta = getLayerMeta(this)
|
|
144
154
|
if (meta === undefined || this.handle.length !== 4) return originalError.call(this, error, req, res, next)
|
|
145
155
|
|
|
146
|
-
const wrappedNext = typeof next === 'function' ? wrapNext(req, next) : next
|
|
156
|
+
const wrappedNext = typeof next === 'function' ? wrapNext(req, meta.name, next) : next
|
|
147
157
|
enterChannel.publish({ name: meta.name, req, route: resolveLayerRoute(meta, this), layer: this })
|
|
148
158
|
|
|
149
159
|
try {
|
|
@@ -170,7 +180,7 @@ function createLayerDispatchWrappers (name) {
|
|
|
170
180
|
const isErrorHandler = original.length === 4
|
|
171
181
|
const req = args[isErrorHandler ? 1 : 0]
|
|
172
182
|
const nextIndex = isErrorHandler ? 3 : 2
|
|
173
|
-
if (typeof args[nextIndex] === 'function') args[nextIndex] = wrapNext(req, args[nextIndex])
|
|
183
|
+
if (typeof args[nextIndex] === 'function') args[nextIndex] = wrapNext(req, meta.name, args[nextIndex])
|
|
174
184
|
|
|
175
185
|
enterChannel.publish({ name: meta.name, req, route: resolveLayerRoute(meta, layer), layer })
|
|
176
186
|
|
|
@@ -5,6 +5,10 @@ const realSetTimeout = setTimeout
|
|
|
5
5
|
|
|
6
6
|
const shimmer = require('../../datadog-shimmer')
|
|
7
7
|
const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
|
|
8
|
+
const {
|
|
9
|
+
RUM_TEST_EXECUTION_ID_COOKIE_NAME: DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME,
|
|
10
|
+
} = require('../../dd-trace/src/ci-visibility/rum')
|
|
11
|
+
const log = require('../../dd-trace/src/log')
|
|
8
12
|
const { addHook, channel } = require('./helpers/instrument')
|
|
9
13
|
|
|
10
14
|
const ciSeleniumDriverGetStartCh = channel('ci:selenium:driver:get')
|
|
@@ -20,7 +24,6 @@ if (window.DD_RUM && window.DD_RUM.stopSession) {
|
|
|
20
24
|
const IS_RUM_ACTIVE_SCRIPT = 'return !!window.DD_RUM'
|
|
21
25
|
|
|
22
26
|
const DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS = getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')
|
|
23
|
-
const DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME = 'datadog-ci-visibility-test-execution-id'
|
|
24
27
|
|
|
25
28
|
// TODO: can we increase the supported version range?
|
|
26
29
|
addHook({
|
|
@@ -32,28 +35,47 @@ addHook({
|
|
|
32
35
|
if (!ciSeleniumDriverGetStartCh.hasSubscribers) {
|
|
33
36
|
return get.apply(this, arguments)
|
|
34
37
|
}
|
|
35
|
-
let traceId
|
|
36
|
-
const setTraceId = (inputTraceId) => {
|
|
37
|
-
traceId = inputTraceId
|
|
38
|
-
}
|
|
39
38
|
const getResult = await get.apply(this, arguments)
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
let isRumActive
|
|
41
|
+
try {
|
|
42
|
+
isRumActive = await this.executeScript(IS_RUM_ACTIVE_SCRIPT)
|
|
43
|
+
} catch (error) {
|
|
44
|
+
log.error('Selenium RUM detection error', error)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let browserName
|
|
48
|
+
let browserVersion
|
|
49
|
+
try {
|
|
50
|
+
const capabilities = await this.getCapabilities()
|
|
51
|
+
browserName = capabilities.getBrowserName()
|
|
52
|
+
browserVersion = capabilities.getBrowserVersion()
|
|
53
|
+
} catch (error) {
|
|
54
|
+
log.error('Selenium browser metadata error', error)
|
|
55
|
+
}
|
|
43
56
|
|
|
44
|
-
|
|
45
|
-
setTraceId,
|
|
57
|
+
const context = {
|
|
46
58
|
seleniumVersion,
|
|
47
|
-
browserName
|
|
48
|
-
browserVersion
|
|
59
|
+
browserName,
|
|
60
|
+
browserVersion,
|
|
49
61
|
isRumActive,
|
|
50
|
-
|
|
62
|
+
testExecutionId: undefined,
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
ciSeleniumDriverGetStartCh.publish(context)
|
|
66
|
+
} catch (error) {
|
|
67
|
+
log.error('Selenium RUM correlation channel error', error)
|
|
68
|
+
}
|
|
51
69
|
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
70
|
+
if (context.testExecutionId && isRumActive) {
|
|
71
|
+
try {
|
|
72
|
+
await this.manage().addCookie({
|
|
73
|
+
name: DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME,
|
|
74
|
+
value: context.testExecutionId,
|
|
75
|
+
})
|
|
76
|
+
} catch (error) {
|
|
77
|
+
log.error('Selenium RUM correlation cookie error', error)
|
|
78
|
+
}
|
|
57
79
|
}
|
|
58
80
|
|
|
59
81
|
return getResult
|
|
@@ -63,16 +85,20 @@ addHook({
|
|
|
63
85
|
if (!ciSeleniumDriverGetStartCh.hasSubscribers) {
|
|
64
86
|
return quit.apply(this, arguments)
|
|
65
87
|
}
|
|
66
|
-
|
|
88
|
+
try {
|
|
89
|
+
const isRumActive = await this.executeScript(RUM_STOP_SESSION_SCRIPT)
|
|
67
90
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
91
|
+
if (isRumActive) {
|
|
92
|
+
// We'll have time for RUM to flush the events (there's no callback to know when it's done)
|
|
93
|
+
await new Promise(resolve => {
|
|
94
|
+
realSetTimeout(() => {
|
|
95
|
+
resolve()
|
|
96
|
+
}, DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS)
|
|
97
|
+
})
|
|
98
|
+
await this.manage().deleteCookie(DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME)
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
log.error('Selenium RUM cleanup error', error)
|
|
76
102
|
}
|
|
77
103
|
|
|
78
104
|
return quit.apply(this, arguments)
|