dd-trace 6.7.0 → 6.9.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 +27 -25
- package/ci/init.js +6 -6
- package/ci/runbook.md +114 -20
- package/ci/test-optimization-validation/approval-artifacts.js +36 -3
- package/ci/test-optimization-validation/approval.js +78 -22
- package/ci/test-optimization-validation/blocker-category.js +24 -0
- package/ci/test-optimization-validation/ci-discovery.js +23 -17
- package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
- package/ci/test-optimization-validation/ci-remediation.js +1 -1
- package/ci/test-optimization-validation/cli.js +280 -52
- package/ci/test-optimization-validation/command-blocker.js +159 -20
- package/ci/test-optimization-validation/command-output-policy.js +14 -8
- package/ci/test-optimization-validation/command-runner.js +9 -7
- package/ci/test-optimization-validation/environment.js +4 -2
- package/ci/test-optimization-validation/executable.js +15 -13
- package/ci/test-optimization-validation/execution-lock.js +70 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
- package/ci/test-optimization-validation/framework-adapters/playwright.js +5 -5
- package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
- package/ci/test-optimization-validation/generated-files.js +47 -37
- package/ci/test-optimization-validation/generated-test-contract.js +9 -7
- package/ci/test-optimization-validation/generated-verifier.js +24 -17
- package/ci/test-optimization-validation/literal-glob.js +52 -0
- package/ci/test-optimization-validation/manifest-loader.js +3 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +301 -52
- package/ci/test-optimization-validation/manifest-schema.js +80 -17
- package/ci/test-optimization-validation/offline-fixtures.js +13 -4
- package/ci/test-optimization-validation/offline-output.js +6 -6
- package/ci/test-optimization-validation/package-check.js +94 -0
- package/ci/test-optimization-validation/plan-writer.js +183 -10
- package/ci/test-optimization-validation/preflight-runner.js +110 -31
- package/ci/test-optimization-validation/project-build-artifact.js +31 -0
- package/ci/test-optimization-validation/redaction.js +18 -27
- package/ci/test-optimization-validation/report-writer.js +182 -48
- package/ci/test-optimization-validation/result-semantics.js +20 -3
- package/ci/test-optimization-validation/runner-command.js +41 -21
- package/ci/test-optimization-validation/runner-contract.js +79 -43
- package/ci/test-optimization-validation/safe-files.js +4 -4
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +60 -6
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
- package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
- package/ci/test-optimization-validation/scenarios/test-management.js +2 -2
- package/ci/test-optimization-validation/static-diagnosis.js +10 -3
- package/ci/test-optimization-validation/test-output.js +2 -1
- package/ci/test-optimization-validation/validation-blocker.js +21 -0
- package/ci/vitest-no-worker-init-setup.mjs +377 -84
- package/index.d.ts +175 -14
- package/init.js +1 -17
- package/initialize.mjs +11 -0
- package/loader-hook.mjs +6 -4
- package/package.json +12 -4
- package/packages/datadog-core/src/utils/src/kebabcase.js +8 -2
- package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
- package/packages/datadog-esbuild/index.js +4 -2
- package/packages/datadog-esbuild/src/utils.js +17 -2
- package/packages/datadog-instrumentations/src/ai.js +61 -34
- package/packages/datadog-instrumentations/src/bluebird.js +6 -6
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +85 -9
- package/packages/datadog-instrumentations/src/cucumber.js +131 -51
- package/packages/datadog-instrumentations/src/dns.js +21 -2
- package/packages/datadog-instrumentations/src/electron.js +1 -0
- package/packages/datadog-instrumentations/src/fastify.js +3 -1
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
- package/packages/datadog-instrumentations/src/helpers/extract-package-and-module-path.js +10 -0
- package/packages/datadog-instrumentations/src/helpers/hook.js +19 -2
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +9 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +34 -9
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +175 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +50 -18
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +38 -7
- package/packages/datadog-instrumentations/src/helpers/shared-utils.js +39 -0
- package/packages/datadog-instrumentations/src/http/client.js +1 -3
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +986 -246
- package/packages/datadog-instrumentations/src/mocha/common.js +1 -3
- package/packages/datadog-instrumentations/src/mocha/main.js +109 -51
- package/packages/datadog-instrumentations/src/mocha/utils.js +120 -32
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +44 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +441 -43
- package/packages/datadog-instrumentations/src/moleculer/client.js +1 -1
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +155 -23
- package/packages/datadog-instrumentations/src/openai.js +14 -0
- package/packages/datadog-instrumentations/src/pg.js +180 -6
- package/packages/datadog-instrumentations/src/playwright.js +176 -81
- package/packages/datadog-instrumentations/src/promise-js.js +4 -4
- package/packages/datadog-instrumentations/src/promise.js +3 -3
- package/packages/datadog-instrumentations/src/rhea.js +1 -1
- package/packages/datadog-instrumentations/src/router.js +195 -19
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +567 -65
- package/packages/datadog-instrumentations/src/vitest-main.js +438 -54
- package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
- package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
- package/packages/datadog-instrumentations/src/webdriverio.js +1090 -0
- package/packages/datadog-instrumentations/src/when.js +3 -3
- package/packages/datadog-instrumentations/src/ws.js +5 -1
- package/packages/datadog-plugin-aerospike/src/index.js +1 -3
- package/packages/datadog-plugin-amqplib/src/client.js +1 -1
- package/packages/datadog-plugin-amqplib/src/producer.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +2 -2
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -3
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +5 -3
- package/packages/datadog-plugin-bullmq/src/producer.js +3 -3
- package/packages/datadog-plugin-child_process/src/index.js +3 -3
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -11
- package/packages/datadog-plugin-cucumber/src/index.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -1
- package/packages/datadog-plugin-cypress/src/support.js +7 -4
- package/packages/datadog-plugin-electron/src/net.js +10 -4
- package/packages/datadog-plugin-fastify/src/tracing.js +1 -1
- package/packages/datadog-plugin-fs/src/index.js +0 -4
- package/packages/datadog-plugin-google-cloud-pubsub/src/consumer.js +14 -7
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +19 -10
- package/packages/datadog-plugin-graphql/src/execute.js +1 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -1
- package/packages/datadog-plugin-http/src/client.js +3 -2
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +3 -21
- package/packages/datadog-plugin-jest/src/util.js +4 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
- package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +342 -6
- package/packages/datadog-plugin-next/src/index.js +57 -9
- package/packages/datadog-plugin-openai/src/tracing.js +4 -0
- package/packages/datadog-plugin-pg/src/index.js +65 -1
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-prisma/src/index.js +5 -1
- package/packages/datadog-plugin-rhea/src/consumer.js +16 -13
- package/packages/datadog-plugin-rhea/src/producer.js +1 -1
- package/packages/datadog-plugin-undici/src/index.js +2 -1
- package/packages/datadog-plugin-vitest/src/index.js +179 -18
- package/packages/datadog-shimmer/src/shimmer.js +6 -2
- package/packages/datadog-webpack/index.js +2 -2
- package/packages/dd-trace/src/aiguard/sdk.js +3 -0
- package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -1
- package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
- package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
- package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
- package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-base-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-password-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/sql-injection-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +1 -3
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +2 -2
- package/packages/dd-trace/src/appsec/index.js +48 -476
- package/packages/dd-trace/src/appsec/sdk/track_event.js +10 -1
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
- package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
- package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/sink.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +29 -15
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +19 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +25 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/webdriverio.js +26 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +9 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +2 -2
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
- package/packages/dd-trace/src/config/defaults.js +14 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -2
- package/packages/dd-trace/src/config/helper.js +2 -0
- package/packages/dd-trace/src/config/index.js +31 -5
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +13 -6
- package/packages/dd-trace/src/datastreams/encoding.js +3 -4
- package/packages/dd-trace/src/debugger/devtools_client/log.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +1 -4
- package/packages/dd-trace/src/debugger/devtools_client/send.js +1 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/index.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/redaction.js +16 -2
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
- package/packages/dd-trace/src/encode/tags-processors.js +2 -2
- package/packages/dd-trace/src/exporters/common/url.js +3 -5
- package/packages/dd-trace/src/exporters/electron/index.js +4 -1
- package/packages/dd-trace/src/external-logger/src/index.js +1 -2
- package/packages/dd-trace/src/flare/index.js +2 -2
- package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
- package/packages/dd-trace/src/guardrails/log.js +1 -10
- package/packages/dd-trace/src/id.js +1 -1
- package/packages/dd-trace/src/llmobs/constants/tags.js +1 -1
- package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +4 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +66 -14
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +289 -95
- package/packages/dd-trace/src/llmobs/experiments/index.js +66 -19
- package/packages/dd-trace/src/llmobs/experiments/noop.js +95 -10
- package/packages/dd-trace/src/llmobs/experiments/result.js +13 -2
- package/packages/dd-trace/src/llmobs/experiments/util.js +220 -0
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +152 -78
- package/packages/dd-trace/src/llmobs/span_processor.js +9 -7
- package/packages/dd-trace/src/llmobs/tagger.js +1 -2
- package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
- package/packages/dd-trace/src/llmobs/writers/spans.js +12 -6
- package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
- package/packages/dd-trace/src/log/channels.js +1 -9
- package/packages/dd-trace/src/log/levels.js +12 -0
- package/packages/dd-trace/src/noop/proxy.js +2 -2
- package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
- package/packages/dd-trace/src/opentelemetry/span.js +4 -0
- package/packages/dd-trace/src/opentracing/span.js +1 -1
- package/packages/dd-trace/src/opentracing/span_context.js +1 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +124 -23
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/plugin.js +1 -1
- package/packages/dd-trace/src/plugins/tracing.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +13 -6
- package/packages/dd-trace/src/plugins/util/git.js +7 -5
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +3 -3
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/llm.js +2 -2
- package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
- package/packages/dd-trace/src/plugins/util/tags.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +356 -86
- package/packages/dd-trace/src/plugins/util/url.js +5 -5
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +24 -2
- package/packages/dd-trace/src/priority_sampler.js +6 -3
- package/packages/dd-trace/src/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/poisson.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/shared.js +2 -2
- package/packages/dd-trace/src/proxy.js +1 -0
- package/packages/dd-trace/src/ritm.js +10 -20
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
- package/packages/dd-trace/src/sampling_rule.js +4 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
- package/packages/dd-trace/src/startup-log.js +3 -0
- package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
- package/packages/dd-trace/src/util.js +16 -0
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +12 -12
- package/vendor/dist/pprof-format/index.js +1 -1
- package/vendor/dist/source-map/index.js +1 -1
- package/version.js +10 -8
- /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const fs = require('fs')
|
|
5
5
|
const { URL } = require('url')
|
|
6
|
+
const { stripVTControlCharacters } = require('node:util')
|
|
6
7
|
const { getLageTestSessionName } = require('../../ci-visibility/lage')
|
|
7
8
|
const log = require('../../log')
|
|
8
|
-
const { getEnvironmentVariable } = require('../../config/helper')
|
|
9
|
+
const { getEnvironmentVariable, getValueFromEnvSources } = require('../../config/helper')
|
|
9
10
|
const { getSegment } = require('../../util')
|
|
10
11
|
const satisfies = require('../../../../../vendor/dist/semifies')
|
|
11
12
|
|
|
@@ -45,6 +46,7 @@ const {
|
|
|
45
46
|
CI_WORKSPACE_PATH,
|
|
46
47
|
CI_PIPELINE_ID,
|
|
47
48
|
CI_PIPELINE_NAME,
|
|
49
|
+
CI_PIPELINE_DISPLAY_NAME,
|
|
48
50
|
CI_PIPELINE_NUMBER,
|
|
49
51
|
CI_PIPELINE_URL,
|
|
50
52
|
CI_JOB_NAME,
|
|
@@ -89,10 +91,12 @@ const {
|
|
|
89
91
|
const TEST_SESSION_NAME = 'test_session.name'
|
|
90
92
|
|
|
91
93
|
const TEST_FRAMEWORK = 'test.framework'
|
|
94
|
+
const TEST_FRAMEWORK_ADAPTER = 'test.framework_adapter'
|
|
92
95
|
const TEST_FRAMEWORK_VERSION = 'test.framework_version'
|
|
93
96
|
const TEST_TYPE = 'test.type'
|
|
94
97
|
const TEST_NAME = 'test.name'
|
|
95
98
|
const TEST_SUITE = 'test.suite'
|
|
99
|
+
const TEST_SUITE_EXECUTION_ID = '_dd.test_suite_execution_id'
|
|
96
100
|
const TEST_STATUS = 'test.status'
|
|
97
101
|
const TEST_FINAL_STATUS = 'test.final_status'
|
|
98
102
|
const TEST_PARAMETERS = 'test.parameters'
|
|
@@ -120,13 +124,10 @@ const TEST_RETRY_REASON = 'test.retry_reason'
|
|
|
120
124
|
const TEST_HAS_FAILED_ALL_RETRIES = 'test.has_failed_all_retries'
|
|
121
125
|
const TEST_IS_MODIFIED = 'test.is_modified'
|
|
122
126
|
const TEST_HAS_DYNAMIC_NAME = '_dd.has_dynamic_name'
|
|
123
|
-
const EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS = [
|
|
124
|
-
{ limitMs: 5 * 1000, key: '5s' },
|
|
125
|
-
{ limitMs: 10 * 1000, key: '10s' },
|
|
126
|
-
{ limitMs: 30 * 1000, key: '30s' },
|
|
127
|
-
{ limitMs: 5 * 60 * 1000, key: '5m' },
|
|
128
|
-
]
|
|
129
127
|
const CI_APP_ORIGIN = 'ciapp-test'
|
|
128
|
+
// eslint-disable-next-line no-control-regex
|
|
129
|
+
const TEST_OPTIMIZATION_NAME_CONTROL_RE = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F-\u009F]/g
|
|
130
|
+
const TEST_OPTIMIZATION_NAME_WHITESPACE_RE = /\s+/g
|
|
130
131
|
|
|
131
132
|
// Matches patterns that are almost certainly runtime-generated values in test names:
|
|
132
133
|
// - Unix timestamps in ms (13 digits, years ~2020-2090) or s (10 digits)
|
|
@@ -159,6 +160,7 @@ const TEST_CODE_COVERAGE_ENABLED = 'test.code_coverage.enabled'
|
|
|
159
160
|
const TEST_ITR_UNSKIPPABLE = 'test.itr.unskippable'
|
|
160
161
|
const TEST_ITR_FORCED_RUN = 'test.itr.forced_run'
|
|
161
162
|
const ITR_CORRELATION_ID = 'itr_correlation_id'
|
|
163
|
+
const TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE = 'Test Impact Analysis skipped all tests.'
|
|
162
164
|
|
|
163
165
|
const TEST_CODE_COVERAGE_LINES_PCT = 'test.code_coverage.lines_pct'
|
|
164
166
|
|
|
@@ -177,16 +179,22 @@ const JEST_WORKER_QUARANTINE_PAYLOAD_CODE = 64
|
|
|
177
179
|
|
|
178
180
|
// cucumber worker variables
|
|
179
181
|
const CUCUMBER_WORKER_TRACE_PAYLOAD_CODE = 70
|
|
182
|
+
const CUCUMBER_WORKER_TELEMETRY_PAYLOAD_CODE = 71
|
|
180
183
|
|
|
181
184
|
// mocha worker variables
|
|
182
185
|
const MOCHA_WORKER_TRACE_PAYLOAD_CODE = 80
|
|
186
|
+
const MOCHA_WORKER_LOGS_PAYLOAD_CODE = 81
|
|
187
|
+
const MOCHA_WORKER_TELEMETRY_PAYLOAD_CODE = 82
|
|
183
188
|
|
|
184
189
|
// playwright worker variables
|
|
185
190
|
const PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE = 90
|
|
191
|
+
const PLAYWRIGHT_WORKER_TELEMETRY_PAYLOAD_CODE = 91
|
|
186
192
|
|
|
187
193
|
// vitest worker variables
|
|
188
194
|
const VITEST_WORKER_TRACE_PAYLOAD_CODE = 100
|
|
195
|
+
const VITEST_WORKER_COVERAGE_PAYLOAD_CODE = 101
|
|
189
196
|
const VITEST_WORKER_LOGS_PAYLOAD_CODE = 102
|
|
197
|
+
const VITEST_WORKER_TELEMETRY_PAYLOAD_CODE = 103
|
|
190
198
|
|
|
191
199
|
const TEST_IS_TEST_FRAMEWORK_WORKER = 'test.is_test_framework_worker'
|
|
192
200
|
|
|
@@ -207,7 +215,7 @@ const DD_CI_LIBRARY_CONFIGURATION_ERROR_KNOWN_TESTS = '_dd.ci.library_configurat
|
|
|
207
215
|
const DD_CI_LIBRARY_CONFIGURATION_ERROR_TEST_MANAGEMENT_TESTS =
|
|
208
216
|
'_dd.ci.library_configuration_error.test_management_tests'
|
|
209
217
|
|
|
210
|
-
const UNSUPPORTED_TIA_FRAMEWORKS = new Set(['playwright', '
|
|
218
|
+
const UNSUPPORTED_TIA_FRAMEWORKS = new Set(['playwright', 'webdriverio'])
|
|
211
219
|
const MINIMUM_FRAMEWORK_VERSION_FOR_EFD = {
|
|
212
220
|
playwright: '>=1.38.0',
|
|
213
221
|
}
|
|
@@ -244,6 +252,7 @@ const TEST_LEVELS_METADATA_TAGS = [
|
|
|
244
252
|
CI_NODE_NAME,
|
|
245
253
|
CI_PIPELINE_ID,
|
|
246
254
|
CI_PIPELINE_NAME,
|
|
255
|
+
CI_PIPELINE_DISPLAY_NAME,
|
|
247
256
|
CI_PIPELINE_NUMBER,
|
|
248
257
|
CI_PIPELINE_URL,
|
|
249
258
|
CI_PROVIDER_NAME,
|
|
@@ -295,7 +304,10 @@ const TEST_MANAGEMENT_IS_QUARANTINED = 'test.test_management.is_quarantined'
|
|
|
295
304
|
const TEST_MANAGEMENT_ENABLED = 'test.test_management.enabled'
|
|
296
305
|
const TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED = 'test.test_management.attempt_to_fix_passed'
|
|
297
306
|
|
|
307
|
+
const MAX_TEST_MANAGEMENT_REPORT_ITEMS = 5
|
|
308
|
+
const MAX_TEST_OPTIMIZATION_NAME_LENGTH = 160
|
|
298
309
|
const MAX_TEST_OPTIMIZATION_SUMMARY_ITEMS = 10
|
|
310
|
+
const testManagementExecutions = new Map()
|
|
299
311
|
|
|
300
312
|
// Impacted tests
|
|
301
313
|
const POSSIBLE_BASE_BRANCHES = ['main', 'master', 'preprod', 'prod', 'dev', 'development', 'trunk']
|
|
@@ -422,10 +434,22 @@ function addTestOptimizationRequest (requestPromises, responseNames, responseNam
|
|
|
422
434
|
}
|
|
423
435
|
}
|
|
424
436
|
|
|
437
|
+
/**
|
|
438
|
+
* Builds the internal key used to correlate a worker test with one suite execution.
|
|
439
|
+
*
|
|
440
|
+
* @param {string} testSuite
|
|
441
|
+
* @param {string|undefined} testSuiteExecutionId
|
|
442
|
+
* @returns {string}
|
|
443
|
+
*/
|
|
444
|
+
function getTestSuiteExecutionKey (testSuite, testSuiteExecutionId) {
|
|
445
|
+
return testSuiteExecutionId ? `${testSuite}\0${testSuiteExecutionId}` : testSuite
|
|
446
|
+
}
|
|
447
|
+
|
|
425
448
|
module.exports = {
|
|
426
449
|
TEST_CODE_OWNERS,
|
|
427
450
|
TEST_SESSION_NAME,
|
|
428
451
|
TEST_FRAMEWORK,
|
|
452
|
+
TEST_FRAMEWORK_ADAPTER,
|
|
429
453
|
TEST_FRAMEWORK_VERSION,
|
|
430
454
|
JEST_TEST_RUNNER,
|
|
431
455
|
JEST_DISPLAY_NAME,
|
|
@@ -435,6 +459,8 @@ module.exports = {
|
|
|
435
459
|
TEST_TYPE,
|
|
436
460
|
TEST_NAME,
|
|
437
461
|
TEST_SUITE,
|
|
462
|
+
TEST_SUITE_EXECUTION_ID,
|
|
463
|
+
getTestSuiteExecutionKey,
|
|
438
464
|
TEST_STATUS,
|
|
439
465
|
TEST_FINAL_STATUS,
|
|
440
466
|
TEST_PARAMETERS,
|
|
@@ -453,10 +479,16 @@ module.exports = {
|
|
|
453
479
|
JEST_WORKER_TELEMETRY_PAYLOAD_CODE,
|
|
454
480
|
JEST_WORKER_QUARANTINE_PAYLOAD_CODE,
|
|
455
481
|
CUCUMBER_WORKER_TRACE_PAYLOAD_CODE,
|
|
482
|
+
CUCUMBER_WORKER_TELEMETRY_PAYLOAD_CODE,
|
|
456
483
|
MOCHA_WORKER_TRACE_PAYLOAD_CODE,
|
|
484
|
+
MOCHA_WORKER_LOGS_PAYLOAD_CODE,
|
|
485
|
+
MOCHA_WORKER_TELEMETRY_PAYLOAD_CODE,
|
|
457
486
|
PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE,
|
|
487
|
+
PLAYWRIGHT_WORKER_TELEMETRY_PAYLOAD_CODE,
|
|
458
488
|
VITEST_WORKER_TRACE_PAYLOAD_CODE,
|
|
489
|
+
VITEST_WORKER_COVERAGE_PAYLOAD_CODE,
|
|
459
490
|
VITEST_WORKER_LOGS_PAYLOAD_CODE,
|
|
491
|
+
VITEST_WORKER_TELEMETRY_PAYLOAD_CODE,
|
|
460
492
|
TEST_IS_TEST_FRAMEWORK_WORKER,
|
|
461
493
|
TEST_SOURCE_START,
|
|
462
494
|
TEST_SKIPPED_BY_ITR,
|
|
@@ -495,6 +527,7 @@ module.exports = {
|
|
|
495
527
|
TEST_ITR_UNSKIPPABLE,
|
|
496
528
|
TEST_ITR_FORCED_RUN,
|
|
497
529
|
ITR_CORRELATION_ID,
|
|
530
|
+
TEST_IMPACT_ANALYSIS_ALL_TESTS_SKIPPED_MESSAGE,
|
|
498
531
|
addIntelligentTestRunnerSpanTags,
|
|
499
532
|
getCoveredFilenamesFromCoverage,
|
|
500
533
|
getCoveredFilesFromCoverage,
|
|
@@ -511,9 +544,6 @@ module.exports = {
|
|
|
511
544
|
removeInvalidMetadata,
|
|
512
545
|
parseAnnotations,
|
|
513
546
|
getIsFaultyEarlyFlakeDetection,
|
|
514
|
-
EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS,
|
|
515
|
-
getEfdRetryCount,
|
|
516
|
-
getMaxEfdRetryCount,
|
|
517
547
|
TEST_BROWSER_DRIVER,
|
|
518
548
|
TEST_BROWSER_DRIVER_VERSION,
|
|
519
549
|
TEST_BROWSER_NAME,
|
|
@@ -564,8 +594,10 @@ module.exports = {
|
|
|
564
594
|
collectDynamicNamesFromTraces,
|
|
565
595
|
collectTestOptimizationSummariesFromTraces,
|
|
566
596
|
logDynamicNamesWarning,
|
|
597
|
+
recordTestManagementExecution,
|
|
567
598
|
recordAttemptToFixExecution,
|
|
568
599
|
collectAttemptToFixExecutionsFromTraces,
|
|
600
|
+
formatTestManagementSummary,
|
|
569
601
|
formatAttemptToFixSummary,
|
|
570
602
|
logAttemptToFixTestExecution,
|
|
571
603
|
formatDynamicNamesSummary,
|
|
@@ -712,6 +744,7 @@ function checkShaDiscrepancies (ciMetadata, userProvidedGitMetadata) {
|
|
|
712
744
|
*
|
|
713
745
|
* @param {string=} testFramework
|
|
714
746
|
* @param {TestEnvironmentConfig=} config
|
|
747
|
+
* @param {boolean=} shouldSkipGitMetadataExtraction
|
|
715
748
|
* @returns {TestEnvironmentMetadata}
|
|
716
749
|
*/
|
|
717
750
|
function getTestEnvironmentMetadata (testFramework, config, shouldSkipGitMetadataExtraction = false) {
|
|
@@ -800,7 +833,7 @@ function getTestLevelsMetadataTags (testEnvironmentMetadata) {
|
|
|
800
833
|
}
|
|
801
834
|
|
|
802
835
|
function getTestTypeFromFramework (testFramework) {
|
|
803
|
-
if (testFramework === 'playwright' || testFramework === 'cypress') {
|
|
836
|
+
if (testFramework === 'playwright' || testFramework === 'cypress' || testFramework === 'webdriverio') {
|
|
804
837
|
return 'browser'
|
|
805
838
|
}
|
|
806
839
|
return 'test'
|
|
@@ -860,9 +893,16 @@ function setRumTestCorrelation (context, activeSpan) {
|
|
|
860
893
|
return testSpan
|
|
861
894
|
}
|
|
862
895
|
|
|
863
|
-
|
|
896
|
+
/**
|
|
897
|
+
* Returns the synthetic parent that makes a test event the root of its trace.
|
|
898
|
+
*
|
|
899
|
+
* @param {import('../../tracer')} tracer
|
|
900
|
+
* @param {string} [testExecutionId]
|
|
901
|
+
* @returns {import('../../opentracing/span_context')}
|
|
902
|
+
*/
|
|
903
|
+
function getTestParentSpan (tracer, testExecutionId) {
|
|
864
904
|
return tracer.extract('text_map', {
|
|
865
|
-
'x-datadog-trace-id': id().toString(10),
|
|
905
|
+
'x-datadog-trace-id': testExecutionId || id().toString(10),
|
|
866
906
|
'x-datadog-parent-id': '0000000000000000',
|
|
867
907
|
})
|
|
868
908
|
}
|
|
@@ -871,6 +911,7 @@ function getTestCommonTags (name, suite, version, testFramework) {
|
|
|
871
911
|
return {
|
|
872
912
|
[SPAN_TYPE]: 'test',
|
|
873
913
|
[TEST_TYPE]: getTestTypeFromFramework(testFramework),
|
|
914
|
+
[TEST_FRAMEWORK]: testFramework,
|
|
874
915
|
[SAMPLING_RULE_DECISION]: 1,
|
|
875
916
|
[SAMPLING_PRIORITY]: AUTO_KEEP,
|
|
876
917
|
[TEST_NAME]: name,
|
|
@@ -884,6 +925,10 @@ function getTestCommonTags (name, suite, version, testFramework) {
|
|
|
884
925
|
/**
|
|
885
926
|
* We want to make sure that test suites are reported the same way for
|
|
886
927
|
* every OS, so we replace `path.sep` by `/`
|
|
928
|
+
*
|
|
929
|
+
* @param {string | undefined} testSuiteAbsolutePath
|
|
930
|
+
* @param {string} sourceRoot
|
|
931
|
+
* @returns {string}
|
|
887
932
|
*/
|
|
888
933
|
function getTestSuitePath (testSuiteAbsolutePath, sourceRoot) {
|
|
889
934
|
if (!testSuiteAbsolutePath) {
|
|
@@ -941,8 +986,7 @@ function getCodeOwnersFileEntries (rootDir) {
|
|
|
941
986
|
const lines = codeOwnersContent.split('\n')
|
|
942
987
|
|
|
943
988
|
for (const line of lines) {
|
|
944
|
-
const
|
|
945
|
-
const trimmed = content.trim()
|
|
989
|
+
const trimmed = getSegment(line, '#', 0).trim()
|
|
946
990
|
if (trimmed === '') continue
|
|
947
991
|
const [pattern, ...owners] = trimmed.split(/\s+/)
|
|
948
992
|
entries.push(setCodeOwnersPatternRegex({ pattern, owners }))
|
|
@@ -988,9 +1032,7 @@ function codeOwnersPatternToRegexSource (pattern) {
|
|
|
988
1032
|
|
|
989
1033
|
if (character === '\\') {
|
|
990
1034
|
const escapedCharacter = pattern[i + 1]
|
|
991
|
-
source += escapedCharacter
|
|
992
|
-
? escapeRegexCharacter(character)
|
|
993
|
-
: escapeRegexCharacter(escapedCharacter)
|
|
1035
|
+
source += escapeRegexCharacter(escapedCharacter ?? character)
|
|
994
1036
|
i++
|
|
995
1037
|
} else if (character === '*') {
|
|
996
1038
|
if (pattern[i + 1] === '*') {
|
|
@@ -1101,6 +1143,7 @@ function getCodeOwnersForFilename (filename, entries) {
|
|
|
1101
1143
|
|
|
1102
1144
|
function getTestLevelCommonTags (command, testFrameworkVersion, testFramework) {
|
|
1103
1145
|
return {
|
|
1146
|
+
[TEST_FRAMEWORK]: testFramework,
|
|
1104
1147
|
[TEST_FRAMEWORK_VERSION]: testFrameworkVersion,
|
|
1105
1148
|
[LIBRARY_VERSION]: ddTraceVersion,
|
|
1106
1149
|
[TEST_TYPE]: getTestTypeFromFramework(testFramework),
|
|
@@ -1184,7 +1227,7 @@ function getCoveredFilenamesFromCoverage (coverage) {
|
|
|
1184
1227
|
}
|
|
1185
1228
|
|
|
1186
1229
|
function getCoverageMap (coverage) {
|
|
1187
|
-
if (coverage?.files && coverage
|
|
1230
|
+
if (coverage?.files && coverage.fileCoverageFor) {
|
|
1188
1231
|
return coverage
|
|
1189
1232
|
}
|
|
1190
1233
|
return istanbul.createCoverageMap(coverage)
|
|
@@ -1499,46 +1542,6 @@ function parseAnnotations (annotations) {
|
|
|
1499
1542
|
}, {})
|
|
1500
1543
|
}
|
|
1501
1544
|
|
|
1502
|
-
/**
|
|
1503
|
-
* Given a test's first-execution duration (ms) and the slow_test_retries map
|
|
1504
|
-
* from the backend, return how many EFD retries to run.
|
|
1505
|
-
*
|
|
1506
|
-
* Returns 0 when the test is too slow to retry (≥ 5 min).
|
|
1507
|
-
*
|
|
1508
|
-
* @param {number} durationMs
|
|
1509
|
-
* @param {Record<string, number>} slowTestRetries e.g. { '5s': 10, '10s': 5, '30s': 3, '5m': 2 }
|
|
1510
|
-
* @returns {number}
|
|
1511
|
-
*/
|
|
1512
|
-
function getEfdRetryCount (durationMs, slowTestRetries) {
|
|
1513
|
-
for (const { limitMs, key } of EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS) {
|
|
1514
|
-
if (durationMs < limitMs) {
|
|
1515
|
-
return slowTestRetries[key] ?? 0
|
|
1516
|
-
}
|
|
1517
|
-
}
|
|
1518
|
-
return 0 // ≥ 5 min — abort
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
/**
|
|
1522
|
-
* Returns the maximum retry count configured by the backend for EFD.
|
|
1523
|
-
*
|
|
1524
|
-
* @param {Record<string, number> | undefined} slowTestRetries
|
|
1525
|
-
* @returns {number | undefined}
|
|
1526
|
-
*/
|
|
1527
|
-
function getMaxEfdRetryCount (slowTestRetries) {
|
|
1528
|
-
if (slowTestRetries === undefined) return
|
|
1529
|
-
|
|
1530
|
-
const retryCounts = Object.values(slowTestRetries)
|
|
1531
|
-
if (retryCounts.length === 0) return
|
|
1532
|
-
|
|
1533
|
-
let maxRetries = 0
|
|
1534
|
-
for (const retryCount of retryCounts) {
|
|
1535
|
-
if (retryCount > maxRetries) {
|
|
1536
|
-
maxRetries = retryCount
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
return maxRetries
|
|
1540
|
-
}
|
|
1541
|
-
|
|
1542
1545
|
function getIsFaultyEarlyFlakeDetection (projectSuites, testsBySuiteName, faultyThresholdPercentage) {
|
|
1543
1546
|
let newSuites = 0
|
|
1544
1547
|
for (const suite of projectSuites) {
|
|
@@ -1680,7 +1683,8 @@ function isFailedTestReplaySupported (testFramework, frameworkVersion) {
|
|
|
1680
1683
|
|
|
1681
1684
|
function getLibraryCapabilitiesTags (testFramework, frameworkVersion, options = {}) {
|
|
1682
1685
|
return {
|
|
1683
|
-
[DD_CAPABILITIES_TEST_IMPACT_ANALYSIS]:
|
|
1686
|
+
[DD_CAPABILITIES_TEST_IMPACT_ANALYSIS]: !options.omitTestImpactAnalysis &&
|
|
1687
|
+
isTiaSupported(testFramework)
|
|
1684
1688
|
? '1'
|
|
1685
1689
|
: undefined,
|
|
1686
1690
|
[DD_CAPABILITIES_EARLY_FLAKE_DETECTION]: isEarlyFlakeDetectionSupported(testFramework, frameworkVersion)
|
|
@@ -1840,6 +1844,17 @@ function getModifiedFilesFromDiff (diff) {
|
|
|
1840
1844
|
* @typedef {Map<string, AttemptToFixExecutionResult>} AttemptToFixExecutions
|
|
1841
1845
|
*/
|
|
1842
1846
|
|
|
1847
|
+
/**
|
|
1848
|
+
* Returns a collision-free identity for a test's suite and name.
|
|
1849
|
+
*
|
|
1850
|
+
* @param {string | undefined} testSuite
|
|
1851
|
+
* @param {string} testName
|
|
1852
|
+
* @returns {string}
|
|
1853
|
+
*/
|
|
1854
|
+
function getTestOptimizationIdentity (testSuite, testName) {
|
|
1855
|
+
return JSON.stringify([testSuite, testName])
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1843
1858
|
/**
|
|
1844
1859
|
* Formats a test name for user-facing Test Optimization summaries.
|
|
1845
1860
|
*
|
|
@@ -1851,6 +1866,79 @@ function formatTestOptimizationName (testSuite, testName) {
|
|
|
1851
1866
|
return testSuite ? `${testSuite} › ${testName}` : testName
|
|
1852
1867
|
}
|
|
1853
1868
|
|
|
1869
|
+
/**
|
|
1870
|
+
* Replaces characters that could make one test occupy multiple CI log lines.
|
|
1871
|
+
*
|
|
1872
|
+
* @param {string} value
|
|
1873
|
+
* @returns {string}
|
|
1874
|
+
*/
|
|
1875
|
+
function sanitizeTestOptimizationName (value) {
|
|
1876
|
+
return stripVTControlCharacters(value)
|
|
1877
|
+
.replaceAll(TEST_OPTIMIZATION_NAME_CONTROL_RE, '')
|
|
1878
|
+
.replaceAll(TEST_OPTIMIZATION_NAME_WHITESPACE_RE, ' ')
|
|
1879
|
+
.trim()
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
/**
|
|
1883
|
+
* Truncates the beginning of a value while preserving its distinguishing suffix.
|
|
1884
|
+
*
|
|
1885
|
+
* @param {string} value
|
|
1886
|
+
* @param {number} maxLength
|
|
1887
|
+
* @returns {string}
|
|
1888
|
+
*/
|
|
1889
|
+
function truncateTestOptimizationNameStart (value, maxLength) {
|
|
1890
|
+
if (value.length <= maxLength) return value
|
|
1891
|
+
|
|
1892
|
+
let start = value.length - maxLength + 1
|
|
1893
|
+
const code = value.charCodeAt(start)
|
|
1894
|
+
if (code >= 0xDC_00 && code <= 0xDF_FF) start++
|
|
1895
|
+
return `…${value.slice(start)}`
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
/**
|
|
1899
|
+
* Truncates the middle of a value while preserving both its context and suffix.
|
|
1900
|
+
*
|
|
1901
|
+
* @param {string} value
|
|
1902
|
+
* @param {number} maxLength
|
|
1903
|
+
* @returns {string}
|
|
1904
|
+
*/
|
|
1905
|
+
function truncateTestOptimizationNameMiddle (value, maxLength) {
|
|
1906
|
+
if (value.length <= maxLength) return value
|
|
1907
|
+
|
|
1908
|
+
let prefixLength = Math.ceil((maxLength - 1) / 2)
|
|
1909
|
+
let suffixLength = maxLength - prefixLength - 1
|
|
1910
|
+
const prefixCode = value.charCodeAt(prefixLength - 1)
|
|
1911
|
+
if (prefixCode >= 0xD8_00 && prefixCode <= 0xDB_FF) prefixLength--
|
|
1912
|
+
const suffixCode = value.charCodeAt(value.length - suffixLength)
|
|
1913
|
+
if (suffixCode >= 0xDC_00 && suffixCode <= 0xDF_FF) suffixLength--
|
|
1914
|
+
return `${value.slice(0, prefixLength)}…${value.slice(-suffixLength)}`
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
/**
|
|
1918
|
+
* Formats a bounded, single-line test name for CI output.
|
|
1919
|
+
*
|
|
1920
|
+
* @param {string | undefined} testSuite
|
|
1921
|
+
* @param {string} testName
|
|
1922
|
+
* @returns {string}
|
|
1923
|
+
*/
|
|
1924
|
+
function formatTestOptimizationDisplayName (testSuite, testName) {
|
|
1925
|
+
const sanitizedSuite = testSuite ? sanitizeTestOptimizationName(testSuite) : ''
|
|
1926
|
+
const sanitizedName = sanitizeTestOptimizationName(testName)
|
|
1927
|
+
if (!sanitizedSuite) {
|
|
1928
|
+
return truncateTestOptimizationNameMiddle(sanitizedName, MAX_TEST_OPTIMIZATION_NAME_LENGTH)
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
const separator = ' › '
|
|
1932
|
+
if (sanitizedSuite.length + separator.length + sanitizedName.length <= MAX_TEST_OPTIMIZATION_NAME_LENGTH) {
|
|
1933
|
+
return `${sanitizedSuite}${separator}${sanitizedName}`
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
const suiteLength = Math.min(60, sanitizedSuite.length)
|
|
1937
|
+
const testNameLength = MAX_TEST_OPTIMIZATION_NAME_LENGTH - separator.length - suiteLength
|
|
1938
|
+
return truncateTestOptimizationNameStart(sanitizedSuite, suiteLength) +
|
|
1939
|
+
`${separator}${truncateTestOptimizationNameMiddle(sanitizedName, testNameLength)}`
|
|
1940
|
+
}
|
|
1941
|
+
|
|
1854
1942
|
/**
|
|
1855
1943
|
* Renders a bounded bullet list for Test Optimization summaries.
|
|
1856
1944
|
*
|
|
@@ -1862,7 +1950,12 @@ function formatTestOptimizationList (items) {
|
|
|
1862
1950
|
const more = items.length - shown.length
|
|
1863
1951
|
const moreSuffix = more > 0 ? `\n ... and ${more} more` : ''
|
|
1864
1952
|
|
|
1865
|
-
return shown
|
|
1953
|
+
return shown
|
|
1954
|
+
.map(({ text, suffix }) => ` • ${truncateTestOptimizationNameMiddle(
|
|
1955
|
+
sanitizeTestOptimizationName(text),
|
|
1956
|
+
MAX_TEST_OPTIMIZATION_NAME_LENGTH
|
|
1957
|
+
)}${suffix ? ` (${suffix})` : ''}`)
|
|
1958
|
+
.join('\n') + moreSuffix
|
|
1866
1959
|
}
|
|
1867
1960
|
|
|
1868
1961
|
/**
|
|
@@ -1875,15 +1968,67 @@ function formatTestOptimizationList (items) {
|
|
|
1875
1968
|
function logAttemptToFixTestExecution (testSuite, testName, loggedAttemptToFixTests) {
|
|
1876
1969
|
if (!testName) return
|
|
1877
1970
|
|
|
1878
|
-
const name = formatTestOptimizationName(testSuite, testName)
|
|
1879
1971
|
if (loggedAttemptToFixTests) {
|
|
1880
|
-
|
|
1972
|
+
const identity = getTestOptimizationIdentity(testSuite, testName)
|
|
1973
|
+
if (loggedAttemptToFixTests.has(identity)) return
|
|
1881
1974
|
|
|
1882
|
-
loggedAttemptToFixTests.add(
|
|
1975
|
+
loggedAttemptToFixTests.add(identity)
|
|
1883
1976
|
}
|
|
1884
1977
|
|
|
1885
1978
|
// eslint-disable-next-line no-console -- Intentional user-facing attempt-to-fix progress report
|
|
1886
|
-
console.warn(`Datadog Test Optimization: attempting to fix ${
|
|
1979
|
+
console.warn(`Datadog Test Optimization: attempting to fix ${formatTestOptimizationDisplayName(testSuite, testName)}`)
|
|
1980
|
+
}
|
|
1981
|
+
|
|
1982
|
+
/**
|
|
1983
|
+
* @typedef {object} TestManagementExecutionResult
|
|
1984
|
+
* @property {string} name
|
|
1985
|
+
* @property {'disabled'|'quarantined'} action
|
|
1986
|
+
* @property {boolean} failed
|
|
1987
|
+
*/
|
|
1988
|
+
|
|
1989
|
+
/**
|
|
1990
|
+
* @typedef {Map<string, TestManagementExecutionResult>} TestManagementExecutions
|
|
1991
|
+
*/
|
|
1992
|
+
|
|
1993
|
+
/**
|
|
1994
|
+
* Records the effective non-Attempt-to-Fix Test Management action for one execution.
|
|
1995
|
+
*
|
|
1996
|
+
* @param {{
|
|
1997
|
+
* testSuite?: string,
|
|
1998
|
+
* testName: string,
|
|
1999
|
+
* status?: string,
|
|
2000
|
+
* isAttemptToFix?: boolean,
|
|
2001
|
+
* isDisabled?: boolean,
|
|
2002
|
+
* isQuarantined?: boolean
|
|
2003
|
+
* }} execution
|
|
2004
|
+
* @param {TestManagementExecutions} [executions]
|
|
2005
|
+
*/
|
|
2006
|
+
function recordTestManagementExecution (execution, executions = testManagementExecutions) {
|
|
2007
|
+
if (!execution?.testName || execution.isAttemptToFix) return
|
|
2008
|
+
|
|
2009
|
+
const action = execution.isDisabled
|
|
2010
|
+
? 'disabled'
|
|
2011
|
+
: execution.isQuarantined ? 'quarantined' : undefined
|
|
2012
|
+
if (!action) return
|
|
2013
|
+
if (action === 'quarantined' && execution.status !== 'pass' && execution.status !== 'fail') return
|
|
2014
|
+
if (getValueFromEnvSources('DD_TEST_MANAGEMENT_REPORT_ENABLED') === false) return
|
|
2015
|
+
|
|
2016
|
+
const identity = getTestOptimizationIdentity(execution.testSuite, execution.testName)
|
|
2017
|
+
const name = formatTestOptimizationName(execution.testSuite, execution.testName)
|
|
2018
|
+
let result = executions.get(identity)
|
|
2019
|
+
|
|
2020
|
+
if (!result) {
|
|
2021
|
+
result = {
|
|
2022
|
+
name,
|
|
2023
|
+
action,
|
|
2024
|
+
failed: false,
|
|
2025
|
+
}
|
|
2026
|
+
executions.set(identity, result)
|
|
2027
|
+
} else if (action === 'disabled') {
|
|
2028
|
+
result.action = action
|
|
2029
|
+
}
|
|
2030
|
+
|
|
2031
|
+
result.failed ||= execution.status === 'fail'
|
|
1887
2032
|
}
|
|
1888
2033
|
|
|
1889
2034
|
/**
|
|
@@ -1900,10 +2045,12 @@ function logAttemptToFixTestExecution (testSuite, testName, loggedAttemptToFixTe
|
|
|
1900
2045
|
*/
|
|
1901
2046
|
function recordAttemptToFixExecution (attemptToFixExecutions, execution) {
|
|
1902
2047
|
if (!execution?.testName) return
|
|
2048
|
+
if (getValueFromEnvSources('DD_TEST_MANAGEMENT_REPORT_ENABLED') === false) return
|
|
1903
2049
|
|
|
1904
2050
|
const { testSuite, testName, status, isDisabled, isQuarantined } = execution
|
|
2051
|
+
const identity = getTestOptimizationIdentity(testSuite, testName)
|
|
1905
2052
|
const name = formatTestOptimizationName(testSuite, testName)
|
|
1906
|
-
let result = attemptToFixExecutions.get(
|
|
2053
|
+
let result = attemptToFixExecutions.get(identity)
|
|
1907
2054
|
|
|
1908
2055
|
if (!result) {
|
|
1909
2056
|
result = {
|
|
@@ -1913,12 +2060,12 @@ function recordAttemptToFixExecution (attemptToFixExecutions, execution) {
|
|
|
1913
2060
|
isDisabled: false,
|
|
1914
2061
|
isQuarantined: false,
|
|
1915
2062
|
}
|
|
1916
|
-
attemptToFixExecutions.set(
|
|
2063
|
+
attemptToFixExecutions.set(identity, result)
|
|
1917
2064
|
}
|
|
1918
2065
|
|
|
1919
2066
|
result.executions++
|
|
1920
|
-
result.isDisabled
|
|
1921
|
-
result.isQuarantined
|
|
2067
|
+
result.isDisabled ||= !!isDisabled
|
|
2068
|
+
result.isQuarantined ||= !!isQuarantined
|
|
1922
2069
|
|
|
1923
2070
|
if (status === 'fail') {
|
|
1924
2071
|
result.failedCount++
|
|
@@ -1949,6 +2096,27 @@ function collectAttemptToFixExecutionFromTraceSpan (span, attemptToFixExecutions
|
|
|
1949
2096
|
})
|
|
1950
2097
|
}
|
|
1951
2098
|
|
|
2099
|
+
/**
|
|
2100
|
+
* Records a Test Management execution from a serialized worker trace span.
|
|
2101
|
+
*
|
|
2102
|
+
* @param {{ meta?: Record<string, string> }} span
|
|
2103
|
+
* @param {TestManagementExecutions} executions
|
|
2104
|
+
* @returns {void}
|
|
2105
|
+
*/
|
|
2106
|
+
function collectTestManagementExecutionFromTraceSpan (span, executions) {
|
|
2107
|
+
const meta = span.meta
|
|
2108
|
+
if (!meta) return
|
|
2109
|
+
|
|
2110
|
+
recordTestManagementExecution({
|
|
2111
|
+
testSuite: meta[TEST_SUITE],
|
|
2112
|
+
testName: meta[TEST_NAME],
|
|
2113
|
+
status: meta[TEST_STATUS],
|
|
2114
|
+
isAttemptToFix: meta[TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX] === 'true',
|
|
2115
|
+
isDisabled: meta[TEST_MANAGEMENT_IS_DISABLED] === 'true',
|
|
2116
|
+
isQuarantined: meta[TEST_MANAGEMENT_IS_QUARANTINED] === 'true',
|
|
2117
|
+
}, executions)
|
|
2118
|
+
}
|
|
2119
|
+
|
|
1952
2120
|
/**
|
|
1953
2121
|
* Scans serialized worker trace payloads and populates Test Optimization summary data.
|
|
1954
2122
|
* Silently ignores parse errors.
|
|
@@ -1956,11 +2124,16 @@ function collectAttemptToFixExecutionFromTraceSpan (span, attemptToFixExecutions
|
|
|
1956
2124
|
* @param {string} data - JSON-serialized traces from a worker
|
|
1957
2125
|
* @param {{
|
|
1958
2126
|
* newTestsWithDynamicNames?: Set<string>,
|
|
1959
|
-
* attemptToFixExecutions?: AttemptToFixExecutions
|
|
2127
|
+
* attemptToFixExecutions?: AttemptToFixExecutions,
|
|
2128
|
+
* testManagementExecutions?: TestManagementExecutions
|
|
1960
2129
|
* }} summaries
|
|
1961
2130
|
*/
|
|
1962
2131
|
function collectTestOptimizationSummariesFromTraces (data, summaries) {
|
|
1963
|
-
const {
|
|
2132
|
+
const {
|
|
2133
|
+
newTestsWithDynamicNames,
|
|
2134
|
+
attemptToFixExecutions,
|
|
2135
|
+
testManagementExecutions: executions = testManagementExecutions,
|
|
2136
|
+
} = summaries
|
|
1964
2137
|
|
|
1965
2138
|
try {
|
|
1966
2139
|
const traces = JSON.parse(data)
|
|
@@ -1972,6 +2145,7 @@ function collectTestOptimizationSummariesFromTraces (data, summaries) {
|
|
|
1972
2145
|
if (attemptToFixExecutions) {
|
|
1973
2146
|
collectAttemptToFixExecutionFromTraceSpan(span, attemptToFixExecutions)
|
|
1974
2147
|
}
|
|
2148
|
+
collectTestManagementExecutionFromTraceSpan(span, executions)
|
|
1975
2149
|
}
|
|
1976
2150
|
}
|
|
1977
2151
|
} catch {
|
|
@@ -2018,7 +2192,10 @@ function hasAttemptToFixManagementNotes (result) {
|
|
|
2018
2192
|
}
|
|
2019
2193
|
|
|
2020
2194
|
function addAttemptToFixResultLine (lines, result) {
|
|
2021
|
-
lines.push(` • ${
|
|
2195
|
+
lines.push(` • ${truncateTestOptimizationNameMiddle(
|
|
2196
|
+
sanitizeTestOptimizationName(result.name),
|
|
2197
|
+
MAX_TEST_OPTIMIZATION_NAME_LENGTH
|
|
2198
|
+
)}`)
|
|
2022
2199
|
|
|
2023
2200
|
for (const note of getAttemptToFixManagementNotes(result)) {
|
|
2024
2201
|
lines.push(` ${note}`)
|
|
@@ -2029,9 +2206,10 @@ function addAttemptToFixResultLine (lines, result) {
|
|
|
2029
2206
|
* Formats the attempt-to-fix end-of-session summary.
|
|
2030
2207
|
*
|
|
2031
2208
|
* @param {AttemptToFixExecutions} attemptToFixExecutions
|
|
2209
|
+
* @param {boolean} [includeTestNames]
|
|
2032
2210
|
* @returns {string}
|
|
2033
2211
|
*/
|
|
2034
|
-
function formatAttemptToFixSummary (attemptToFixExecutions) {
|
|
2212
|
+
function formatAttemptToFixSummary (attemptToFixExecutions, includeTestNames = true) {
|
|
2035
2213
|
if (attemptToFixExecutions.size === 0) return ''
|
|
2036
2214
|
|
|
2037
2215
|
const results = [...attemptToFixExecutions.values()]
|
|
@@ -2043,9 +2221,11 @@ function formatAttemptToFixSummary (attemptToFixExecutions) {
|
|
|
2043
2221
|
`Attempt to fix passed: all ${totalExecutions} execution(s) passed for ${results.length} test(s).`,
|
|
2044
2222
|
]
|
|
2045
2223
|
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2224
|
+
if (includeTestNames) {
|
|
2225
|
+
for (const result of results) {
|
|
2226
|
+
if (hasAttemptToFixManagementNotes(result)) {
|
|
2227
|
+
addAttemptToFixResultLine(lines, result)
|
|
2228
|
+
}
|
|
2049
2229
|
}
|
|
2050
2230
|
}
|
|
2051
2231
|
|
|
@@ -2061,13 +2241,67 @@ function formatAttemptToFixSummary (attemptToFixExecutions) {
|
|
|
2061
2241
|
`across ${failedResults.length} of ${results.length} test(s).`,
|
|
2062
2242
|
]
|
|
2063
2243
|
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
}
|
|
2067
|
-
for (const result of results) {
|
|
2068
|
-
if (result.failedCount === 0 && hasAttemptToFixManagementNotes(result)) {
|
|
2244
|
+
if (includeTestNames) {
|
|
2245
|
+
for (const result of failedResults) {
|
|
2069
2246
|
addAttemptToFixResultLine(lines, result)
|
|
2070
2247
|
}
|
|
2248
|
+
for (const result of results) {
|
|
2249
|
+
if (result.failedCount === 0 && hasAttemptToFixManagementNotes(result)) {
|
|
2250
|
+
addAttemptToFixResultLine(lines, result)
|
|
2251
|
+
}
|
|
2252
|
+
}
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
return lines.join('\n')
|
|
2256
|
+
}
|
|
2257
|
+
|
|
2258
|
+
/**
|
|
2259
|
+
* Formats the disabled and quarantined Test Management summary.
|
|
2260
|
+
*
|
|
2261
|
+
* @param {TestManagementExecutions} executions
|
|
2262
|
+
* @param {boolean} [includeTestNames]
|
|
2263
|
+
* @returns {string}
|
|
2264
|
+
*/
|
|
2265
|
+
function formatTestManagementSummary (executions, includeTestNames = true) {
|
|
2266
|
+
if (executions.size === 0) return ''
|
|
2267
|
+
|
|
2268
|
+
const results = [...executions.values()]
|
|
2269
|
+
const disabledResults = results.filter(result => result.action === 'disabled')
|
|
2270
|
+
const quarantinedResults = results.filter(result => result.action === 'quarantined')
|
|
2271
|
+
const failedQuarantinedResults = quarantinedResults.filter(result => result.failed)
|
|
2272
|
+
const lines = ['Test Management']
|
|
2273
|
+
|
|
2274
|
+
if (disabledResults.length > 0) {
|
|
2275
|
+
const testLabel = disabledResults.length === 1 ? 'test' : 'tests'
|
|
2276
|
+
lines.push(` Disabled: ${disabledResults.length} ${testLabel} skipped.`)
|
|
2277
|
+
}
|
|
2278
|
+
if (quarantinedResults.length > 0) {
|
|
2279
|
+
const testLabel = quarantinedResults.length === 1 ? 'test' : 'tests'
|
|
2280
|
+
if (failedQuarantinedResults.length > 0) {
|
|
2281
|
+
const failureLabel = failedQuarantinedResults.length === 1 ? 'failure' : 'failures'
|
|
2282
|
+
lines.push(
|
|
2283
|
+
` Quarantined: ${quarantinedResults.length} ${testLabel} run; ` +
|
|
2284
|
+
`${failedQuarantinedResults.length} ${failureLabel} did not affect the test session.`
|
|
2285
|
+
)
|
|
2286
|
+
} else {
|
|
2287
|
+
lines.push(` Quarantined: ${quarantinedResults.length} ${testLabel} run; all passed.`)
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
if (includeTestNames) {
|
|
2292
|
+
lines.push('', ' Affected tests:')
|
|
2293
|
+
results.sort((a, b) => a.name.localeCompare(b.name))
|
|
2294
|
+
for (const result of results) {
|
|
2295
|
+
const status = result.action === 'disabled'
|
|
2296
|
+
? 'disabled'
|
|
2297
|
+
: result.failed ? 'quarantined, failed' : 'quarantined, passed'
|
|
2298
|
+
lines.push(
|
|
2299
|
+
` • [${status}] ${truncateTestOptimizationNameMiddle(
|
|
2300
|
+
sanitizeTestOptimizationName(result.name),
|
|
2301
|
+
MAX_TEST_OPTIMIZATION_NAME_LENGTH
|
|
2302
|
+
)}`
|
|
2303
|
+
)
|
|
2304
|
+
}
|
|
2071
2305
|
}
|
|
2072
2306
|
|
|
2073
2307
|
return lines.join('\n')
|
|
@@ -2098,25 +2332,57 @@ function formatDynamicNamesSummary (newTestsWithDynamicNames) {
|
|
|
2098
2332
|
*
|
|
2099
2333
|
* @param {{
|
|
2100
2334
|
* attemptToFixExecutions?: AttemptToFixExecutions,
|
|
2335
|
+
* testManagementExecutions?: TestManagementExecutions,
|
|
2101
2336
|
* newTestsWithDynamicNames?: Set<string>,
|
|
2102
2337
|
* extraSections?: string[]
|
|
2103
2338
|
* }} summary
|
|
2104
2339
|
*/
|
|
2105
2340
|
function logTestOptimizationSummary (summary) {
|
|
2106
|
-
const {
|
|
2341
|
+
const {
|
|
2342
|
+
attemptToFixExecutions,
|
|
2343
|
+
testManagementExecutions: executions = testManagementExecutions,
|
|
2344
|
+
newTestsWithDynamicNames,
|
|
2345
|
+
extraSections = [],
|
|
2346
|
+
} = summary
|
|
2107
2347
|
const sections = []
|
|
2108
|
-
const
|
|
2109
|
-
|
|
2348
|
+
const isTestManagementReportEnabled =
|
|
2349
|
+
getValueFromEnvSources('DD_TEST_MANAGEMENT_REPORT_ENABLED') !== false
|
|
2350
|
+
const testManagementTestCount = (attemptToFixExecutions?.size || 0) + executions.size
|
|
2351
|
+
const includeTestNames = testManagementTestCount <= MAX_TEST_MANAGEMENT_REPORT_ITEMS
|
|
2352
|
+
const attemptToFixSummary = isTestManagementReportEnabled && attemptToFixExecutions
|
|
2353
|
+
? formatAttemptToFixSummary(attemptToFixExecutions, includeTestNames)
|
|
2354
|
+
: ''
|
|
2355
|
+
const testManagementSummary = isTestManagementReportEnabled
|
|
2356
|
+
? formatTestManagementSummary(executions, includeTestNames)
|
|
2110
2357
|
: ''
|
|
2111
2358
|
const dynamicNamesSummary = newTestsWithDynamicNames
|
|
2112
2359
|
? formatDynamicNamesSummary(newTestsWithDynamicNames)
|
|
2113
2360
|
: ''
|
|
2114
2361
|
|
|
2115
2362
|
if (attemptToFixSummary) sections.push(attemptToFixSummary)
|
|
2363
|
+
if (testManagementSummary) sections.push(testManagementSummary)
|
|
2364
|
+
if (attemptToFixSummary || testManagementSummary) {
|
|
2365
|
+
if (!includeTestNames) {
|
|
2366
|
+
sections.push(`Individual test names omitted because ${testManagementTestCount} tests were affected.`)
|
|
2367
|
+
}
|
|
2368
|
+
sections.push('Hide this report: DD_TEST_MANAGEMENT_REPORT_ENABLED=false')
|
|
2369
|
+
}
|
|
2116
2370
|
sections.push(...extraSections.filter(Boolean))
|
|
2117
2371
|
if (dynamicNamesSummary) sections.push(dynamicNamesSummary)
|
|
2118
2372
|
|
|
2119
|
-
if (sections.length === 0)
|
|
2373
|
+
if (sections.length === 0) {
|
|
2374
|
+
executions.clear()
|
|
2375
|
+
if (executions !== testManagementExecutions) {
|
|
2376
|
+
testManagementExecutions.clear()
|
|
2377
|
+
}
|
|
2378
|
+
if (attemptToFixExecutions) {
|
|
2379
|
+
attemptToFixExecutions.clear()
|
|
2380
|
+
}
|
|
2381
|
+
if (newTestsWithDynamicNames) {
|
|
2382
|
+
newTestsWithDynamicNames.clear()
|
|
2383
|
+
}
|
|
2384
|
+
return
|
|
2385
|
+
}
|
|
2120
2386
|
|
|
2121
2387
|
const line = '-'.repeat(50)
|
|
2122
2388
|
// eslint-disable-next-line no-console -- Intentional user-facing session summary
|
|
@@ -2125,6 +2391,10 @@ function logTestOptimizationSummary (summary) {
|
|
|
2125
2391
|
if (attemptToFixExecutions) {
|
|
2126
2392
|
attemptToFixExecutions.clear()
|
|
2127
2393
|
}
|
|
2394
|
+
executions.clear()
|
|
2395
|
+
if (executions !== testManagementExecutions) {
|
|
2396
|
+
testManagementExecutions.clear()
|
|
2397
|
+
}
|
|
2128
2398
|
if (newTestsWithDynamicNames) {
|
|
2129
2399
|
newTestsWithDynamicNames.clear()
|
|
2130
2400
|
}
|