dd-trace 6.8.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 +9 -3
- 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/vitest.js +517 -0
- package/ci/test-optimization-validation/generated-files.js +38 -28
- package/ci/test-optimization-validation/generated-test-contract.js +8 -4
- 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 +300 -50
- 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/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 +40 -20
- package/ci/test-optimization-validation/runner-contract.js +79 -43
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
- 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/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 +30 -34
- package/index.d.ts +98 -1
- package/init.js +1 -17
- package/initialize.mjs +11 -0
- package/loader-hook.mjs +6 -4
- package/package.json +5 -3
- package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
- package/packages/datadog-esbuild/src/utils.js +16 -1
- 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 +81 -5
- package/packages/datadog-instrumentations/src/cucumber.js +124 -47
- 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/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +33 -9
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +118 -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/router-helper.js +37 -6
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +979 -233
- package/packages/datadog-instrumentations/src/mocha/main.js +107 -9
- package/packages/datadog-instrumentations/src/mocha/utils.js +64 -42
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
- 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 +173 -79
- package/packages/datadog-instrumentations/src/promise-js.js +4 -4
- package/packages/datadog-instrumentations/src/promise.js +3 -3
- package/packages/datadog-instrumentations/src/router.js +195 -19
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -10
- package/packages/datadog-instrumentations/src/vitest-main.js +360 -56
- 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 +194 -18
- package/packages/datadog-instrumentations/src/when.js +3 -3
- package/packages/datadog-instrumentations/src/ws.js +5 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
- 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/support.js +7 -4
- package/packages/datadog-plugin-electron/src/net.js +10 -4
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +1 -18
- package/packages/datadog-plugin-jest/src/util.js +2 -0
- package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
- package/packages/datadog-plugin-mocha/src/index.js +318 -1
- 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-vitest/src/index.js +95 -7
- package/packages/datadog-shimmer/src/shimmer.js +6 -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/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/hsts-header-missing-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +48 -476
- package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
- 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-visibility-exporter.js +27 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -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/upload-coverage-report.js +21 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/helper.js +2 -0
- package/packages/dd-trace/src/config/index.js +16 -5
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +8 -0
- 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/guardrails/apply-pm2-env.js +16 -0
- package/packages/dd-trace/src/guardrails/log.js +1 -10
- package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
- package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
- 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/bedrockruntime.js +5 -5
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +149 -68
- 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/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/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/periodic_metric_reader.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +4 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +6 -0
- package/packages/dd-trace/src/plugins/util/git.js +6 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
- package/packages/dd-trace/src/plugins/util/test.js +319 -75
- package/packages/dd-trace/src/plugins/util/url.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +23 -1
- 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/proxy.js +1 -0
- package/packages/dd-trace/src/ritm.js +5 -0
- 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/telemetry/logs/index.js +3 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/version.js +10 -8
- /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
|
|
3
5
|
const log = require('../../dd-trace/src/log')
|
|
6
|
+
const {
|
|
7
|
+
EMPTY_EFD_RETRY_POLICY,
|
|
8
|
+
hasEfdRetries,
|
|
9
|
+
} = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
|
|
4
10
|
const { channel } = require('./helpers/instrument')
|
|
5
11
|
|
|
6
12
|
// test hooks
|
|
@@ -28,9 +34,25 @@ const testManagementTestsCh = channel('ci:vitest:test-management-tests')
|
|
|
28
34
|
const modifiedFilesCh = channel('ci:vitest:modified-files')
|
|
29
35
|
|
|
30
36
|
const workerReportTraceCh = channel('ci:vitest:worker-report:trace')
|
|
37
|
+
const workerReportCoverageCh = channel('ci:vitest:worker-report:coverage')
|
|
31
38
|
const workerReportLogsCh = channel('ci:vitest:worker-report:logs')
|
|
39
|
+
const workerReportTelemetryCh = channel('ci:vitest:worker-report:telemetry')
|
|
32
40
|
const codeCoverageReportCh = channel('ci:vitest:coverage-report')
|
|
33
41
|
|
|
42
|
+
/**
|
|
43
|
+
* Resolves a path without failing Test Optimization when the path is unavailable.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} filepath
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
function realpath (filepath) {
|
|
49
|
+
try {
|
|
50
|
+
return fs.realpathSync(filepath)
|
|
51
|
+
} catch {
|
|
52
|
+
return filepath
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
34
56
|
function findExportByName (pkg, name) {
|
|
35
57
|
for (const [key, value] of Object.entries(pkg)) {
|
|
36
58
|
if (value?.name === name) {
|
|
@@ -95,8 +117,7 @@ function getProvidedContext () {
|
|
|
95
117
|
_ddIsEarlyFlakeDetectionEnabled,
|
|
96
118
|
_ddIsDiEnabled,
|
|
97
119
|
_ddTestPropertiesByFilepath: testPropertiesByFilepath,
|
|
98
|
-
|
|
99
|
-
_ddEarlyFlakeDetectionSlowTestRetries: slowTestRetries,
|
|
120
|
+
_ddEarlyFlakeDetectionRetryPolicy: earlyFlakeDetectionRetryPolicy,
|
|
100
121
|
_ddIsKnownTestsEnabled: isKnownTestsEnabled,
|
|
101
122
|
_ddIsTestManagementTestsEnabled: isTestManagementTestsEnabled,
|
|
102
123
|
_ddTestManagementAttemptToFixRetries: testManagementAttemptToFixRetries,
|
|
@@ -110,14 +131,18 @@ function getProvidedContext () {
|
|
|
110
131
|
_ddTestCommand: testCommand,
|
|
111
132
|
_ddRepositoryRoot: repositoryRoot,
|
|
112
133
|
_ddCodeOwnersEntries: codeOwnersEntries,
|
|
134
|
+
_ddIsCodeCoverageEnabled: isCodeCoverageEnabled,
|
|
135
|
+
_ddItrCorrelationId: itrCorrelationId,
|
|
136
|
+
_ddUnskippableSuites: unskippableSuites,
|
|
137
|
+
_ddForcedToRunSuites: forcedToRunSuites,
|
|
113
138
|
} = globalThis.__vitest_worker__.providedContext
|
|
139
|
+
const retryPolicy = earlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
|
|
114
140
|
|
|
115
141
|
return {
|
|
116
142
|
isDiEnabled: _ddIsDiEnabled,
|
|
117
|
-
isEarlyFlakeDetectionEnabled: _ddIsEarlyFlakeDetectionEnabled,
|
|
143
|
+
isEarlyFlakeDetectionEnabled: _ddIsEarlyFlakeDetectionEnabled && hasEfdRetries(retryPolicy),
|
|
118
144
|
testPropertiesByFilepath,
|
|
119
|
-
|
|
120
|
-
slowTestRetries: slowTestRetries ?? {},
|
|
145
|
+
earlyFlakeDetectionRetryPolicy: retryPolicy,
|
|
121
146
|
isKnownTestsEnabled,
|
|
122
147
|
isTestManagementTestsEnabled,
|
|
123
148
|
testManagementAttemptToFixRetries,
|
|
@@ -131,6 +156,10 @@ function getProvidedContext () {
|
|
|
131
156
|
testCommand,
|
|
132
157
|
repositoryRoot,
|
|
133
158
|
codeOwnersEntries,
|
|
159
|
+
isCodeCoverageEnabled,
|
|
160
|
+
itrCorrelationId,
|
|
161
|
+
unskippableSuites,
|
|
162
|
+
forcedToRunSuites,
|
|
134
163
|
}
|
|
135
164
|
} catch {
|
|
136
165
|
log.error('Vitest workers could not parse provided context, so some features will not work.')
|
|
@@ -138,8 +167,7 @@ function getProvidedContext () {
|
|
|
138
167
|
isDiEnabled: false,
|
|
139
168
|
isEarlyFlakeDetectionEnabled: false,
|
|
140
169
|
testPropertiesByFilepath: {},
|
|
141
|
-
|
|
142
|
-
slowTestRetries: {},
|
|
170
|
+
earlyFlakeDetectionRetryPolicy: EMPTY_EFD_RETRY_POLICY,
|
|
143
171
|
isKnownTestsEnabled: false,
|
|
144
172
|
isTestManagementTestsEnabled: false,
|
|
145
173
|
testManagementAttemptToFixRetries: 0,
|
|
@@ -153,6 +181,10 @@ function getProvidedContext () {
|
|
|
153
181
|
testCommand: undefined,
|
|
154
182
|
repositoryRoot: undefined,
|
|
155
183
|
codeOwnersEntries: undefined,
|
|
184
|
+
isCodeCoverageEnabled: false,
|
|
185
|
+
itrCorrelationId: undefined,
|
|
186
|
+
unskippableSuites: {},
|
|
187
|
+
forcedToRunSuites: {},
|
|
156
188
|
}
|
|
157
189
|
}
|
|
158
190
|
}
|
|
@@ -230,8 +262,11 @@ module.exports = {
|
|
|
230
262
|
testManagementTestsCh,
|
|
231
263
|
modifiedFilesCh,
|
|
232
264
|
workerReportTraceCh,
|
|
265
|
+
workerReportCoverageCh,
|
|
233
266
|
workerReportLogsCh,
|
|
267
|
+
workerReportTelemetryCh,
|
|
234
268
|
codeCoverageReportCh,
|
|
269
|
+
realpath,
|
|
235
270
|
findExportByName,
|
|
236
271
|
getTestRunnerExport,
|
|
237
272
|
getTypeTasks,
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const path = require('node:path')
|
|
3
4
|
const { performance } = require('node:perf_hooks')
|
|
5
|
+
const { fileURLToPath } = require('node:url')
|
|
4
6
|
|
|
5
7
|
const shimmer = require('../../datadog-shimmer')
|
|
8
|
+
const log = require('../../dd-trace/src/log')
|
|
9
|
+
const { getEfdRetryCountForDuration } = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
|
|
6
10
|
const {
|
|
7
11
|
DYNAMIC_NAME_RE,
|
|
8
|
-
|
|
12
|
+
getTestSuitePath,
|
|
9
13
|
recordAttemptToFixExecution,
|
|
10
14
|
logAttemptToFixTestExecution,
|
|
11
15
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
@@ -27,6 +31,7 @@ const {
|
|
|
27
31
|
getTypeTasks,
|
|
28
32
|
getTestName,
|
|
29
33
|
getProvidedContext,
|
|
34
|
+
realpath,
|
|
30
35
|
isFlakyTestRetriesEnabledForTask,
|
|
31
36
|
getVitestTestProperties,
|
|
32
37
|
} = require('./vitest-util')
|
|
@@ -52,10 +57,187 @@ const efdExecutionStartByTask = new WeakMap()
|
|
|
52
57
|
const efdSkippedRetryResults = new WeakMap()
|
|
53
58
|
const attemptToFixExecutions = new Map()
|
|
54
59
|
const loggedAttemptToFixTests = new Set()
|
|
55
|
-
const switchedStatuses = new
|
|
60
|
+
const switchedStatuses = new WeakMap()
|
|
56
61
|
let vitestGetFn = null
|
|
57
62
|
let vitestSetFn = null
|
|
58
63
|
let vitestGetHooks = null
|
|
64
|
+
let preciseCoverageSession
|
|
65
|
+
let isPreciseCoverageUnavailable = false
|
|
66
|
+
let vitestCoverageSnapshot
|
|
67
|
+
const wrappedCoverageWorkerStates = new WeakSet()
|
|
68
|
+
const nonIsolatedCoverageFiles = new Set()
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Sends a command to a Node.js inspector session.
|
|
72
|
+
*
|
|
73
|
+
* @param {import('node:inspector').Session} session
|
|
74
|
+
* @param {string} method
|
|
75
|
+
* @param {object} [params]
|
|
76
|
+
* @returns {Promise<object>}
|
|
77
|
+
*/
|
|
78
|
+
function postInspectorCommand (session, method, params) {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
session.post(method, params, (error, result) => {
|
|
81
|
+
if (error) {
|
|
82
|
+
reject(error)
|
|
83
|
+
} else {
|
|
84
|
+
resolve(result || {})
|
|
85
|
+
}
|
|
86
|
+
})
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async function startPreciseCoverage () {
|
|
91
|
+
if (preciseCoverageSession) return true
|
|
92
|
+
if (isPreciseCoverageUnavailable) return false
|
|
93
|
+
|
|
94
|
+
let session
|
|
95
|
+
try {
|
|
96
|
+
const inspector = require('node:inspector')
|
|
97
|
+
session = new inspector.Session()
|
|
98
|
+
session.connect()
|
|
99
|
+
await postInspectorCommand(session, 'Profiler.enable')
|
|
100
|
+
await postInspectorCommand(session, 'Profiler.startPreciseCoverage', {
|
|
101
|
+
callCount: false,
|
|
102
|
+
detailed: false,
|
|
103
|
+
})
|
|
104
|
+
preciseCoverageSession = session
|
|
105
|
+
return true
|
|
106
|
+
} catch (error) {
|
|
107
|
+
isPreciseCoverageUnavailable = true
|
|
108
|
+
try {
|
|
109
|
+
session?.disconnect()
|
|
110
|
+
} catch {}
|
|
111
|
+
log.warn('Could not start Vitest TIA code coverage: %s', error?.message)
|
|
112
|
+
return false
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function getCoverageFilename (url) {
|
|
117
|
+
if (!url) return
|
|
118
|
+
|
|
119
|
+
if (url.startsWith('file://')) {
|
|
120
|
+
try {
|
|
121
|
+
return fileURLToPath(url)
|
|
122
|
+
} catch {
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (path.isAbsolute(url)) return url
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function isFileInRepository (filename, repositoryRoot) {
|
|
131
|
+
const relativeFilename = path.relative(repositoryRoot, filename)
|
|
132
|
+
return relativeFilename !== '..' &&
|
|
133
|
+
!relativeFilename.startsWith(`..${path.sep}`) &&
|
|
134
|
+
!path.isAbsolute(relativeFilename) &&
|
|
135
|
+
!relativeFilename.startsWith(`node_modules${path.sep}`) &&
|
|
136
|
+
!relativeFilename.includes(`${path.sep}node_modules${path.sep}`)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function isV8ScriptCovered (scriptCoverage) {
|
|
140
|
+
for (const functionCoverage of scriptCoverage.functions) {
|
|
141
|
+
for (const range of functionCoverage.ranges) {
|
|
142
|
+
if (range.count > 0) return true
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function getCoveredFilesFromV8Result (coverage, repositoryRoot) {
|
|
149
|
+
const coveredFiles = []
|
|
150
|
+
const scriptCoverageResults = coverage?.result
|
|
151
|
+
if (scriptCoverageResults) {
|
|
152
|
+
for (const scriptCoverage of scriptCoverageResults) {
|
|
153
|
+
if (!isV8ScriptCovered(scriptCoverage)) continue
|
|
154
|
+
|
|
155
|
+
const coverageFilename = getCoverageFilename(scriptCoverage.url)
|
|
156
|
+
if (!coverageFilename) continue
|
|
157
|
+
|
|
158
|
+
const filename = realpath(coverageFilename)
|
|
159
|
+
if (isFileInRepository(filename, repositoryRoot)) {
|
|
160
|
+
coveredFiles.push(filename)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return coveredFiles
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function getVitestCoverageOptions () {
|
|
168
|
+
return globalThis.__vitest_worker__?.config?.coverage
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Check whether the current Vitest worker reuses its module cache across test suites.
|
|
173
|
+
*
|
|
174
|
+
* @returns {boolean}
|
|
175
|
+
*/
|
|
176
|
+
function isNonIsolatedRun () {
|
|
177
|
+
const config = globalThis.__vitest_worker__?.config
|
|
178
|
+
if (config?.isolate === false) return true
|
|
179
|
+
|
|
180
|
+
return config?.poolOptions?.[config.pool]?.isolate === false
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Conservatively include files covered by earlier suites when Vitest reuses its module cache.
|
|
185
|
+
*
|
|
186
|
+
* @param {string[] | undefined} coverageFiles
|
|
187
|
+
* @returns {string[] | undefined}
|
|
188
|
+
*/
|
|
189
|
+
function includePreviouslyCoveredFiles (coverageFiles) {
|
|
190
|
+
if (!coverageFiles || !isNonIsolatedRun()) return coverageFiles
|
|
191
|
+
|
|
192
|
+
// TODO: Track module cache hits per suite instead; cumulative coverage can under-skip with isolate:false.
|
|
193
|
+
for (const filename of coverageFiles) {
|
|
194
|
+
nonIsolatedCoverageFiles.add(filename)
|
|
195
|
+
}
|
|
196
|
+
return [...nonIsolatedCoverageFiles]
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function usesVitestV8CoverageSnapshot (coverageOptions) {
|
|
200
|
+
return coverageOptions?.enabled === true &&
|
|
201
|
+
coverageOptions.provider === 'v8'
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function getPreciseCoverageFiles (repositoryRoot) {
|
|
205
|
+
if (!await startPreciseCoverage()) return
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
const coverage = await postInspectorCommand(preciseCoverageSession, 'Profiler.takePreciseCoverage')
|
|
209
|
+
return getCoveredFilesFromV8Result(coverage, repositoryRoot)
|
|
210
|
+
} catch (error) {
|
|
211
|
+
isPreciseCoverageUnavailable = true
|
|
212
|
+
try {
|
|
213
|
+
preciseCoverageSession.disconnect()
|
|
214
|
+
} catch {}
|
|
215
|
+
preciseCoverageSession = undefined
|
|
216
|
+
log.warn('Could not collect Vitest TIA code coverage: %s', error?.message)
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function getVitestCoverageFiles (repositoryRoot) {
|
|
221
|
+
return getCoveredFilesFromV8Result(vitestCoverageSnapshot, repositoryRoot)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function wrapVitestCoverageRpc () {
|
|
225
|
+
const workerState = globalThis.__vitest_worker__
|
|
226
|
+
if (!workerState?.rpc || wrappedCoverageWorkerStates.has(workerState)) return
|
|
227
|
+
|
|
228
|
+
wrappedCoverageWorkerStates.add(workerState)
|
|
229
|
+
workerState.rpc = new Proxy(workerState.rpc, {
|
|
230
|
+
get (target, property, receiver) {
|
|
231
|
+
const value = Reflect.get(target, property, receiver)
|
|
232
|
+
if (property !== 'onAfterSuiteRun' || typeof value !== 'function') return value
|
|
233
|
+
|
|
234
|
+
return function (metadata) {
|
|
235
|
+
vitestCoverageSnapshot = metadata?.coverage
|
|
236
|
+
return value.apply(this, arguments)
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
})
|
|
240
|
+
}
|
|
59
241
|
|
|
60
242
|
function waitForHitProbe () {
|
|
61
243
|
const promises = {}
|
|
@@ -208,7 +390,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
208
390
|
const {
|
|
209
391
|
isEarlyFlakeDetectionEnabled,
|
|
210
392
|
isKnownTestsEnabled,
|
|
211
|
-
|
|
393
|
+
earlyFlakeDetectionRetryPolicy,
|
|
212
394
|
isTestManagementTestsEnabled,
|
|
213
395
|
testManagementAttemptToFixRetries,
|
|
214
396
|
isImpactedTestsEnabled,
|
|
@@ -247,7 +429,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
247
429
|
if (isEarlyFlakeDetectionEnabled) {
|
|
248
430
|
efdRetryTasks.add(task)
|
|
249
431
|
disableFrameworkRetries(task)
|
|
250
|
-
task.repeats =
|
|
432
|
+
task.repeats = earlyFlakeDetectionRetryPolicy.schedulingRetryCount
|
|
251
433
|
}
|
|
252
434
|
modifiedTasks.add(task)
|
|
253
435
|
taskToStatuses.set(task, [])
|
|
@@ -257,7 +439,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
257
439
|
if (isEarlyFlakeDetectionEnabled && !modifiedTasks.has(task)) {
|
|
258
440
|
efdRetryTasks.add(task)
|
|
259
441
|
disableFrameworkRetries(task)
|
|
260
|
-
task.repeats =
|
|
442
|
+
task.repeats = earlyFlakeDetectionRetryPolicy.schedulingRetryCount
|
|
261
443
|
}
|
|
262
444
|
newTasks.add(task)
|
|
263
445
|
taskToStatuses.set(task, [])
|
|
@@ -281,14 +463,14 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
281
463
|
if (isAttemptingToFix) {
|
|
282
464
|
const statuses = attemptToFixTaskToStatuses.get(task)
|
|
283
465
|
if (task.result.state === 'pass' && statuses?.includes('fail')) {
|
|
284
|
-
switchedStatuses.
|
|
466
|
+
switchedStatuses.set(task, task.result.state)
|
|
285
467
|
task.result.state = 'fail'
|
|
286
468
|
}
|
|
287
469
|
}
|
|
288
470
|
|
|
289
471
|
if (!isAttemptingToFix && isQuarantined) {
|
|
290
472
|
if (task.result.state === 'fail') {
|
|
291
|
-
switchedStatuses.
|
|
473
|
+
switchedStatuses.set(task, task.result.state)
|
|
292
474
|
}
|
|
293
475
|
task.result.state = 'pass'
|
|
294
476
|
}
|
|
@@ -299,7 +481,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
299
481
|
// If the test has passed at least once, we consider it passed
|
|
300
482
|
if (statuses.includes('pass')) {
|
|
301
483
|
if (task.result.state === 'fail') {
|
|
302
|
-
switchedStatuses.
|
|
484
|
+
switchedStatuses.set(task, task.result.state)
|
|
303
485
|
}
|
|
304
486
|
task.result.state = 'pass'
|
|
305
487
|
}
|
|
@@ -321,7 +503,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
321
503
|
isKnownTestsEnabled,
|
|
322
504
|
isEarlyFlakeDetectionEnabled,
|
|
323
505
|
isDiEnabled,
|
|
324
|
-
|
|
506
|
+
earlyFlakeDetectionRetryPolicy,
|
|
325
507
|
} = providedContext
|
|
326
508
|
|
|
327
509
|
if (isKnownTestsEnabled) {
|
|
@@ -337,7 +519,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
337
519
|
const duration = previousExecutionStart === undefined
|
|
338
520
|
? task.result?.duration ?? 0
|
|
339
521
|
: performance.now() - previousExecutionStart
|
|
340
|
-
const retryCount =
|
|
522
|
+
const retryCount = getEfdRetryCountForDuration(duration, earlyFlakeDetectionRetryPolicy)
|
|
341
523
|
efdDeterminedRetries.set(task, retryCount)
|
|
342
524
|
task.repeats = retryCount
|
|
343
525
|
if (retryCount === 0) {
|
|
@@ -517,7 +699,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
517
699
|
const {
|
|
518
700
|
isEarlyFlakeDetectionEnabled,
|
|
519
701
|
testManagementAttemptToFixRetries,
|
|
520
|
-
|
|
702
|
+
earlyFlakeDetectionRetryPolicy,
|
|
521
703
|
} = getProvidedContext()
|
|
522
704
|
|
|
523
705
|
const status = getVitestTestStatus(task, retryInfo.retry)
|
|
@@ -545,7 +727,7 @@ function wrapVitestTestRunner (VitestTestRunner) {
|
|
|
545
727
|
) {
|
|
546
728
|
const executionStart = efdExecutionStartByTask.get(task)
|
|
547
729
|
const duration = executionStart === undefined ? task.result?.duration ?? 0 : performance.now() - executionStart
|
|
548
|
-
const retryCount =
|
|
730
|
+
const retryCount = getEfdRetryCountForDuration(duration, earlyFlakeDetectionRetryPolicy)
|
|
549
731
|
efdDeterminedRetries.set(task, retryCount)
|
|
550
732
|
task.repeats = retryCount
|
|
551
733
|
if (retryCount === 0) {
|
|
@@ -649,6 +831,19 @@ addHook({
|
|
|
649
831
|
// From >=3.0.1, the first arguments changes from a string to an object containing the filepath
|
|
650
832
|
const testSuiteAbsolutePath = testPaths[0]?.filepath || testPaths[0]
|
|
651
833
|
const providedContext = getProvidedContext()
|
|
834
|
+
const repositoryRoot = providedContext.repositoryRoot || process.cwd()
|
|
835
|
+
const testSuite = getTestSuitePath(testSuiteAbsolutePath, repositoryRoot)
|
|
836
|
+
const coverageOptions = getVitestCoverageOptions()
|
|
837
|
+
const shouldUseVitestCoverage = usesVitestV8CoverageSnapshot(coverageOptions)
|
|
838
|
+
const coverageLibrary = 'v8'
|
|
839
|
+
vitestCoverageSnapshot = undefined
|
|
840
|
+
if (providedContext.isCodeCoverageEnabled) {
|
|
841
|
+
if (shouldUseVitestCoverage) {
|
|
842
|
+
wrapVitestCoverageRpc()
|
|
843
|
+
} else {
|
|
844
|
+
await startPreciseCoverage()
|
|
845
|
+
}
|
|
846
|
+
}
|
|
652
847
|
|
|
653
848
|
const testSuiteCtx = {
|
|
654
849
|
testSuiteAbsolutePath,
|
|
@@ -656,8 +851,13 @@ addHook({
|
|
|
656
851
|
testSessionId: providedContext.testSessionId,
|
|
657
852
|
testModuleId: providedContext.testModuleId,
|
|
658
853
|
testCommand: providedContext.testCommand,
|
|
659
|
-
repositoryRoot
|
|
854
|
+
repositoryRoot,
|
|
660
855
|
codeOwnersEntries: providedContext.codeOwnersEntries,
|
|
856
|
+
isCodeCoverageEnabled: providedContext.isCodeCoverageEnabled,
|
|
857
|
+
coverageLibrary,
|
|
858
|
+
itrCorrelationId: providedContext.itrCorrelationId,
|
|
859
|
+
isUnskippable: providedContext.unskippableSuites?.[testSuite] === true,
|
|
860
|
+
isForcedToRun: providedContext.forcedToRunSuites?.[testSuite] === true,
|
|
661
861
|
}
|
|
662
862
|
testSuiteStartCh.runStores(testSuiteCtx, () => {})
|
|
663
863
|
const startTestsResponse = await startTests.apply(this, arguments)
|
|
@@ -671,14 +871,15 @@ addHook({
|
|
|
671
871
|
const { result } = task
|
|
672
872
|
// We have to trick vitest into thinking that the test has passed
|
|
673
873
|
// but we want to report it as failed if it did fail
|
|
674
|
-
const
|
|
874
|
+
const switchedStatus = switchedStatuses.get(task)
|
|
875
|
+
const isSwitchedStatus = switchedStatus !== undefined
|
|
675
876
|
|
|
676
877
|
if (result) {
|
|
677
878
|
const { state, duration, errors } = result
|
|
678
879
|
const testError = getCurrentAttemptTestError(task, errors)
|
|
679
880
|
if (attemptToFixTasks.has(task)) {
|
|
680
|
-
const
|
|
681
|
-
recordFinalAttemptToFixExecution(task,
|
|
881
|
+
const attemptToFixStatus = getFinalAttemptToFixStatus(task, state, isSwitchedStatus, testCtx)
|
|
882
|
+
recordFinalAttemptToFixExecution(task, attemptToFixStatus, providedContext)
|
|
682
883
|
}
|
|
683
884
|
|
|
684
885
|
if (state === 'skip') { // programmatic skip
|
|
@@ -686,7 +887,9 @@ addHook({
|
|
|
686
887
|
testName: getTestName(task),
|
|
687
888
|
testSuiteAbsolutePath: task.file.filepath,
|
|
688
889
|
isNew: newTasks.has(task),
|
|
890
|
+
isAttemptToFix: attemptToFixTasks.has(task),
|
|
689
891
|
isDisabled: disabledTasks.has(task),
|
|
892
|
+
isQuarantined: quarantinedTasks.has(task),
|
|
690
893
|
})
|
|
691
894
|
} else if (state === 'pass' && !isSwitchedStatus) {
|
|
692
895
|
if (testCtx) {
|
|
@@ -722,7 +925,8 @@ addHook({
|
|
|
722
925
|
providedContext.isEarlyFlakeDetectionEnabled && (newTasks.has(task) || modifiedTasks.has(task))
|
|
723
926
|
if (isEfdRetry) {
|
|
724
927
|
const statuses = taskToStatuses.get(task)
|
|
725
|
-
const efdRetryCount = efdDeterminedRetries.get(task) ??
|
|
928
|
+
const efdRetryCount = efdDeterminedRetries.get(task) ??
|
|
929
|
+
providedContext.earlyFlakeDetectionRetryPolicy.schedulingRetryCount
|
|
726
930
|
// statuses only includes repetitions (not the initial run), so we check against retry count (not +1)
|
|
727
931
|
if (efdRetryCount > 0 && statuses && statuses.length === efdRetryCount &&
|
|
728
932
|
statuses.every(status => status === 'fail')) {
|
|
@@ -783,7 +987,9 @@ addHook({
|
|
|
783
987
|
testName: getTestName(task),
|
|
784
988
|
testSuiteAbsolutePath: task.file.filepath,
|
|
785
989
|
isNew: newTasks.has(task),
|
|
990
|
+
isAttemptToFix: attemptToFixTasks.has(task),
|
|
786
991
|
isDisabled: disabledTasks.has(task),
|
|
992
|
+
isQuarantined: quarantinedTasks.has(task),
|
|
787
993
|
})
|
|
788
994
|
}
|
|
789
995
|
}
|
|
@@ -807,8 +1013,19 @@ addHook({
|
|
|
807
1013
|
testSuiteErrorCh.runStores(testSuiteCtx, () => {})
|
|
808
1014
|
}
|
|
809
1015
|
|
|
1016
|
+
let coverageFiles
|
|
1017
|
+
if (providedContext.isCodeCoverageEnabled) {
|
|
1018
|
+
const currentCoverageFiles = shouldUseVitestCoverage
|
|
1019
|
+
? getVitestCoverageFiles(repositoryRoot)
|
|
1020
|
+
: await getPreciseCoverageFiles(repositoryRoot)
|
|
1021
|
+
coverageFiles = includePreviouslyCoveredFiles(currentCoverageFiles)
|
|
1022
|
+
}
|
|
1023
|
+
|
|
810
1024
|
await getChannelPromise(testSuiteFinishCh, {
|
|
811
1025
|
status: testSuiteResult.state,
|
|
1026
|
+
coverageFiles,
|
|
1027
|
+
coverageLibrary,
|
|
1028
|
+
testSuiteAbsolutePath,
|
|
812
1029
|
...testSuiteCtx.currentStore,
|
|
813
1030
|
})
|
|
814
1031
|
|