dd-trace 6.7.0 → 6.8.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/test-optimization-validation/command-output-policy.js +5 -5
- package/ci/test-optimization-validation/framework-adapters/playwright.js +5 -5
- package/ci/test-optimization-validation/generated-files.js +9 -9
- package/ci/test-optimization-validation/generated-test-contract.js +1 -3
- package/ci/test-optimization-validation/manifest-scaffold.js +1 -2
- package/ci/test-optimization-validation/offline-output.js +6 -6
- package/ci/test-optimization-validation/runner-command.js +1 -1
- package/ci/test-optimization-validation/safe-files.js +4 -4
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +1 -1
- package/ci/test-optimization-validation/scenarios/test-management.js +2 -2
- package/ci/vitest-no-worker-init-setup.mjs +348 -51
- package/index.d.ts +77 -13
- package/package.json +9 -3
- package/packages/datadog-core/src/utils/src/kebabcase.js +8 -2
- package/packages/datadog-esbuild/index.js +4 -2
- package/packages/datadog-esbuild/src/utils.js +1 -1
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +4 -4
- package/packages/datadog-instrumentations/src/cucumber.js +9 -6
- 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 +2 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +9 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +57 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +50 -18
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +1 -1
- 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.js +8 -14
- package/packages/datadog-instrumentations/src/mocha/common.js +1 -3
- package/packages/datadog-instrumentations/src/mocha/main.js +2 -42
- package/packages/datadog-instrumentations/src/mocha/utils.js +68 -2
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +19 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +446 -42
- package/packages/datadog-instrumentations/src/moleculer/client.js +1 -1
- package/packages/datadog-instrumentations/src/playwright.js +3 -2
- package/packages/datadog-instrumentations/src/rhea.js +1 -1
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +525 -56
- package/packages/datadog-instrumentations/src/vitest-main.js +90 -10
- package/packages/datadog-instrumentations/src/webdriverio.js +914 -0
- 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/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 +5 -8
- package/packages/datadog-plugin-cypress/src/source-map-utils.js +1 -1
- 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 +2 -1
- package/packages/datadog-plugin-jest/src/index.js +2 -3
- package/packages/datadog-plugin-jest/src/util.js +2 -1
- package/packages/datadog-plugin-langchain/src/tokens.js +2 -2
- package/packages/datadog-plugin-mocha/src/index.js +25 -6
- 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 +84 -11
- package/packages/datadog-webpack/index.js +2 -2
- package/packages/dd-trace/src/appsec/downstream_requests.js +1 -1
- 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/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 +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +2 -2
- package/packages/dd-trace/src/appsec/sdk/track_event.js +1 -1
- package/packages/dd-trace/src/appsec/waf/waf_manager.js +1 -1
- 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 +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -1
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +4 -0
- 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/requests/request.js +3 -3
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
- package/packages/dd-trace/src/config/defaults.js +14 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -2
- package/packages/dd-trace/src/config/index.js +15 -0
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/supported-configurations.json +5 -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/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/id.js +1 -1
- package/packages/dd-trace/src/llmobs/constants/tags.js +1 -1
- 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 +51 -12
- 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 +218 -0
- package/packages/dd-trace/src/llmobs/plugins/anthropic/index.js +1 -1
- 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/sdk.js +4 -11
- 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/writers/spans.js +12 -6
- package/packages/dd-trace/src/noop/proxy.js +2 -2
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_transformer.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +2 -1
- 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 +105 -20
- package/packages/dd-trace/src/plugins/index.js +1 -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 +7 -6
- package/packages/dd-trace/src/plugins/util/git.js +1 -1
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +1 -1
- package/packages/dd-trace/src/plugins/util/llm.js +2 -2
- package/packages/dd-trace/src/plugins/util/tags.js +2 -0
- package/packages/dd-trace/src/plugins/util/test.js +38 -12
- 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 +1 -1
- package/packages/dd-trace/src/priority_sampler.js +6 -3
- 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/ritm.js +5 -20
- package/packages/dd-trace/src/startup-log.js +3 -0
- package/packages/dd-trace/src/telemetry/logs/log-collector.js +3 -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
|
@@ -4,6 +4,7 @@ const path = require('node:path')
|
|
|
4
4
|
|
|
5
5
|
const satisfies = require('../../../vendor/dist/semifies')
|
|
6
6
|
|
|
7
|
+
const { RUM_TEST_EXECUTION_ID_COOKIE_NAME } = require('../../dd-trace/src/ci-visibility/rum')
|
|
7
8
|
const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
|
|
8
9
|
const log = require('../../dd-trace/src/log')
|
|
9
10
|
const {
|
|
@@ -29,9 +30,9 @@ const {
|
|
|
29
30
|
testSuiteStartCh,
|
|
30
31
|
} = require('./vitest-util')
|
|
31
32
|
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
33
|
+
// Main-process Vitest reporting used by no-worker-init and Browser Mode.
|
|
34
|
+
// Execution changes are applied by an instrumentation-less setup file in the test environment,
|
|
35
|
+
// while this module creates test spans from Vitest reporter events.
|
|
35
36
|
const mainProcessReporterStates = new WeakMap()
|
|
36
37
|
const loggedAttemptToFixTests = new Set()
|
|
37
38
|
|
|
@@ -53,16 +54,181 @@ const VITEST_NO_WORKER_INIT_SETUP_FILE = path.join(
|
|
|
53
54
|
'ci',
|
|
54
55
|
'vitest-no-worker-init-setup.mjs'
|
|
55
56
|
)
|
|
57
|
+
const VITEST_BROWSER_SETUP_FILE_PLUGIN = {
|
|
58
|
+
name: 'datadog:vitest-browser-setup-file',
|
|
59
|
+
config: configureVitestBrowserSetupFile,
|
|
60
|
+
configResolved: allowVitestBrowserSetupFile,
|
|
61
|
+
}
|
|
56
62
|
const VITEST_NO_WORKER_INIT_ISOLATE_WARNING =
|
|
57
63
|
`${VITEST_NO_WORKER_INIT_REQUEST_ENV} is ignored because Vitest isolate is disabled. ` +
|
|
58
64
|
'The lighter Vitest worker path only works when each test file runs in an isolated worker.'
|
|
59
65
|
const NODE_OPTIONS_QUOTE_RE = /[\s"\\]/
|
|
66
|
+
const VITEST_BROWSER_STACK_LOCATION_RE = /:\d+:\d+$/
|
|
67
|
+
const VITEST_BROWSER_URL_RE = /https?:\/\/[^\s)"'>\]]+/g
|
|
60
68
|
let hasWarnedDisabledIsolate = false
|
|
61
69
|
let hasWarnedUnsupportedVersion = false
|
|
62
70
|
let nodeOptionsBeforeNoWorkerInit
|
|
63
71
|
|
|
64
72
|
function noop () {}
|
|
65
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Removes Vite and Vitest runtime query parameters from browser error URLs.
|
|
76
|
+
*
|
|
77
|
+
* @param {string} url
|
|
78
|
+
* @returns {string}
|
|
79
|
+
*/
|
|
80
|
+
function removeVitestBrowserUrlMetadata (url) {
|
|
81
|
+
const locationMatch = url.match(VITEST_BROWSER_STACK_LOCATION_RE)
|
|
82
|
+
const location = locationMatch?.[0] || ''
|
|
83
|
+
const urlWithoutLocation = location ? url.slice(0, -location.length) : url
|
|
84
|
+
const queryStart = urlWithoutLocation.indexOf('?')
|
|
85
|
+
if (queryStart === -1) return url
|
|
86
|
+
|
|
87
|
+
const fragmentStart = urlWithoutLocation.indexOf('#', queryStart)
|
|
88
|
+
const queryEnd = fragmentStart === -1 ? urlWithoutLocation.length : fragmentStart
|
|
89
|
+
const parameters = urlWithoutLocation.slice(queryStart + 1, queryEnd).split('&')
|
|
90
|
+
const keptParameters = []
|
|
91
|
+
let removedParameter = false
|
|
92
|
+
|
|
93
|
+
for (const parameter of parameters) {
|
|
94
|
+
const valueSeparator = parameter.indexOf('=')
|
|
95
|
+
const name = valueSeparator === -1 ? parameter : parameter.slice(0, valueSeparator)
|
|
96
|
+
if (name === 'import' || name === 'browserv') {
|
|
97
|
+
removedParameter = true
|
|
98
|
+
} else {
|
|
99
|
+
keptParameters.push(parameter)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!removedParameter) return url
|
|
104
|
+
|
|
105
|
+
const query = keptParameters.length ? `?${keptParameters.join('&')}` : ''
|
|
106
|
+
return urlWithoutLocation.slice(0, queryStart) + query + urlWithoutLocation.slice(queryEnd) + location
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Removes Vite and Vitest runtime URL metadata from browser error text.
|
|
111
|
+
*
|
|
112
|
+
* @param {string} value
|
|
113
|
+
* @returns {string}
|
|
114
|
+
*/
|
|
115
|
+
function normalizeVitestBrowserErrorText (value) {
|
|
116
|
+
return value.replaceAll(VITEST_BROWSER_URL_RE, removeVitestBrowserUrlMetadata)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Removes the ephemeral Vite server origin in addition to its runtime query parameters.
|
|
121
|
+
*
|
|
122
|
+
* @param {string} url
|
|
123
|
+
* @returns {string}
|
|
124
|
+
*/
|
|
125
|
+
function normalizeVitestBrowserStackUrl (url) {
|
|
126
|
+
const normalizedUrl = removeVitestBrowserUrlMetadata(url)
|
|
127
|
+
const protocolEnd = normalizedUrl.indexOf('://')
|
|
128
|
+
const pathStart = normalizedUrl.indexOf('/', protocolEnd + 3)
|
|
129
|
+
return pathStart === -1 ? normalizedUrl : normalizedUrl.slice(pathStart)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Normalizes browser URLs in a stack when Vitest does not provide parsed frames.
|
|
134
|
+
*
|
|
135
|
+
* @param {string} value
|
|
136
|
+
* @returns {string}
|
|
137
|
+
*/
|
|
138
|
+
function normalizeVitestBrowserStackText (value) {
|
|
139
|
+
return value.replaceAll(VITEST_BROWSER_URL_RE, normalizeVitestBrowserStackUrl)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Rebuilds an error stack from the source-mapped frames already parsed by Vitest.
|
|
144
|
+
*
|
|
145
|
+
* @param {{
|
|
146
|
+
* stack?: string,
|
|
147
|
+
* stacks?: Array<{ column: number, file: string, line: number, method?: string }>
|
|
148
|
+
* }} error
|
|
149
|
+
* @returns {string|undefined}
|
|
150
|
+
*/
|
|
151
|
+
function normalizeVitestBrowserErrorStack (error) {
|
|
152
|
+
if (typeof error.stack !== 'string') return error.stack
|
|
153
|
+
if (!Array.isArray(error.stacks)) {
|
|
154
|
+
return normalizeVitestBrowserStackText(error.stack)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const firstLineEnd = error.stack.indexOf('\n')
|
|
158
|
+
const stackLines = new Array(error.stacks.length + 1)
|
|
159
|
+
stackLines[0] = normalizeVitestBrowserErrorText(
|
|
160
|
+
firstLineEnd === -1 ? error.stack : error.stack.slice(0, firstLineEnd)
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
for (let index = 0; index < error.stacks.length; index++) {
|
|
164
|
+
const frame = error.stacks[index]
|
|
165
|
+
const location = `${frame.file}:${frame.line}:${frame.column}`
|
|
166
|
+
stackLines[index + 1] = frame.method
|
|
167
|
+
? ` at ${frame.method} (${location})`
|
|
168
|
+
: ` at ${location}`
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return stackLines.join('\n')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Returns an error copy with browser-only URL metadata removed.
|
|
176
|
+
*
|
|
177
|
+
* @param {{
|
|
178
|
+
* message?: string,
|
|
179
|
+
* name?: string,
|
|
180
|
+
* stack?: string,
|
|
181
|
+
* stacks?: Array<{ column: number, file: string, line: number, method?: string }>
|
|
182
|
+
* }|undefined} error
|
|
183
|
+
* @returns {{
|
|
184
|
+
* message?: string,
|
|
185
|
+
* name?: string,
|
|
186
|
+
* stack?: string,
|
|
187
|
+
* stacks?: Array<{ column: number, file: string, line: number, method?: string }>
|
|
188
|
+
* }|undefined}
|
|
189
|
+
*/
|
|
190
|
+
function normalizeVitestBrowserError (error) {
|
|
191
|
+
if (!error) return error
|
|
192
|
+
|
|
193
|
+
const message = typeof error.message === 'string'
|
|
194
|
+
? normalizeVitestBrowserErrorText(error.message)
|
|
195
|
+
: error.message
|
|
196
|
+
const stack = normalizeVitestBrowserErrorStack(error)
|
|
197
|
+
if (message === error.message && stack === error.stack) return error
|
|
198
|
+
|
|
199
|
+
const normalizedError = { ...error }
|
|
200
|
+
if (error.name !== undefined) normalizedError.name = error.name
|
|
201
|
+
if (message !== undefined) normalizedError.message = message
|
|
202
|
+
if (stack !== undefined) normalizedError.stack = stack
|
|
203
|
+
return normalizedError
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Normalizes browser errors without mutating Vitest's task result.
|
|
208
|
+
*
|
|
209
|
+
* @param {Array<{
|
|
210
|
+
* message?: string,
|
|
211
|
+
* name?: string,
|
|
212
|
+
* stack?: string,
|
|
213
|
+
* stacks?: Array<{ column: number, file: string, line: number, method?: string }>
|
|
214
|
+
* }>|undefined} errors
|
|
215
|
+
* @returns {Array<{
|
|
216
|
+
* message?: string,
|
|
217
|
+
* name?: string,
|
|
218
|
+
* stack?: string,
|
|
219
|
+
* stacks?: Array<{ column: number, file: string, line: number, method?: string }>
|
|
220
|
+
* }>}
|
|
221
|
+
*/
|
|
222
|
+
function normalizeVitestBrowserErrors (errors) {
|
|
223
|
+
if (!errors?.length) return []
|
|
224
|
+
|
|
225
|
+
const normalizedErrors = new Array(errors.length)
|
|
226
|
+
for (let index = 0; index < errors.length; index++) {
|
|
227
|
+
normalizedErrors[index] = normalizeVitestBrowserError(errors[index])
|
|
228
|
+
}
|
|
229
|
+
return normalizedErrors
|
|
230
|
+
}
|
|
231
|
+
|
|
66
232
|
function isRequested () {
|
|
67
233
|
return getValueFromEnvSources(VITEST_NO_WORKER_INIT_REQUEST_ENV) === true
|
|
68
234
|
}
|
|
@@ -144,19 +310,22 @@ function isNoWorkerInitPool (pool, isVitestWorkerPool) {
|
|
|
144
310
|
}
|
|
145
311
|
|
|
146
312
|
function configure (ctx, frameworkVersion, testSpecifications, setupData, options) {
|
|
147
|
-
const { getConfiguredEfdRetryCount, state } = options
|
|
313
|
+
const { getConfiguredEfdRetryCount, shouldReportTestModule, state } = options
|
|
148
314
|
addSetupFileToVitestConfigs(ctx, VITEST_NO_WORKER_INIT_SETUP_FILE, testSpecifications)
|
|
315
|
+
addVitestBrowserSetupFileAccess(testSpecifications)
|
|
149
316
|
|
|
150
317
|
const {
|
|
151
318
|
knownTestsBySuite,
|
|
152
319
|
modifiedFiles,
|
|
153
320
|
repositoryRoot,
|
|
154
321
|
testManagementTestsBySuite,
|
|
322
|
+
testPropertiesByFilepath,
|
|
155
323
|
testSessionConfiguration,
|
|
156
324
|
} = setupData
|
|
157
325
|
|
|
158
326
|
setProvidedContext(ctx, {
|
|
159
327
|
_ddVitestWorkerSetup: {
|
|
328
|
+
isActive: true,
|
|
160
329
|
attemptToFixRetries: state.testManagementAttemptToFixRetries,
|
|
161
330
|
attemptToFixTests: getSelectedTestManagementTests(testManagementTestsBySuite, 'isAttemptToFix'),
|
|
162
331
|
disabledTests: getSelectedTestManagementTests(testManagementTestsBySuite, 'isDisabled'),
|
|
@@ -167,14 +336,19 @@ function configure (ctx, frameworkVersion, testSpecifications, setupData, option
|
|
|
167
336
|
earlyFlakeDetectionRetryThresholds: EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS,
|
|
168
337
|
earlyFlakeDetectionSlowRetries: state.earlyFlakeDetectionSlowTestRetries,
|
|
169
338
|
isEarlyFlakeDetectionEnabled: state.isEarlyFlakeDetectionEnabled && !state.isEarlyFlakeDetectionFaulty,
|
|
339
|
+
isRumCorrelationEnabled: !canRaceRumCorrelation(ctx, testSpecifications),
|
|
170
340
|
knownTests: knownTestsBySuite || {},
|
|
171
341
|
modifiedFiles: modifiedFiles || {},
|
|
172
342
|
quarantinedTests: getSelectedTestManagementTests(testManagementTestsBySuite, 'isQuarantined'),
|
|
173
343
|
repositoryRoot: repositoryRoot || process.cwd(),
|
|
344
|
+
rumTestExecutionIdCookieName: RUM_TEST_EXECUTION_ID_COOKIE_NAME,
|
|
345
|
+
testPropertiesByFilepath: testPropertiesByFilepath || {},
|
|
174
346
|
},
|
|
175
|
-
}, 'Could not send Vitest
|
|
347
|
+
}, 'Could not send Vitest setup context, so main-process execution changes will not work.')
|
|
176
348
|
|
|
177
|
-
installMainProcessReporter(ctx, frameworkVersion, testSessionConfiguration || {}, setupData, state
|
|
349
|
+
installMainProcessReporter(ctx, frameworkVersion, testSessionConfiguration || {}, setupData, state, {
|
|
350
|
+
shouldReportTestModule,
|
|
351
|
+
})
|
|
178
352
|
}
|
|
179
353
|
|
|
180
354
|
function deactivate (ctx) {
|
|
@@ -182,6 +356,11 @@ function deactivate (ctx) {
|
|
|
182
356
|
if (installedReporterState) {
|
|
183
357
|
installedReporterState.isActive = false
|
|
184
358
|
}
|
|
359
|
+
setProvidedContext(ctx, {
|
|
360
|
+
_ddVitestWorkerSetup: {
|
|
361
|
+
isActive: false,
|
|
362
|
+
},
|
|
363
|
+
}, 'Could not deactivate Vitest main-process execution changes.')
|
|
185
364
|
}
|
|
186
365
|
|
|
187
366
|
function configureWorkerEnv (workerEnv, shouldSkipWorkerInit = false) {
|
|
@@ -224,14 +403,75 @@ function addSetupFileToVitestConfigs (ctx, setupFile, testSpecifications) {
|
|
|
224
403
|
for (const config of configs) {
|
|
225
404
|
if (!config) continue
|
|
226
405
|
|
|
406
|
+
config.includeTaskLocation = true
|
|
227
407
|
if (config.setupFiles === undefined) {
|
|
228
408
|
config.setupFiles = []
|
|
229
409
|
} else if (typeof config.setupFiles === 'string') {
|
|
230
410
|
config.setupFiles = [config.setupFiles]
|
|
231
411
|
}
|
|
232
412
|
|
|
233
|
-
|
|
234
|
-
|
|
413
|
+
const setupFileIndex = config.setupFiles.indexOf(setupFile)
|
|
414
|
+
if (setupFileIndex !== 0) {
|
|
415
|
+
if (setupFileIndex > 0) {
|
|
416
|
+
config.setupFiles.splice(setupFileIndex, 1)
|
|
417
|
+
}
|
|
418
|
+
config.setupFiles.unshift(setupFile)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Resolves the Vitest runner from the browser project's dependency tree.
|
|
425
|
+
*
|
|
426
|
+
* @param {{
|
|
427
|
+
* resolve?: { dedupe?: string[] }
|
|
428
|
+
* }} viteConfig
|
|
429
|
+
* @returns {void}
|
|
430
|
+
*/
|
|
431
|
+
function configureVitestBrowserSetupFile (viteConfig) {
|
|
432
|
+
viteConfig.resolve ||= {}
|
|
433
|
+
viteConfig.resolve.dedupe ||= []
|
|
434
|
+
|
|
435
|
+
if (!viteConfig.resolve.dedupe.includes('@vitest/runner')) {
|
|
436
|
+
viteConfig.resolve.dedupe.push('@vitest/runner')
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Allows Vite to serve the Datadog setup file after its default workspace detection has run.
|
|
442
|
+
*
|
|
443
|
+
* @param {{ server?: { fs?: { allow?: string[] } } }} viteConfig
|
|
444
|
+
* @returns {void}
|
|
445
|
+
*/
|
|
446
|
+
function allowVitestBrowserSetupFile (viteConfig) {
|
|
447
|
+
const allow = viteConfig.server?.fs?.allow
|
|
448
|
+
if (allow && !allow.includes(VITEST_NO_WORKER_INIT_SETUP_FILE)) {
|
|
449
|
+
allow.push(VITEST_NO_WORKER_INIT_SETUP_FILE)
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Installs the Vite access plugin on each parent project that owns a Browser Mode server.
|
|
455
|
+
*
|
|
456
|
+
* @param {object[]|undefined} testSpecifications
|
|
457
|
+
* @returns {void}
|
|
458
|
+
*/
|
|
459
|
+
function addVitestBrowserSetupFileAccess (testSpecifications) {
|
|
460
|
+
if (!Array.isArray(testSpecifications)) return
|
|
461
|
+
|
|
462
|
+
const configuredProjects = new Set()
|
|
463
|
+
for (const testSpecification of testSpecifications) {
|
|
464
|
+
const project = getTestSpecificationProject(testSpecification)
|
|
465
|
+
if (!isBrowserTestSpecification(testSpecification)) continue
|
|
466
|
+
|
|
467
|
+
const browserServerProject = project?._parent || project
|
|
468
|
+
if (!browserServerProject || configuredProjects.has(browserServerProject)) continue
|
|
469
|
+
|
|
470
|
+
configuredProjects.add(browserServerProject)
|
|
471
|
+
browserServerProject.options ||= {}
|
|
472
|
+
browserServerProject.options.plugins ||= []
|
|
473
|
+
if (!browserServerProject.options.plugins.includes(VITEST_BROWSER_SETUP_FILE_PLUGIN)) {
|
|
474
|
+
browserServerProject.options.plugins.push(VITEST_BROWSER_SETUP_FILE_PLUGIN)
|
|
235
475
|
}
|
|
236
476
|
}
|
|
237
477
|
}
|
|
@@ -279,12 +519,20 @@ function getSelectedTestManagementTests (testManagementTestsBySuite, propertyNam
|
|
|
279
519
|
return selectedTests
|
|
280
520
|
}
|
|
281
521
|
|
|
282
|
-
function installMainProcessReporter (
|
|
522
|
+
function installMainProcessReporter (
|
|
523
|
+
ctx,
|
|
524
|
+
frameworkVersion,
|
|
525
|
+
testSessionConfiguration,
|
|
526
|
+
testOptimizationData,
|
|
527
|
+
state,
|
|
528
|
+
options
|
|
529
|
+
) {
|
|
283
530
|
if (!ctx?.reporters) return
|
|
284
531
|
|
|
285
532
|
const reporterState = {
|
|
286
533
|
frameworkVersion,
|
|
287
534
|
isActive: true,
|
|
535
|
+
shouldReportTestModule: options.shouldReportTestModule,
|
|
288
536
|
state,
|
|
289
537
|
testSessionConfiguration,
|
|
290
538
|
testOptimizationData,
|
|
@@ -307,26 +555,26 @@ function createMainProcessReporter (reporterState) {
|
|
|
307
555
|
|
|
308
556
|
return {
|
|
309
557
|
onTestModuleStart (testModule) {
|
|
310
|
-
if (!
|
|
558
|
+
if (!shouldReport(testModule)) return
|
|
311
559
|
|
|
312
560
|
startTestSuite(testModule)
|
|
313
561
|
},
|
|
314
562
|
|
|
315
563
|
onTestModuleEnd (testModule) {
|
|
316
|
-
if (!
|
|
564
|
+
if (!shouldReport(testModule)) return
|
|
317
565
|
|
|
318
566
|
return reportTestModule(testModule)
|
|
319
567
|
},
|
|
320
568
|
|
|
321
569
|
onTestCaseResult (testCase) {
|
|
322
|
-
if (!
|
|
570
|
+
if (!shouldReport(testCase.module || testCase.task?.file)) return
|
|
323
571
|
|
|
324
572
|
const task = getTestCaseTask(testCase)
|
|
325
573
|
recordFinalTaskAttemptResult(task)
|
|
326
574
|
},
|
|
327
575
|
|
|
328
576
|
onTaskUpdate (packs, events) {
|
|
329
|
-
if (!
|
|
577
|
+
if (!isReporterActive()) return
|
|
330
578
|
if (!events) return
|
|
331
579
|
|
|
332
580
|
for (const event of events) {
|
|
@@ -336,23 +584,43 @@ function createMainProcessReporter (reporterState) {
|
|
|
336
584
|
}
|
|
337
585
|
},
|
|
338
586
|
|
|
587
|
+
onWatcherRerun () {
|
|
588
|
+
for (const testSuiteCtx of testSuiteContexts.values()) {
|
|
589
|
+
testSuiteFinishCh.publish({
|
|
590
|
+
status: 'skip',
|
|
591
|
+
deferFlush: true,
|
|
592
|
+
onDone: noop,
|
|
593
|
+
...testSuiteCtx.currentStore,
|
|
594
|
+
})
|
|
595
|
+
}
|
|
596
|
+
testSuiteContexts.clear()
|
|
597
|
+
finishedTestModules.clear()
|
|
598
|
+
taskAttemptStatuses.clear()
|
|
599
|
+
tasksWithRecordedFinalAttempt.clear()
|
|
600
|
+
},
|
|
601
|
+
|
|
339
602
|
onFinished (files) {
|
|
340
|
-
if (!
|
|
603
|
+
if (!isReporterActive()) return
|
|
341
604
|
if (!files) return
|
|
342
605
|
|
|
343
606
|
for (const file of files) {
|
|
344
607
|
const testModule = createTestModuleFromFile(file)
|
|
345
|
-
if (!finishedTestModules.has(file.id)) {
|
|
608
|
+
if (shouldReport(testModule) && !finishedTestModules.has(file.id)) {
|
|
346
609
|
reportTestModule(testModule)
|
|
347
610
|
}
|
|
348
611
|
}
|
|
349
612
|
},
|
|
350
613
|
}
|
|
351
614
|
|
|
352
|
-
function
|
|
615
|
+
function isReporterActive () {
|
|
353
616
|
return reporterState.isActive === true
|
|
354
617
|
}
|
|
355
618
|
|
|
619
|
+
function shouldReport (testModule) {
|
|
620
|
+
return isReporterActive() &&
|
|
621
|
+
(!reporterState.shouldReportTestModule || reporterState.shouldReportTestModule(testModule))
|
|
622
|
+
}
|
|
623
|
+
|
|
356
624
|
function recordTaskAttemptStatus (taskId, status, attemptCount) {
|
|
357
625
|
let statuses = taskAttemptStatuses.get(taskId)
|
|
358
626
|
if (!statuses) {
|
|
@@ -389,7 +657,7 @@ function createMainProcessReporter (reporterState) {
|
|
|
389
657
|
let testSuiteError
|
|
390
658
|
|
|
391
659
|
for (const task of testTasks) {
|
|
392
|
-
const testReport = getTestReport(task, testSuiteCtx.currentStore)
|
|
660
|
+
const testReport = getTestReport(task, testSuiteCtx.currentStore, testSuiteCtx.browserEnvironment)
|
|
393
661
|
testReports.push(testReport)
|
|
394
662
|
|
|
395
663
|
for (const attempt of testReport.nonFinalAttempts) {
|
|
@@ -412,7 +680,9 @@ function createMainProcessReporter (reporterState) {
|
|
|
412
680
|
}
|
|
413
681
|
|
|
414
682
|
if (testSuiteError) {
|
|
415
|
-
testSuiteCtx.error =
|
|
683
|
+
testSuiteCtx.error = testSuiteCtx.browserEnvironment
|
|
684
|
+
? normalizeVitestBrowserError(testSuiteError)
|
|
685
|
+
: testSuiteError
|
|
416
686
|
testSuiteErrorCh.runStores(testSuiteCtx, () => {})
|
|
417
687
|
}
|
|
418
688
|
|
|
@@ -428,16 +698,22 @@ function createMainProcessReporter (reporterState) {
|
|
|
428
698
|
function startTestSuite (testModule) {
|
|
429
699
|
const { frameworkVersion, testSessionConfiguration } = reporterState
|
|
430
700
|
const testModuleId = getTestModuleId(testModule)
|
|
701
|
+
const browserEnvironment = getTestModuleBrowserEnvironment(testModule)
|
|
431
702
|
const testSuiteCtx = {
|
|
703
|
+
browserEnvironment,
|
|
704
|
+
browserDriver: browserEnvironment?.browserDriver,
|
|
705
|
+
browserName: browserEnvironment?.browserName,
|
|
706
|
+
browserProjectName: browserEnvironment?.browserProjectName,
|
|
432
707
|
testSuiteAbsolutePath: getTestModuleFilepath(testModule),
|
|
433
708
|
frameworkVersion,
|
|
434
709
|
testSessionId: testSessionConfiguration.testSessionId,
|
|
435
710
|
testModuleId: testSessionConfiguration.testModuleId,
|
|
436
711
|
testCommand: testSessionConfiguration.testCommand,
|
|
437
712
|
repositoryRoot: testSessionConfiguration.repositoryRoot,
|
|
438
|
-
codeOwnersEntries: testSessionConfiguration.codeOwnersEntries,
|
|
713
|
+
codeOwnersEntries: testSessionConfiguration.codeOwnersEntries ?? undefined,
|
|
439
714
|
requestErrorTags: reporterState.state.requestErrorTags,
|
|
440
715
|
isTestFrameworkWorker: true,
|
|
716
|
+
isBrowserMode: browserEnvironment !== undefined,
|
|
441
717
|
isVitestNoWorkerInitActive: true,
|
|
442
718
|
}
|
|
443
719
|
testSuiteStartCh.runStores(testSuiteCtx, () => {})
|
|
@@ -445,7 +721,7 @@ function createMainProcessReporter (reporterState) {
|
|
|
445
721
|
return testSuiteCtx
|
|
446
722
|
}
|
|
447
723
|
|
|
448
|
-
function getTestReport (task, testSuiteStore) {
|
|
724
|
+
function getTestReport (task, testSuiteStore, browserEnvironment) {
|
|
449
725
|
const result = task.result
|
|
450
726
|
const testSuiteAbsolutePath = task.file?.filepath
|
|
451
727
|
const testName = getTestName(task)
|
|
@@ -459,6 +735,7 @@ function createMainProcessReporter (reporterState) {
|
|
|
459
735
|
|
|
460
736
|
if (testProperties.isAttemptToFix && task.meta?.__ddTestOptAtfStatuses?.length) {
|
|
461
737
|
return getRepeatedTestReport(task, testName, testSuiteAbsolutePath, testProperties, status, {
|
|
738
|
+
browserEnvironment,
|
|
462
739
|
errorCounts: task.meta.__ddTestOptAtfErrorCounts,
|
|
463
740
|
finalStatus: getAttemptToFixFinalStatus,
|
|
464
741
|
state,
|
|
@@ -470,6 +747,7 @@ function createMainProcessReporter (reporterState) {
|
|
|
470
747
|
|
|
471
748
|
if (testProperties.isEarlyFlakeDetection && task.meta?.__ddTestOptEfdStatuses?.length) {
|
|
472
749
|
return getRepeatedTestReport(task, testName, testSuiteAbsolutePath, testProperties, status, {
|
|
750
|
+
browserEnvironment,
|
|
473
751
|
errorCounts: task.meta.__ddTestOptEfdErrorCounts,
|
|
474
752
|
finalStatus: getEarlyFlakeDetectionFinalStatus,
|
|
475
753
|
state,
|
|
@@ -481,8 +759,9 @@ function createMainProcessReporter (reporterState) {
|
|
|
481
759
|
|
|
482
760
|
if (!testProperties.isAttemptToFix && task.meta?.__ddTestOptRepeatStatuses?.length) {
|
|
483
761
|
return getRepeatedTestReport(task, testName, testSuiteAbsolutePath, testProperties, status, {
|
|
762
|
+
browserEnvironment,
|
|
484
763
|
errorCounts: task.meta.__ddTestOptRepeatErrorCounts,
|
|
485
|
-
finalStatus: () => status,
|
|
764
|
+
finalStatus: () => getExternalFinalStatus(status, testProperties),
|
|
486
765
|
state,
|
|
487
766
|
statuses: task.meta.__ddTestOptRepeatStatuses,
|
|
488
767
|
testSuiteStore,
|
|
@@ -491,11 +770,18 @@ function createMainProcessReporter (reporterState) {
|
|
|
491
770
|
}
|
|
492
771
|
|
|
493
772
|
const attemptStatuses = taskAttemptStatuses.get(task.id)
|
|
494
|
-
|
|
773
|
+
const retryCount = task.result?.retryCount || 0
|
|
774
|
+
if (
|
|
775
|
+
!testProperties.isAttemptToFix &&
|
|
776
|
+
!testProperties.isEarlyFlakeDetection &&
|
|
777
|
+
(attemptStatuses?.length > 1 || retryCount > 0)
|
|
778
|
+
) {
|
|
495
779
|
return getRepeatedTestReport(task, testName, testSuiteAbsolutePath, testProperties, status, {
|
|
496
|
-
|
|
780
|
+
browserEnvironment,
|
|
781
|
+
errorCounts: task.meta?.__ddTestOptRetryErrorCounts,
|
|
782
|
+
finalStatus: () => getExternalFinalStatus(status, testProperties),
|
|
497
783
|
state,
|
|
498
|
-
statuses: attemptStatuses,
|
|
784
|
+
statuses: getRetriedTaskStatuses(attemptStatuses, retryCount, status),
|
|
499
785
|
testSuiteStore,
|
|
500
786
|
type: 'external',
|
|
501
787
|
})
|
|
@@ -503,6 +789,7 @@ function createMainProcessReporter (reporterState) {
|
|
|
503
789
|
|
|
504
790
|
if (!testProperties.isAttemptToFix && task.result?.repeatCount > 0) {
|
|
505
791
|
return getRepeatedTestReport(task, testName, testSuiteAbsolutePath, testProperties, status, {
|
|
792
|
+
browserEnvironment,
|
|
506
793
|
finalStatus: () => status,
|
|
507
794
|
state,
|
|
508
795
|
statuses: getRepeatedTaskStatuses(task, status),
|
|
@@ -522,24 +809,14 @@ function createMainProcessReporter (reporterState) {
|
|
|
522
809
|
result.state = 'pass'
|
|
523
810
|
}
|
|
524
811
|
|
|
525
|
-
const errors =
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if (status !== 'skip') {
|
|
530
|
-
for (let index = 0; index < errors.length; index++) {
|
|
531
|
-
if (index === finalErrorIndex) continue
|
|
532
|
-
nonFinalAttempts.push({
|
|
533
|
-
error: errors[index],
|
|
534
|
-
isRetry: index > 0,
|
|
535
|
-
status: 'fail',
|
|
536
|
-
})
|
|
537
|
-
}
|
|
538
|
-
}
|
|
812
|
+
const errors = browserEnvironment
|
|
813
|
+
? normalizeVitestBrowserErrors(result?.errors)
|
|
814
|
+
: result?.errors || []
|
|
539
815
|
|
|
540
816
|
return {
|
|
817
|
+
browserEnvironment,
|
|
541
818
|
errors,
|
|
542
|
-
nonFinalAttempts,
|
|
819
|
+
nonFinalAttempts: [],
|
|
543
820
|
state,
|
|
544
821
|
status,
|
|
545
822
|
task,
|
|
@@ -643,6 +920,7 @@ function createTaskFromReportedTask (reportedTask, file, suite) {
|
|
|
643
920
|
id: reportedTask.id,
|
|
644
921
|
type: reportedTask.type,
|
|
645
922
|
name: reportedTask.name || reportedTask.relativeModuleId || reportedTask.moduleId,
|
|
923
|
+
location: reportedTask.location,
|
|
646
924
|
mode: reportedTask.options?.mode,
|
|
647
925
|
meta: getReportedTaskMeta(reportedTask),
|
|
648
926
|
result: getReportedTaskResult(reportedTask),
|
|
@@ -731,8 +1009,10 @@ function getTestModuleProjectName (testModule) {
|
|
|
731
1009
|
|
|
732
1010
|
function getRepeatedTestReport (task, testName, testSuiteAbsolutePath, testProperties, status, options) {
|
|
733
1011
|
const result = task.result
|
|
734
|
-
const
|
|
735
|
-
const
|
|
1012
|
+
const { browserEnvironment, errorCounts, finalStatus, state, statuses, testSuiteStore, type } = options
|
|
1013
|
+
const errors = browserEnvironment
|
|
1014
|
+
? normalizeVitestBrowserErrors(result?.errors)
|
|
1015
|
+
: result?.errors || []
|
|
736
1016
|
const finalAttemptStatus = finalStatus(statuses)
|
|
737
1017
|
const hasFailure = finalAttemptStatus === 'fail'
|
|
738
1018
|
const attempts = []
|
|
@@ -757,7 +1037,9 @@ function getRepeatedTestReport (task, testName, testSuiteAbsolutePath, testPrope
|
|
|
757
1037
|
errorIndex++
|
|
758
1038
|
}
|
|
759
1039
|
const attempt = {
|
|
1040
|
+
index,
|
|
760
1041
|
attemptToFixFailed: type === 'attempt_to_fix' && isFinalAttempt && hasFailure,
|
|
1042
|
+
duration: task.meta?.__ddTestOptAttemptDurations?.[index],
|
|
761
1043
|
earlyFlakeAbortReason: type === 'early_flake_detection' && isFinalAttempt
|
|
762
1044
|
? task.meta?.__ddTestOptEfdAbortReason
|
|
763
1045
|
: undefined,
|
|
@@ -783,6 +1065,7 @@ function getRepeatedTestReport (task, testName, testSuiteAbsolutePath, testPrope
|
|
|
783
1065
|
}
|
|
784
1066
|
|
|
785
1067
|
return {
|
|
1068
|
+
browserEnvironment,
|
|
786
1069
|
errors,
|
|
787
1070
|
finalAttempt: attempts.at(-1),
|
|
788
1071
|
nonFinalAttempts: attempts.slice(0, -1),
|
|
@@ -841,6 +1124,17 @@ function getEarlyFlakeDetectionFinalStatus (statuses) {
|
|
|
841
1124
|
return statuses.includes('pass') ? 'pass' : 'fail'
|
|
842
1125
|
}
|
|
843
1126
|
|
|
1127
|
+
/**
|
|
1128
|
+
* Returns the final status for user-configured retries and repeats.
|
|
1129
|
+
*
|
|
1130
|
+
* @param {string} status
|
|
1131
|
+
* @param {{ isDisabled?: boolean, isQuarantined?: boolean }} testProperties
|
|
1132
|
+
* @returns {string}
|
|
1133
|
+
*/
|
|
1134
|
+
function getExternalFinalStatus (status, testProperties) {
|
|
1135
|
+
return testProperties.isDisabled || testProperties.isQuarantined ? 'skip' : status
|
|
1136
|
+
}
|
|
1137
|
+
|
|
844
1138
|
function getRepeatedTaskStatuses (task, status) {
|
|
845
1139
|
const repeatedStatuses = []
|
|
846
1140
|
const repeatCount = task.result?.repeatCount || 0
|
|
@@ -850,8 +1144,27 @@ function getRepeatedTaskStatuses (task, status) {
|
|
|
850
1144
|
return repeatedStatuses
|
|
851
1145
|
}
|
|
852
1146
|
|
|
1147
|
+
/**
|
|
1148
|
+
* Completes reporter retry events with Vitest's final retry count and status.
|
|
1149
|
+
*
|
|
1150
|
+
* @param {string[]} recordedStatuses
|
|
1151
|
+
* @param {number} retryCount
|
|
1152
|
+
* @param {string} finalStatus
|
|
1153
|
+
* @returns {string[]}
|
|
1154
|
+
*/
|
|
1155
|
+
function getRetriedTaskStatuses (recordedStatuses = [], retryCount, finalStatus) {
|
|
1156
|
+
const statuses = [...recordedStatuses]
|
|
1157
|
+
const attemptCount = Math.max(statuses.length, retryCount + 1)
|
|
1158
|
+
while (statuses.length < attemptCount - 1) {
|
|
1159
|
+
statuses.push('fail')
|
|
1160
|
+
}
|
|
1161
|
+
statuses[attemptCount - 1] = finalStatus
|
|
1162
|
+
return statuses
|
|
1163
|
+
}
|
|
1164
|
+
|
|
853
1165
|
function reportFinalTestAttempt (testReport) {
|
|
854
1166
|
const {
|
|
1167
|
+
browserEnvironment,
|
|
855
1168
|
errors,
|
|
856
1169
|
state,
|
|
857
1170
|
status,
|
|
@@ -863,13 +1176,21 @@ function reportFinalTestAttempt (testReport) {
|
|
|
863
1176
|
} = testReport
|
|
864
1177
|
|
|
865
1178
|
if (status === 'skip') {
|
|
1179
|
+
const {
|
|
1180
|
+
isRumActive,
|
|
1181
|
+
testExecutionId,
|
|
1182
|
+
} = getRumCorrelation(task)
|
|
866
1183
|
testSkipCh.publish({
|
|
1184
|
+
...browserEnvironment,
|
|
867
1185
|
testName,
|
|
868
1186
|
testSuiteAbsolutePath,
|
|
869
1187
|
isNew: testProperties.isNew,
|
|
870
1188
|
isDisabled: testProperties.isDisabled,
|
|
1189
|
+
isRumActive,
|
|
871
1190
|
isTestFrameworkWorker: true,
|
|
872
1191
|
requestErrorTags: state.requestErrorTags,
|
|
1192
|
+
testStartLine: task.location?.line,
|
|
1193
|
+
testExecutionId,
|
|
873
1194
|
...testSuiteStore,
|
|
874
1195
|
})
|
|
875
1196
|
return
|
|
@@ -880,7 +1201,7 @@ function reportFinalTestAttempt (testReport) {
|
|
|
880
1201
|
const finalAttempt = testReport.finalAttempt
|
|
881
1202
|
|
|
882
1203
|
if (status === 'fail') {
|
|
883
|
-
const error =
|
|
1204
|
+
const error = finalAttempt?.error || errors[0]
|
|
884
1205
|
reportTestAttempt(testReport, finalAttempt || {
|
|
885
1206
|
error,
|
|
886
1207
|
finalStatus,
|
|
@@ -892,7 +1213,7 @@ function reportFinalTestAttempt (testReport) {
|
|
|
892
1213
|
true,
|
|
893
1214
|
true
|
|
894
1215
|
),
|
|
895
|
-
isRetry:
|
|
1216
|
+
isRetry: (result?.retryCount || 0) > 0 || (result?.repeatCount || 0) > 0,
|
|
896
1217
|
status: 'fail',
|
|
897
1218
|
})
|
|
898
1219
|
return finalStatus === 'skip' ? undefined : error
|
|
@@ -900,13 +1221,14 @@ function reportFinalTestAttempt (testReport) {
|
|
|
900
1221
|
|
|
901
1222
|
reportTestAttempt(testReport, finalAttempt || {
|
|
902
1223
|
finalStatus,
|
|
903
|
-
isRetry:
|
|
1224
|
+
isRetry: (result?.retryCount || 0) > 0 || (result?.repeatCount || 0) > 0,
|
|
904
1225
|
status: 'pass',
|
|
905
1226
|
})
|
|
906
1227
|
}
|
|
907
1228
|
|
|
908
1229
|
function reportTestAttempt (testReport, attempt) {
|
|
909
1230
|
const {
|
|
1231
|
+
browserEnvironment,
|
|
910
1232
|
state,
|
|
911
1233
|
task,
|
|
912
1234
|
testName,
|
|
@@ -916,7 +1238,27 @@ function reportTestAttempt (testReport, attempt) {
|
|
|
916
1238
|
} = testReport
|
|
917
1239
|
const result = task.result
|
|
918
1240
|
const status = attempt.status
|
|
1241
|
+
const {
|
|
1242
|
+
isRumActive,
|
|
1243
|
+
testExecutionId,
|
|
1244
|
+
} = getRumCorrelation(task, attempt.index)
|
|
1245
|
+
const attemptStartTimes = task.meta?.__ddTestOptAttemptStartTimes
|
|
1246
|
+
const attemptStartIndex = attempt.index ?? (attemptStartTimes?.length || 1) - 1
|
|
1247
|
+
const attemptStartTime = attemptStartTimes?.[attemptStartIndex]
|
|
1248
|
+
let duration
|
|
1249
|
+
if (status === 'pass') {
|
|
1250
|
+
if (
|
|
1251
|
+
attempt.duration !== undefined ||
|
|
1252
|
+
(!attempt.isRetry && isFinalTestAttempt(testReport, attempt) && result?.duration !== undefined)
|
|
1253
|
+
) {
|
|
1254
|
+
duration = attempt.duration ?? result.duration
|
|
1255
|
+
}
|
|
1256
|
+
} else {
|
|
1257
|
+
duration = attempt.duration ??
|
|
1258
|
+
(shouldUseTaskDurationForFailure(testReport, attempt) ? result?.duration : undefined)
|
|
1259
|
+
}
|
|
919
1260
|
const testCtx = {
|
|
1261
|
+
...browserEnvironment,
|
|
920
1262
|
currentStore: testSuiteStore,
|
|
921
1263
|
testName,
|
|
922
1264
|
testSuiteAbsolutePath,
|
|
@@ -931,8 +1273,14 @@ function reportTestAttempt (testReport, attempt) {
|
|
|
931
1273
|
isRetryReasonAttemptToFix: testProperties.isAttemptToFix && attempt.isRetry,
|
|
932
1274
|
isRetryReasonAtr: !testProperties.isAttemptToFix && !testProperties.isEarlyFlakeDetection &&
|
|
933
1275
|
testProperties.isFlakyTestRetries,
|
|
1276
|
+
isRumActive,
|
|
934
1277
|
isTestFrameworkWorker: true,
|
|
935
1278
|
requestErrorTags: state.requestErrorTags,
|
|
1279
|
+
startTime: Number.isFinite(attemptStartTime)
|
|
1280
|
+
? attemptStartTime
|
|
1281
|
+
: (Number.isFinite(duration) && duration >= 0 ? Date.now() - duration : undefined),
|
|
1282
|
+
testStartLine: task.location?.line,
|
|
1283
|
+
testExecutionId,
|
|
936
1284
|
}
|
|
937
1285
|
if (testProperties.isAttemptToFix) {
|
|
938
1286
|
recordAttemptToFixExecution(state.attemptToFixExecutions, {
|
|
@@ -954,13 +1302,8 @@ function reportTestAttempt (testReport, attempt) {
|
|
|
954
1302
|
testStartCh.runStores(testCtx, () => {})
|
|
955
1303
|
testCtx.status = status
|
|
956
1304
|
testCtx.task = task
|
|
957
|
-
if (
|
|
958
|
-
|
|
959
|
-
!attempt.isRetry &&
|
|
960
|
-
isFinalTestAttempt(testReport, attempt) &&
|
|
961
|
-
result?.duration !== undefined
|
|
962
|
-
) {
|
|
963
|
-
testCtx.duration = result.duration
|
|
1305
|
+
if (status === 'pass' && duration !== undefined) {
|
|
1306
|
+
testCtx.duration = duration
|
|
964
1307
|
}
|
|
965
1308
|
testFinishTimeCh.runStores(testCtx, () => {})
|
|
966
1309
|
|
|
@@ -975,7 +1318,7 @@ function reportTestAttempt (testReport, attempt) {
|
|
|
975
1318
|
}
|
|
976
1319
|
|
|
977
1320
|
testErrorCh.publish({
|
|
978
|
-
duration
|
|
1321
|
+
duration,
|
|
979
1322
|
error: attempt.error,
|
|
980
1323
|
earlyFlakeAbortReason: attempt.earlyFlakeAbortReason,
|
|
981
1324
|
finalStatus: attempt.finalStatus,
|
|
@@ -985,6 +1328,24 @@ function reportTestAttempt (testReport, attempt) {
|
|
|
985
1328
|
})
|
|
986
1329
|
}
|
|
987
1330
|
|
|
1331
|
+
/**
|
|
1332
|
+
* Returns the browser RUM correlation metadata for a test attempt.
|
|
1333
|
+
*
|
|
1334
|
+
* @param {object} task
|
|
1335
|
+
* @param {number|undefined} attemptIndex
|
|
1336
|
+
* @returns {{ isRumActive?: boolean, testExecutionId?: string }}
|
|
1337
|
+
*/
|
|
1338
|
+
function getRumCorrelation (task, attemptIndex) {
|
|
1339
|
+
const testExecutionIds = task.meta?.__ddTestOptRumTestExecutionIds
|
|
1340
|
+
if (!Array.isArray(testExecutionIds) || testExecutionIds.length === 0) return {}
|
|
1341
|
+
|
|
1342
|
+
attemptIndex ??= testExecutionIds.length - 1
|
|
1343
|
+
return {
|
|
1344
|
+
isRumActive: task.meta?.__ddTestOptRumActive?.[attemptIndex] === true,
|
|
1345
|
+
testExecutionId: testExecutionIds[attemptIndex],
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
|
|
988
1349
|
function isFinalTestAttempt (testReport, attempt) {
|
|
989
1350
|
return attempt.finalStatus !== undefined || testReport.finalAttempt === undefined
|
|
990
1351
|
}
|
|
@@ -1207,10 +1568,11 @@ function getTestSpecificationPool (testSpecification) {
|
|
|
1207
1568
|
const options = getTestSpecificationOptions(testSpecification)
|
|
1208
1569
|
const file = getTestSpecificationFile(testSpecification)
|
|
1209
1570
|
const project = getTestSpecificationProject(testSpecification)
|
|
1571
|
+
const projectConfig = safeConfig(project)
|
|
1210
1572
|
return options?.pool ||
|
|
1211
1573
|
file?.pool ||
|
|
1212
1574
|
testSpecification?.pool ||
|
|
1213
|
-
|
|
1575
|
+
projectConfig?.pool ||
|
|
1214
1576
|
project?.serializedConfig?.pool ||
|
|
1215
1577
|
project?.pool
|
|
1216
1578
|
}
|
|
@@ -1243,15 +1605,16 @@ function getTestSpecificationIsolate (testSpecification, pool) {
|
|
|
1243
1605
|
const options = getTestSpecificationOptions(testSpecification)
|
|
1244
1606
|
const file = getTestSpecificationFile(testSpecification)
|
|
1245
1607
|
const project = getTestSpecificationProject(testSpecification)
|
|
1608
|
+
const projectConfig = safeConfig(project)
|
|
1246
1609
|
return getPoolOptionsIsolate(options, pool) ??
|
|
1247
1610
|
getPoolOptionsIsolate(file, pool) ??
|
|
1248
|
-
getPoolOptionsIsolate(
|
|
1611
|
+
getPoolOptionsIsolate(projectConfig, pool) ??
|
|
1249
1612
|
getPoolOptionsIsolate(project?.serializedConfig, pool) ??
|
|
1250
1613
|
getPoolOptionsIsolate(project, pool) ??
|
|
1251
1614
|
getPoolOptionsIsolate(testSpecification, pool) ??
|
|
1252
1615
|
options?.isolate ??
|
|
1253
1616
|
file?.isolate ??
|
|
1254
|
-
|
|
1617
|
+
projectConfig?.isolate ??
|
|
1255
1618
|
project?.serializedConfig?.isolate ??
|
|
1256
1619
|
project?.isolate ??
|
|
1257
1620
|
testSpecification?.isolate
|
|
@@ -1263,7 +1626,7 @@ function getEffectiveTestSpecificationIsolate (testSpecification, pool, defaultI
|
|
|
1263
1626
|
}
|
|
1264
1627
|
|
|
1265
1628
|
function getProjectName (project) {
|
|
1266
|
-
return normalizeProjectName(project?.name || project?.
|
|
1629
|
+
return normalizeProjectName(project?.name || safeConfig(project)?.name || project?.test?.name)
|
|
1267
1630
|
}
|
|
1268
1631
|
|
|
1269
1632
|
function normalizeProjectName (name) {
|
|
@@ -1273,6 +1636,111 @@ function normalizeProjectName (name) {
|
|
|
1273
1636
|
return typeof label === 'string' ? label : undefined
|
|
1274
1637
|
}
|
|
1275
1638
|
|
|
1639
|
+
/**
|
|
1640
|
+
* Returns whether a Vitest specification runs in Browser Mode.
|
|
1641
|
+
*
|
|
1642
|
+
* @param {object} testSpecification
|
|
1643
|
+
* @returns {boolean}
|
|
1644
|
+
*/
|
|
1645
|
+
function isBrowserTestSpecification (testSpecification) {
|
|
1646
|
+
return getTestSpecificationPool(testSpecification) === 'browser' ||
|
|
1647
|
+
isBrowserProject(getTestSpecificationProject(testSpecification))
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
/**
|
|
1651
|
+
* Returns whether a Vitest project has Browser Mode enabled.
|
|
1652
|
+
*
|
|
1653
|
+
* @param {object|undefined} project
|
|
1654
|
+
* @returns {boolean}
|
|
1655
|
+
*/
|
|
1656
|
+
function isBrowserProject (project) {
|
|
1657
|
+
try {
|
|
1658
|
+
if (project?.isBrowserEnabled?.() === true) return true
|
|
1659
|
+
} catch {}
|
|
1660
|
+
return getProjectReportingConfig(project)?.browser?.enabled === true
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
/**
|
|
1664
|
+
* Returns whether Vitest can overlap tests or their setup and teardown.
|
|
1665
|
+
*
|
|
1666
|
+
* @param {object|undefined} config
|
|
1667
|
+
* @returns {boolean}
|
|
1668
|
+
*/
|
|
1669
|
+
function hasConcurrentTestExecution (config) {
|
|
1670
|
+
const sequence = config?.sequence
|
|
1671
|
+
return sequence?.concurrent === true ||
|
|
1672
|
+
sequence?.hooks === 'parallel' ||
|
|
1673
|
+
sequence?.setupFiles === 'parallel'
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
/**
|
|
1677
|
+
* Returns whether Vitest can overlap browser execution that shares the RUM correlation cookie origin.
|
|
1678
|
+
*
|
|
1679
|
+
* @param {object} ctx
|
|
1680
|
+
* @param {object[]|undefined} testSpecifications
|
|
1681
|
+
* @returns {boolean}
|
|
1682
|
+
*/
|
|
1683
|
+
function canRaceRumCorrelation (ctx, testSpecifications) {
|
|
1684
|
+
if (!Array.isArray(testSpecifications)) {
|
|
1685
|
+
return hasConcurrentTestExecution(safeConfig(ctx))
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
let browserFileCount = 0
|
|
1689
|
+
let hasBrowserProject = false
|
|
1690
|
+
let project
|
|
1691
|
+
for (const testSpecification of testSpecifications) {
|
|
1692
|
+
const testProject = getTestSpecificationProject(testSpecification)
|
|
1693
|
+
if (!isBrowserTestSpecification(testSpecification)) continue
|
|
1694
|
+
|
|
1695
|
+
const config = getProjectReportingConfig(testProject) || safeConfig(ctx)
|
|
1696
|
+
if (hasConcurrentTestExecution(config)) return true
|
|
1697
|
+
|
|
1698
|
+
browserFileCount++
|
|
1699
|
+
if (hasBrowserProject && testProject !== project) return true
|
|
1700
|
+
project = testProject
|
|
1701
|
+
hasBrowserProject = true
|
|
1702
|
+
}
|
|
1703
|
+
if (browserFileCount < 2) return false
|
|
1704
|
+
|
|
1705
|
+
const config = getProjectReportingConfig(project) || safeConfig(ctx)
|
|
1706
|
+
const fileParallelism = config?.browser?.fileParallelism ?? config?.fileParallelism
|
|
1707
|
+
return fileParallelism !== false && config?.maxWorkers !== 1
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
function getTestModuleBrowserEnvironment (testModule) {
|
|
1711
|
+
const project = testModule?.project
|
|
1712
|
+
const config = getProjectReportingConfig(project)
|
|
1713
|
+
const isBrowserMode = testModule?.task?.pool === 'browser' ||
|
|
1714
|
+
testModule?.pool === 'browser' ||
|
|
1715
|
+
isBrowserProject(project)
|
|
1716
|
+
if (!isBrowserMode) return
|
|
1717
|
+
|
|
1718
|
+
const provider = config?.browser?.provider
|
|
1719
|
+
const browserDriver = typeof provider === 'string' ? provider : provider?.name
|
|
1720
|
+
|
|
1721
|
+
return {
|
|
1722
|
+
browserDriver,
|
|
1723
|
+
browserName: config?.browser?.name,
|
|
1724
|
+
browserProjectName: getTestModuleProjectName(testModule),
|
|
1725
|
+
isBrowserMode: true,
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
/**
|
|
1730
|
+
* Returns the runtime or serialized project configuration used for read-only reporting metadata.
|
|
1731
|
+
*
|
|
1732
|
+
* @param {object|undefined} project
|
|
1733
|
+
* @returns {object|undefined}
|
|
1734
|
+
*/
|
|
1735
|
+
function getProjectReportingConfig (project) {
|
|
1736
|
+
const config = safeConfig(project)
|
|
1737
|
+
if (config) return config
|
|
1738
|
+
|
|
1739
|
+
try {
|
|
1740
|
+
return project?.serializedConfig
|
|
1741
|
+
} catch {}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1276
1744
|
function safeConfig (project) {
|
|
1277
1745
|
let config
|
|
1278
1746
|
try {
|
|
@@ -1293,5 +1761,6 @@ module.exports = {
|
|
|
1293
1761
|
configure,
|
|
1294
1762
|
deactivate,
|
|
1295
1763
|
configureWorkerEnv,
|
|
1764
|
+
isSupportedVersion,
|
|
1296
1765
|
shouldUse,
|
|
1297
1766
|
}
|