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.
- 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/index.electron.js +3 -0
- 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 +15 -8
- package/packages/datadog-instrumentations/src/ai.js +37 -26
- 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 +24 -10
- package/packages/datadog-instrumentations/src/cypress-config.js +398 -52
- package/packages/datadog-instrumentations/src/express.js +20 -2
- package/packages/datadog-instrumentations/src/fastify.js +7 -11
- package/packages/datadog-instrumentations/src/grpc/server.js +18 -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/helpers/router-helper.js +8 -8
- package/packages/datadog-instrumentations/src/http2/server.js +143 -17
- package/packages/datadog-instrumentations/src/jest.js +36 -4
- package/packages/datadog-instrumentations/src/mariadb.js +1 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +0 -2
- 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 +159 -22
- package/packages/datadog-instrumentations/src/process.js +3 -0
- package/packages/datadog-instrumentations/src/router.js +191 -106
- package/packages/datadog-instrumentations/src/selenium.js +52 -26
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +2 -2
- package/packages/datadog-instrumentations/src/vitest-main.js +0 -2
- 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/services/kinesis.js +41 -16
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +21 -21
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +14 -8
- package/packages/datadog-plugin-aws-sdk/src/util.js +2 -24
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +24 -62
- 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 +177 -59
- package/packages/datadog-plugin-graphql/src/parse.js +22 -1
- package/packages/datadog-plugin-graphql/src/request.js +32 -1
- package/packages/datadog-plugin-graphql/src/utils.js +42 -0
- package/packages/datadog-plugin-graphql/src/validate.js +15 -3
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/server.js +38 -6
- package/packages/datadog-plugin-jest/src/util.js +21 -6
- 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-playwright/src/index.js +177 -64
- 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 +4 -2
- 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/index.electron.js +3 -0
- package/packages/dd-trace/index.js +2 -37
- 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/bootstrap.js +39 -0
- 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 +55 -5
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +22 -6
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +5 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +62 -7
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -2
- 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 +173 -9
- package/packages/dd-trace/src/ci-visibility/test-screenshot.js +90 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +20 -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 +83 -0
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/pathway.js +6 -4
- 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 +29 -0
- package/packages/dd-trace/src/llmobs/constants/tags.js +1 -0
- 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/index.js +10 -2
- 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 +79 -7
- 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/noop/proxy.js +5 -4
- 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 +35 -0
- 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/opentracing/propagation/log.js +11 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +80 -14
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +10 -1
- package/packages/dd-trace/src/opentracing/tracer.js +7 -1
- 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 +65 -8
- package/packages/dd-trace/src/plugins/util/web.js +12 -0
- package/packages/dd-trace/src/proxy.js +99 -10
- package/packages/dd-trace/src/span_processor.js +5 -0
- package/packages/dd-trace/src/standalone/index.js +0 -36
- 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
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
const { DsmPathwayCodec, getHeadersSize } = require('../../../dd-trace/src/datastreams')
|
|
3
3
|
const log = require('../../../dd-trace/src/log')
|
|
4
4
|
const BaseAwsSdkPlugin = require('../base')
|
|
5
|
-
const { isEmpty } = require('../util')
|
|
6
5
|
|
|
7
6
|
class Sns extends BaseAwsSdkPlugin {
|
|
8
7
|
static id = 'sns'
|
|
@@ -71,6 +70,12 @@ class Sns extends BaseAwsSdkPlugin {
|
|
|
71
70
|
}
|
|
72
71
|
}
|
|
73
72
|
|
|
73
|
+
/**
|
|
74
|
+
* @param {import('../../../dd-trace/src/opentracing/span') | null} span
|
|
75
|
+
* @param {{ Message?: string, MessageAttributes?: Record<string, object> }} params
|
|
76
|
+
* @param {string | undefined} topicArn
|
|
77
|
+
* @param {boolean} injectTraceContext
|
|
78
|
+
*/
|
|
74
79
|
injectToMessage (span, params, topicArn, injectTraceContext) {
|
|
75
80
|
if (!params.MessageAttributes) {
|
|
76
81
|
params.MessageAttributes = {}
|
|
@@ -79,37 +84,32 @@ class Sns extends BaseAwsSdkPlugin {
|
|
|
79
84
|
return
|
|
80
85
|
}
|
|
81
86
|
|
|
82
|
-
|
|
87
|
+
let ddInfo
|
|
83
88
|
// for now, we only want to inject to the first message, this may change for batches in the future
|
|
84
89
|
if (injectTraceContext) {
|
|
85
|
-
this.tracer.inject(span, 'text_map'
|
|
86
|
-
// add ddInfo before checking DSM so we can include DD attributes in payload size
|
|
87
|
-
params.MessageAttributes._datadog = {
|
|
88
|
-
DataType: 'Binary',
|
|
89
|
-
BinaryValue: ddInfo,
|
|
90
|
-
}
|
|
90
|
+
ddInfo = this.tracer.inject(span, 'text_map')
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
if (this.config.dsmEnabled) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
94
|
+
// Add the placeholder before the checkpoint so its payload size includes DD attributes.
|
|
95
|
+
params.MessageAttributes._datadog = {
|
|
96
|
+
DataType: 'Binary',
|
|
97
|
+
BinaryValue: ddInfo ?? {},
|
|
99
98
|
}
|
|
100
99
|
|
|
101
100
|
const dataStreamsContext = this.setDSMCheckpoint(span, params, topicArn)
|
|
102
|
-
DsmPathwayCodec.encode(dataStreamsContext, ddInfo)
|
|
101
|
+
ddInfo = DsmPathwayCodec.encode(dataStreamsContext, ddInfo) ?? ddInfo
|
|
103
102
|
}
|
|
104
103
|
|
|
105
|
-
if (
|
|
106
|
-
if (params.MessageAttributes._datadog) {
|
|
107
|
-
// let's avoid adding any additional information to payload if we failed to inject
|
|
108
|
-
delete params.MessageAttributes._datadog
|
|
109
|
-
}
|
|
110
|
-
} else {
|
|
104
|
+
if (ddInfo) {
|
|
111
105
|
// BINARY types are automatically base64 encoded
|
|
112
|
-
params.MessageAttributes._datadog
|
|
106
|
+
params.MessageAttributes._datadog = {
|
|
107
|
+
DataType: 'Binary',
|
|
108
|
+
BinaryValue: Buffer.from(JSON.stringify(ddInfo)),
|
|
109
|
+
}
|
|
110
|
+
} else if (params.MessageAttributes._datadog) {
|
|
111
|
+
// let's avoid adding any additional information to payload if we failed to inject
|
|
112
|
+
delete params.MessageAttributes._datadog
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const log = require('../../../dd-trace/src/log')
|
|
4
4
|
const BaseAwsSdkPlugin = require('../base')
|
|
5
5
|
const { DsmPathwayCodec, getHeadersSize } = require('../../../dd-trace/src/datastreams')
|
|
6
|
-
const { extractQueueMetadata
|
|
6
|
+
const { extractQueueMetadata } = require('../util')
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @typedef {{
|
|
@@ -322,6 +322,12 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
324
|
|
|
325
|
+
/**
|
|
326
|
+
* @param {import('../../../dd-trace/src/opentracing/span') | null} span
|
|
327
|
+
* @param {{ MessageBody?: string, MessageAttributes?: Record<string, object> } | undefined} params
|
|
328
|
+
* @param {string} queueUrl
|
|
329
|
+
* @param {boolean} injectTraceContext
|
|
330
|
+
*/
|
|
325
331
|
injectToMessage (span, params, queueUrl, injectTraceContext) {
|
|
326
332
|
if (!params) {
|
|
327
333
|
params = {}
|
|
@@ -333,10 +339,10 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
333
339
|
return
|
|
334
340
|
}
|
|
335
341
|
|
|
336
|
-
|
|
342
|
+
let ddInfo
|
|
337
343
|
// For now we only inject to the first message; batches may change later.
|
|
338
344
|
if (injectTraceContext) {
|
|
339
|
-
this.tracer.inject(span, 'text_map'
|
|
345
|
+
ddInfo = this.tracer.inject(span, 'text_map')
|
|
340
346
|
}
|
|
341
347
|
|
|
342
348
|
if (this.config.dsmEnabled) {
|
|
@@ -344,19 +350,19 @@ class Sqs extends BaseAwsSdkPlugin {
|
|
|
344
350
|
// matches the on-wire payload, then update with the encoded context.
|
|
345
351
|
params.MessageAttributes._datadog = {
|
|
346
352
|
DataType: 'String',
|
|
347
|
-
StringValue: JSON.stringify(ddInfo),
|
|
353
|
+
StringValue: JSON.stringify(ddInfo ?? {}),
|
|
348
354
|
}
|
|
349
355
|
const dataStreamsContext = this.setDSMCheckpoint(span, params, queueUrl)
|
|
350
|
-
|
|
351
|
-
|
|
356
|
+
ddInfo = DsmPathwayCodec.encode(dataStreamsContext, ddInfo) ?? ddInfo
|
|
357
|
+
if (ddInfo) {
|
|
352
358
|
params.MessageAttributes._datadog.StringValue = JSON.stringify(ddInfo)
|
|
353
|
-
} else
|
|
359
|
+
} else {
|
|
354
360
|
delete params.MessageAttributes._datadog
|
|
355
361
|
}
|
|
356
362
|
return
|
|
357
363
|
}
|
|
358
364
|
|
|
359
|
-
if (
|
|
365
|
+
if (!ddInfo) return
|
|
360
366
|
|
|
361
367
|
params.MessageAttributes._datadog = {
|
|
362
368
|
DataType: 'String',
|
|
@@ -120,8 +120,8 @@ const extractQueueMetadata = queueURL => {
|
|
|
120
120
|
|
|
121
121
|
if (parts.length < minParts) return null
|
|
122
122
|
|
|
123
|
-
const accountId = parts
|
|
124
|
-
const queueName = parts
|
|
123
|
+
const accountId = parts.at(-2)
|
|
124
|
+
const queueName = parts.at(-1)
|
|
125
125
|
const host = hasScheme ? parts[1] : parts[0]
|
|
126
126
|
|
|
127
127
|
let region = 'us-east-1' // Default region if not found in URL
|
|
@@ -143,31 +143,9 @@ const extractQueueMetadata = queueURL => {
|
|
|
143
143
|
return { queueName, arn }
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
-
/**
|
|
147
|
-
* Returns true when `obj` has no own enumerable properties. The
|
|
148
|
-
* `for-in` loop with an early return is the only allocation-free shape
|
|
149
|
-
* for this check; benchmarks pin it as 1.3-1.4x faster than
|
|
150
|
-
* `Object.keys(obj).length === 0` across small / medium / large
|
|
151
|
-
* objects, and this is the hot path on every AWS messaging send.
|
|
152
|
-
*
|
|
153
|
-
* Callers in this package only pass plain objects they construct
|
|
154
|
-
* locally, so prototype-enumerable keys are not a concern here. Do not
|
|
155
|
-
* reuse this helper on caller-supplied objects without revisiting that
|
|
156
|
-
* assumption.
|
|
157
|
-
*
|
|
158
|
-
* @param {object} obj
|
|
159
|
-
* @returns {boolean}
|
|
160
|
-
*/
|
|
161
|
-
const isEmpty = obj => {
|
|
162
|
-
// eslint-disable-next-line no-unreachable-loop
|
|
163
|
-
for (const _ in obj) return false
|
|
164
|
-
return true
|
|
165
|
-
}
|
|
166
|
-
|
|
167
146
|
module.exports = {
|
|
168
147
|
generatePointerHash,
|
|
169
148
|
encodeValue,
|
|
170
149
|
extractPrimaryKeys,
|
|
171
150
|
extractQueueMetadata,
|
|
172
|
-
isEmpty,
|
|
173
151
|
}
|
|
@@ -438,7 +438,7 @@ class CucumberPlugin extends CiPlugin {
|
|
|
438
438
|
const isModified = isModifiedTest(
|
|
439
439
|
testScenarioPath,
|
|
440
440
|
scenario.location.line,
|
|
441
|
-
scenario.steps
|
|
441
|
+
scenario.steps.at(-1).location.line,
|
|
442
442
|
modifiedFiles,
|
|
443
443
|
'cucumber'
|
|
444
444
|
)
|
|
@@ -2,15 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
// Capture real timers at module load, before any test can install fake timers.
|
|
4
4
|
const { performance } = require('perf_hooks')
|
|
5
|
-
const { statSync } = require('node:fs')
|
|
6
5
|
const { basename } = require('node:path')
|
|
7
6
|
const dateNow = Date.now
|
|
8
7
|
|
|
9
8
|
const { createCoverageMap } = require('../../../vendor/dist/istanbul-lib-coverage')
|
|
10
9
|
const satisfies = require('../../../vendor/dist/semifies')
|
|
10
|
+
const { RUM_TEST_EXECUTION_ID_COOKIE_NAME } = require('../../dd-trace/src/ci-visibility/rum')
|
|
11
11
|
const {
|
|
12
12
|
TEST_STATUS,
|
|
13
|
-
|
|
13
|
+
setRumTestTags,
|
|
14
14
|
TEST_CODE_OWNERS,
|
|
15
15
|
getTestEnvironmentMetadata,
|
|
16
16
|
getTestLevelsMetadataTags,
|
|
@@ -79,14 +79,19 @@ const {
|
|
|
79
79
|
getMaxEfdRetryCount,
|
|
80
80
|
getPullRequestBaseBranch,
|
|
81
81
|
TEST_FINAL_STATUS,
|
|
82
|
-
TEST_FAILURE_SCREENSHOT_UPLOADED,
|
|
83
|
-
TEST_FAILURE_SCREENSHOT_UPLOAD_ERROR,
|
|
84
82
|
getTestOptimizationRequestResults,
|
|
85
83
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
86
84
|
const { isMarkedAsUnskippable } = require('../../datadog-plugin-jest/src/util')
|
|
87
85
|
const { ORIGIN_KEY, COMPONENT } = require('../../dd-trace/src/constants')
|
|
88
86
|
const { RESOURCE_NAME } = require('../../../ext/tags')
|
|
89
87
|
const getConfig = require('../../dd-trace/src/config')
|
|
88
|
+
const {
|
|
89
|
+
SCREENSHOT_UPLOAD_RESULT_ERROR,
|
|
90
|
+
SCREENSHOT_UPLOAD_RESULT_UPLOADED,
|
|
91
|
+
getScreenshotCapturedAtMs,
|
|
92
|
+
getScreenshotUploadResult,
|
|
93
|
+
setScreenshotUploadTags,
|
|
94
|
+
} = require('../../dd-trace/src/ci-visibility/test-screenshot')
|
|
90
95
|
const { appClosing: appClosingTelemetry } = require('../../dd-trace/src/telemetry')
|
|
91
96
|
const log = require('../../dd-trace/src/log')
|
|
92
97
|
|
|
@@ -141,8 +146,6 @@ const CYPRESS_STATUS_TO_TEST_STATUS = {
|
|
|
141
146
|
}
|
|
142
147
|
|
|
143
148
|
const SCREENSHOT_ATTEMPT_RE = /\(attempt \d+\)/
|
|
144
|
-
const SCREENSHOT_UPLOAD_RESULT_UPLOADED = 'uploaded'
|
|
145
|
-
const SCREENSHOT_UPLOAD_RESULT_ERROR = 'error'
|
|
146
149
|
|
|
147
150
|
function getScreenshotFilePath (screenshot) {
|
|
148
151
|
return typeof screenshot === 'string' ? screenshot : screenshot?.path
|
|
@@ -155,31 +158,6 @@ function isFailureScreenshotByMetadata (screenshot, screenshotFilePath) {
|
|
|
155
158
|
return screenshotFilePath.includes('(failed)')
|
|
156
159
|
}
|
|
157
160
|
|
|
158
|
-
/**
|
|
159
|
-
* Resolves a screenshot's capture time (epoch ms) for the media upload. Cypress
|
|
160
|
-
* screenshot objects carry an ISO `takenAt`; falls back to the file's mtime, then
|
|
161
|
-
* to the current time. Stamped once here and reused on retry via the idempotency
|
|
162
|
-
* key, so the stored object is overwritten rather than duplicated.
|
|
163
|
-
*
|
|
164
|
-
* @param {object|string} screenshot - Cypress screenshot object or path
|
|
165
|
-
* @param {string} filePath - Resolved screenshot file path
|
|
166
|
-
* @returns {number} Capture time in epoch milliseconds
|
|
167
|
-
*/
|
|
168
|
-
function getScreenshotCapturedAtMs (screenshot, filePath) {
|
|
169
|
-
const takenAt = screenshot !== null && typeof screenshot === 'object' ? screenshot.takenAt : undefined
|
|
170
|
-
if (takenAt) {
|
|
171
|
-
const parsedMs = new Date(takenAt).getTime()
|
|
172
|
-
if (Number.isInteger(parsedMs) && parsedMs > 0) {
|
|
173
|
-
return parsedMs
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
try {
|
|
177
|
-
return Math.floor(statSync(filePath).mtimeMs)
|
|
178
|
-
} catch {
|
|
179
|
-
return dateNow()
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
161
|
function isFailureScreenshotForUpload (screenshot) {
|
|
184
162
|
const screenshotFilePath = getScreenshotFilePath(screenshot)
|
|
185
163
|
if (!screenshotFilePath) {
|
|
@@ -240,27 +218,6 @@ function getTestScreenshots (cypressTest, attemptIndex, specScreenshots) {
|
|
|
240
218
|
return specScreenshots.filter(screenshot => isScreenshotForTestAttempt(screenshot, titleParts, attemptIndex))
|
|
241
219
|
}
|
|
242
220
|
|
|
243
|
-
function getScreenshotUploadResult (uploadResults) {
|
|
244
|
-
let hasUploaded = false
|
|
245
|
-
for (const uploadResult of uploadResults) {
|
|
246
|
-
if (uploadResult === SCREENSHOT_UPLOAD_RESULT_ERROR) {
|
|
247
|
-
return SCREENSHOT_UPLOAD_RESULT_ERROR
|
|
248
|
-
}
|
|
249
|
-
if (uploadResult === SCREENSHOT_UPLOAD_RESULT_UPLOADED) {
|
|
250
|
-
hasUploaded = true
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
return hasUploaded ? SCREENSHOT_UPLOAD_RESULT_UPLOADED : undefined
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function setScreenshotUploadTags (testSpan, uploadResult) {
|
|
257
|
-
if (uploadResult === SCREENSHOT_UPLOAD_RESULT_ERROR) {
|
|
258
|
-
testSpan.setTag(TEST_FAILURE_SCREENSHOT_UPLOAD_ERROR, 'true')
|
|
259
|
-
} else if (uploadResult === SCREENSHOT_UPLOAD_RESULT_UPLOADED) {
|
|
260
|
-
testSpan.setTag(TEST_FAILURE_SCREENSHOT_UPLOADED, 'true')
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
221
|
function getSessionStatus (summary) {
|
|
265
222
|
if (summary.totalFailed !== undefined && summary.totalFailed > 0) {
|
|
266
223
|
return 'fail'
|
|
@@ -901,6 +858,12 @@ class CypressPlugin {
|
|
|
901
858
|
if (isFlakyTestRetriesEnabled && this.isTestIsolationEnabled) {
|
|
902
859
|
this.isFlakyTestRetriesEnabled = true
|
|
903
860
|
this.flakyTestRetriesCount = flakyTestRetriesCount ?? 0
|
|
861
|
+
if (typeof this.cypressConfig.retries === 'number') {
|
|
862
|
+
this.cypressConfig.retries = {
|
|
863
|
+
openMode: this.cypressConfig.retries,
|
|
864
|
+
runMode: this.cypressConfig.retries,
|
|
865
|
+
}
|
|
866
|
+
}
|
|
904
867
|
this.cypressConfig.retries.runMode = this.flakyTestRetriesCount
|
|
905
868
|
} else {
|
|
906
869
|
this.flakyTestRetriesCount = 0
|
|
@@ -1444,13 +1407,13 @@ class CypressPlugin {
|
|
|
1444
1407
|
// and create a skipped test span for each of them
|
|
1445
1408
|
for (const { title } of cypressTests) {
|
|
1446
1409
|
const cypressTestName = title.join(' ')
|
|
1447
|
-
const isTestFinished = finishedTests.
|
|
1410
|
+
const isTestFinished = finishedTests.some(({ testName }) => cypressTestName === testName)
|
|
1448
1411
|
|
|
1449
1412
|
if (isTestFinished) {
|
|
1450
1413
|
continue
|
|
1451
1414
|
}
|
|
1452
1415
|
|
|
1453
|
-
const isSkippedByItr = this.testsToSkip.
|
|
1416
|
+
const isSkippedByItr = this.testsToSkip.some(test =>
|
|
1454
1417
|
cypressTestName === test.name && spec.relative === test.suite
|
|
1455
1418
|
)
|
|
1456
1419
|
const testSourceFile = spec.absolute && this.repositoryRoot
|
|
@@ -1721,6 +1684,7 @@ class CypressPlugin {
|
|
|
1721
1684
|
repositoryRoot: this.repositoryRoot,
|
|
1722
1685
|
isTestIsolationEnabled: this.isTestIsolationEnabled,
|
|
1723
1686
|
rumFlushWaitMillis: this.rumFlushWaitMillis,
|
|
1687
|
+
rumTestExecutionIdCookieName: RUM_TEST_EXECUTION_ID_COOKIE_NAME,
|
|
1724
1688
|
}
|
|
1725
1689
|
|
|
1726
1690
|
this.testSuiteSpan ||= this.getTestSuiteSpan({ testSuite, testSuiteAbsolutePath })
|
|
@@ -1835,21 +1799,19 @@ class CypressPlugin {
|
|
|
1835
1799
|
}
|
|
1836
1800
|
this.activeTestSpan.setTag(TEST_STATUS, testStatus)
|
|
1837
1801
|
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1802
|
+
const testIdentifier = `${testSuite}\0${testName}`
|
|
1803
|
+
let testStatuses = this.testStatuses[testIdentifier]
|
|
1804
|
+
if (testStatuses) {
|
|
1805
|
+
testStatuses.push(testStatus)
|
|
1841
1806
|
} else {
|
|
1842
|
-
this.testStatuses[
|
|
1807
|
+
testStatuses = this.testStatuses[testIdentifier] = [testStatus]
|
|
1843
1808
|
}
|
|
1844
|
-
const testStatuses = this.testStatuses[testName]
|
|
1845
1809
|
const activeSpanTags = this.activeTestSpan.context().getTags()
|
|
1846
1810
|
|
|
1847
1811
|
if (error) {
|
|
1848
1812
|
this.activeTestSpan.setTag('error', error)
|
|
1849
1813
|
}
|
|
1850
|
-
|
|
1851
|
-
this.activeTestSpan.setTag(TEST_IS_RUM_ACTIVE, 'true')
|
|
1852
|
-
}
|
|
1814
|
+
setRumTestTags(this.activeTestSpan, isRUMActive)
|
|
1853
1815
|
// Source-line resolution strategy:
|
|
1854
1816
|
// 1. If plain JS and no source map, trust invocationDetails.line directly.
|
|
1855
1817
|
// 2. Otherwise, try invocationDetails.stack line mapped through source map.
|
|
@@ -80,7 +80,12 @@ class CypressPlugin extends Plugin {
|
|
|
80
80
|
// Already initialized by manual plugin call — just chain user handlers.
|
|
81
81
|
// Pass the plugin's afterScreenshot so chaining a user handler doesn't drop the upload
|
|
82
82
|
// (the chained registration replaces the one plugin.js set, so it must include it).
|
|
83
|
-
|
|
83
|
+
if (userAfterSpecHandlers.length > 0) {
|
|
84
|
+
on('after:spec', (spec, results) => userAfterSpecHandlers.reduce(
|
|
85
|
+
(chain, handler) => chain.then(() => handler(spec, results)),
|
|
86
|
+
Promise.resolve()
|
|
87
|
+
))
|
|
88
|
+
}
|
|
84
89
|
registerAfterScreenshot(datadogAfterScreenshotHandler)
|
|
85
90
|
registerAfterRunWithCleanup()
|
|
86
91
|
payload.registered = true
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const DD_CIVISIBILITY_TEST_EXECUTION_ID_COOKIE_NAME = 'datadog-ci-visibility-test-execution-id'
|
|
4
3
|
let rumFlushWaitMillis = 500
|
|
4
|
+
let rumTestExecutionIdCookieName
|
|
5
5
|
|
|
6
6
|
let isEarlyFlakeDetectionEnabled = false
|
|
7
7
|
let isKnownTestsEnabled = false
|
|
@@ -129,6 +129,89 @@ function runBeforeEachTask (test) {
|
|
|
129
129
|
})
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
/**
|
|
133
|
+
* @param {string} message
|
|
134
|
+
* @param {unknown} error
|
|
135
|
+
* @returns {false}
|
|
136
|
+
*/
|
|
137
|
+
function logRumCorrelationCookieError (message, error) {
|
|
138
|
+
try {
|
|
139
|
+
Cypress.log({
|
|
140
|
+
name: 'dd-trace',
|
|
141
|
+
message,
|
|
142
|
+
consoleProps: () => ({ Error: error }),
|
|
143
|
+
})
|
|
144
|
+
} catch (loggingError) {
|
|
145
|
+
// eslint-disable-next-line no-console
|
|
146
|
+
console.error(message, error, loggingError)
|
|
147
|
+
}
|
|
148
|
+
return false
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* @param {unknown} error
|
|
153
|
+
* @returns {false}
|
|
154
|
+
*/
|
|
155
|
+
function handleRumCorrelationCookieError (error) {
|
|
156
|
+
return logRumCorrelationCookieError('Could not set the RUM correlation cookie', error)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* @param {unknown} error
|
|
161
|
+
* @returns {false}
|
|
162
|
+
*/
|
|
163
|
+
function handleRumCorrelationCookieCleanupError (error) {
|
|
164
|
+
return logRumCorrelationCookieError('Could not clear the previous RUM correlation cookie', error)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @param {string} traceId
|
|
169
|
+
* @returns {Promise<boolean>}
|
|
170
|
+
*/
|
|
171
|
+
function setRumCorrelationCookie (traceId) {
|
|
172
|
+
if (typeof cy.now !== 'function') {
|
|
173
|
+
return Cypress.Promise.resolve(handleRumCorrelationCookieError(new Error('Cypress cy.now is not available')))
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
let clearCookiePromise = Cypress.Promise.resolve()
|
|
177
|
+
if (!isTestIsolationEnabled) {
|
|
178
|
+
clearCookiePromise = Cypress.Promise.try(() => {
|
|
179
|
+
return cy.now('clearCookie', rumTestExecutionIdCookieName, { log: false })
|
|
180
|
+
}).then(undefined, handleRumCorrelationCookieCleanupError)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return clearCookiePromise.then(() => {
|
|
184
|
+
return Cypress.Promise.try(() => {
|
|
185
|
+
return cy.now('setCookie', rumTestExecutionIdCookieName, traceId, { log: false })
|
|
186
|
+
})
|
|
187
|
+
}).then(() => true, handleRumCorrelationCookieError)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* @param {boolean} isCookieSet
|
|
192
|
+
* @returns {void}
|
|
193
|
+
*/
|
|
194
|
+
function restartRumSession (isCookieSet) {
|
|
195
|
+
if (!isCookieSet || isTestIsolationEnabled || !originalWindow) {
|
|
196
|
+
return
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const rum = safeGetRum(originalWindow)
|
|
200
|
+
if (rum) {
|
|
201
|
+
try {
|
|
202
|
+
const evt = new originalWindow.MouseEvent('click', { bubbles: true, cancelable: true })
|
|
203
|
+
// The browser-sdk addEventListener wrapper filters out untrusted synthetic events
|
|
204
|
+
// unless __ddIsTrusted is set. Set it so the click triggers expandOrRenewSession().
|
|
205
|
+
// See: https://github.com/DataDog/browser-sdk/blob/v6.27.1/packages/core/src/browser/addEventListener.ts#L119
|
|
206
|
+
Object.defineProperty(evt, '__ddIsTrusted', { value: true })
|
|
207
|
+
originalWindow.dispatchEvent(evt)
|
|
208
|
+
} catch {}
|
|
209
|
+
if (rum.startView) {
|
|
210
|
+
rum.startView()
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
132
215
|
// Catch test failures for quarantined tests and suppress them
|
|
133
216
|
// By not re-throwing the error, Cypress marks the test as passed
|
|
134
217
|
// This allows quarantined tests to run but not affect the exit code
|
|
@@ -150,6 +233,8 @@ Cypress.on('fail', (err, runnable) => {
|
|
|
150
233
|
// If command:end fired for all commands (none in-flight) but the last command
|
|
151
234
|
// has no error, it means command:end fired before the error was attached to it.
|
|
152
235
|
if (!hadInFlightCommands && currentTestCommands.length > 0) {
|
|
236
|
+
// We have to support very old cypress versions in v5
|
|
237
|
+
// eslint-disable-next-line unicorn/prefer-at
|
|
153
238
|
const lastCommand = currentTestCommands[currentTestCommands.length - 1]
|
|
154
239
|
if (!lastCommand.error) {
|
|
155
240
|
lastCommand.error = { message: err.message, stack: err.stack, name: err.name }
|
|
@@ -316,36 +401,16 @@ beforeEach(function () {
|
|
|
316
401
|
if (shouldDiscard) {
|
|
317
402
|
this.currentTest._ddShouldDiscard = true
|
|
318
403
|
}
|
|
404
|
+
let rumCookiePromise
|
|
319
405
|
if (traceId) {
|
|
320
|
-
|
|
321
|
-
// When testIsolation:false, the page is not reset between tests, so the RUM session
|
|
322
|
-
// stopped in afterEach must be explicitly restarted so events in this test are
|
|
323
|
-
// associated with the new testExecutionId.
|
|
324
|
-
//
|
|
325
|
-
// After stopSession(), the RUM SDK creates a new session upon a user interaction
|
|
326
|
-
// (click, scroll, keydown, or touchstart). We dispatch a synthetic click on the window
|
|
327
|
-
// to trigger session renewal, then call startView() to establish a view boundary.
|
|
328
|
-
if (!isTestIsolationEnabled && originalWindow) {
|
|
329
|
-
const rum = safeGetRum(originalWindow)
|
|
330
|
-
if (rum) {
|
|
331
|
-
try {
|
|
332
|
-
const evt = new originalWindow.MouseEvent('click', { bubbles: true, cancelable: true })
|
|
333
|
-
// The browser-sdk addEventListener wrapper filters out untrusted synthetic events
|
|
334
|
-
// unless __ddIsTrusted is set. Set it so the click triggers expandOrRenewSession().
|
|
335
|
-
// See: https://github.com/DataDog/browser-sdk/blob/v6.27.1/packages/core/src/browser/addEventListener.ts#L119
|
|
336
|
-
Object.defineProperty(evt, '__ddIsTrusted', { value: true })
|
|
337
|
-
originalWindow.dispatchEvent(evt)
|
|
338
|
-
} catch {}
|
|
339
|
-
if (rum.startView) {
|
|
340
|
-
rum.startView()
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
})
|
|
406
|
+
rumCookiePromise = setRumCorrelationCookie(traceId)
|
|
345
407
|
}
|
|
346
408
|
if (shouldSkip) {
|
|
347
409
|
this.skip()
|
|
348
410
|
}
|
|
411
|
+
if (rumCookiePromise) {
|
|
412
|
+
return rumCookiePromise.then(restartRumSession)
|
|
413
|
+
}
|
|
349
414
|
}).then(() => {
|
|
350
415
|
// Clear any commands accumulated during DD-owned setup (e.g. setCookie, RUM restart)
|
|
351
416
|
// so they are not reported as user test steps.
|
|
@@ -370,6 +435,7 @@ before(function () {
|
|
|
370
435
|
isImpactedTestsEnabled = suiteConfig.isImpactedTestsEnabled
|
|
371
436
|
isModifiedTest = suiteConfig.isModifiedTest
|
|
372
437
|
isTestIsolationEnabled = suiteConfig.isTestIsolationEnabled
|
|
438
|
+
rumTestExecutionIdCookieName = suiteConfig.rumTestExecutionIdCookieName
|
|
373
439
|
if (Number.isFinite(suiteConfig.rumFlushWaitMillis)) {
|
|
374
440
|
rumFlushWaitMillis = suiteConfig.rumFlushWaitMillis
|
|
375
441
|
}
|
|
@@ -46,7 +46,7 @@ class ElectronRendererReceivePlugin extends ConsumerPlugin {
|
|
|
46
46
|
|
|
47
47
|
if (channel?.startsWith('datadog:')) return
|
|
48
48
|
|
|
49
|
-
const childOf = this._tracer.extract('text_map', args
|
|
49
|
+
const childOf = this._tracer.extract('text_map', args.at(-1))
|
|
50
50
|
|
|
51
51
|
if (childOf) {
|
|
52
52
|
args.pop()
|