dd-trace 5.110.0 → 5.112.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/LICENSE-3rdparty.csv +1 -2
- package/README.md +13 -8
- package/ci/init.js +21 -2
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +34 -1
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +76 -55
- package/package.json +37 -34
- package/packages/datadog-instrumentations/src/ai.js +45 -0
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -13
- package/packages/datadog-instrumentations/src/child_process.js +3 -3
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +587 -0
- package/packages/datadog-instrumentations/src/cucumber.js +102 -43
- package/packages/datadog-instrumentations/src/cypress-config.js +11 -3
- package/packages/datadog-instrumentations/src/fastify.js +27 -8
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -495
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +7 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +6 -6
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/claude-agent-sdk.js +17 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +201 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/jest.js +713 -63
- package/packages/datadog-instrumentations/src/mocha/main.js +24 -3
- package/packages/datadog-instrumentations/src/mocha/utils.js +128 -22
- package/packages/datadog-instrumentations/src/mocha/worker.js +13 -0
- package/packages/datadog-instrumentations/src/mongodb.js +74 -0
- package/packages/datadog-instrumentations/src/mongoose.js +13 -2
- package/packages/datadog-instrumentations/src/playwright.js +13 -9
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1297 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1594 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +254 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +823 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1855
- package/packages/datadog-plugin-ai/src/index.js +1 -1
- package/packages/datadog-plugin-ai/src/tracing.js +66 -3
- package/packages/datadog-plugin-ai/src/utils.js +17 -4
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/client.js +2 -2
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/context.js +19 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/handler.js +1 -0
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-child_process/src/index.js +13 -2
- package/packages/datadog-plugin-claude-agent-sdk/src/index.js +31 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/tracing.js +107 -0
- package/packages/datadog-plugin-claude-agent-sdk/src/util.js +15 -0
- package/packages/datadog-plugin-cucumber/src/index.js +15 -24
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +360 -14
- package/packages/datadog-plugin-cypress/src/index.js +47 -2
- package/packages/datadog-plugin-cypress/src/plugin.js +1 -0
- package/packages/datadog-plugin-cypress/src/support.js +45 -1
- package/packages/datadog-plugin-express/src/code_origin.js +1 -1
- package/packages/datadog-plugin-fastify/src/code_origin.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +639 -12
- package/packages/datadog-plugin-graphql/src/index.js +37 -9
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/utils.js +9 -2
- package/packages/datadog-plugin-graphql/src/validate.js +17 -6
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-jest/src/index.js +31 -15
- package/packages/datadog-plugin-mocha/src/index.js +40 -15
- package/packages/datadog-plugin-mongodb-core/src/bulk-write.js +43 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +8 -443
- package/packages/datadog-plugin-mongodb-core/src/query.js +452 -0
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +4 -3
- package/packages/datadog-plugin-vitest/src/index.js +120 -72
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/dd-trace/src/aiguard/index.js +9 -14
- package/packages/dd-trace/src/aiguard/integrations/index.js +34 -0
- package/packages/dd-trace/src/aiguard/integrations/openai.js +47 -33
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +42 -28
- package/packages/dd-trace/src/aiguard/sdk.js +2 -2
- package/packages/dd-trace/src/appsec/api_security/sampler.js +3 -3
- package/packages/dd-trace/src/appsec/channels.js +3 -1
- package/packages/dd-trace/src/appsec/downstream_requests.js +4 -4
- package/packages/dd-trace/src/appsec/graphql.js +14 -11
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +23 -23
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/telemetry/index.js +2 -1
- package/packages/dd-trace/src/appsec/index.js +10 -1
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +49 -10
- package/packages/dd-trace/src/appsec/telemetry/index.js +1 -1
- package/packages/dd-trace/src/appsec/waf/index.js +16 -4
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +4 -4
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +26 -1
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/worker/index.js +115 -24
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +5 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/index.js +6 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +1 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +130 -20
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +2 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/writer.js +17 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +47 -26
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +2 -2
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +64 -53
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +156 -0
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +23 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +128 -0
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +287 -0
- package/packages/dd-trace/src/config/defaults.js +3 -2
- package/packages/dd-trace/src/config/generated-config-types.d.ts +71 -57
- package/packages/dd-trace/src/config/helper.js +1 -0
- package/packages/dd-trace/src/config/index.js +35 -22
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +127 -56
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/crashtracking/crashtracker.js +2 -2
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/exporters/agent/index.js +1 -1
- package/packages/dd-trace/src/exporters/agentless/writer.js +4 -4
- package/packages/dd-trace/src/llmobs/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +403 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/index.js +8 -395
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +27 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +363 -0
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +270 -0
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/utils.js +10 -0
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +3 -3
- package/packages/dd-trace/src/llmobs/span_processor.js +1 -1
- package/packages/dd-trace/src/llmobs/tagger.js +3 -3
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +40 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_transformer.js +169 -0
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +38 -39
- package/packages/dd-trace/src/opentelemetry/span.js +5 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +4 -0
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +23 -7
- package/packages/dd-trace/src/plugins/ci_plugin.js +159 -10
- package/packages/dd-trace/src/plugins/index.js +2 -0
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +15 -9
- package/packages/dd-trace/src/profiling/exporter_cli.js +2 -2
- package/packages/dd-trace/src/profiling/exporters/agent.js +28 -3
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/span_processor.js +5 -6
- package/packages/dd-trace/src/span_stats.js +96 -78
- package/packages/dd-trace/src/startup-log.js +2 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +6 -4
- package/packages/dd-trace/src/telemetry/index.js +2 -2
- package/packages/dd-trace/src/telemetry/logs/index.js +1 -1
- package/packages/dd-trace/src/telemetry/send-data.js +8 -8
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/shell-quote/index.js +1 -1
- package/packages/datadog-plugin-graphql/src/resolve.js +0 -170
|
@@ -0,0 +1,1297 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const path = require('node:path')
|
|
4
|
+
|
|
5
|
+
const satisfies = require('../../../vendor/dist/semifies')
|
|
6
|
+
|
|
7
|
+
const { getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
|
|
8
|
+
const log = require('../../dd-trace/src/log')
|
|
9
|
+
const {
|
|
10
|
+
DYNAMIC_NAME_RE,
|
|
11
|
+
EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS,
|
|
12
|
+
getTestSuitePath,
|
|
13
|
+
logAttemptToFixTestExecution,
|
|
14
|
+
recordAttemptToFixExecution,
|
|
15
|
+
} = require('../../dd-trace/src/plugins/util/test')
|
|
16
|
+
const {
|
|
17
|
+
getTestName,
|
|
18
|
+
getTypeTasks,
|
|
19
|
+
getWorkspaceProject,
|
|
20
|
+
isFlakyTestRetriesEnabledForTask,
|
|
21
|
+
setProvidedContext,
|
|
22
|
+
testErrorCh,
|
|
23
|
+
testFinishTimeCh,
|
|
24
|
+
testPassCh,
|
|
25
|
+
testSkipCh,
|
|
26
|
+
testStartCh,
|
|
27
|
+
testSuiteErrorCh,
|
|
28
|
+
testSuiteFinishCh,
|
|
29
|
+
testSuiteStartCh,
|
|
30
|
+
} = require('./vitest-util')
|
|
31
|
+
|
|
32
|
+
// No-worker-init instrumentation for DD_EXPERIMENTAL_TEST_OPT_VITEST_NO_WORKER_INIT.
|
|
33
|
+
// When enabled, Vitest workers do not initialize dd-trace, so this main-process instrumentation
|
|
34
|
+
// takes over some worker responsibilities, including test span creation and lifecycle reporting.
|
|
35
|
+
const mainProcessReporterStates = new WeakMap()
|
|
36
|
+
const loggedAttemptToFixTests = new Set()
|
|
37
|
+
|
|
38
|
+
const DATADOG_TEST_OPTIMIZATION_BOOTSTRAPS = new Set([
|
|
39
|
+
'dd-trace/register.js',
|
|
40
|
+
'dd-trace/ci/init',
|
|
41
|
+
'dd-trace/ci/init.js',
|
|
42
|
+
])
|
|
43
|
+
const DATADOG_TEST_OPTIMIZATION_NODE_OPTION_FLAGS = new Set(['--import', '--require', '-r'])
|
|
44
|
+
const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTIVE'
|
|
45
|
+
const VITEST_NO_WORKER_INIT_REQUEST_ENV = 'DD_EXPERIMENTAL_TEST_OPT_VITEST_NO_WORKER_INIT'
|
|
46
|
+
const VITEST_NO_WORKER_INIT_MINIMUM_VERSION = '3.2.6'
|
|
47
|
+
const VITEST_DEFAULT_POOL = 'forks'
|
|
48
|
+
const VITEST_NO_WORKER_INIT_SETUP_FILE = path.join(
|
|
49
|
+
__dirname,
|
|
50
|
+
'..',
|
|
51
|
+
'..',
|
|
52
|
+
'..',
|
|
53
|
+
'ci',
|
|
54
|
+
'vitest-no-worker-init-setup.mjs'
|
|
55
|
+
)
|
|
56
|
+
const VITEST_NO_WORKER_INIT_ISOLATE_WARNING =
|
|
57
|
+
`${VITEST_NO_WORKER_INIT_REQUEST_ENV} is ignored because Vitest isolate is disabled. ` +
|
|
58
|
+
'The lighter Vitest worker path only works when each test file runs in an isolated worker.'
|
|
59
|
+
const NODE_OPTIONS_QUOTE_RE = /[\s"\\]/
|
|
60
|
+
let hasWarnedDisabledIsolate = false
|
|
61
|
+
let hasWarnedUnsupportedVersion = false
|
|
62
|
+
let nodeOptionsBeforeNoWorkerInit
|
|
63
|
+
|
|
64
|
+
function noop () {}
|
|
65
|
+
|
|
66
|
+
function isRequested () {
|
|
67
|
+
return getValueFromEnvSources(VITEST_NO_WORKER_INIT_REQUEST_ENV) === true
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function shouldUse (ctx, frameworkVersion, testSpecifications, options) {
|
|
71
|
+
if (!isRequested()) return false
|
|
72
|
+
if (!isSupportedVersion(frameworkVersion)) {
|
|
73
|
+
warnUnsupportedVersion(frameworkVersion)
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const config = ctx?.config
|
|
78
|
+
if (!config) return false
|
|
79
|
+
|
|
80
|
+
const { isVitestWorkerPool } = options
|
|
81
|
+
if (Array.isArray(testSpecifications)) {
|
|
82
|
+
return shouldUseForTestSpecifications(config, testSpecifications, isVitestWorkerPool)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const pool = getEffectiveConfigPool(config)
|
|
86
|
+
if (!isNoWorkerInitPool(pool, isVitestWorkerPool)) return false
|
|
87
|
+
if (getEffectiveConfigIsolate(config, pool) === false) {
|
|
88
|
+
warnDisabledIsolate()
|
|
89
|
+
return false
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return true
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isSupportedVersion (frameworkVersion) {
|
|
96
|
+
return !!frameworkVersion && satisfies(frameworkVersion, `>=${VITEST_NO_WORKER_INIT_MINIMUM_VERSION}`)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function warnUnsupportedVersion (frameworkVersion) {
|
|
100
|
+
if (hasWarnedUnsupportedVersion) return
|
|
101
|
+
|
|
102
|
+
hasWarnedUnsupportedVersion = true
|
|
103
|
+
log.warn(
|
|
104
|
+
'%s is only supported for vitest >=%s. Falling back to normal Vitest worker instrumentation for vitest %s.',
|
|
105
|
+
VITEST_NO_WORKER_INIT_REQUEST_ENV,
|
|
106
|
+
VITEST_NO_WORKER_INIT_MINIMUM_VERSION,
|
|
107
|
+
frameworkVersion || 'unknown'
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function warnDisabledIsolate () {
|
|
112
|
+
if (hasWarnedDisabledIsolate) return
|
|
113
|
+
|
|
114
|
+
hasWarnedDisabledIsolate = true
|
|
115
|
+
log.warn(VITEST_NO_WORKER_INIT_ISOLATE_WARNING)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function shouldUseForTestSpecifications (config, testSpecifications, isVitestWorkerPool) {
|
|
119
|
+
const defaultPool = getEffectiveConfigPool(config)
|
|
120
|
+
let hasNoWorkerInitSpecification = false
|
|
121
|
+
let hasNonWorkerSpecification = false
|
|
122
|
+
|
|
123
|
+
for (const testSpecification of testSpecifications) {
|
|
124
|
+
const pool = getEffectiveTestSpecificationPool(testSpecification, defaultPool)
|
|
125
|
+
if (!isNoWorkerInitPool(pool, isVitestWorkerPool)) {
|
|
126
|
+
hasNonWorkerSpecification = true
|
|
127
|
+
continue
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const defaultIsolate = getEffectiveConfigIsolate(config, pool)
|
|
131
|
+
if (getEffectiveTestSpecificationIsolate(testSpecification, pool, defaultIsolate) === false) {
|
|
132
|
+
warnDisabledIsolate()
|
|
133
|
+
return false
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
hasNoWorkerInitSpecification = true
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return hasNoWorkerInitSpecification && !hasNonWorkerSpecification
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function isNoWorkerInitPool (pool, isVitestWorkerPool) {
|
|
143
|
+
return pool === undefined || isVitestWorkerPool(pool)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function configure (ctx, frameworkVersion, testSpecifications, setupData, options) {
|
|
147
|
+
const { getConfiguredEfdRetryCount, state } = options
|
|
148
|
+
addSetupFileToVitestConfigs(ctx, VITEST_NO_WORKER_INIT_SETUP_FILE, testSpecifications)
|
|
149
|
+
|
|
150
|
+
const {
|
|
151
|
+
knownTestsBySuite,
|
|
152
|
+
modifiedFiles,
|
|
153
|
+
repositoryRoot,
|
|
154
|
+
testManagementTestsBySuite,
|
|
155
|
+
testSessionConfiguration,
|
|
156
|
+
} = setupData
|
|
157
|
+
|
|
158
|
+
setProvidedContext(ctx, {
|
|
159
|
+
_ddVitestWorkerSetup: {
|
|
160
|
+
attemptToFixRetries: state.testManagementAttemptToFixRetries,
|
|
161
|
+
attemptToFixTests: getSelectedTestManagementTests(testManagementTestsBySuite, 'isAttemptToFix'),
|
|
162
|
+
disabledTests: getSelectedTestManagementTests(testManagementTestsBySuite, 'isDisabled'),
|
|
163
|
+
earlyFlakeDetectionRetries: getConfiguredEfdRetryCount(
|
|
164
|
+
state.earlyFlakeDetectionSlowTestRetries,
|
|
165
|
+
state.earlyFlakeDetectionNumRetries
|
|
166
|
+
),
|
|
167
|
+
earlyFlakeDetectionRetryThresholds: EARLY_FLAKE_DETECTION_RETRY_THRESHOLDS,
|
|
168
|
+
earlyFlakeDetectionSlowRetries: state.earlyFlakeDetectionSlowTestRetries,
|
|
169
|
+
isEarlyFlakeDetectionEnabled: state.isEarlyFlakeDetectionEnabled && !state.isEarlyFlakeDetectionFaulty,
|
|
170
|
+
knownTests: knownTestsBySuite || {},
|
|
171
|
+
modifiedFiles: modifiedFiles || {},
|
|
172
|
+
quarantinedTests: getSelectedTestManagementTests(testManagementTestsBySuite, 'isQuarantined'),
|
|
173
|
+
repositoryRoot: repositoryRoot || process.cwd(),
|
|
174
|
+
},
|
|
175
|
+
}, 'Could not send Vitest worker setup context, so no-worker execution changes will not work.')
|
|
176
|
+
|
|
177
|
+
installMainProcessReporter(ctx, frameworkVersion, testSessionConfiguration || {}, setupData, state)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function deactivate (ctx) {
|
|
181
|
+
const installedReporterState = mainProcessReporterStates.get(ctx)
|
|
182
|
+
if (installedReporterState) {
|
|
183
|
+
installedReporterState.isActive = false
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function configureWorkerEnv (workerEnv, shouldSkipWorkerInit = false) {
|
|
188
|
+
if (!shouldSkipWorkerInit) {
|
|
189
|
+
delete workerEnv[VITEST_NO_WORKER_INIT_ACTIVE_ENV]
|
|
190
|
+
restoreDatadogTestOptimizationNodeOptions(workerEnv)
|
|
191
|
+
return workerEnv
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
workerEnv[VITEST_NO_WORKER_INIT_ACTIVE_ENV] = '1'
|
|
195
|
+
rememberDatadogTestOptimizationNodeOptions(workerEnv.NODE_OPTIONS)
|
|
196
|
+
|
|
197
|
+
const nodeOptions = removeDatadogTestOptimizationNodeOptions(workerEnv.NODE_OPTIONS)
|
|
198
|
+
if (nodeOptions) {
|
|
199
|
+
workerEnv.NODE_OPTIONS = nodeOptions
|
|
200
|
+
} else {
|
|
201
|
+
delete workerEnv.NODE_OPTIONS
|
|
202
|
+
}
|
|
203
|
+
return workerEnv
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function rememberDatadogTestOptimizationNodeOptions (nodeOptions) {
|
|
207
|
+
if (hasDatadogTestOptimizationNodeOptions(nodeOptions)) {
|
|
208
|
+
nodeOptionsBeforeNoWorkerInit = nodeOptions
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function restoreDatadogTestOptimizationNodeOptions (workerEnv) {
|
|
213
|
+
if (!nodeOptionsBeforeNoWorkerInit || hasDatadogTestOptimizationNodeOptions(workerEnv.NODE_OPTIONS)) return
|
|
214
|
+
|
|
215
|
+
const strippedNodeOptions = removeDatadogTestOptimizationNodeOptions(nodeOptionsBeforeNoWorkerInit)
|
|
216
|
+
if (workerEnv.NODE_OPTIONS === strippedNodeOptions || (!workerEnv.NODE_OPTIONS && !strippedNodeOptions)) {
|
|
217
|
+
workerEnv.NODE_OPTIONS = nodeOptionsBeforeNoWorkerInit
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function addSetupFileToVitestConfigs (ctx, setupFile, testSpecifications) {
|
|
222
|
+
const configs = getNoWorkerInitVitestConfigs(ctx, testSpecifications)
|
|
223
|
+
|
|
224
|
+
for (const config of configs) {
|
|
225
|
+
if (!config) continue
|
|
226
|
+
|
|
227
|
+
if (config.setupFiles === undefined) {
|
|
228
|
+
config.setupFiles = []
|
|
229
|
+
} else if (typeof config.setupFiles === 'string') {
|
|
230
|
+
config.setupFiles = [config.setupFiles]
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (!config.setupFiles.includes(setupFile)) {
|
|
234
|
+
config.setupFiles.push(setupFile)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function getNoWorkerInitVitestConfigs (ctx, testSpecifications) {
|
|
240
|
+
const configs = new Set()
|
|
241
|
+
if (Array.isArray(testSpecifications)) {
|
|
242
|
+
let shouldAddRootConfig = false
|
|
243
|
+
for (const testSpecification of testSpecifications) {
|
|
244
|
+
const project = getTestSpecificationProject(testSpecification)
|
|
245
|
+
const config = safeConfig(project)
|
|
246
|
+
if (config) {
|
|
247
|
+
addNoWorkerInitConfig(configs, config)
|
|
248
|
+
} else {
|
|
249
|
+
shouldAddRootConfig = true
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (!shouldAddRootConfig && configs.size > 0) {
|
|
254
|
+
return configs
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
addNoWorkerInitConfig(configs, safeConfig(ctx))
|
|
259
|
+
addNoWorkerInitConfig(configs, safeConfig(safeWorkspaceProject(ctx)))
|
|
260
|
+
return configs
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function addNoWorkerInitConfig (configs, config) {
|
|
264
|
+
if (config) configs.add(config)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function getSelectedTestManagementTests (testManagementTestsBySuite, propertyName) {
|
|
268
|
+
if (!testManagementTestsBySuite) return {}
|
|
269
|
+
|
|
270
|
+
const selectedTests = {}
|
|
271
|
+
for (const [testSuite, tests] of Object.entries(testManagementTestsBySuite)) {
|
|
272
|
+
for (const [testName, properties] of Object.entries(tests)) {
|
|
273
|
+
if (properties?.[propertyName]) {
|
|
274
|
+
selectedTests[testSuite] ||= {}
|
|
275
|
+
selectedTests[testSuite][testName] = true
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return selectedTests
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function installMainProcessReporter (ctx, frameworkVersion, testSessionConfiguration, testOptimizationData, state) {
|
|
283
|
+
if (!ctx?.reporters) return
|
|
284
|
+
|
|
285
|
+
const reporterState = {
|
|
286
|
+
frameworkVersion,
|
|
287
|
+
isActive: true,
|
|
288
|
+
state,
|
|
289
|
+
testSessionConfiguration,
|
|
290
|
+
testOptimizationData,
|
|
291
|
+
}
|
|
292
|
+
const installedReporterState = mainProcessReporterStates.get(ctx)
|
|
293
|
+
if (installedReporterState) {
|
|
294
|
+
Object.assign(installedReporterState, reporterState)
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
mainProcessReporterStates.set(ctx, reporterState)
|
|
299
|
+
ctx.reporters.push(createMainProcessReporter(reporterState))
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function createMainProcessReporter (reporterState) {
|
|
303
|
+
const testSuiteContexts = new Map()
|
|
304
|
+
const finishedTestModules = new Set()
|
|
305
|
+
const taskAttemptStatuses = new Map()
|
|
306
|
+
const tasksWithRecordedFinalAttempt = new Set()
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
onTestModuleStart (testModule) {
|
|
310
|
+
if (!isActive()) return
|
|
311
|
+
|
|
312
|
+
startTestSuite(testModule)
|
|
313
|
+
},
|
|
314
|
+
|
|
315
|
+
onTestModuleEnd (testModule) {
|
|
316
|
+
if (!isActive()) return
|
|
317
|
+
|
|
318
|
+
return reportTestModule(testModule)
|
|
319
|
+
},
|
|
320
|
+
|
|
321
|
+
onTestCaseResult (testCase) {
|
|
322
|
+
if (!isActive()) return
|
|
323
|
+
|
|
324
|
+
const task = getTestCaseTask(testCase)
|
|
325
|
+
recordFinalTaskAttemptResult(task)
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
onTaskUpdate (packs, events) {
|
|
329
|
+
if (!isActive()) return
|
|
330
|
+
if (!events) return
|
|
331
|
+
|
|
332
|
+
for (const event of events) {
|
|
333
|
+
if (event[1] === 'test-retried') {
|
|
334
|
+
recordTaskAttemptStatus(event[0], 'fail', getTaskUpdateAttemptCount(event[0], packs))
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
onFinished (files) {
|
|
340
|
+
if (!isActive()) return
|
|
341
|
+
if (!files) return
|
|
342
|
+
|
|
343
|
+
for (const file of files) {
|
|
344
|
+
const testModule = createTestModuleFromFile(file)
|
|
345
|
+
if (!finishedTestModules.has(file.id)) {
|
|
346
|
+
reportTestModule(testModule)
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
},
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function isActive () {
|
|
353
|
+
return reporterState.isActive === true
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function recordTaskAttemptStatus (taskId, status, attemptCount) {
|
|
357
|
+
let statuses = taskAttemptStatuses.get(taskId)
|
|
358
|
+
if (!statuses) {
|
|
359
|
+
statuses = []
|
|
360
|
+
taskAttemptStatuses.set(taskId, statuses)
|
|
361
|
+
}
|
|
362
|
+
if (attemptCount !== undefined && statuses.length >= attemptCount) return
|
|
363
|
+
|
|
364
|
+
if (tasksWithRecordedFinalAttempt.has(taskId)) {
|
|
365
|
+
statuses.splice(-1, 0, status)
|
|
366
|
+
return
|
|
367
|
+
}
|
|
368
|
+
statuses.push(status)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function recordFinalTaskAttemptResult (task) {
|
|
372
|
+
const statuses = taskAttemptStatuses.get(task.id)
|
|
373
|
+
if (!statuses) return
|
|
374
|
+
|
|
375
|
+
const attemptCount = getRepeatedAttemptCount(task, statuses)
|
|
376
|
+
if (statuses.length < attemptCount) {
|
|
377
|
+
statuses.push(getFinalRepeatedTaskStatus(task))
|
|
378
|
+
tasksWithRecordedFinalAttempt.add(task.id)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function reportTestModule (testModule) {
|
|
383
|
+
const testModuleId = getTestModuleId(testModule)
|
|
384
|
+
const testModuleTask = getTestModuleTask(testModule)
|
|
385
|
+
finishedTestModules.add(testModuleId)
|
|
386
|
+
const testSuiteCtx = testSuiteContexts.get(testModuleId) || startTestSuite(testModule)
|
|
387
|
+
const testTasks = getTypeTasks(testModuleTask.tasks)
|
|
388
|
+
const testReports = []
|
|
389
|
+
let testSuiteError
|
|
390
|
+
|
|
391
|
+
for (const task of testTasks) {
|
|
392
|
+
const testReport = getTestReport(task, testSuiteCtx.currentStore)
|
|
393
|
+
testReports.push(testReport)
|
|
394
|
+
|
|
395
|
+
for (const attempt of testReport.nonFinalAttempts) {
|
|
396
|
+
reportTestAttempt(testReport, attempt)
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
for (const testReport of testReports) {
|
|
401
|
+
const error = reportFinalTestAttempt(testReport)
|
|
402
|
+
testSuiteError ||= error
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const suiteTaskError = getSuiteTaskError(testModuleTask)
|
|
406
|
+
recomputeTaskState(testModuleTask)
|
|
407
|
+
const testSuiteResult = testModuleTask.result
|
|
408
|
+
if (testSuiteResult?.errors?.length) {
|
|
409
|
+
testSuiteError = testSuiteResult.errors[0]
|
|
410
|
+
} else if (suiteTaskError) {
|
|
411
|
+
testSuiteError = suiteTaskError
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
if (testSuiteError) {
|
|
415
|
+
testSuiteCtx.error = testSuiteError
|
|
416
|
+
testSuiteErrorCh.runStores(testSuiteCtx, () => {})
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
testSuiteFinishCh.publish({
|
|
420
|
+
status: getDatadogStatus(testSuiteResult),
|
|
421
|
+
deferFlush: true,
|
|
422
|
+
onFinish: noop,
|
|
423
|
+
...testSuiteCtx.currentStore,
|
|
424
|
+
})
|
|
425
|
+
testSuiteContexts.delete(testModuleId)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function startTestSuite (testModule) {
|
|
429
|
+
const { frameworkVersion, testSessionConfiguration } = reporterState
|
|
430
|
+
const testModuleId = getTestModuleId(testModule)
|
|
431
|
+
const testSuiteCtx = {
|
|
432
|
+
testSuiteAbsolutePath: getTestModuleFilepath(testModule),
|
|
433
|
+
frameworkVersion,
|
|
434
|
+
testSessionId: testSessionConfiguration.testSessionId,
|
|
435
|
+
testModuleId: testSessionConfiguration.testModuleId,
|
|
436
|
+
testCommand: testSessionConfiguration.testCommand,
|
|
437
|
+
repositoryRoot: testSessionConfiguration.repositoryRoot,
|
|
438
|
+
codeOwnersEntries: testSessionConfiguration.codeOwnersEntries,
|
|
439
|
+
requestErrorTags: reporterState.state.requestErrorTags,
|
|
440
|
+
isTestFrameworkWorker: true,
|
|
441
|
+
isVitestNoWorkerInitActive: true,
|
|
442
|
+
}
|
|
443
|
+
testSuiteStartCh.runStores(testSuiteCtx, () => {})
|
|
444
|
+
testSuiteContexts.set(testModuleId, testSuiteCtx)
|
|
445
|
+
return testSuiteCtx
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function getTestReport (task, testSuiteStore) {
|
|
449
|
+
const result = task.result
|
|
450
|
+
const testSuiteAbsolutePath = task.file?.filepath
|
|
451
|
+
const testName = getTestName(task)
|
|
452
|
+
const testProperties = getMainProcessTestProperties(task, testSuiteAbsolutePath, testName)
|
|
453
|
+
const { state } = reporterState
|
|
454
|
+
let status = getDatadogStatus(result)
|
|
455
|
+
|
|
456
|
+
if (task.meta?.__ddTestOptQuarantinedFailed && testProperties.isQuarantined && !testProperties.isAttemptToFix) {
|
|
457
|
+
status = 'fail'
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (testProperties.isAttemptToFix && task.meta?.__ddTestOptAtfStatuses?.length) {
|
|
461
|
+
return getRepeatedTestReport(task, testName, testSuiteAbsolutePath, testProperties, status, {
|
|
462
|
+
errorCounts: task.meta.__ddTestOptAtfErrorCounts,
|
|
463
|
+
finalStatus: getAttemptToFixFinalStatus,
|
|
464
|
+
state,
|
|
465
|
+
statuses: task.meta.__ddTestOptAtfStatuses,
|
|
466
|
+
testSuiteStore,
|
|
467
|
+
type: 'attempt_to_fix',
|
|
468
|
+
})
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
if (testProperties.isEarlyFlakeDetection && task.meta?.__ddTestOptEfdStatuses?.length) {
|
|
472
|
+
return getRepeatedTestReport(task, testName, testSuiteAbsolutePath, testProperties, status, {
|
|
473
|
+
errorCounts: task.meta.__ddTestOptEfdErrorCounts,
|
|
474
|
+
finalStatus: getEarlyFlakeDetectionFinalStatus,
|
|
475
|
+
state,
|
|
476
|
+
statuses: task.meta.__ddTestOptEfdStatuses,
|
|
477
|
+
testSuiteStore,
|
|
478
|
+
type: 'early_flake_detection',
|
|
479
|
+
})
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (!testProperties.isAttemptToFix && task.meta?.__ddTestOptRepeatStatuses?.length) {
|
|
483
|
+
return getRepeatedTestReport(task, testName, testSuiteAbsolutePath, testProperties, status, {
|
|
484
|
+
errorCounts: task.meta.__ddTestOptRepeatErrorCounts,
|
|
485
|
+
finalStatus: () => status,
|
|
486
|
+
state,
|
|
487
|
+
statuses: task.meta.__ddTestOptRepeatStatuses,
|
|
488
|
+
testSuiteStore,
|
|
489
|
+
type: 'external',
|
|
490
|
+
})
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
const attemptStatuses = taskAttemptStatuses.get(task.id)
|
|
494
|
+
if (!testProperties.isAttemptToFix && !testProperties.isEarlyFlakeDetection && attemptStatuses?.length > 1) {
|
|
495
|
+
return getRepeatedTestReport(task, testName, testSuiteAbsolutePath, testProperties, status, {
|
|
496
|
+
finalStatus: () => status,
|
|
497
|
+
state,
|
|
498
|
+
statuses: attemptStatuses,
|
|
499
|
+
testSuiteStore,
|
|
500
|
+
type: 'external',
|
|
501
|
+
})
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (!testProperties.isAttemptToFix && task.result?.repeatCount > 0) {
|
|
505
|
+
return getRepeatedTestReport(task, testName, testSuiteAbsolutePath, testProperties, status, {
|
|
506
|
+
finalStatus: () => status,
|
|
507
|
+
state,
|
|
508
|
+
statuses: getRepeatedTaskStatuses(task, status),
|
|
509
|
+
testSuiteStore,
|
|
510
|
+
type: 'external',
|
|
511
|
+
})
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (testProperties.isAttemptToFix) {
|
|
515
|
+
logAttemptToFixTestExecution(testProperties.testSuite, testName, loggedAttemptToFixTests)
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
if (testProperties.isDisabled && !testProperties.isAttemptToFix) {
|
|
519
|
+
status = 'skip'
|
|
520
|
+
if (result) result.state = 'skip'
|
|
521
|
+
} else if (testProperties.isQuarantined && status === 'fail' && !testProperties.isAttemptToFix) {
|
|
522
|
+
result.state = 'pass'
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const errors = result?.errors || []
|
|
526
|
+
const finalErrorIndex = status === 'fail' ? errors.length - 1 : -1
|
|
527
|
+
const nonFinalAttempts = []
|
|
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
|
+
}
|
|
539
|
+
|
|
540
|
+
return {
|
|
541
|
+
errors,
|
|
542
|
+
nonFinalAttempts,
|
|
543
|
+
state,
|
|
544
|
+
status,
|
|
545
|
+
task,
|
|
546
|
+
testName,
|
|
547
|
+
testProperties,
|
|
548
|
+
testSuiteAbsolutePath,
|
|
549
|
+
testSuiteStore,
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function getMainProcessTestProperties (task, testSuiteAbsolutePath, testName) {
|
|
554
|
+
const { state, testOptimizationData, testSessionConfiguration } = reporterState
|
|
555
|
+
const testProperties = testOptimizationData.testPropertiesByFilepath?.[testSuiteAbsolutePath]
|
|
556
|
+
const testSuite = testProperties?.testSuite || getTestSuitePath(
|
|
557
|
+
testSuiteAbsolutePath,
|
|
558
|
+
testSessionConfiguration.repositoryRoot || process.cwd()
|
|
559
|
+
)
|
|
560
|
+
const knownTests = testProperties?.knownTests
|
|
561
|
+
const testManagementProperties = testProperties?.testManagementTests?.[testName] || {}
|
|
562
|
+
const isAttemptToFix = testManagementProperties.isAttemptToFix
|
|
563
|
+
const isNew = !!(
|
|
564
|
+
!isAttemptToFix &&
|
|
565
|
+
state.isKnownTestsEnabled &&
|
|
566
|
+
knownTests &&
|
|
567
|
+
!state.isEarlyFlakeDetectionFaulty &&
|
|
568
|
+
!knownTests.includes(testName)
|
|
569
|
+
)
|
|
570
|
+
const isModified = testProperties?.isModified === true
|
|
571
|
+
const { flakyTestRetriesConfiguration } = testOptimizationData
|
|
572
|
+
const isFlakyTestRetries = !!flakyTestRetriesConfiguration && isFlakyTestRetriesEnabledForTask({
|
|
573
|
+
isFlakyTestRetriesEnabled: state.isFlakyTestRetriesEnabled,
|
|
574
|
+
flakyTestRetriesIncludesUnnamedProject: flakyTestRetriesConfiguration.includesUnnamedProject,
|
|
575
|
+
flakyTestRetriesProjectNames: flakyTestRetriesConfiguration.projectNames,
|
|
576
|
+
}, task)
|
|
577
|
+
|
|
578
|
+
return {
|
|
579
|
+
isAttemptToFix,
|
|
580
|
+
isDisabled: testManagementProperties.isDisabled,
|
|
581
|
+
isEarlyFlakeDetection: (isNew || isModified) && state.isEarlyFlakeDetectionEnabled,
|
|
582
|
+
isFlakyTestRetries,
|
|
583
|
+
isQuarantined: testManagementProperties.isQuarantined,
|
|
584
|
+
isModified,
|
|
585
|
+
isNew,
|
|
586
|
+
testSuite,
|
|
587
|
+
hasDynamicName: isNew && DYNAMIC_NAME_RE.test(testName),
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function getTaskUpdateAttemptCount (taskId, packs) {
|
|
593
|
+
if (!packs) return
|
|
594
|
+
|
|
595
|
+
for (const pack of packs) {
|
|
596
|
+
if (pack[0] === taskId) {
|
|
597
|
+
return getRepeatedAttemptCount({
|
|
598
|
+
id: taskId,
|
|
599
|
+
result: pack[1],
|
|
600
|
+
meta: pack[2],
|
|
601
|
+
}, [])
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function createTestModuleFromFile (file) {
|
|
607
|
+
return {
|
|
608
|
+
id: file.id,
|
|
609
|
+
moduleId: file.filepath,
|
|
610
|
+
project: file.project,
|
|
611
|
+
projectName: file.projectName,
|
|
612
|
+
task: file,
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function getTestModuleId (testModule) {
|
|
617
|
+
return testModule.id || getTestModuleFilepath(testModule)
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function getTestModuleTask (testModule) {
|
|
621
|
+
if (testModule.task?.tasks) {
|
|
622
|
+
return testModule.task
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
const task = createTaskFromReportedTask(testModule)
|
|
626
|
+
task.file = task
|
|
627
|
+
return task
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function getTestCaseTask (testCase) {
|
|
631
|
+
if (testCase.task) {
|
|
632
|
+
return testCase.task
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return createTaskFromReportedTask(testCase, {
|
|
636
|
+
filepath: getTestModuleFilepath(testCase.module),
|
|
637
|
+
projectName: getTestModuleProjectName(testCase.module),
|
|
638
|
+
})
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function createTaskFromReportedTask (reportedTask, file, suite) {
|
|
642
|
+
const task = {
|
|
643
|
+
id: reportedTask.id,
|
|
644
|
+
type: reportedTask.type,
|
|
645
|
+
name: reportedTask.name || reportedTask.relativeModuleId || reportedTask.moduleId,
|
|
646
|
+
mode: reportedTask.options?.mode,
|
|
647
|
+
meta: getReportedTaskMeta(reportedTask),
|
|
648
|
+
result: getReportedTaskResult(reportedTask),
|
|
649
|
+
file,
|
|
650
|
+
suite,
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (reportedTask.type === 'module') {
|
|
654
|
+
task.filepath = reportedTask.moduleId
|
|
655
|
+
task.projectName = getTestModuleProjectName(reportedTask)
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
const children = getReportedTaskChildren(reportedTask)
|
|
659
|
+
if (children) {
|
|
660
|
+
task.tasks = []
|
|
661
|
+
const taskFile = file || task
|
|
662
|
+
const childSuite = task.type === 'suite' ? task : undefined
|
|
663
|
+
for (const child of children) {
|
|
664
|
+
task.tasks.push(createTaskFromReportedTask(child, taskFile, childSuite))
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return task
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function getReportedTaskChildren (reportedTask) {
|
|
672
|
+
const children = reportedTask.children
|
|
673
|
+
if (!children) return
|
|
674
|
+
|
|
675
|
+
if (typeof children.array === 'function') {
|
|
676
|
+
return children.array()
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
if (Array.isArray(children)) {
|
|
680
|
+
return children
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
return [...children]
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
function getReportedTaskMeta (reportedTask) {
|
|
687
|
+
if (typeof reportedTask.meta === 'function') {
|
|
688
|
+
return reportedTask.meta()
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
return reportedTask.meta || {}
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
function getReportedTaskResult (reportedTask) {
|
|
695
|
+
let result
|
|
696
|
+
if (typeof reportedTask.result === 'function') {
|
|
697
|
+
result = reportedTask.result()
|
|
698
|
+
} else if (typeof reportedTask.state === 'function') {
|
|
699
|
+
result = {
|
|
700
|
+
errors: typeof reportedTask.errors === 'function' ? reportedTask.errors() : undefined,
|
|
701
|
+
state: reportedTask.state(),
|
|
702
|
+
}
|
|
703
|
+
} else {
|
|
704
|
+
result = reportedTask.result
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
const diagnostic = typeof reportedTask.diagnostic === 'function'
|
|
708
|
+
? reportedTask.diagnostic()
|
|
709
|
+
: undefined
|
|
710
|
+
|
|
711
|
+
return {
|
|
712
|
+
duration: diagnostic?.duration,
|
|
713
|
+
errors: result?.errors,
|
|
714
|
+
note: result?.note,
|
|
715
|
+
repeatCount: diagnostic?.repeatCount,
|
|
716
|
+
retryCount: diagnostic?.retryCount,
|
|
717
|
+
state: result?.state,
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
function getTestModuleFilepath (testModule) {
|
|
722
|
+
return testModule.moduleId || testModule.task?.filepath || testModule.filepath
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function getTestModuleProjectName (testModule) {
|
|
726
|
+
return normalizeProjectName(
|
|
727
|
+
testModule.projectName || getProjectName(testModule.project) || testModule.task?.projectName ||
|
|
728
|
+
testModule.task?.file?.projectName
|
|
729
|
+
)
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
function getRepeatedTestReport (task, testName, testSuiteAbsolutePath, testProperties, status, options) {
|
|
733
|
+
const result = task.result
|
|
734
|
+
const errors = result?.errors || []
|
|
735
|
+
const { errorCounts, finalStatus, state, statuses, testSuiteStore, type } = options
|
|
736
|
+
const finalAttemptStatus = finalStatus(statuses)
|
|
737
|
+
const hasFailure = finalAttemptStatus === 'fail'
|
|
738
|
+
const attempts = []
|
|
739
|
+
const attemptCount = getRepeatedAttemptCount(task, statuses)
|
|
740
|
+
let errorIndex = 0
|
|
741
|
+
let previousErrorCount = 0
|
|
742
|
+
|
|
743
|
+
if (type === 'attempt_to_fix') {
|
|
744
|
+
logAttemptToFixTestExecution(testProperties.testSuite, testName, loggedAttemptToFixTests)
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
for (let index = 0; index < attemptCount; index++) {
|
|
748
|
+
const isFinalAttempt = index === attemptCount - 1
|
|
749
|
+
const attemptStatus = statuses[index] || 'pass'
|
|
750
|
+
const nextErrorCount = errorCounts?.[index]
|
|
751
|
+
const error = attemptStatus === 'fail'
|
|
752
|
+
? errors[nextErrorCount === undefined ? errorIndex : previousErrorCount] || errors[errorIndex] || errors[0]
|
|
753
|
+
: undefined
|
|
754
|
+
if (nextErrorCount !== undefined) {
|
|
755
|
+
previousErrorCount = nextErrorCount
|
|
756
|
+
} else if (attemptStatus === 'fail') {
|
|
757
|
+
errorIndex++
|
|
758
|
+
}
|
|
759
|
+
const attempt = {
|
|
760
|
+
attemptToFixFailed: type === 'attempt_to_fix' && isFinalAttempt && hasFailure,
|
|
761
|
+
earlyFlakeAbortReason: type === 'early_flake_detection' && isFinalAttempt
|
|
762
|
+
? task.meta?.__ddTestOptEfdAbortReason
|
|
763
|
+
: undefined,
|
|
764
|
+
error,
|
|
765
|
+
finalStatus: isFinalAttempt ? finalAttemptStatus : undefined,
|
|
766
|
+
hasFailedAllRetries: hasFailedAllManagedRetries(
|
|
767
|
+
task,
|
|
768
|
+
testProperties,
|
|
769
|
+
type,
|
|
770
|
+
statuses,
|
|
771
|
+
isFinalAttempt,
|
|
772
|
+
hasFailure
|
|
773
|
+
),
|
|
774
|
+
isRetry: index > 0,
|
|
775
|
+
status: attemptStatus,
|
|
776
|
+
}
|
|
777
|
+
if (type === 'attempt_to_fix') {
|
|
778
|
+
attempt.attemptToFixPassed = isFinalAttempt && !hasFailure
|
|
779
|
+
} else if (type === 'early_flake_detection') {
|
|
780
|
+
attempt.isRetryReasonEfd = index > 0
|
|
781
|
+
}
|
|
782
|
+
attempts.push(attempt)
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
return {
|
|
786
|
+
errors,
|
|
787
|
+
finalAttempt: attempts[attempts.length - 1],
|
|
788
|
+
nonFinalAttempts: attempts.slice(0, -1),
|
|
789
|
+
state,
|
|
790
|
+
status,
|
|
791
|
+
task,
|
|
792
|
+
testName,
|
|
793
|
+
testProperties,
|
|
794
|
+
testSuiteAbsolutePath,
|
|
795
|
+
testSuiteStore,
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Returns whether a final failed attempt exhausted Datadog-managed retries.
|
|
801
|
+
*
|
|
802
|
+
* @param {{
|
|
803
|
+
* meta?: { __ddTestOptEfdRetries?: number },
|
|
804
|
+
* repeats?: number,
|
|
805
|
+
* result?: { retryCount?: number }
|
|
806
|
+
* }} task
|
|
807
|
+
* @param {{ isFlakyTestRetries?: boolean }} testProperties
|
|
808
|
+
* @param {'attempt_to_fix'|'early_flake_detection'|'external'} type
|
|
809
|
+
* @param {string[]} statuses
|
|
810
|
+
* @param {boolean} isFinalAttempt
|
|
811
|
+
* @param {boolean} hasFailure
|
|
812
|
+
* @returns {boolean}
|
|
813
|
+
*/
|
|
814
|
+
function hasFailedAllManagedRetries (task, testProperties, type, statuses, isFinalAttempt, hasFailure) {
|
|
815
|
+
if (!isFinalAttempt || !hasFailure || statuses.length === 0 || !statuses.every(status => status === 'fail')) {
|
|
816
|
+
return false
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
if (type === 'attempt_to_fix') {
|
|
820
|
+
return true
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
if (type === 'early_flake_detection') {
|
|
824
|
+
return (task.meta?.__ddTestOptEfdRetries || 0) > 0
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
return testProperties.isFlakyTestRetries === true && (task.result?.retryCount || 0) > 0
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
function getRepeatedAttemptCount (task, statuses) {
|
|
831
|
+
const retries = task.meta?.__ddTestOptAtfRetries ?? task.meta?.__ddTestOptEfdRetries ??
|
|
832
|
+
Math.max(task.result?.retryCount || 0, task.repeats || 0)
|
|
833
|
+
return Math.max(statuses.length, retries + 1)
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
function getAttemptToFixFinalStatus (statuses) {
|
|
837
|
+
return statuses.includes('fail') ? 'fail' : 'pass'
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
function getEarlyFlakeDetectionFinalStatus (statuses) {
|
|
841
|
+
return statuses.includes('pass') ? 'pass' : 'fail'
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function getRepeatedTaskStatuses (task, status) {
|
|
845
|
+
const repeatedStatuses = []
|
|
846
|
+
const repeatCount = task.result?.repeatCount || 0
|
|
847
|
+
for (let index = 0; index <= repeatCount; index++) {
|
|
848
|
+
repeatedStatuses.push(status)
|
|
849
|
+
}
|
|
850
|
+
return repeatedStatuses
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
function reportFinalTestAttempt (testReport) {
|
|
854
|
+
const {
|
|
855
|
+
errors,
|
|
856
|
+
state,
|
|
857
|
+
status,
|
|
858
|
+
task,
|
|
859
|
+
testName,
|
|
860
|
+
testProperties,
|
|
861
|
+
testSuiteAbsolutePath,
|
|
862
|
+
testSuiteStore,
|
|
863
|
+
} = testReport
|
|
864
|
+
|
|
865
|
+
if (status === 'skip') {
|
|
866
|
+
testSkipCh.publish({
|
|
867
|
+
testName,
|
|
868
|
+
testSuiteAbsolutePath,
|
|
869
|
+
isNew: testProperties.isNew,
|
|
870
|
+
isDisabled: testProperties.isDisabled,
|
|
871
|
+
isTestFrameworkWorker: true,
|
|
872
|
+
requestErrorTags: state.requestErrorTags,
|
|
873
|
+
...testSuiteStore,
|
|
874
|
+
})
|
|
875
|
+
return
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
const result = task.result
|
|
879
|
+
const finalStatus = getFinalTestStatus(testReport)
|
|
880
|
+
const finalAttempt = testReport.finalAttempt
|
|
881
|
+
|
|
882
|
+
if (status === 'fail') {
|
|
883
|
+
const error = errors[errors.length - 1] || errors[0]
|
|
884
|
+
reportTestAttempt(testReport, finalAttempt || {
|
|
885
|
+
error,
|
|
886
|
+
finalStatus,
|
|
887
|
+
hasFailedAllRetries: hasFailedAllManagedRetries(
|
|
888
|
+
task,
|
|
889
|
+
testProperties,
|
|
890
|
+
'external',
|
|
891
|
+
['fail'],
|
|
892
|
+
true,
|
|
893
|
+
true
|
|
894
|
+
),
|
|
895
|
+
isRetry: errors.length > 1 || (result?.retryCount || 0) > 0 || (result?.repeatCount || 0) > 0,
|
|
896
|
+
status: 'fail',
|
|
897
|
+
})
|
|
898
|
+
return finalStatus === 'skip' ? undefined : error
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
reportTestAttempt(testReport, finalAttempt || {
|
|
902
|
+
finalStatus,
|
|
903
|
+
isRetry: errors.length > 0 || (result?.retryCount || 0) > 0 || (result?.repeatCount || 0) > 0,
|
|
904
|
+
status: 'pass',
|
|
905
|
+
})
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
function reportTestAttempt (testReport, attempt) {
|
|
909
|
+
const {
|
|
910
|
+
state,
|
|
911
|
+
task,
|
|
912
|
+
testName,
|
|
913
|
+
testProperties,
|
|
914
|
+
testSuiteAbsolutePath,
|
|
915
|
+
testSuiteStore,
|
|
916
|
+
} = testReport
|
|
917
|
+
const result = task.result
|
|
918
|
+
const status = attempt.status
|
|
919
|
+
const testCtx = {
|
|
920
|
+
currentStore: testSuiteStore,
|
|
921
|
+
testName,
|
|
922
|
+
testSuiteAbsolutePath,
|
|
923
|
+
isRetry: attempt.isRetry,
|
|
924
|
+
isNew: testProperties.isNew,
|
|
925
|
+
hasDynamicName: testProperties.hasDynamicName,
|
|
926
|
+
isAttemptToFix: testProperties.isAttemptToFix,
|
|
927
|
+
isDisabled: testProperties.isDisabled,
|
|
928
|
+
isQuarantined: testProperties.isQuarantined,
|
|
929
|
+
isModified: testProperties.isModified,
|
|
930
|
+
isRetryReasonEfd: attempt.isRetryReasonEfd,
|
|
931
|
+
isRetryReasonAttemptToFix: testProperties.isAttemptToFix && attempt.isRetry,
|
|
932
|
+
isRetryReasonAtr: !testProperties.isAttemptToFix && !testProperties.isEarlyFlakeDetection &&
|
|
933
|
+
testProperties.isFlakyTestRetries,
|
|
934
|
+
isTestFrameworkWorker: true,
|
|
935
|
+
requestErrorTags: state.requestErrorTags,
|
|
936
|
+
}
|
|
937
|
+
if (testProperties.isAttemptToFix) {
|
|
938
|
+
recordAttemptToFixExecution(state.attemptToFixExecutions, {
|
|
939
|
+
testSuite: testProperties.testSuite,
|
|
940
|
+
testName,
|
|
941
|
+
status,
|
|
942
|
+
isDisabled: testProperties.isDisabled,
|
|
943
|
+
isQuarantined: testProperties.isQuarantined,
|
|
944
|
+
})
|
|
945
|
+
}
|
|
946
|
+
if (testProperties.hasDynamicName) {
|
|
947
|
+
state.newTestsWithDynamicNames.add(`${testProperties.testSuite} › ${testName}`)
|
|
948
|
+
}
|
|
949
|
+
if (attempt.attemptToFixPassed) {
|
|
950
|
+
testCtx.attemptToFixPassed = true
|
|
951
|
+
} else if (attempt.attemptToFixFailed) {
|
|
952
|
+
testCtx.attemptToFixFailed = true
|
|
953
|
+
}
|
|
954
|
+
testStartCh.runStores(testCtx, () => {})
|
|
955
|
+
testCtx.status = status
|
|
956
|
+
testCtx.task = task
|
|
957
|
+
if (
|
|
958
|
+
status === 'pass' &&
|
|
959
|
+
!attempt.isRetry &&
|
|
960
|
+
isFinalTestAttempt(testReport, attempt) &&
|
|
961
|
+
result?.duration !== undefined
|
|
962
|
+
) {
|
|
963
|
+
testCtx.duration = result.duration
|
|
964
|
+
}
|
|
965
|
+
testFinishTimeCh.runStores(testCtx, () => {})
|
|
966
|
+
|
|
967
|
+
if (status === 'pass') {
|
|
968
|
+
testPassCh.publish({
|
|
969
|
+
task,
|
|
970
|
+
earlyFlakeAbortReason: attempt.earlyFlakeAbortReason,
|
|
971
|
+
finalStatus: attempt.finalStatus,
|
|
972
|
+
...testCtx.currentStore,
|
|
973
|
+
})
|
|
974
|
+
return
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
testErrorCh.publish({
|
|
978
|
+
duration: shouldUseTaskDurationForFailure(testReport, attempt) ? result?.duration : undefined,
|
|
979
|
+
error: attempt.error,
|
|
980
|
+
earlyFlakeAbortReason: attempt.earlyFlakeAbortReason,
|
|
981
|
+
finalStatus: attempt.finalStatus,
|
|
982
|
+
hasFailedAllRetries: attempt.hasFailedAllRetries,
|
|
983
|
+
attemptToFixFailed: attempt.attemptToFixFailed,
|
|
984
|
+
...testCtx.currentStore,
|
|
985
|
+
})
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
function isFinalTestAttempt (testReport, attempt) {
|
|
989
|
+
return attempt.finalStatus !== undefined || testReport.finalAttempt === undefined
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
function shouldUseTaskDurationForFailure (testReport, attempt) {
|
|
993
|
+
return !attempt.isRetry && attempt.finalStatus !== undefined && testReport.finalAttempt === undefined
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
function getFinalTestStatus (testReport) {
|
|
997
|
+
const testProperties = testReport.testProperties
|
|
998
|
+
if (testProperties.isAttemptToFix) {
|
|
999
|
+
return testReport.finalAttempt?.finalStatus
|
|
1000
|
+
}
|
|
1001
|
+
if (testProperties.isDisabled || testProperties.isQuarantined) {
|
|
1002
|
+
return 'skip'
|
|
1003
|
+
}
|
|
1004
|
+
return testReport.status
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function getDatadogStatus (result) {
|
|
1008
|
+
const state = result?.state
|
|
1009
|
+
if (state === 'pass' || state === 'passed') return 'pass'
|
|
1010
|
+
if (state === 'fail' || state === 'failed') return 'fail'
|
|
1011
|
+
return 'skip'
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
function getFinalRepeatedTaskStatus (task) {
|
|
1015
|
+
return task.meta?.__ddTestOptQuarantinedFailed ? 'fail' : getDatadogStatus(task.result)
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
function getSuiteTaskError (task) {
|
|
1019
|
+
const suiteTasks = getTypeTasks(task.tasks || [], 'suite')
|
|
1020
|
+
for (const suiteTask of suiteTasks) {
|
|
1021
|
+
if (suiteTask.result?.state === 'fail' && suiteTask.result?.errors?.length) {
|
|
1022
|
+
return suiteTask.result.errors[0]
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
function recomputeTaskState (task) {
|
|
1028
|
+
if (!task.tasks) {
|
|
1029
|
+
return getDatadogStatus(task.result)
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
const hasErrors = task.result?.errors?.length > 0
|
|
1033
|
+
let hasFailed = false
|
|
1034
|
+
let hasPassed = false
|
|
1035
|
+
|
|
1036
|
+
for (const child of task.tasks) {
|
|
1037
|
+
const childStatus = recomputeTaskState(child)
|
|
1038
|
+
hasFailed ||= childStatus === 'fail'
|
|
1039
|
+
hasPassed ||= childStatus === 'pass'
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
const status = hasErrors || hasFailed ? 'fail' : hasPassed ? 'pass' : 'skip'
|
|
1043
|
+
task.result ||= {}
|
|
1044
|
+
task.result.state = status
|
|
1045
|
+
return status
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
function removeDatadogTestOptimizationNodeOptions (nodeOptions) {
|
|
1049
|
+
if (!nodeOptions) return nodeOptions
|
|
1050
|
+
|
|
1051
|
+
const tokens = splitNodeOptions(nodeOptions)
|
|
1052
|
+
const filteredTokens = []
|
|
1053
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
1054
|
+
const token = tokens[index]
|
|
1055
|
+
const valueSeparator = token.indexOf('=')
|
|
1056
|
+
if (valueSeparator !== -1) {
|
|
1057
|
+
const flag = token.slice(0, valueSeparator)
|
|
1058
|
+
const value = token.slice(valueSeparator + 1)
|
|
1059
|
+
if (shouldRemoveNodeOption(flag, value)) {
|
|
1060
|
+
continue
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
if (token.startsWith('-r') && token.length > 2 && isDatadogTestOptimizationBootstrap(token.slice(2))) {
|
|
1065
|
+
continue
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
if (
|
|
1069
|
+
DATADOG_TEST_OPTIMIZATION_NODE_OPTION_FLAGS.has(token) &&
|
|
1070
|
+
isDatadogTestOptimizationBootstrap(tokens[index + 1])
|
|
1071
|
+
) {
|
|
1072
|
+
index++
|
|
1073
|
+
continue
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
filteredTokens.push(token)
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
return serializeNodeOptions(filteredTokens)
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
function hasDatadogTestOptimizationNodeOptions (nodeOptions) {
|
|
1083
|
+
if (!nodeOptions) return false
|
|
1084
|
+
|
|
1085
|
+
const tokens = splitNodeOptions(nodeOptions)
|
|
1086
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
1087
|
+
const token = tokens[index]
|
|
1088
|
+
const valueSeparator = token.indexOf('=')
|
|
1089
|
+
if (valueSeparator !== -1) {
|
|
1090
|
+
const flag = token.slice(0, valueSeparator)
|
|
1091
|
+
const value = token.slice(valueSeparator + 1)
|
|
1092
|
+
if (shouldRemoveNodeOption(flag, value)) {
|
|
1093
|
+
return true
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
if (token.startsWith('-r') && token.length > 2 && isDatadogTestOptimizationBootstrap(token.slice(2))) {
|
|
1098
|
+
return true
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
if (
|
|
1102
|
+
DATADOG_TEST_OPTIMIZATION_NODE_OPTION_FLAGS.has(token) &&
|
|
1103
|
+
isDatadogTestOptimizationBootstrap(tokens[index + 1])
|
|
1104
|
+
) {
|
|
1105
|
+
return true
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
return false
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function shouldRemoveNodeOption (flag, value) {
|
|
1112
|
+
return DATADOG_TEST_OPTIMIZATION_NODE_OPTION_FLAGS.has(flag) &&
|
|
1113
|
+
isDatadogTestOptimizationBootstrap(value)
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
function isDatadogTestOptimizationBootstrap (value) {
|
|
1117
|
+
if (!value) return false
|
|
1118
|
+
|
|
1119
|
+
const normalizedValue = value.replaceAll('\\', '/')
|
|
1120
|
+
if (DATADOG_TEST_OPTIMIZATION_BOOTSTRAPS.has(normalizedValue)) return true
|
|
1121
|
+
|
|
1122
|
+
for (const bootstrap of DATADOG_TEST_OPTIMIZATION_BOOTSTRAPS) {
|
|
1123
|
+
if (normalizedValue.endsWith(`/node_modules/${bootstrap}`)) return true
|
|
1124
|
+
}
|
|
1125
|
+
return false
|
|
1126
|
+
}
|
|
1127
|
+
|
|
1128
|
+
function splitNodeOptions (nodeOptions) {
|
|
1129
|
+
const tokens = []
|
|
1130
|
+
let token = ''
|
|
1131
|
+
let quote
|
|
1132
|
+
|
|
1133
|
+
for (let index = 0; index < nodeOptions.length; index++) {
|
|
1134
|
+
const char = nodeOptions[index]
|
|
1135
|
+
|
|
1136
|
+
if (quote) {
|
|
1137
|
+
if (char === '\\' && nodeOptions[index + 1] === quote) {
|
|
1138
|
+
token += quote
|
|
1139
|
+
index++
|
|
1140
|
+
continue
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
if (char === quote) {
|
|
1144
|
+
quote = undefined
|
|
1145
|
+
} else {
|
|
1146
|
+
token += char
|
|
1147
|
+
}
|
|
1148
|
+
continue
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
if (char === '"' || char === "'") {
|
|
1152
|
+
quote = char
|
|
1153
|
+
continue
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
if (char === ' ' || char === '\n' || char === '\t') {
|
|
1157
|
+
if (token) {
|
|
1158
|
+
tokens.push(token)
|
|
1159
|
+
token = ''
|
|
1160
|
+
}
|
|
1161
|
+
continue
|
|
1162
|
+
}
|
|
1163
|
+
|
|
1164
|
+
token += char
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
if (token) {
|
|
1168
|
+
tokens.push(token)
|
|
1169
|
+
}
|
|
1170
|
+
return tokens
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
function serializeNodeOptions (tokens) {
|
|
1174
|
+
const serializedTokens = []
|
|
1175
|
+
for (const token of tokens) {
|
|
1176
|
+
if (NODE_OPTIONS_QUOTE_RE.test(token)) {
|
|
1177
|
+
serializedTokens.push(JSON.stringify(token))
|
|
1178
|
+
} else {
|
|
1179
|
+
serializedTokens.push(token)
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
return serializedTokens.join(' ')
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
function getTestSpecificationProject (testSpecification) {
|
|
1186
|
+
if (Array.isArray(testSpecification)) {
|
|
1187
|
+
return testSpecification[0]
|
|
1188
|
+
}
|
|
1189
|
+
return testSpecification?.project
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
function getTestSpecificationFile (testSpecification) {
|
|
1193
|
+
if (Array.isArray(testSpecification)) {
|
|
1194
|
+
return testSpecification[1]
|
|
1195
|
+
}
|
|
1196
|
+
return testSpecification
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
function getTestSpecificationOptions (testSpecification) {
|
|
1200
|
+
if (Array.isArray(testSpecification)) {
|
|
1201
|
+
return testSpecification[2]
|
|
1202
|
+
}
|
|
1203
|
+
return testSpecification
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
function getTestSpecificationPool (testSpecification) {
|
|
1207
|
+
const options = getTestSpecificationOptions(testSpecification)
|
|
1208
|
+
const file = getTestSpecificationFile(testSpecification)
|
|
1209
|
+
const project = getTestSpecificationProject(testSpecification)
|
|
1210
|
+
return options?.pool ||
|
|
1211
|
+
file?.pool ||
|
|
1212
|
+
testSpecification?.pool ||
|
|
1213
|
+
project?.config?.pool ||
|
|
1214
|
+
project?.serializedConfig?.pool ||
|
|
1215
|
+
project?.pool
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
function getEffectiveTestSpecificationPool (testSpecification, defaultPool) {
|
|
1219
|
+
return getTestSpecificationPool(testSpecification) || defaultPool
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
/**
|
|
1223
|
+
* Resolves Vitest's default worker pool when the user did not configure one.
|
|
1224
|
+
*
|
|
1225
|
+
* @param {{ pool?: string }|undefined} config
|
|
1226
|
+
* @returns {string}
|
|
1227
|
+
*/
|
|
1228
|
+
function getEffectiveConfigPool (config) {
|
|
1229
|
+
return config?.pool || VITEST_DEFAULT_POOL
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
function getPoolOptionsIsolate (config, pool) {
|
|
1233
|
+
if (!pool) return
|
|
1234
|
+
|
|
1235
|
+
return config?.poolOptions?.[pool]?.isolate
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
function getEffectiveConfigIsolate (config, pool) {
|
|
1239
|
+
return getPoolOptionsIsolate(config, pool) ?? config?.isolate
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
function getTestSpecificationIsolate (testSpecification, pool) {
|
|
1243
|
+
const options = getTestSpecificationOptions(testSpecification)
|
|
1244
|
+
const file = getTestSpecificationFile(testSpecification)
|
|
1245
|
+
const project = getTestSpecificationProject(testSpecification)
|
|
1246
|
+
return getPoolOptionsIsolate(options, pool) ??
|
|
1247
|
+
getPoolOptionsIsolate(file, pool) ??
|
|
1248
|
+
getPoolOptionsIsolate(project?.config, pool) ??
|
|
1249
|
+
getPoolOptionsIsolate(project?.serializedConfig, pool) ??
|
|
1250
|
+
getPoolOptionsIsolate(project, pool) ??
|
|
1251
|
+
getPoolOptionsIsolate(testSpecification, pool) ??
|
|
1252
|
+
options?.isolate ??
|
|
1253
|
+
file?.isolate ??
|
|
1254
|
+
project?.config?.isolate ??
|
|
1255
|
+
project?.serializedConfig?.isolate ??
|
|
1256
|
+
project?.isolate ??
|
|
1257
|
+
testSpecification?.isolate
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
function getEffectiveTestSpecificationIsolate (testSpecification, pool, defaultIsolate) {
|
|
1261
|
+
const isolate = getTestSpecificationIsolate(testSpecification, pool)
|
|
1262
|
+
return isolate === undefined ? defaultIsolate : isolate
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
function getProjectName (project) {
|
|
1266
|
+
return normalizeProjectName(project?.name || project?.config?.name || project?.test?.name)
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
function normalizeProjectName (name) {
|
|
1270
|
+
if (typeof name === 'string') return name
|
|
1271
|
+
|
|
1272
|
+
const label = name?.label
|
|
1273
|
+
return typeof label === 'string' ? label : undefined
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
function safeConfig (project) {
|
|
1277
|
+
let config
|
|
1278
|
+
try {
|
|
1279
|
+
config = project?.config
|
|
1280
|
+
} catch {}
|
|
1281
|
+
return config
|
|
1282
|
+
}
|
|
1283
|
+
|
|
1284
|
+
function safeWorkspaceProject (ctx) {
|
|
1285
|
+
let project
|
|
1286
|
+
try {
|
|
1287
|
+
project = getWorkspaceProject(ctx)
|
|
1288
|
+
} catch {}
|
|
1289
|
+
return project
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
module.exports = {
|
|
1293
|
+
configure,
|
|
1294
|
+
deactivate,
|
|
1295
|
+
configureWorkerEnv,
|
|
1296
|
+
shouldUse,
|
|
1297
|
+
}
|