dd-trace 6.20.1 → 6.22.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/ci/diagnose.js +9 -14
- package/ci/init.js +1 -2
- package/ci/test-optimization-validation/ci-discovery.js +9 -4
- package/ci/test-optimization-validation/executable.js +1 -5
- package/ci/test-optimization-validation/framework-adapters/cypress.js +1 -4
- package/ci/test-optimization-validation/framework-adapters/vitest.js +1 -1
- package/ci/test-optimization-validation/generated-files.js +6 -4
- package/ci/test-optimization-validation/generated-test-contract.js +7 -3
- package/ci/test-optimization-validation/manifest-scaffold.js +7 -2
- package/ci/test-optimization-validation/manifest-schema.js +6 -7
- package/ci/test-optimization-validation/report-writer.js +6 -2
- package/ci/test-optimization-validation/runner-command.js +2 -4
- package/ci/test-optimization-validation/runner-contract.js +1 -2
- package/ci/test-optimization-validation/scenarios/auto-test-retries.js +1 -15
- package/ci/vitest-no-worker-init-setup.mjs +10 -7
- package/index.d.ts +116 -17
- package/initialize.mjs +1 -3
- package/openfeature/rules-based.d.ts +11 -0
- package/openfeature/rules-based.js +7 -0
- package/package.json +13 -11
- package/packages/datadog-core/src/storage.js +16 -0
- package/packages/datadog-esbuild/index.js +18 -12
- package/packages/datadog-instrumentations/src/aerospike.js +3 -3
- package/packages/datadog-instrumentations/src/ai.js +3 -3
- package/packages/datadog-instrumentations/src/aws-durable-execution-sdk-js.js +0 -6
- package/packages/datadog-instrumentations/src/aws-sdk.js +21 -13
- package/packages/datadog-instrumentations/src/cassandra-driver.js +25 -20
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +9 -23
- package/packages/datadog-instrumentations/src/cucumber.js +48 -33
- package/packages/datadog-instrumentations/src/elasticsearch.js +1 -5
- package/packages/datadog-instrumentations/src/electron.js +6 -6
- package/packages/datadog-instrumentations/src/fastify.js +13 -4
- package/packages/datadog-instrumentations/src/fs.js +2 -2
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +9 -5
- package/packages/datadog-instrumentations/src/grpc/client.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/bundler-register.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/instrument.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/register.js +36 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentation-registry.js +87 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +5 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/postgres.js +48 -31
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +64 -11
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms/postgres.js +42 -4
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +7 -10
- package/packages/datadog-instrumentations/src/ioredis.js +7 -5
- package/packages/datadog-instrumentations/src/iovalkey.js +7 -5
- package/packages/datadog-instrumentations/src/jest.js +122 -11
- package/packages/datadog-instrumentations/src/knex.js +9 -5
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +3 -3
- package/packages/datadog-instrumentations/src/mocha/common.js +20 -8
- package/packages/datadog-instrumentations/src/mocha/main.js +140 -43
- package/packages/datadog-instrumentations/src/mocha/utils.js +127 -38
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +61 -34
- package/packages/datadog-instrumentations/src/mocha/worker.js +7 -5
- package/packages/datadog-instrumentations/src/mongodb-core.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +3 -3
- package/packages/datadog-instrumentations/src/nova-sonic/audio.js +173 -0
- package/packages/datadog-instrumentations/src/nova-sonic/index.js +209 -0
- package/packages/datadog-instrumentations/src/nova-sonic/session.js +483 -0
- package/packages/datadog-instrumentations/src/openai-realtime/index.js +18 -6
- package/packages/datadog-instrumentations/src/openai-realtime/session.js +1 -1
- package/packages/datadog-instrumentations/src/openai.js +7 -0
- package/packages/datadog-instrumentations/src/pino.js +5 -6
- package/packages/datadog-instrumentations/src/playwright.js +3 -0
- package/packages/datadog-instrumentations/src/redis.js +27 -13
- package/packages/datadog-instrumentations/src/router.js +30 -0
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +2 -6
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +7 -5
- package/packages/datadog-instrumentations/src/vitest-main.js +9 -17
- package/packages/datadog-instrumentations/src/vitest-util.js +2 -2
- package/packages/datadog-instrumentations/src/vitest-worker.js +1 -2
- package/packages/datadog-instrumentations/src/vitest.js +2 -2
- package/packages/datadog-instrumentations/src/webdriverio.js +118 -21
- package/packages/datadog-instrumentations/src/ws.js +1 -4
- package/packages/datadog-plugin-amqplib/src/client.js +1 -2
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -2
- package/packages/datadog-plugin-aws-sdk/src/base.js +5 -8
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/index.js +18 -0
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/sonic.js +45 -0
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +9 -3
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +2 -5
- package/packages/datadog-plugin-azure-durable-functions/src/index.js +5 -4
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -3
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +18 -5
- package/packages/datadog-plugin-cypress/src/index.js +6 -6
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -2
- package/packages/datadog-plugin-cypress/src/support.js +1 -4
- package/packages/datadog-plugin-express/src/code_origin.js +1 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +2 -2
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +1 -2
- package/packages/datadog-plugin-graphql/src/execute.js +5 -5
- package/packages/datadog-plugin-grpc/src/util.js +1 -2
- package/packages/datadog-plugin-kafkajs/src/producer.js +1 -2
- package/packages/datadog-plugin-mocha/src/index.js +223 -39
- package/packages/datadog-plugin-mocha/src/webdriverio-video.js +453 -0
- package/packages/datadog-plugin-mongodb-core/src/query.js +8 -5
- package/packages/datadog-plugin-next/src/index.js +1 -2
- package/packages/datadog-plugin-openai/src/tracing.js +5 -2
- package/packages/datadog-plugin-openai-agents/src/integration.js +6 -2
- package/packages/datadog-plugin-openai-agents/src/processor.js +2 -4
- package/packages/datadog-plugin-postgres/src/index.js +24 -0
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -5
- package/packages/datadog-plugin-rhea/src/consumer.js +1 -4
- package/packages/datadog-plugin-vitest/src/index.js +5 -2
- package/packages/datadog-shimmer/src/shimmer.js +5 -4
- package/packages/datadog-webpack/index.js +55 -43
- package/packages/datadog-webpack/src/loader.js +56 -8
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -4
- package/packages/dd-trace/src/aiguard/messages/utils.js +1 -2
- package/packages/dd-trace/src/aiguard/redaction.js +8 -2
- package/packages/dd-trace/src/appsec/api_security/normalized-route.js +3 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +2 -6
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -9
- package/packages/dd-trace/src/appsec/handlers/http-request.js +3 -9
- package/packages/dd-trace/src/appsec/handlers/http-response.js +7 -2
- package/packages/dd-trace/src/appsec/iast/analyzers/path-traversal-analyzer.js +4 -3
- package/packages/dd-trace/src/appsec/iast/analyzers/set-cookies-header-interceptor.js +1 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/operations-taint-object.js +7 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +1 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter-esm.mjs +1 -3
- package/packages/dd-trace/src/appsec/iast/telemetry/iast-metric.js +1 -6
- package/packages/dd-trace/src/appsec/lambda.js +1 -3
- package/packages/dd-trace/src/appsec/rasp/fs-plugin.js +37 -27
- package/packages/dd-trace/src/appsec/rasp/lfi.js +9 -5
- package/packages/dd-trace/src/appsec/reporter.js +21 -1
- package/packages/dd-trace/src/appsec/stack_trace.js +1 -2
- package/packages/dd-trace/src/ci-visibility/dynamic-atr-retries.js +3 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/msgpack-to-json.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +13 -7
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +2 -8
- package/packages/dd-trace/src/ci-visibility/resolve-runfile-path.js +54 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +5 -8
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +2 -53
- package/packages/dd-trace/src/config/config-types.d.ts +2 -2
- package/packages/dd-trace/src/config/defaults.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +8 -0
- package/packages/dd-trace/src/config/helper.js +34 -3
- package/packages/dd-trace/src/config/index.js +10 -9
- package/packages/dd-trace/src/config/normalize-service.js +1 -1
- package/packages/dd-trace/src/config/parsers.js +2 -2
- package/packages/dd-trace/src/config/supported-configurations.json +35 -0
- package/packages/dd-trace/src/datastreams/processor.js +3 -2
- package/packages/dd-trace/src/datastreams/schemas/schema_builder.js +1 -4
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/index.js +5 -3
- package/packages/dd-trace/src/debugger/devtools_client/remote_config.js +17 -12
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -2
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/worker-error.js +21 -0
- package/packages/dd-trace/src/debugger/guardrail-metrics.js +2 -2
- package/packages/dd-trace/src/debugger/index.js +24 -2
- package/packages/dd-trace/src/debugger/probe_sampler.js +108 -11
- package/packages/dd-trace/src/dogstatsd.js +1 -1
- package/packages/dd-trace/src/encode/tags-processors.js +2 -2
- package/packages/dd-trace/src/exporters/common/request.js +2 -5
- package/packages/dd-trace/src/exporters/common/url.js +1 -1
- package/packages/dd-trace/src/id.js +15 -5
- package/packages/dd-trace/src/lambda/handler.js +179 -19
- package/packages/dd-trace/src/lambda/index.js +15 -1
- package/packages/dd-trace/src/lambda/runtime/patch.js +34 -7
- package/packages/dd-trace/src/llmobs/agent-manifest.js +296 -0
- package/packages/dd-trace/src/llmobs/audio-utils.js +3 -2
- package/packages/dd-trace/src/llmobs/constants/tags.js +13 -0
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +15 -0
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/chat_model.js +1 -3
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/nova-sonic.js +92 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +56 -2
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +38 -0
- package/packages/dd-trace/src/llmobs/prompts/manager.js +5 -7
- package/packages/dd-trace/src/llmobs/prompts/prompt.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +23 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +18 -8
- package/packages/dd-trace/src/llmobs/tagger.js +56 -3
- package/packages/dd-trace/src/llmobs/util.js +1 -2
- package/packages/dd-trace/src/llmobs/writers/base.js +7 -6
- package/packages/dd-trace/src/llmobs/writers/util.js +3 -1
- package/packages/dd-trace/src/log/log.js +1 -5
- package/packages/dd-trace/src/openfeature/agentless_configuration_source.js +30 -1
- package/packages/dd-trace/src/openfeature/configuration_source.js +17 -2
- package/packages/dd-trace/src/openfeature/debug-logging-hook.js +25 -0
- package/packages/dd-trace/src/openfeature/evaluation-telemetry-opt-out-hook.js +51 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +59 -3
- package/packages/dd-trace/src/openfeature/remote_config.js +9 -0
- package/packages/dd-trace/src/openfeature/writers/util.js +43 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -2
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +1 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +6 -23
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +53 -13
- package/packages/dd-trace/src/otel-sampling.js +4 -5
- package/packages/dd-trace/src/otel-thread-ctx.js +81 -15
- package/packages/dd-trace/src/plugins/database.js +7 -16
- package/packages/dd-trace/src/plugins/util/git.js +9 -25
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +1 -5
- package/packages/dd-trace/src/plugins/util/test.js +48 -4
- package/packages/dd-trace/src/priority_sampler.js +1 -2
- package/packages/dd-trace/src/profiling/profiler.js +4 -6
- package/packages/dd-trace/src/profiling/profilers/wall.js +4 -10
- package/packages/dd-trace/src/proxy.js +3 -2
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +10 -0
- package/packages/dd-trace/src/span_processor.js +13 -17
- package/packages/dd-trace/src/storage-channels.js +112 -46
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -3
- package/packages/dd-trace/src/util.js +29 -0
- package/packages/dd-trace/src/web-tags-cache.js +6 -2
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/LICENSE +201 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.d.ts +925 -0
- package/vendor/dist/@datadog/openfeature-node-server/rules-based/index.js +1 -0
- 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
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { performance } = require('node:perf_hooks')
|
|
4
|
-
const {
|
|
4
|
+
const { clearTimeout, setTimeout } = require('node:timers')
|
|
5
5
|
|
|
6
6
|
const { channel } = require('dc-polyfill')
|
|
7
7
|
|
|
8
8
|
const CiPlugin = require('../../dd-trace/src/plugins/ci_plugin')
|
|
9
9
|
const { storage } = require('../../datadog-core')
|
|
10
|
-
const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
|
|
10
|
+
const { getEnvironmentVariable, getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
|
|
11
11
|
const {
|
|
12
12
|
getEfdRetryCountForDuration,
|
|
13
13
|
hasEfdRetries,
|
|
@@ -15,16 +15,20 @@ const {
|
|
|
15
15
|
const {
|
|
16
16
|
getDynamicAtrRetryCount,
|
|
17
17
|
} = require('../../dd-trace/src/ci-visibility/dynamic-atr-retries')
|
|
18
|
+
const { FINAL_FLUSH_TIMEOUT } = require('../../dd-trace/src/ci-visibility/final-flush')
|
|
18
19
|
const {
|
|
19
20
|
SCREENSHOT_UPLOAD_RESULT_ERROR,
|
|
20
21
|
SCREENSHOT_UPLOAD_RESULT_UPLOADED,
|
|
21
22
|
setScreenshotUploadTags,
|
|
22
23
|
} = require('../../dd-trace/src/ci-visibility/test-screenshot')
|
|
24
|
+
const { setVideoUploadTags } = require('../../dd-trace/src/ci-visibility/test-video')
|
|
23
25
|
const log = require('../../dd-trace/src/log')
|
|
24
26
|
const {
|
|
25
27
|
requestWebdriverioScreenshotUpload,
|
|
28
|
+
requestWebdriverioVideoUpload,
|
|
26
29
|
sendWebdriverioWorkerMessage,
|
|
27
30
|
SUITE_FINISH,
|
|
31
|
+
VIDEO_UPLOAD_FLUSH,
|
|
28
32
|
WEBDRIVERIO_WORKER_ENV,
|
|
29
33
|
} = require('../../datadog-instrumentations/src/mocha/webdriverio-protocol')
|
|
30
34
|
|
|
@@ -83,6 +87,11 @@ const {
|
|
|
83
87
|
TELEMETRY_TEST_SESSION,
|
|
84
88
|
} = require('../../dd-trace/src/ci-visibility/telemetry')
|
|
85
89
|
|
|
90
|
+
// Load the recorder and capture clocks before test code can replace globals with fake timers.
|
|
91
|
+
const createWebdriverioVideo = require('./webdriverio-video')
|
|
92
|
+
|
|
93
|
+
const dateNow = Date.now
|
|
94
|
+
|
|
86
95
|
const jasmineAdapterRunAsyncEndCh = 'tracing:orchestrion:@wdio/jasmine-framework:JasmineAdapter_run:asyncEnd'
|
|
87
96
|
const jasmineDoneCh = 'ci:webdriverio:jasmine:done'
|
|
88
97
|
const jasmineExecuteAsyncEndCh = 'tracing:orchestrion:@wdio/utils:executeAsync:asyncEnd'
|
|
@@ -106,6 +115,7 @@ const WEBDRIVERIO_JASMINE_FAILED_EXPECTATION_COUNT = Symbol('webdriverioJasmineF
|
|
|
106
115
|
const WEBDRIVERIO_JASMINE_FUNCTION_TYPE = Symbol('webdriverioJasmineFunctionType')
|
|
107
116
|
const WEBDRIVERIO_JASMINE_TEST = Symbol('webdriverioJasmineTest')
|
|
108
117
|
const isWebdriverioWorker = !!getEnvironmentVariable(WEBDRIVERIO_WORKER_ENV)
|
|
118
|
+
const shouldRecordWebdriverioVideos = getValueFromEnvSources('DD_TEST_FAILURE_VIDEOS_ENABLED', true) === true
|
|
109
119
|
const PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])
|
|
110
120
|
const BASE64_RE = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$/
|
|
111
121
|
|
|
@@ -121,6 +131,20 @@ const BASE64_RE = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$
|
|
|
121
131
|
* @property {string|undefined} status
|
|
122
132
|
*/
|
|
123
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Waits for an attempt's final capture, bounded by its existing deadline, without waiting for encoding or upload.
|
|
136
|
+
*
|
|
137
|
+
* @param {ReturnType<typeof createWebdriverioVideo>} video
|
|
138
|
+
* @param {Promise<unknown>} [finishPromise]
|
|
139
|
+
*/
|
|
140
|
+
function waitForWebdriverioCapture (video, finishPromise) {
|
|
141
|
+
if (!video) return finishPromise
|
|
142
|
+
// WDIO awaits its async framework hook, so bridge the recorder's callback before it can start the next attempt.
|
|
143
|
+
/** @type {Promise<void>} */
|
|
144
|
+
const captureWait = new Promise(resolve => video.waitForCapture(resolve))
|
|
145
|
+
return finishPromise ? Promise.all([finishPromise, captureWait]) : captureWait
|
|
146
|
+
}
|
|
147
|
+
|
|
124
148
|
/**
|
|
125
149
|
* Normalizes a WebdriverIO Jasmine spec identifier to a filesystem path.
|
|
126
150
|
*
|
|
@@ -128,7 +152,20 @@ const BASE64_RE = /^(?:[A-Za-z\d+/]{4})*(?:[A-Za-z\d+/]{2}==|[A-Za-z\d+/]{3}=)?$
|
|
|
128
152
|
* @returns {string|undefined}
|
|
129
153
|
*/
|
|
130
154
|
function normalizeJasmineFile (file) {
|
|
131
|
-
return file?.startsWith('file://') ? fileURLToPath(file) : file
|
|
155
|
+
return file?.startsWith('file://') ? require('node:url').fileURLToPath(file) : file
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function getWebdriverioJasmineSpecResult (spec) {
|
|
159
|
+
if (spec?.result) return spec.result
|
|
160
|
+
if (typeof spec?.startedEvent === 'function') return spec.startedEvent()
|
|
161
|
+
if (!spec?.id) return
|
|
162
|
+
return {
|
|
163
|
+
description: spec.description,
|
|
164
|
+
filename: spec.filename,
|
|
165
|
+
fullName: typeof spec.getFullName === 'function' ? spec.getFullName() : spec.description,
|
|
166
|
+
id: spec.id,
|
|
167
|
+
parentSuiteId: spec.parentSuiteId,
|
|
168
|
+
}
|
|
132
169
|
}
|
|
133
170
|
|
|
134
171
|
/**
|
|
@@ -245,14 +282,19 @@ function getJasmineFailureFile (result, specs) {
|
|
|
245
282
|
class MochaPlugin extends CiPlugin {
|
|
246
283
|
static id = 'mocha'
|
|
247
284
|
|
|
285
|
+
#pendingWebdriverioVideos = new Set()
|
|
286
|
+
#webdriverioCapture
|
|
287
|
+
#webdriverioVideoFlushTimer
|
|
288
|
+
|
|
248
289
|
constructor (...args) {
|
|
249
290
|
super(...args)
|
|
250
291
|
|
|
251
292
|
this._testTitleToParams = {}
|
|
252
293
|
this.sourceRoot = process.cwd()
|
|
253
294
|
this._webdriverioScreenshotUploads = new WeakMap()
|
|
254
|
-
this.
|
|
255
|
-
this.
|
|
295
|
+
this._webdriverioVideos = new Map()
|
|
296
|
+
this._pendingWebdriverioMediaUploads = 0
|
|
297
|
+
this._webdriverioMediaUploadCallbacks = []
|
|
256
298
|
|
|
257
299
|
this.addSub('ci:webdriverio:screenshot:capabilities', (ctx) => {
|
|
258
300
|
ctx.enabled = Boolean(
|
|
@@ -283,6 +325,36 @@ class MochaPlugin extends CiPlugin {
|
|
|
283
325
|
exporter.uploadTestScreenshot({ content, traceId, idempotencyKey, capturedAtMs }, onDone)
|
|
284
326
|
})
|
|
285
327
|
|
|
328
|
+
this.addSub('ci:webdriverio:video:capabilities',
|
|
329
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (
|
|
330
|
+
/** @param {{enabled?: boolean}} ctx */
|
|
331
|
+
(ctx) => {
|
|
332
|
+
ctx.enabled = Boolean(
|
|
333
|
+
shouldRecordWebdriverioVideos &&
|
|
334
|
+
this._tracerConfig.testOptimization?.DD_TEST_FAILURE_VIDEOS_ENABLED &&
|
|
335
|
+
(isWebdriverioWorker
|
|
336
|
+
? this.libraryConfig?.isTestFailureVideosEnabled && createWebdriverioVideo.isFfmpegAvailable()
|
|
337
|
+
: this.tracer._exporter?.canUploadTestVideos?.())
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
))
|
|
341
|
+
|
|
342
|
+
this.addSub('ci:webdriverio:video:upload',
|
|
343
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (
|
|
344
|
+
/**
|
|
345
|
+
* @param {{filePath: string, traceId: string, idempotencyKey: string, capturedAtMs: number,
|
|
346
|
+
* signal?: AbortSignal, onDone: (error?: Error) => void}} options
|
|
347
|
+
*/
|
|
348
|
+
({ filePath, traceId, idempotencyKey, capturedAtMs, signal, onDone }) => {
|
|
349
|
+
const exporter = this.tracer?._exporter
|
|
350
|
+
if (!exporter?.canUploadTestVideos?.() || !exporter.uploadTestVideo) {
|
|
351
|
+
onDone(new Error('WebdriverIO video upload is not supported by the active Test Optimization transport'))
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
exporter.uploadTestVideo({ filePath, traceId, idempotencyKey, capturedAtMs, signal }, onDone)
|
|
355
|
+
}
|
|
356
|
+
))
|
|
357
|
+
|
|
286
358
|
this.addSub('ci:mocha:worker:configuration', ({
|
|
287
359
|
libraryConfig,
|
|
288
360
|
repositoryRoot,
|
|
@@ -306,6 +378,19 @@ class MochaPlugin extends CiPlugin {
|
|
|
306
378
|
'active Test Optimization transport.'
|
|
307
379
|
)
|
|
308
380
|
}
|
|
381
|
+
if (
|
|
382
|
+
testFramework === WEBDRIVERIO_FRAMEWORK &&
|
|
383
|
+
shouldRecordWebdriverioVideos &&
|
|
384
|
+
this._tracerConfig.testOptimization.DD_TEST_FAILURE_VIDEOS_ENABLED &&
|
|
385
|
+
!(isWebdriverioWorker
|
|
386
|
+
? libraryConfig.isTestFailureVideosEnabled
|
|
387
|
+
: this.tracer._exporter?.canUploadTestVideos?.())
|
|
388
|
+
) {
|
|
389
|
+
log.warn(
|
|
390
|
+
'DD_TEST_FAILURE_VIDEOS_ENABLED is true, but WebdriverIO video upload is not supported by the %s',
|
|
391
|
+
'active Test Optimization transport.'
|
|
392
|
+
)
|
|
393
|
+
}
|
|
309
394
|
if (testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
310
395
|
this._webdriverioJasmineState = {
|
|
311
396
|
completedTestStatuses: new Map(),
|
|
@@ -481,7 +566,14 @@ class MochaPlugin extends CiPlugin {
|
|
|
481
566
|
this.addSub(jasmineReporterSpecDoneEndCh, (ctx) => {
|
|
482
567
|
if (this.testFrameworkAdapter === WEBDRIVERIO_JASMINE_ADAPTER) {
|
|
483
568
|
const result = ctx.result
|
|
484
|
-
const
|
|
569
|
+
const testResult = ctx.arguments?.[0]
|
|
570
|
+
const test = this._webdriverioJasmineState?.tests.get(testResult?.id)
|
|
571
|
+
const video = this._webdriverioVideos.get(test?.span)
|
|
572
|
+
const finishTestPromise = this.#finishWebdriverioJasmineTest(testResult, ctx.self?._specs)
|
|
573
|
+
// Replay can defer video.finish(), which must start the final capture before we wait for it.
|
|
574
|
+
const finishPromise = finishTestPromise
|
|
575
|
+
? finishTestPromise.then(() => waitForWebdriverioCapture(video))
|
|
576
|
+
: waitForWebdriverioCapture(video)
|
|
485
577
|
if (finishPromise) {
|
|
486
578
|
ctx.result = finishPromise.then(() => result)
|
|
487
579
|
}
|
|
@@ -559,9 +651,9 @@ class MochaPlugin extends CiPlugin {
|
|
|
559
651
|
testSuiteMetadata[TEST_ITR_FORCED_RUN] = 'true'
|
|
560
652
|
this.telemetry.count(TELEMETRY_ITR_FORCED_TO_RUN, { testLevel: 'suite' })
|
|
561
653
|
}
|
|
562
|
-
testSuiteMetadata[TEST_SOURCE_FILE] = this.repositoryRoot
|
|
563
|
-
?
|
|
564
|
-
:
|
|
654
|
+
testSuiteMetadata[TEST_SOURCE_FILE] = this.repositoryRoot === this.sourceRoot || !this.repositoryRoot
|
|
655
|
+
? testSuite
|
|
656
|
+
: getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
565
657
|
if (testSuiteMetadata[TEST_SOURCE_FILE]) {
|
|
566
658
|
testSuiteMetadata[TEST_SOURCE_START] = 1
|
|
567
659
|
}
|
|
@@ -641,6 +733,8 @@ class MochaPlugin extends CiPlugin {
|
|
|
641
733
|
this.addBind('ci:mocha:test:start', (ctx) => {
|
|
642
734
|
const store = storage('legacy').getStore()
|
|
643
735
|
const span = this.startTestSpan(ctx)
|
|
736
|
+
const { isDisabled, isAttemptToFix } = /** @type {{isDisabled?: boolean, isAttemptToFix?: boolean}} */ (ctx)
|
|
737
|
+
if (!isDisabled || isAttemptToFix) this.#startWebdriverioVideo(span)
|
|
644
738
|
|
|
645
739
|
ctx.parentStore = store
|
|
646
740
|
ctx.currentStore = { ...store, span }
|
|
@@ -650,13 +744,42 @@ class MochaPlugin extends CiPlugin {
|
|
|
650
744
|
return ctx.currentStore
|
|
651
745
|
})
|
|
652
746
|
|
|
747
|
+
this.addSub('ci:mocha:after-each:finish', (ctx) => {
|
|
748
|
+
const context = /** @type {{isWaiting: boolean, onDone: () => void}} */ (ctx)
|
|
749
|
+
const video = this.#webdriverioCapture
|
|
750
|
+
this.#webdriverioCapture = undefined
|
|
751
|
+
if (!video) return
|
|
752
|
+
context.isWaiting = true
|
|
753
|
+
video.waitForCapture(context.onDone)
|
|
754
|
+
})
|
|
755
|
+
|
|
653
756
|
this.addSub('ci:mocha:worker:finish', ({ onDone } = {}) => {
|
|
654
|
-
|
|
655
|
-
|
|
757
|
+
// An interrupted runner may exit without reporting the active attempt.
|
|
758
|
+
for (const span of this._webdriverioVideos.keys()) {
|
|
759
|
+
this.#finishWebdriverioMedia(span, 'skip', () => {})
|
|
760
|
+
}
|
|
761
|
+
const flush = () => {
|
|
762
|
+
clearTimeout(this.#webdriverioVideoFlushTimer)
|
|
763
|
+
this.#webdriverioVideoFlushTimer = undefined
|
|
764
|
+
if (isWebdriverioWorker && this.libraryConfig?.isTestFailureVideosEnabled) {
|
|
765
|
+
sendWebdriverioWorkerMessage({ name: VIDEO_UPLOAD_FLUSH, content: { complete: true } })
|
|
766
|
+
}
|
|
767
|
+
this.tracer._exporter.flush(onDone)
|
|
768
|
+
}
|
|
769
|
+
if (this._pendingWebdriverioMediaUploads === 0) {
|
|
656
770
|
flush()
|
|
657
771
|
return
|
|
658
772
|
}
|
|
659
|
-
this.
|
|
773
|
+
if (isWebdriverioWorker && this.libraryConfig?.isTestFailureVideosEnabled) {
|
|
774
|
+
sendWebdriverioWorkerMessage({ name: VIDEO_UPLOAD_FLUSH })
|
|
775
|
+
}
|
|
776
|
+
this._webdriverioMediaUploadCallbacks.push(flush)
|
|
777
|
+
if (this.#pendingWebdriverioVideos.size) {
|
|
778
|
+
this.#webdriverioVideoFlushTimer ??= setTimeout(() => {
|
|
779
|
+
for (const video of this.#pendingWebdriverioVideos) video.cancel()
|
|
780
|
+
}, FINAL_FLUSH_TIMEOUT)
|
|
781
|
+
this.#webdriverioVideoFlushTimer.unref?.()
|
|
782
|
+
}
|
|
660
783
|
})
|
|
661
784
|
|
|
662
785
|
this.addSub('ci:mocha:test:finish', ({
|
|
@@ -720,7 +843,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
720
843
|
this.activeTestSpan = null
|
|
721
844
|
}
|
|
722
845
|
}
|
|
723
|
-
if (
|
|
846
|
+
if (!this.#finishWebdriverioMedia(span, status, finishSpan)) {
|
|
724
847
|
finishSpan()
|
|
725
848
|
}
|
|
726
849
|
}
|
|
@@ -830,23 +953,24 @@ class MochaPlugin extends CiPlugin {
|
|
|
830
953
|
this.runningTestProbe) {
|
|
831
954
|
finishWait = this.waitForInFlightDiBreakpointHits()
|
|
832
955
|
}
|
|
833
|
-
let
|
|
834
|
-
let
|
|
956
|
+
let mediaFinished = false
|
|
957
|
+
let canFinishMedia = false
|
|
835
958
|
let replayFinished = !finishWait
|
|
836
959
|
let spanFinished = false
|
|
837
960
|
const finishWhenReady = () => {
|
|
838
|
-
if (!spanFinished && replayFinished && (
|
|
961
|
+
if (!spanFinished && replayFinished && (mediaStarted === false || mediaFinished)) {
|
|
839
962
|
spanFinished = true
|
|
840
963
|
finishSpan()
|
|
841
964
|
}
|
|
842
965
|
}
|
|
843
|
-
const
|
|
844
|
-
|
|
845
|
-
|
|
966
|
+
const video = this._webdriverioVideos.get(span)
|
|
967
|
+
const mediaStarted = this.#finishWebdriverioMedia(span, 'fail', () => {
|
|
968
|
+
mediaFinished = true
|
|
969
|
+
if (canFinishMedia) {
|
|
846
970
|
finishWhenReady()
|
|
847
971
|
}
|
|
848
972
|
})
|
|
849
|
-
|
|
973
|
+
canFinishMedia = true
|
|
850
974
|
if (finishWait) {
|
|
851
975
|
const finishReplay = () => {
|
|
852
976
|
replayFinished = true
|
|
@@ -857,6 +981,9 @@ class MochaPlugin extends CiPlugin {
|
|
|
857
981
|
promises.finishTestPromise = finishTestPromise
|
|
858
982
|
}
|
|
859
983
|
}
|
|
984
|
+
if (video && promises) {
|
|
985
|
+
promises.finishTestPromise = waitForWebdriverioCapture(video, promises.finishTestPromise)
|
|
986
|
+
}
|
|
860
987
|
finishWhenReady()
|
|
861
988
|
}
|
|
862
989
|
})
|
|
@@ -986,10 +1113,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
986
1113
|
#startWebdriverioJasmineTest (result, specs, currentSuite) {
|
|
987
1114
|
const state = this._webdriverioJasmineState
|
|
988
1115
|
const currentStore = storage('legacy').getStore()
|
|
989
|
-
if (!state || !result?.id) {
|
|
990
|
-
return currentStore
|
|
991
|
-
}
|
|
992
|
-
if (state.completedTestStatuses.has(result.id)) {
|
|
1116
|
+
if (!state || !result?.id || state.completedTestStatuses.has(result.id)) {
|
|
993
1117
|
return currentStore
|
|
994
1118
|
}
|
|
995
1119
|
|
|
@@ -1131,6 +1255,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
1131
1255
|
title: test.title,
|
|
1132
1256
|
})
|
|
1133
1257
|
test.span = span
|
|
1258
|
+
if (!test.isDisabled || test.isAttemptToFix) this.#startWebdriverioVideo(span)
|
|
1134
1259
|
test.currentStore = {
|
|
1135
1260
|
...parentStore,
|
|
1136
1261
|
span,
|
|
@@ -1153,8 +1278,9 @@ class MochaPlugin extends CiPlugin {
|
|
|
1153
1278
|
return
|
|
1154
1279
|
}
|
|
1155
1280
|
|
|
1281
|
+
const specResult = getWebdriverioJasmineSpecResult(spec)
|
|
1156
1282
|
if (this.libraryConfig?.isTestManagementTestsEnabled) {
|
|
1157
|
-
this.#startWebdriverioJasmineTest(
|
|
1283
|
+
this.#startWebdriverioJasmineTest(specResult)
|
|
1158
1284
|
const test = this._webdriverioJasmineState?.tests.get(spec.id)
|
|
1159
1285
|
if (test?.isDisabled && !test.isAttemptToFix) {
|
|
1160
1286
|
spec.pend('Skipped by Datadog Test Management')
|
|
@@ -1486,6 +1612,71 @@ class MochaPlugin extends CiPlugin {
|
|
|
1486
1612
|
}
|
|
1487
1613
|
}
|
|
1488
1614
|
|
|
1615
|
+
/** @param {object} span - Span owning one WebdriverIO attempt */
|
|
1616
|
+
#startWebdriverioVideo (span) {
|
|
1617
|
+
if (this.testFramework !== WEBDRIVERIO_FRAMEWORK ||
|
|
1618
|
+
!shouldRecordWebdriverioVideos ||
|
|
1619
|
+
!this._tracerConfig.testOptimization?.DD_TEST_FAILURE_VIDEOS_ENABLED) return
|
|
1620
|
+
const canUpload = isWebdriverioWorker
|
|
1621
|
+
? this.libraryConfig?.isTestFailureVideosEnabled
|
|
1622
|
+
: this.tracer._exporter?.canUploadTestVideos?.()
|
|
1623
|
+
if (!canUpload) return
|
|
1624
|
+
|
|
1625
|
+
const browser = globalThis._wdioGlobals?.get?.('browser') || globalThis.browser
|
|
1626
|
+
const video = createWebdriverioVideo(browser)
|
|
1627
|
+
this.#webdriverioCapture = video
|
|
1628
|
+
if (video) this._webdriverioVideos.set(span, video)
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
/**
|
|
1632
|
+
* Finishes a span only after its independent screenshot and video uploads settle.
|
|
1633
|
+
*
|
|
1634
|
+
* @param {object} span
|
|
1635
|
+
* @param {string} status
|
|
1636
|
+
* @param {() => void} onDone
|
|
1637
|
+
*/
|
|
1638
|
+
#finishWebdriverioMedia (span, status, onDone) {
|
|
1639
|
+
const video = this._webdriverioVideos.get(span)
|
|
1640
|
+
if (!video) return status === 'fail' && this.#startWebdriverioScreenshotUpload(span, onDone)
|
|
1641
|
+
|
|
1642
|
+
this._webdriverioVideos.delete(span)
|
|
1643
|
+
this.#pendingWebdriverioVideos.add(video)
|
|
1644
|
+
this._pendingWebdriverioMediaUploads++
|
|
1645
|
+
const traceId = span.context().toTraceId()
|
|
1646
|
+
const capturedAtMs = dateNow()
|
|
1647
|
+
let pending = 2
|
|
1648
|
+
const complete = () => {
|
|
1649
|
+
if (--pending !== 0) return
|
|
1650
|
+
onDone()
|
|
1651
|
+
this.#completeWebdriverioMediaUpload()
|
|
1652
|
+
}
|
|
1653
|
+
if (status !== 'fail' || !this.#startWebdriverioScreenshotUpload(span, complete)) complete()
|
|
1654
|
+
video.finish(status === 'fail', (filePath, index, callback) => {
|
|
1655
|
+
const options = {
|
|
1656
|
+
filePath,
|
|
1657
|
+
traceId,
|
|
1658
|
+
idempotencyKey: `${traceId}:webdriverio-failure-${index}.webm`,
|
|
1659
|
+
capturedAtMs,
|
|
1660
|
+
}
|
|
1661
|
+
if (isWebdriverioWorker) requestWebdriverioVideoUpload(options, callback)
|
|
1662
|
+
else this.tracer._exporter.uploadTestVideo(options, callback)
|
|
1663
|
+
}, result => {
|
|
1664
|
+
this.#pendingWebdriverioVideos.delete(video)
|
|
1665
|
+
setVideoUploadTags(span, result)
|
|
1666
|
+
complete()
|
|
1667
|
+
})
|
|
1668
|
+
return true
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
/** Releases worker shutdown after all media-owned spans have finished. */
|
|
1672
|
+
#completeWebdriverioMediaUpload () {
|
|
1673
|
+
this._pendingWebdriverioMediaUploads--
|
|
1674
|
+
if (this._pendingWebdriverioMediaUploads !== 0) return
|
|
1675
|
+
const callbacks = this._webdriverioMediaUploadCallbacks
|
|
1676
|
+
this._webdriverioMediaUploadCallbacks = []
|
|
1677
|
+
for (const callback of callbacks) callback()
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1489
1680
|
/**
|
|
1490
1681
|
* Captures and uploads the current WebdriverIO browser state once for a failed test attempt.
|
|
1491
1682
|
*
|
|
@@ -1493,6 +1684,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
1493
1684
|
* @param {() => void} [onDone] - Called after the screenshot upload finishes
|
|
1494
1685
|
*/
|
|
1495
1686
|
#startWebdriverioScreenshotUpload (span, onDone) {
|
|
1687
|
+
this._webdriverioVideos.get(span)?.capture()
|
|
1496
1688
|
if (
|
|
1497
1689
|
!span ||
|
|
1498
1690
|
this.testFramework !== WEBDRIVERIO_FRAMEWORK ||
|
|
@@ -1527,7 +1719,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
1527
1719
|
finished: false,
|
|
1528
1720
|
}
|
|
1529
1721
|
this._webdriverioScreenshotUploads.set(span, upload)
|
|
1530
|
-
this.
|
|
1722
|
+
this._pendingWebdriverioMediaUploads++
|
|
1531
1723
|
const finishUpload = (result) => this.#finishWebdriverioScreenshotUpload(span, upload, result)
|
|
1532
1724
|
let captureTimeout
|
|
1533
1725
|
const failCapture = (error) => {
|
|
@@ -1586,18 +1778,10 @@ class MochaPlugin extends CiPlugin {
|
|
|
1586
1778
|
const uploadCallbacks = upload.callbacks
|
|
1587
1779
|
upload.callbacks = []
|
|
1588
1780
|
|
|
1589
|
-
this._pendingWebdriverioScreenshotUploads--
|
|
1590
|
-
let workerCallbacks = []
|
|
1591
|
-
if (this._pendingWebdriverioScreenshotUploads === 0) {
|
|
1592
|
-
workerCallbacks = this._webdriverioScreenshotUploadCallbacks
|
|
1593
|
-
this._webdriverioScreenshotUploadCallbacks = []
|
|
1594
|
-
}
|
|
1595
1781
|
for (const callback of uploadCallbacks) {
|
|
1596
1782
|
callback()
|
|
1597
1783
|
}
|
|
1598
|
-
|
|
1599
|
-
callback()
|
|
1600
|
-
}
|
|
1784
|
+
this.#completeWebdriverioMediaUpload()
|
|
1601
1785
|
}
|
|
1602
1786
|
|
|
1603
1787
|
/**
|
|
@@ -1616,7 +1800,7 @@ class MochaPlugin extends CiPlugin {
|
|
|
1616
1800
|
}
|
|
1617
1801
|
const exporter = this.tracer?._exporter
|
|
1618
1802
|
const traceId = span.context().toTraceId()
|
|
1619
|
-
const capturedAtMs =
|
|
1803
|
+
const capturedAtMs = dateNow()
|
|
1620
1804
|
let pendingUploads = screenshotList.length
|
|
1621
1805
|
let hasUploadError = false
|
|
1622
1806
|
const finishOne = (error) => {
|
|
@@ -1775,9 +1959,9 @@ class MochaPlugin extends CiPlugin {
|
|
|
1775
1959
|
const testSuite = getTestSuitePath(testSuiteAbsolutePath, this.sourceRoot)
|
|
1776
1960
|
const testSuiteSpan = this._testSuiteSpansByTestSuite.get(testSuite)
|
|
1777
1961
|
|
|
1778
|
-
extraTags[TEST_SOURCE_FILE] = this.repositoryRoot
|
|
1779
|
-
?
|
|
1780
|
-
:
|
|
1962
|
+
extraTags[TEST_SOURCE_FILE] = this.repositoryRoot === this.sourceRoot || !this.repositoryRoot
|
|
1963
|
+
? testSuite
|
|
1964
|
+
: getTestSuitePath(testSuiteAbsolutePath, this.repositoryRoot)
|
|
1781
1965
|
|
|
1782
1966
|
if (isNew) {
|
|
1783
1967
|
extraTags[TEST_IS_NEW] = 'true'
|