dd-trace 5.109.0 → 5.111.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -8
- package/ci/init.js +25 -5
- package/ci/vitest-no-worker-init-setup.mjs +430 -0
- package/ext/tags.js +2 -0
- package/index.d.ts +79 -24
- package/initialize.mjs +5 -6
- package/loader-hook.mjs +154 -47
- package/package.json +20 -16
- package/packages/datadog-esbuild/index.js +26 -0
- package/packages/datadog-esbuild/src/utils.js +46 -3
- package/packages/datadog-instrumentations/src/connect.js +4 -3
- package/packages/datadog-instrumentations/src/fastify.js +4 -12
- package/packages/datadog-instrumentations/src/fs.js +8 -6
- package/packages/datadog-instrumentations/src/graphql.js +26 -484
- package/packages/datadog-instrumentations/src/helpers/check-require-cache.js +81 -8
- package/packages/datadog-instrumentations/src/helpers/instrument.js +27 -2
- package/packages/datadog-instrumentations/src/helpers/optional-peer-bundler.js +65 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +6 -4
- package/packages/datadog-instrumentations/src/helpers/require-optional-peer.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 +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +18 -2
- package/packages/datadog-instrumentations/src/koa.js +3 -2
- package/packages/datadog-instrumentations/src/mariadb.js +4 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +3 -3
- package/packages/datadog-instrumentations/src/mongoose.js +14 -2
- package/packages/datadog-instrumentations/src/next.js +10 -10
- package/packages/datadog-instrumentations/src/otel-sdk-trace.js +7 -6
- package/packages/datadog-instrumentations/src/playwright.js +1 -1
- package/packages/datadog-instrumentations/src/restify.js +4 -3
- package/packages/datadog-instrumentations/src/router.js +18 -5
- package/packages/datadog-instrumentations/src/selenium.js +1 -2
- package/packages/datadog-instrumentations/src/tedious.js +28 -0
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +1293 -0
- package/packages/datadog-instrumentations/src/vitest-main.js +1567 -0
- package/packages/datadog-instrumentations/src/vitest-util.js +249 -0
- package/packages/datadog-instrumentations/src/vitest-worker.js +753 -0
- package/packages/datadog-instrumentations/src/vitest.js +11 -1598
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +2 -2
- 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 +18 -10
- package/packages/datadog-plugin-aws-durable-execution-sdk-js/src/util.js +47 -11
- package/packages/datadog-plugin-aws-sdk/src/base.js +13 -9
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +43 -6
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +83 -91
- package/packages/datadog-plugin-cucumber/src/index.js +1 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +19 -5
- 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 +627 -11
- package/packages/datadog-plugin-graphql/src/index.js +20 -8
- package/packages/datadog-plugin-graphql/src/parse.js +24 -4
- package/packages/datadog-plugin-graphql/src/validate.js +16 -5
- package/packages/datadog-plugin-http/src/client.js +8 -3
- package/packages/datadog-plugin-http/src/index.js +6 -8
- package/packages/datadog-plugin-http2/src/client.js +5 -2
- package/packages/datadog-plugin-jest/src/index.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +1 -1
- package/packages/datadog-plugin-mysql/src/index.js +7 -2
- package/packages/datadog-plugin-openai/src/services.js +2 -2
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +2 -2
- package/packages/datadog-plugin-undici/src/index.js +5 -2
- package/packages/datadog-plugin-vitest/src/index.js +76 -64
- package/packages/datadog-shimmer/src/shimmer.js +37 -16
- package/packages/datadog-webpack/index.js +17 -1
- package/packages/datadog-webpack/src/optional-peer-loader.js +17 -0
- package/packages/dd-trace/src/aiguard/sdk.js +15 -3
- package/packages/dd-trace/src/appsec/api_security/index.js +55 -0
- package/packages/dd-trace/src/appsec/api_security/sampler.js +147 -0
- 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 +5 -5
- package/packages/dd-trace/src/appsec/iast/analyzers/nosql-injection-mongodb-analyzer.js +26 -19
- 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 +20 -7
- package/packages/dd-trace/src/appsec/lambda.js +135 -0
- package/packages/dd-trace/src/appsec/reporter.js +50 -10
- package/packages/dd-trace/src/appsec/telemetry/api_security.js +34 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +27 -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/early-flake-detection/get-known-tests.js +21 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -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 +3 -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 +55 -13
- 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/request.js +1 -17
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +3 -3
- 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 +25 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +596 -58
- package/packages/dd-trace/src/config/helper.js +134 -33
- package/packages/dd-trace/src/config/index.js +39 -23
- package/packages/dd-trace/src/config/major-overrides.js +4 -2
- package/packages/dd-trace/src/config/parsers.js +19 -1
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +109 -57
- package/packages/dd-trace/src/constants.js +7 -0
- package/packages/dd-trace/src/datastreams/processor.js +11 -3
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +47 -35
- package/packages/dd-trace/src/debugger/devtools_client/index.js +31 -66
- package/packages/dd-trace/src/debugger/devtools_client/probe_sampler.js +83 -0
- package/packages/dd-trace/src/debugger/devtools_client/state.js +1 -0
- package/packages/dd-trace/src/debugger/index.js +5 -0
- package/packages/dd-trace/src/debugger/probe_sampler.js +112 -0
- package/packages/dd-trace/src/debugger/probe_sampler_constants.js +19 -0
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +77 -26
- 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/exporters/common/request.js +1 -20
- package/packages/dd-trace/src/exporters/common/url.js +34 -0
- package/packages/dd-trace/src/exporters/span-stats/writer.js +1 -3
- package/packages/dd-trace/src/guardrails/index.js +8 -5
- package/packages/dd-trace/src/index.js +6 -4
- package/packages/dd-trace/src/llmobs/constants/tags.js +6 -0
- package/packages/dd-trace/src/llmobs/index.js +17 -6
- package/packages/dd-trace/src/llmobs/plugins/base.js +4 -4
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +3 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +5 -1
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +8 -6
- package/packages/dd-trace/src/llmobs/span_processor.js +5 -1
- package/packages/dd-trace/src/llmobs/tagger.js +58 -3
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +1 -1
- package/packages/dd-trace/src/log/index.js +18 -20
- package/packages/dd-trace/src/openfeature/flagging_provider.js +2 -7
- package/packages/dd-trace/src/opentelemetry/bridge-span-base.js +5 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +54 -5
- package/packages/dd-trace/src/opentelemetry/metrics/instruments.js +2 -1
- 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/metrics/periodic_metric_reader.js +4 -3
- package/packages/dd-trace/src/opentelemetry/metrics/time.js +19 -0
- package/packages/dd-trace/src/opentelemetry/otlp/common.proto +2 -2
- package/packages/dd-trace/src/opentelemetry/otlp/logs.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/logs_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/metrics_service.proto +1 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +44 -38
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +29 -13
- package/packages/dd-trace/src/opentelemetry/span.js +14 -3
- package/packages/dd-trace/src/opentelemetry/span_context.js +1 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +3 -2
- package/packages/dd-trace/src/opentelemetry/trace/otlp_transformer.js +26 -13
- package/packages/dd-trace/src/opentelemetry/tracer_provider.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +3 -3
- package/packages/dd-trace/src/opentracing/span_context.js +17 -0
- package/packages/dd-trace/src/opentracing/tracer.js +6 -1
- package/packages/dd-trace/src/plugin_manager.js +14 -8
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +276 -0
- package/packages/dd-trace/src/plugins/util/llm.js +6 -1
- package/packages/dd-trace/src/plugins/util/test.js +91 -9
- package/packages/dd-trace/src/plugins/util/url.js +68 -1
- package/packages/dd-trace/src/plugins/util/web.js +19 -36
- package/packages/dd-trace/src/priority_sampler.js +1 -13
- package/packages/dd-trace/src/profiling/config.js +129 -198
- package/packages/dd-trace/src/profiling/constants.js +4 -7
- package/packages/dd-trace/src/profiling/exporter_cli.js +10 -10
- package/packages/dd-trace/src/profiling/exporters/agent.js +23 -16
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +45 -14
- package/packages/dd-trace/src/profiling/exporters/file.js +16 -5
- package/packages/dd-trace/src/profiling/oom.js +71 -0
- package/packages/dd-trace/src/profiling/profiler.js +35 -23
- package/packages/dd-trace/src/profiling/profilers/events.js +14 -7
- package/packages/dd-trace/src/profiling/profilers/space.js +27 -18
- package/packages/dd-trace/src/profiling/profilers/wall.js +35 -25
- package/packages/dd-trace/src/proxy.js +8 -8
- package/packages/dd-trace/src/ritm.js +4 -2
- package/packages/dd-trace/src/serverless.js +1 -2
- package/packages/dd-trace/src/service-naming/schemas/util.js +36 -5
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +5 -3
- package/packages/dd-trace/src/span_format.js +2 -1
- package/packages/dd-trace/src/span_processor.js +11 -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 +20 -18
- package/packages/dd-trace/src/telemetry/session-propagation.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +9 -9
- package/packages/dd-trace/src/tracer.js +15 -1
- package/packages/dd-trace/src/util.js +14 -0
- package/register.js +58 -1
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/pprof-format/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
- package/packages/dd-trace/src/appsec/api_security_sampler.js +0 -117
|
@@ -1,1604 +1,17 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const
|
|
3
|
+
const { getEnvironmentVariable } = require('../../dd-trace/src/config/helper')
|
|
4
|
+
const { isTrue } = require('../../dd-trace/src/util')
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const { performance } = require('node:perf_hooks')
|
|
6
|
+
const VITEST_NO_WORKER_INIT_ACTIVE_ENV = 'DD_TEST_OPT_VITEST_NO_WORKER_INIT_ACTIVE'
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
DYNAMIC_NAME_RE,
|
|
15
|
-
getTestSuitePath,
|
|
16
|
-
getEfdRetryCount,
|
|
17
|
-
getMaxEfdRetryCount,
|
|
18
|
-
recordAttemptToFixExecution,
|
|
19
|
-
collectTestOptimizationSummariesFromTraces,
|
|
20
|
-
logAttemptToFixTestExecution,
|
|
21
|
-
logTestOptimizationSummary,
|
|
22
|
-
getTestOptimizationRequestResults,
|
|
23
|
-
} = require('../../dd-trace/src/plugins/util/test')
|
|
24
|
-
const { addHook, channel } = require('./helpers/instrument')
|
|
8
|
+
if (getEnvironmentVariable('DD_VITEST_WORKER') === '1') {
|
|
9
|
+
// In no-worker-init mode the main process reports Vitest events from reporter hooks.
|
|
10
|
+
// Loading worker instrumentation here would reintroduce the worker tracer path.
|
|
11
|
+
// eslint-disable-next-line eslint-rules/eslint-process-env
|
|
12
|
+
if (isTrue(process.env[VITEST_NO_WORKER_INIT_ACTIVE_ENV])) return
|
|
25
13
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const testPassCh = channel('ci:vitest:test:pass')
|
|
30
|
-
const testErrorCh = channel('ci:vitest:test:error')
|
|
31
|
-
const testSkipCh = channel('ci:vitest:test:skip')
|
|
32
|
-
const isNewTestCh = channel('ci:vitest:test:is-new')
|
|
33
|
-
const isAttemptToFixCh = channel('ci:vitest:test:is-attempt-to-fix')
|
|
34
|
-
const isDisabledCh = channel('ci:vitest:test:is-disabled')
|
|
35
|
-
const isQuarantinedCh = channel('ci:vitest:test:is-quarantined')
|
|
36
|
-
const isModifiedCh = channel('ci:vitest:test:is-modified')
|
|
37
|
-
const testFnCh = channel('ci:vitest:test:fn')
|
|
38
|
-
|
|
39
|
-
// test suite hooks
|
|
40
|
-
const testSuiteStartCh = channel('ci:vitest:test-suite:start')
|
|
41
|
-
const testSuiteFinishCh = channel('ci:vitest:test-suite:finish')
|
|
42
|
-
const testSuiteErrorCh = channel('ci:vitest:test-suite:error')
|
|
43
|
-
|
|
44
|
-
// test session hooks
|
|
45
|
-
const testSessionStartCh = channel('ci:vitest:session:start')
|
|
46
|
-
const testSessionFinishCh = channel('ci:vitest:session:finish')
|
|
47
|
-
const testSessionConfigurationCh = channel('ci:vitest:session:configuration')
|
|
48
|
-
const libraryConfigurationCh = channel('ci:vitest:library-configuration')
|
|
49
|
-
const knownTestsCh = channel('ci:vitest:known-tests')
|
|
50
|
-
const isEarlyFlakeDetectionFaultyCh = channel('ci:vitest:is-early-flake-detection-faulty')
|
|
51
|
-
const testManagementTestsCh = channel('ci:vitest:test-management-tests')
|
|
52
|
-
const modifiedFilesCh = channel('ci:vitest:modified-files')
|
|
53
|
-
|
|
54
|
-
const workerReportTraceCh = channel('ci:vitest:worker-report:trace')
|
|
55
|
-
const workerReportLogsCh = channel('ci:vitest:worker-report:logs')
|
|
56
|
-
const codeCoverageReportCh = channel('ci:vitest:coverage-report')
|
|
57
|
-
|
|
58
|
-
const taskToCtx = new WeakMap()
|
|
59
|
-
const taskToStatuses = new WeakMap()
|
|
60
|
-
const taskToReportedErrorCount = new WeakMap()
|
|
61
|
-
const attemptToFixTaskToStatuses = new WeakMap()
|
|
62
|
-
const originalHookFns = new WeakMap()
|
|
63
|
-
const newTasks = new WeakSet()
|
|
64
|
-
const dynamicNameTasks = new WeakSet()
|
|
65
|
-
const newTestsWithDynamicNames = new Set()
|
|
66
|
-
const disabledTasks = new WeakSet()
|
|
67
|
-
const quarantinedTasks = new WeakSet()
|
|
68
|
-
const attemptToFixTasks = new WeakSet()
|
|
69
|
-
const modifiedTasks = new WeakSet()
|
|
70
|
-
const efdDeterminedRetries = new WeakMap()
|
|
71
|
-
const efdSlowAbortedTasks = new WeakSet()
|
|
72
|
-
const efdExecutionStartByTask = new WeakMap()
|
|
73
|
-
const efdSkippedRetryResults = new WeakMap()
|
|
74
|
-
const attemptToFixExecutions = new Map()
|
|
75
|
-
const loggedAttemptToFixTests = new Set()
|
|
76
|
-
let isRetryReasonEfd = false
|
|
77
|
-
let isRetryReasonAttemptToFix = false
|
|
78
|
-
const switchedStatuses = new WeakSet()
|
|
79
|
-
const workerProcesses = new WeakSet()
|
|
80
|
-
const mainProcessSetupPromises = new WeakMap()
|
|
81
|
-
const coverageWrappedProviders = new WeakSet()
|
|
82
|
-
const finishWrappedContexts = new WeakSet()
|
|
83
|
-
let isFlakyTestRetriesEnabled = false
|
|
84
|
-
let flakyTestRetriesCount = 0
|
|
85
|
-
let isEarlyFlakeDetectionEnabled = false
|
|
86
|
-
let earlyFlakeDetectionNumRetries = 0
|
|
87
|
-
let earlyFlakeDetectionSlowTestRetries = {}
|
|
88
|
-
let isEarlyFlakeDetectionFaulty = false
|
|
89
|
-
let isKnownTestsEnabled = false
|
|
90
|
-
let isTestManagementTestsEnabled = false
|
|
91
|
-
let isImpactedTestsEnabled = false
|
|
92
|
-
let vitestGetFn = null
|
|
93
|
-
let vitestSetFn = null
|
|
94
|
-
let vitestGetHooks = null
|
|
95
|
-
let testManagementAttemptToFixRetries = 0
|
|
96
|
-
let isDiEnabled = false
|
|
97
|
-
let testCodeCoverageLinesTotal
|
|
98
|
-
let coverageRootDir
|
|
99
|
-
let isSessionStarted = false
|
|
100
|
-
let vitestPool = null
|
|
101
|
-
|
|
102
|
-
const BREAKPOINT_HIT_GRACE_PERIOD_MS = 400
|
|
103
|
-
|
|
104
|
-
function getConfiguredEfdRetryCount (slowTestRetries, fallbackRetryCount) {
|
|
105
|
-
if (!slowTestRetries || !Object.keys(slowTestRetries).length) {
|
|
106
|
-
return fallbackRetryCount
|
|
107
|
-
}
|
|
108
|
-
return getMaxEfdRetryCount(slowTestRetries)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function getTestCommand () {
|
|
112
|
-
return `vitest ${process.argv.slice(2).join(' ')}`
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function waitForHitProbe () {
|
|
116
|
-
return new Promise(resolve => {
|
|
117
|
-
realSetTimeout(() => {
|
|
118
|
-
resolve()
|
|
119
|
-
}, BREAKPOINT_HIT_GRACE_PERIOD_MS)
|
|
120
|
-
})
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function isValidKnownTests (receivedKnownTests) {
|
|
124
|
-
return !!receivedKnownTests.vitest
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
function getProvidedContext () {
|
|
128
|
-
try {
|
|
129
|
-
const {
|
|
130
|
-
_ddIsEarlyFlakeDetectionEnabled,
|
|
131
|
-
_ddIsDiEnabled,
|
|
132
|
-
_ddKnownTests: knownTests,
|
|
133
|
-
_ddEarlyFlakeDetectionNumRetries: numRepeats,
|
|
134
|
-
_ddEarlyFlakeDetectionSlowTestRetries: slowTestRetries,
|
|
135
|
-
_ddIsKnownTestsEnabled: isKnownTestsEnabled,
|
|
136
|
-
_ddIsTestManagementTestsEnabled: isTestManagementTestsEnabled,
|
|
137
|
-
_ddTestManagementAttemptToFixRetries: testManagementAttemptToFixRetries,
|
|
138
|
-
_ddTestManagementTests: testManagementTests,
|
|
139
|
-
_ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
|
|
140
|
-
_ddFlakyTestRetriesCount: flakyTestRetriesCount,
|
|
141
|
-
_ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
|
|
142
|
-
_ddModifiedFiles: modifiedFiles,
|
|
143
|
-
_ddTestSessionId: testSessionId,
|
|
144
|
-
_ddTestModuleId: testModuleId,
|
|
145
|
-
_ddTestCommand: testCommand,
|
|
146
|
-
_ddRepositoryRoot: repositoryRoot,
|
|
147
|
-
_ddCodeOwnersEntries: codeOwnersEntries,
|
|
148
|
-
} = globalThis.__vitest_worker__.providedContext
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
isDiEnabled: _ddIsDiEnabled,
|
|
152
|
-
isEarlyFlakeDetectionEnabled: _ddIsEarlyFlakeDetectionEnabled,
|
|
153
|
-
knownTests,
|
|
154
|
-
numRepeats,
|
|
155
|
-
slowTestRetries: slowTestRetries ?? {},
|
|
156
|
-
isKnownTestsEnabled,
|
|
157
|
-
isTestManagementTestsEnabled,
|
|
158
|
-
testManagementAttemptToFixRetries,
|
|
159
|
-
testManagementTests,
|
|
160
|
-
isFlakyTestRetriesEnabled,
|
|
161
|
-
flakyTestRetriesCount: flakyTestRetriesCount ?? 0,
|
|
162
|
-
isImpactedTestsEnabled,
|
|
163
|
-
modifiedFiles,
|
|
164
|
-
testSessionId,
|
|
165
|
-
testModuleId,
|
|
166
|
-
testCommand,
|
|
167
|
-
repositoryRoot,
|
|
168
|
-
codeOwnersEntries,
|
|
169
|
-
}
|
|
170
|
-
} catch {
|
|
171
|
-
log.error('Vitest workers could not parse provided context, so some features will not work.')
|
|
172
|
-
return {
|
|
173
|
-
isDiEnabled: false,
|
|
174
|
-
isEarlyFlakeDetectionEnabled: false,
|
|
175
|
-
knownTests: {},
|
|
176
|
-
numRepeats: 0,
|
|
177
|
-
slowTestRetries: {},
|
|
178
|
-
isKnownTestsEnabled: false,
|
|
179
|
-
isTestManagementTestsEnabled: false,
|
|
180
|
-
testManagementAttemptToFixRetries: 0,
|
|
181
|
-
testManagementTests: {},
|
|
182
|
-
isFlakyTestRetriesEnabled: false,
|
|
183
|
-
flakyTestRetriesCount: 0,
|
|
184
|
-
isImpactedTestsEnabled: false,
|
|
185
|
-
modifiedFiles: {},
|
|
186
|
-
testSessionId: undefined,
|
|
187
|
-
testModuleId: undefined,
|
|
188
|
-
testCommand: undefined,
|
|
189
|
-
repositoryRoot: undefined,
|
|
190
|
-
codeOwnersEntries: undefined,
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function isReporterPackage (vitestPackage) {
|
|
196
|
-
return vitestPackage.B?.name === 'BaseSequencer'
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// from 2.0.0
|
|
200
|
-
function isReporterPackageNew (vitestPackage) {
|
|
201
|
-
return vitestPackage.e?.name === 'BaseSequencer'
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function isReporterPackageNewest (vitestPackage) {
|
|
205
|
-
return vitestPackage.h?.name === 'BaseSequencer'
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Finds an export by its `.name` property in a minified vitest chunk.
|
|
210
|
-
* Minified export keys change across versions, so we search by function/class name.
|
|
211
|
-
* @param {object} pkg - The module exports object
|
|
212
|
-
* @param {string} name - The `.name` value to look for
|
|
213
|
-
* @returns {{ key: string, value: Function } | undefined}
|
|
214
|
-
*/
|
|
215
|
-
function findExportByName (pkg, name) {
|
|
216
|
-
for (const [key, value] of Object.entries(pkg)) {
|
|
217
|
-
if (value?.name === name) {
|
|
218
|
-
return { key, value }
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function getBaseSequencerExport (vitestPackage) {
|
|
224
|
-
return findExportByName(vitestPackage, 'BaseSequencer')
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function getChannelPromise (channelToPublishTo, frameworkVersion) {
|
|
228
|
-
return new Promise(resolve => {
|
|
229
|
-
channelToPublishTo.publish({ onDone: resolve, frameworkVersion })
|
|
230
|
-
})
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
function isCliApiPackage (vitestPackage) {
|
|
234
|
-
return !!findExportByName(vitestPackage, 'startVitest')
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
function getTestRunnerExport (testPackage) {
|
|
238
|
-
return findExportByName(testPackage, 'VitestTestRunner') || findExportByName(testPackage, 'TestRunner')
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
function getVitestExport (vitestPackage) {
|
|
242
|
-
return findExportByName(vitestPackage, 'Vitest')
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function getForksPoolWorkerExport (vitestPackage) {
|
|
246
|
-
return findExportByName(vitestPackage, 'ForksPoolWorker')
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function getThreadsPoolWorkerExport (vitestPackage) {
|
|
250
|
-
return findExportByName(vitestPackage, 'ThreadsPoolWorker')
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function getSessionStatus (state) {
|
|
254
|
-
if (state.getCountOfFailedTests() > 0) {
|
|
255
|
-
return 'fail'
|
|
256
|
-
}
|
|
257
|
-
if (state.pathsSet.size === 0) {
|
|
258
|
-
return 'skip'
|
|
259
|
-
}
|
|
260
|
-
return 'pass'
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// From https://github.com/vitest-dev/vitest/blob/51c04e2f44d91322b334f8ccbcdb368facc3f8ec/packages/runner/src/run.ts#L243-L250
|
|
264
|
-
function getVitestTestStatus (test, retryCount) {
|
|
265
|
-
if (test.result.state !== 'fail' && (!test.repeats || (test.retry ?? 0) === retryCount)) {
|
|
266
|
-
return 'pass'
|
|
267
|
-
}
|
|
268
|
-
return 'fail'
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
function getTypeTasks (fileTasks, type = 'test') {
|
|
272
|
-
const typeTasks = []
|
|
273
|
-
|
|
274
|
-
function getTasks (tasks) {
|
|
275
|
-
for (const task of tasks) {
|
|
276
|
-
if (task.type === type) {
|
|
277
|
-
typeTasks.push(task)
|
|
278
|
-
} else if (task.tasks) {
|
|
279
|
-
getTasks(task.tasks)
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
getTasks(fileTasks)
|
|
285
|
-
|
|
286
|
-
return typeTasks
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function getTestName (task) {
|
|
290
|
-
let testName = task.name
|
|
291
|
-
let currentTask = task.suite
|
|
292
|
-
|
|
293
|
-
while (currentTask) {
|
|
294
|
-
if (currentTask.name) {
|
|
295
|
-
testName = `${currentTask.name} ${testName}`
|
|
296
|
-
}
|
|
297
|
-
currentTask = currentTask.suite
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
return testName
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function getFinalAttemptToFixStatus (task, state, isSwitchedStatus, testCtx) {
|
|
304
|
-
if (isSwitchedStatus && attemptToFixTasks.has(task) && testCtx?.status) {
|
|
305
|
-
return testCtx.status
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return state === 'fail' ? 'fail' : 'pass'
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function recordFinalAttemptToFixExecution (task, status, providedContext) {
|
|
312
|
-
const statuses = attemptToFixTaskToStatuses.get(task)
|
|
313
|
-
if (statuses && statuses.length <= providedContext.testManagementAttemptToFixRetries) {
|
|
314
|
-
statuses.push(status)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
recordAttemptToFixExecution(attemptToFixExecutions, {
|
|
318
|
-
testSuite: getTestSuitePath(task.file.filepath, process.cwd()),
|
|
319
|
-
testName: getTestName(task),
|
|
320
|
-
status,
|
|
321
|
-
isDisabled: disabledTasks.has(task),
|
|
322
|
-
isQuarantined: quarantinedTasks.has(task),
|
|
323
|
-
})
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function disableFrameworkRetries (task) {
|
|
327
|
-
task.retry = 0
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Vitest accumulates retry and repeat errors on one task result. The first error added since
|
|
332
|
-
* the last reported attempt is the primary error for the failed attempt currently being reported.
|
|
333
|
-
*
|
|
334
|
-
* @param {object} task
|
|
335
|
-
* @param {Array<object> | undefined} errors
|
|
336
|
-
* @returns {object | undefined}
|
|
337
|
-
*/
|
|
338
|
-
function getCurrentAttemptTestError (task, errors) {
|
|
339
|
-
if (!errors?.length) return
|
|
340
|
-
|
|
341
|
-
const previousErrorCount = taskToReportedErrorCount.get(task) ?? 0
|
|
342
|
-
const testError = errors[previousErrorCount] ?? errors[0]
|
|
343
|
-
taskToReportedErrorCount.set(task, errors.length)
|
|
344
|
-
return testError
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
/**
|
|
348
|
-
* Wraps a function so it runs inside the current test span context.
|
|
349
|
-
* @param {object} task
|
|
350
|
-
* @param {Function} fn
|
|
351
|
-
* @returns {Function}
|
|
352
|
-
*/
|
|
353
|
-
function wrapTestScopedFn (task, fn) {
|
|
354
|
-
return shimmer.wrapFunction(fn, fn => function (...args) {
|
|
355
|
-
return testFnCh.runStores(taskToCtx.get(task), () => fn.apply(this, args))
|
|
356
|
-
})
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
/**
|
|
360
|
-
* Wraps a `beforeEach` cleanup callback so it inherits the test span context.
|
|
361
|
-
* Vitest allows `beforeEach` to return a cleanup function, including via a promise.
|
|
362
|
-
* @param {object} task
|
|
363
|
-
* @param {unknown} result
|
|
364
|
-
* @returns {unknown}
|
|
365
|
-
*/
|
|
366
|
-
function wrapBeforeEachCleanupResult (task, result) {
|
|
367
|
-
if (typeof result === 'function') {
|
|
368
|
-
return wrapTestScopedFn(task, result)
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (result && typeof result.then === 'function') {
|
|
372
|
-
return result.then(cleanupFn => wrapBeforeEachCleanupResult(task, cleanupFn))
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
return result
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
function getWorkspaceProject (ctx) {
|
|
379
|
-
return ctx.getCoreWorkspaceProject
|
|
380
|
-
? ctx.getCoreWorkspaceProject()
|
|
381
|
-
: ctx.getRootProject()
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
function setProvidedContext (ctx, values, warningMessage) {
|
|
385
|
-
try {
|
|
386
|
-
Object.assign(getWorkspaceProject(ctx)._provided, values)
|
|
387
|
-
} catch {
|
|
388
|
-
log.warn(warningMessage)
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
function getTestFilepathsFromSpecifications (testSpecifications) {
|
|
393
|
-
if (!Array.isArray(testSpecifications) || !testSpecifications.length) {
|
|
394
|
-
return
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
return testSpecifications.map(testSpecification => {
|
|
398
|
-
const testFile = Array.isArray(testSpecification) ? testSpecification[1] : testSpecification
|
|
399
|
-
return testFile?.moduleId || testFile?.filepath || testFile
|
|
400
|
-
})
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
function getTestFilepaths (ctx, testSpecifications) {
|
|
404
|
-
const testFilepaths = getTestFilepathsFromSpecifications(testSpecifications)
|
|
405
|
-
if (testFilepaths) {
|
|
406
|
-
return testFilepaths
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
const getFilePaths = ctx.getTestFilepaths || ctx._globTestFilepaths
|
|
410
|
-
return getFilePaths.call(ctx)
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
function wrapCoverageProvider (ctx) {
|
|
414
|
-
const { coverageProvider } = ctx
|
|
415
|
-
if (!coverageProvider?.generateCoverage || coverageWrappedProviders.has(coverageProvider)) {
|
|
416
|
-
return
|
|
417
|
-
}
|
|
418
|
-
coverageWrappedProviders.add(coverageProvider)
|
|
419
|
-
|
|
420
|
-
// Capture coverage root directory from config (default is 'coverage' in cwd)
|
|
421
|
-
try {
|
|
422
|
-
const coverageConfig = ctx.config?.coverage
|
|
423
|
-
const reportsDirectory = coverageConfig?.reportsDirectory || 'coverage'
|
|
424
|
-
const rootDir = ctx.config?.root || process.cwd()
|
|
425
|
-
coverageRootDir = path.isAbsolute(reportsDirectory) ? reportsDirectory : path.join(rootDir, reportsDirectory)
|
|
426
|
-
} catch {
|
|
427
|
-
// Fallback to cwd if we can't get config
|
|
428
|
-
coverageRootDir = process.cwd()
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
shimmer.wrap(coverageProvider, 'generateCoverage', generateCoverage => async function () {
|
|
432
|
-
const totalCodeCoverage = await generateCoverage.apply(this, arguments)
|
|
433
|
-
|
|
434
|
-
try {
|
|
435
|
-
testCodeCoverageLinesTotal = totalCodeCoverage.getCoverageSummary().lines.pct
|
|
436
|
-
} catch {
|
|
437
|
-
// ignore errors
|
|
438
|
-
}
|
|
439
|
-
return totalCodeCoverage
|
|
440
|
-
})
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
function wrapSessionFinish (ctx) {
|
|
444
|
-
if (finishWrappedContexts.has(ctx)) {
|
|
445
|
-
return
|
|
446
|
-
}
|
|
447
|
-
finishWrappedContexts.add(ctx)
|
|
448
|
-
|
|
449
|
-
shimmer.wrap(ctx, 'exit', getFinishWrapper)
|
|
450
|
-
shimmer.wrap(ctx, 'close', getFinishWrapper)
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
async function runMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
|
|
454
|
-
if (!testSessionFinishCh.hasSubscribers) {
|
|
455
|
-
return
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
try {
|
|
459
|
-
const { err, libraryConfig } = await getChannelPromise(libraryConfigurationCh, frameworkVersion)
|
|
460
|
-
if (!err) {
|
|
461
|
-
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
462
|
-
flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
|
|
463
|
-
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
|
|
464
|
-
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
|
|
465
|
-
earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
|
|
466
|
-
isDiEnabled = libraryConfig.isDiEnabled
|
|
467
|
-
isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
|
|
468
|
-
isTestManagementTestsEnabled = libraryConfig.isTestManagementEnabled
|
|
469
|
-
testManagementAttemptToFixRetries = libraryConfig.testManagementAttemptToFixRetries
|
|
470
|
-
isImpactedTestsEnabled = libraryConfig.isImpactedTestsEnabled
|
|
471
|
-
}
|
|
472
|
-
} catch {
|
|
473
|
-
isFlakyTestRetriesEnabled = false
|
|
474
|
-
isEarlyFlakeDetectionEnabled = false
|
|
475
|
-
isDiEnabled = false
|
|
476
|
-
isKnownTestsEnabled = false
|
|
477
|
-
isImpactedTestsEnabled = false
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
if (testSessionConfigurationCh.hasSubscribers) {
|
|
481
|
-
const { testSessionId, testModuleId, testCommand, repositoryRoot, codeOwnersEntries } = await getChannelPromise(
|
|
482
|
-
testSessionConfigurationCh,
|
|
483
|
-
frameworkVersion
|
|
484
|
-
)
|
|
485
|
-
setProvidedContext(ctx, {
|
|
486
|
-
_ddTestSessionId: testSessionId,
|
|
487
|
-
_ddTestModuleId: testModuleId,
|
|
488
|
-
_ddTestCommand: testCommand,
|
|
489
|
-
_ddRepositoryRoot: repositoryRoot,
|
|
490
|
-
_ddCodeOwnersEntries: codeOwnersEntries,
|
|
491
|
-
}, 'Could not send test session configuration to workers.')
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
const {
|
|
495
|
-
knownTestsResponse,
|
|
496
|
-
testManagementTestsResponse,
|
|
497
|
-
} = await getTestOptimizationRequestResults({
|
|
498
|
-
isKnownTestsEnabled,
|
|
499
|
-
isTestManagementTestsEnabled,
|
|
500
|
-
getKnownTests: () => getChannelPromise(knownTestsCh),
|
|
501
|
-
getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
|
|
502
|
-
})
|
|
503
|
-
|
|
504
|
-
if (isFlakyTestRetriesEnabled && !ctx.config.retry && flakyTestRetriesCount > 0) {
|
|
505
|
-
ctx.config.retry = flakyTestRetriesCount
|
|
506
|
-
setProvidedContext(ctx, {
|
|
507
|
-
_ddIsFlakyTestRetriesEnabled: isFlakyTestRetriesEnabled,
|
|
508
|
-
_ddFlakyTestRetriesCount: flakyTestRetriesCount,
|
|
509
|
-
}, 'Could not send library configuration to workers.')
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
if (isKnownTestsEnabled) {
|
|
513
|
-
const currentKnownTestsResponse = knownTestsResponse || await getChannelPromise(knownTestsCh)
|
|
514
|
-
if (currentKnownTestsResponse.err) {
|
|
515
|
-
isEarlyFlakeDetectionEnabled = false
|
|
516
|
-
} else {
|
|
517
|
-
const knownTests = currentKnownTestsResponse.knownTests
|
|
518
|
-
const testFilepaths = await getTestFilepaths(ctx, testSpecifications)
|
|
519
|
-
|
|
520
|
-
if (isValidKnownTests(knownTests)) {
|
|
521
|
-
isEarlyFlakeDetectionFaultyCh.publish({
|
|
522
|
-
knownTests: knownTests.vitest,
|
|
523
|
-
testFilepaths,
|
|
524
|
-
onDone: (isFaulty) => {
|
|
525
|
-
isEarlyFlakeDetectionFaulty = isFaulty
|
|
526
|
-
},
|
|
527
|
-
})
|
|
528
|
-
if (isEarlyFlakeDetectionFaulty) {
|
|
529
|
-
isEarlyFlakeDetectionEnabled = false
|
|
530
|
-
log.warn('New test detection is disabled because the number of new tests is too high.')
|
|
531
|
-
} else {
|
|
532
|
-
setProvidedContext(ctx, {
|
|
533
|
-
_ddIsKnownTestsEnabled: isKnownTestsEnabled,
|
|
534
|
-
_ddKnownTests: knownTests,
|
|
535
|
-
_ddIsEarlyFlakeDetectionEnabled: isEarlyFlakeDetectionEnabled,
|
|
536
|
-
_ddEarlyFlakeDetectionNumRetries:
|
|
537
|
-
getConfiguredEfdRetryCount(earlyFlakeDetectionSlowTestRetries, earlyFlakeDetectionNumRetries),
|
|
538
|
-
_ddEarlyFlakeDetectionSlowTestRetries: earlyFlakeDetectionSlowTestRetries,
|
|
539
|
-
}, 'Could not send known tests to workers so Early Flake Detection will not work.')
|
|
540
|
-
}
|
|
541
|
-
} else {
|
|
542
|
-
isEarlyFlakeDetectionFaulty = true
|
|
543
|
-
isEarlyFlakeDetectionEnabled = false
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
if (isDiEnabled) {
|
|
549
|
-
setProvidedContext(ctx, {
|
|
550
|
-
_ddIsDiEnabled: isDiEnabled,
|
|
551
|
-
}, 'Could not send Dynamic Instrumentation configuration to workers.')
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
if (isTestManagementTestsEnabled) {
|
|
555
|
-
const { err, testManagementTests: receivedTestManagementTests } =
|
|
556
|
-
testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
|
|
557
|
-
if (err) {
|
|
558
|
-
isTestManagementTestsEnabled = false
|
|
559
|
-
log.error('Could not get test management tests.')
|
|
560
|
-
} else {
|
|
561
|
-
setProvidedContext(ctx, {
|
|
562
|
-
_ddIsTestManagementTestsEnabled: isTestManagementTestsEnabled,
|
|
563
|
-
_ddTestManagementAttemptToFixRetries: testManagementAttemptToFixRetries,
|
|
564
|
-
_ddTestManagementTests: receivedTestManagementTests,
|
|
565
|
-
}, 'Could not send test management tests to workers so Test Management will not work.')
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
if (isImpactedTestsEnabled) {
|
|
570
|
-
const { err, modifiedFiles } = await getChannelPromise(modifiedFilesCh)
|
|
571
|
-
if (err) {
|
|
572
|
-
log.error('Could not get modified tests.')
|
|
573
|
-
} else {
|
|
574
|
-
setProvidedContext(ctx, {
|
|
575
|
-
_ddIsImpactedTestsEnabled: isImpactedTestsEnabled,
|
|
576
|
-
_ddModifiedFiles: modifiedFiles,
|
|
577
|
-
}, 'Could not send modified tests to workers so Impacted Tests will not work.')
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
wrapCoverageProvider(ctx)
|
|
582
|
-
wrapSessionFinish(ctx)
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
function ensureMainProcessSetup (ctx, frameworkVersion, testSpecifications) {
|
|
586
|
-
let setupPromise = mainProcessSetupPromises.get(ctx)
|
|
587
|
-
if (!setupPromise) {
|
|
588
|
-
setupPromise = runMainProcessSetup(ctx, frameworkVersion, testSpecifications)
|
|
589
|
-
mainProcessSetupPromises.set(ctx, setupPromise)
|
|
590
|
-
}
|
|
591
|
-
return setupPromise
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
function getSortWrapper (sort, frameworkVersion) {
|
|
595
|
-
return async function () {
|
|
596
|
-
await ensureMainProcessSetup(this.ctx, frameworkVersion, arguments[0])
|
|
597
|
-
return sort.apply(this, arguments)
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function getFinishWrapper (exitOrClose) {
|
|
602
|
-
let isClosed = false
|
|
603
|
-
return async function () {
|
|
604
|
-
if (isClosed) { // needed because exit calls close
|
|
605
|
-
return exitOrClose.apply(this, arguments)
|
|
606
|
-
}
|
|
607
|
-
isClosed = true
|
|
608
|
-
|
|
609
|
-
if (!testSessionFinishCh.hasSubscribers) {
|
|
610
|
-
return exitOrClose.apply(this, arguments)
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
let onFinish
|
|
614
|
-
|
|
615
|
-
const flushPromise = new Promise(resolve => {
|
|
616
|
-
onFinish = resolve
|
|
617
|
-
})
|
|
618
|
-
const failedSuites = this.state.getFailedFilepaths()
|
|
619
|
-
let error
|
|
620
|
-
if (failedSuites.length) {
|
|
621
|
-
error = new Error(`Test suites failed: ${failedSuites.length}.`)
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
testSessionFinishCh.publish({
|
|
625
|
-
status: getSessionStatus(this.state),
|
|
626
|
-
testCodeCoverageLinesTotal,
|
|
627
|
-
error,
|
|
628
|
-
isEarlyFlakeDetectionEnabled,
|
|
629
|
-
isEarlyFlakeDetectionFaulty,
|
|
630
|
-
isTestManagementTestsEnabled,
|
|
631
|
-
vitestPool,
|
|
632
|
-
onFinish,
|
|
633
|
-
})
|
|
634
|
-
|
|
635
|
-
logTestOptimizationSummary({ attemptToFixExecutions, newTestsWithDynamicNames })
|
|
636
|
-
loggedAttemptToFixTests.clear()
|
|
637
|
-
|
|
638
|
-
await flushPromise
|
|
639
|
-
|
|
640
|
-
// If coverage was generated, publish coverage report channel for upload
|
|
641
|
-
if (coverageRootDir && codeCoverageReportCh.hasSubscribers) {
|
|
642
|
-
await new Promise((resolve) => {
|
|
643
|
-
codeCoverageReportCh.publish({ rootDir: coverageRootDir, onDone: resolve })
|
|
644
|
-
})
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
return exitOrClose.apply(this, arguments)
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
function getCliOrStartVitestWrapper (frameworkVersion) {
|
|
652
|
-
return function (oldCliOrStartVitest) {
|
|
653
|
-
return function (...args) {
|
|
654
|
-
if (!testSessionStartCh.hasSubscribers || isSessionStarted) {
|
|
655
|
-
return oldCliOrStartVitest.apply(this, args)
|
|
656
|
-
}
|
|
657
|
-
isSessionStarted = true
|
|
658
|
-
testSessionStartCh.publish({ command: getTestCommand(), frameworkVersion })
|
|
659
|
-
return oldCliOrStartVitest.apply(this, args)
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
|
|
664
|
-
function isForkPool (pool) {
|
|
665
|
-
return pool === 'forks' || pool === 'vmForks'
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
function isThreadPool (pool) {
|
|
669
|
-
return pool === 'threads' || pool === 'vmThreads'
|
|
14
|
+
require('./vitest-worker')
|
|
15
|
+
} else {
|
|
16
|
+
require('./vitest-main')
|
|
670
17
|
}
|
|
671
|
-
|
|
672
|
-
function getTestSpecificationPool (testSpecification) {
|
|
673
|
-
const project = Array.isArray(testSpecification) ? testSpecification[0] : testSpecification?.project
|
|
674
|
-
return project?.config?.pool || project?.serializedConfig?.pool || project?.pool || testSpecification?.pool
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
function hasForkPoolTestSpecification (testSpecifications) {
|
|
678
|
-
if (!Array.isArray(testSpecifications)) {
|
|
679
|
-
return false
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
for (const testSpecification of testSpecifications) {
|
|
683
|
-
if (isForkPool(getTestSpecificationPool(testSpecification))) {
|
|
684
|
-
return true
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
return false
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
function shouldMarkVitestWorkerEnv (pool, testSpecifications) {
|
|
692
|
-
return isForkPool(pool) || hasForkPoolTestSpecification(testSpecifications) ||
|
|
693
|
-
(!testSpecifications && !isThreadPool(pool))
|
|
694
|
-
}
|
|
695
|
-
|
|
696
|
-
function markVitestWorkerEnv (ctx, testSpecifications) {
|
|
697
|
-
const config = ctx?.config
|
|
698
|
-
if (!config || !shouldMarkVitestWorkerEnv(config.pool, testSpecifications)) {
|
|
699
|
-
return
|
|
700
|
-
}
|
|
701
|
-
config.env = config.env || {}
|
|
702
|
-
config.env.DD_VITEST_WORKER = '1'
|
|
703
|
-
}
|
|
704
|
-
|
|
705
|
-
function wrapVitestRunFiles (Vitest, frameworkVersion) {
|
|
706
|
-
if (!Vitest?.prototype?.runFiles) {
|
|
707
|
-
return
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
shimmer.wrap(Vitest.prototype, 'runFiles', runFiles => async function (testSpecifications) {
|
|
711
|
-
markVitestWorkerEnv(this, testSpecifications)
|
|
712
|
-
await ensureMainProcessSetup(this, frameworkVersion, testSpecifications)
|
|
713
|
-
return runFiles.apply(this, arguments)
|
|
714
|
-
})
|
|
715
|
-
|
|
716
|
-
if (Vitest.prototype.collectTests) {
|
|
717
|
-
shimmer.wrap(Vitest.prototype, 'collectTests', collectTests => function () {
|
|
718
|
-
markVitestWorkerEnv(this)
|
|
719
|
-
return collectTests.apply(this, arguments)
|
|
720
|
-
})
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
|
|
724
|
-
function getCreateCliWrapper (vitestPackage, frameworkVersion) {
|
|
725
|
-
const createCliExport = findExportByName(vitestPackage, 'createCLI')
|
|
726
|
-
if (!createCliExport) {
|
|
727
|
-
return vitestPackage
|
|
728
|
-
}
|
|
729
|
-
shimmer.wrap(vitestPackage, createCliExport.key, getCliOrStartVitestWrapper(frameworkVersion))
|
|
730
|
-
|
|
731
|
-
return vitestPackage
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
function threadHandler (thread) {
|
|
735
|
-
const { runtime } = thread
|
|
736
|
-
let workerProcess
|
|
737
|
-
if (runtime === 'child_process') {
|
|
738
|
-
vitestPool = 'child_process'
|
|
739
|
-
workerProcess = thread.process
|
|
740
|
-
} else if (runtime === 'worker_threads') {
|
|
741
|
-
vitestPool = 'worker_threads'
|
|
742
|
-
workerProcess = thread.thread
|
|
743
|
-
} else {
|
|
744
|
-
vitestPool = 'unknown'
|
|
745
|
-
}
|
|
746
|
-
if (!workerProcess) {
|
|
747
|
-
log.error('Vitest error: could not get process or thread from TinyPool#run')
|
|
748
|
-
return
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
if (workerProcesses.has(workerProcess)) {
|
|
752
|
-
return
|
|
753
|
-
}
|
|
754
|
-
workerProcesses.add(workerProcess)
|
|
755
|
-
workerProcess.on('message', (message) => {
|
|
756
|
-
if (message.__tinypool_worker_message__ && message.data) {
|
|
757
|
-
if (message.interprocessCode === VITEST_WORKER_TRACE_PAYLOAD_CODE) {
|
|
758
|
-
collectTestOptimizationSummariesFromTraces(message.data, {
|
|
759
|
-
newTestsWithDynamicNames,
|
|
760
|
-
attemptToFixExecutions,
|
|
761
|
-
})
|
|
762
|
-
workerReportTraceCh.publish(message.data)
|
|
763
|
-
} else if (message.interprocessCode === VITEST_WORKER_LOGS_PAYLOAD_CODE) {
|
|
764
|
-
workerReportLogsCh.publish(message.data)
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
})
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
function wrapTinyPoolRun (TinyPool) {
|
|
771
|
-
shimmer.wrap(TinyPool.prototype, 'run', run => async function () {
|
|
772
|
-
// We have to do this before and after because the threads list gets recycled, that is, the processes are re-created
|
|
773
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
774
|
-
this.threads.forEach(threadHandler)
|
|
775
|
-
const runResult = await run.apply(this, arguments)
|
|
776
|
-
// eslint-disable-next-line unicorn/no-array-for-each
|
|
777
|
-
this.threads.forEach(threadHandler)
|
|
778
|
-
return runResult
|
|
779
|
-
})
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
addHook({
|
|
783
|
-
name: 'tinypool',
|
|
784
|
-
// version from tinypool@0.8 was used in vitest@1.6.0
|
|
785
|
-
versions: ['>=0.8.0'],
|
|
786
|
-
}, (TinyPool) => {
|
|
787
|
-
wrapTinyPoolRun(TinyPool)
|
|
788
|
-
return TinyPool
|
|
789
|
-
})
|
|
790
|
-
|
|
791
|
-
function getWrappedOn (on) {
|
|
792
|
-
return function (event, callback) {
|
|
793
|
-
if (event !== 'message') {
|
|
794
|
-
return on.apply(this, arguments)
|
|
795
|
-
}
|
|
796
|
-
// `arguments[1]` is the callback function, which
|
|
797
|
-
// we modify to intercept our messages to not interfere
|
|
798
|
-
// with vitest's own messages
|
|
799
|
-
arguments[1] = shimmer.wrapFunction(callback, callback => function (message) {
|
|
800
|
-
if (message.type !== 'Buffer' && Array.isArray(message)) {
|
|
801
|
-
const [interprocessCode, data] = message
|
|
802
|
-
if (interprocessCode === VITEST_WORKER_TRACE_PAYLOAD_CODE) {
|
|
803
|
-
collectTestOptimizationSummariesFromTraces(data, {
|
|
804
|
-
newTestsWithDynamicNames,
|
|
805
|
-
attemptToFixExecutions,
|
|
806
|
-
})
|
|
807
|
-
workerReportTraceCh.publish(data)
|
|
808
|
-
} else if (interprocessCode === VITEST_WORKER_LOGS_PAYLOAD_CODE) {
|
|
809
|
-
workerReportLogsCh.publish(data)
|
|
810
|
-
}
|
|
811
|
-
// If we execute the callback vitest crashes, as the message is not supported
|
|
812
|
-
return
|
|
813
|
-
}
|
|
814
|
-
return callback.apply(this, arguments)
|
|
815
|
-
})
|
|
816
|
-
return on.apply(this, arguments)
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
|
|
820
|
-
function getStartVitestWrapper (cliApiPackage, frameworkVersion) {
|
|
821
|
-
if (!isCliApiPackage(cliApiPackage)) {
|
|
822
|
-
return cliApiPackage
|
|
823
|
-
}
|
|
824
|
-
const startVitestExport = findExportByName(cliApiPackage, 'startVitest')
|
|
825
|
-
shimmer.wrap(cliApiPackage, startVitestExport.key, getCliOrStartVitestWrapper(frameworkVersion))
|
|
826
|
-
|
|
827
|
-
const vitest = getVitestExport(cliApiPackage)
|
|
828
|
-
if (vitest) {
|
|
829
|
-
wrapVitestRunFiles(vitest.value, frameworkVersion)
|
|
830
|
-
}
|
|
831
|
-
|
|
832
|
-
const forksPoolWorker = getForksPoolWorkerExport(cliApiPackage)
|
|
833
|
-
if (forksPoolWorker) {
|
|
834
|
-
// function is async
|
|
835
|
-
shimmer.wrap(forksPoolWorker.value.prototype, 'start', start => function (...args) {
|
|
836
|
-
vitestPool = 'child_process'
|
|
837
|
-
this.env.DD_VITEST_WORKER = '1'
|
|
838
|
-
|
|
839
|
-
return start.apply(this, args)
|
|
840
|
-
})
|
|
841
|
-
shimmer.wrap(forksPoolWorker.value.prototype, 'on', getWrappedOn)
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
const threadsPoolWorker = getThreadsPoolWorkerExport(cliApiPackage)
|
|
845
|
-
if (threadsPoolWorker) {
|
|
846
|
-
// function is async
|
|
847
|
-
shimmer.wrap(threadsPoolWorker.value.prototype, 'start', start => function (...args) {
|
|
848
|
-
vitestPool = 'worker_threads'
|
|
849
|
-
this.env.DD_VITEST_WORKER = '1'
|
|
850
|
-
return start.apply(this, args)
|
|
851
|
-
})
|
|
852
|
-
shimmer.wrap(threadsPoolWorker.value.prototype, 'on', getWrappedOn)
|
|
853
|
-
}
|
|
854
|
-
return cliApiPackage
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
function wrapVitestTestRunner (VitestTestRunner) {
|
|
858
|
-
// `onBeforeRunTask` is run before any repetition or attempt is run
|
|
859
|
-
// `onBeforeRunTask` is an async function
|
|
860
|
-
shimmer.wrap(VitestTestRunner.prototype, 'onBeforeRunTask', onBeforeRunTask => function (task) {
|
|
861
|
-
const testName = getTestName(task)
|
|
862
|
-
|
|
863
|
-
const {
|
|
864
|
-
knownTests,
|
|
865
|
-
isEarlyFlakeDetectionEnabled,
|
|
866
|
-
isKnownTestsEnabled,
|
|
867
|
-
numRepeats,
|
|
868
|
-
isTestManagementTestsEnabled,
|
|
869
|
-
testManagementAttemptToFixRetries,
|
|
870
|
-
testManagementTests,
|
|
871
|
-
isImpactedTestsEnabled,
|
|
872
|
-
modifiedFiles,
|
|
873
|
-
} = getProvidedContext()
|
|
874
|
-
|
|
875
|
-
if (isTestManagementTestsEnabled) {
|
|
876
|
-
isAttemptToFixCh.publish({
|
|
877
|
-
testManagementTests,
|
|
878
|
-
testSuiteAbsolutePath: task.file.filepath,
|
|
879
|
-
testName,
|
|
880
|
-
onDone: (isAttemptToFix) => {
|
|
881
|
-
if (isAttemptToFix) {
|
|
882
|
-
isRetryReasonAttemptToFix = task.repeats !== testManagementAttemptToFixRetries
|
|
883
|
-
disableFrameworkRetries(task)
|
|
884
|
-
task.repeats = testManagementAttemptToFixRetries
|
|
885
|
-
attemptToFixTasks.add(task)
|
|
886
|
-
attemptToFixTaskToStatuses.set(task, [])
|
|
887
|
-
}
|
|
888
|
-
},
|
|
889
|
-
})
|
|
890
|
-
isDisabledCh.publish({
|
|
891
|
-
testManagementTests,
|
|
892
|
-
testSuiteAbsolutePath: task.file.filepath,
|
|
893
|
-
testName,
|
|
894
|
-
onDone: (isTestDisabled) => {
|
|
895
|
-
if (isTestDisabled) {
|
|
896
|
-
disabledTasks.add(task)
|
|
897
|
-
if (!attemptToFixTasks.has(task)) {
|
|
898
|
-
// we only actually skip if the test is not being attempted to be fixed
|
|
899
|
-
task.mode = 'skip'
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
},
|
|
903
|
-
})
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
if (isImpactedTestsEnabled) {
|
|
907
|
-
isModifiedCh.publish({
|
|
908
|
-
modifiedFiles,
|
|
909
|
-
testSuiteAbsolutePath: task.file.filepath,
|
|
910
|
-
onDone: (isImpacted) => {
|
|
911
|
-
if (isImpacted) {
|
|
912
|
-
if (isEarlyFlakeDetectionEnabled) {
|
|
913
|
-
isRetryReasonEfd = true
|
|
914
|
-
disableFrameworkRetries(task)
|
|
915
|
-
task.repeats = numRepeats
|
|
916
|
-
}
|
|
917
|
-
modifiedTasks.add(task)
|
|
918
|
-
taskToStatuses.set(task, [])
|
|
919
|
-
}
|
|
920
|
-
},
|
|
921
|
-
})
|
|
922
|
-
}
|
|
923
|
-
|
|
924
|
-
if (isKnownTestsEnabled) {
|
|
925
|
-
isNewTestCh.publish({
|
|
926
|
-
knownTests,
|
|
927
|
-
testSuiteAbsolutePath: task.file.filepath,
|
|
928
|
-
testName,
|
|
929
|
-
onDone: (isNew) => {
|
|
930
|
-
if (isNew && !attemptToFixTasks.has(task)) {
|
|
931
|
-
if (isEarlyFlakeDetectionEnabled && !modifiedTasks.has(task)) {
|
|
932
|
-
isRetryReasonEfd = true
|
|
933
|
-
disableFrameworkRetries(task)
|
|
934
|
-
task.repeats = numRepeats
|
|
935
|
-
}
|
|
936
|
-
newTasks.add(task)
|
|
937
|
-
taskToStatuses.set(task, [])
|
|
938
|
-
if (DYNAMIC_NAME_RE.test(testName)) {
|
|
939
|
-
dynamicNameTasks.add(task)
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
},
|
|
943
|
-
})
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
return onBeforeRunTask.apply(this, arguments)
|
|
947
|
-
})
|
|
948
|
-
|
|
949
|
-
// `onAfterRunTask` is run after all repetitions or attempts are run
|
|
950
|
-
// `onAfterRunTask` is an async function
|
|
951
|
-
shimmer.wrap(VitestTestRunner.prototype, 'onAfterRunTask', onAfterRunTask => function (task) {
|
|
952
|
-
const { isEarlyFlakeDetectionEnabled, isTestManagementTestsEnabled } = getProvidedContext()
|
|
953
|
-
|
|
954
|
-
if (isTestManagementTestsEnabled) {
|
|
955
|
-
const isAttemptingToFix = attemptToFixTasks.has(task)
|
|
956
|
-
const isQuarantined = quarantinedTasks.has(task)
|
|
957
|
-
|
|
958
|
-
if (isAttemptingToFix) {
|
|
959
|
-
const statuses = attemptToFixTaskToStatuses.get(task)
|
|
960
|
-
if (task.result.state === 'pass' && statuses?.includes('fail')) {
|
|
961
|
-
switchedStatuses.add(task)
|
|
962
|
-
task.result.state = 'fail'
|
|
963
|
-
}
|
|
964
|
-
}
|
|
965
|
-
|
|
966
|
-
if (!isAttemptingToFix && isQuarantined) {
|
|
967
|
-
if (task.result.state === 'fail') {
|
|
968
|
-
switchedStatuses.add(task)
|
|
969
|
-
}
|
|
970
|
-
task.result.state = 'pass'
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
if (isEarlyFlakeDetectionEnabled && taskToStatuses.has(task) && !attemptToFixTasks.has(task)) {
|
|
975
|
-
const statuses = taskToStatuses.get(task)
|
|
976
|
-
// If the test has passed at least once, we consider it passed
|
|
977
|
-
if (statuses.includes('pass')) {
|
|
978
|
-
if (task.result.state === 'fail') {
|
|
979
|
-
switchedStatuses.add(task)
|
|
980
|
-
}
|
|
981
|
-
task.result.state = 'pass'
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
return onAfterRunTask.apply(this, arguments)
|
|
986
|
-
})
|
|
987
|
-
|
|
988
|
-
// test start (only tests that are not marked as skip or todo)
|
|
989
|
-
// `onBeforeTryTask` is run for every repetition and attempt of the test
|
|
990
|
-
shimmer.wrap(VitestTestRunner.prototype, 'onBeforeTryTask', onBeforeTryTask => async function (task, retryInfo) {
|
|
991
|
-
if (!testPassCh.hasSubscribers && !testErrorCh.hasSubscribers && !testSkipCh.hasSubscribers) {
|
|
992
|
-
return onBeforeTryTask.apply(this, arguments)
|
|
993
|
-
}
|
|
994
|
-
const testName = getTestName(task)
|
|
995
|
-
let isNew = false
|
|
996
|
-
let isQuarantined = false
|
|
997
|
-
|
|
998
|
-
const {
|
|
999
|
-
isKnownTestsEnabled,
|
|
1000
|
-
isEarlyFlakeDetectionEnabled,
|
|
1001
|
-
isDiEnabled,
|
|
1002
|
-
isTestManagementTestsEnabled,
|
|
1003
|
-
testManagementTests,
|
|
1004
|
-
isFlakyTestRetriesEnabled,
|
|
1005
|
-
slowTestRetries,
|
|
1006
|
-
} = getProvidedContext()
|
|
1007
|
-
|
|
1008
|
-
if (isKnownTestsEnabled) {
|
|
1009
|
-
isNew = newTasks.has(task)
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
if (isTestManagementTestsEnabled) {
|
|
1013
|
-
isQuarantinedCh.publish({
|
|
1014
|
-
testManagementTests,
|
|
1015
|
-
testSuiteAbsolutePath: task.file.filepath,
|
|
1016
|
-
testName,
|
|
1017
|
-
onDone: (isTestQuarantined) => {
|
|
1018
|
-
isQuarantined = isTestQuarantined
|
|
1019
|
-
if (isTestQuarantined) {
|
|
1020
|
-
quarantinedTasks.add(task)
|
|
1021
|
-
}
|
|
1022
|
-
},
|
|
1023
|
-
})
|
|
1024
|
-
}
|
|
1025
|
-
|
|
1026
|
-
const { retry: numAttempt, repeats: numRepetition } = retryInfo
|
|
1027
|
-
const isEfdManagedTask = isEarlyFlakeDetectionEnabled && taskToStatuses.has(task) && !attemptToFixTasks.has(task)
|
|
1028
|
-
|
|
1029
|
-
if (isEfdManagedTask && numRepetition > 0 && !efdDeterminedRetries.has(task)) {
|
|
1030
|
-
const previousExecutionStart = efdExecutionStartByTask.get(task)
|
|
1031
|
-
const duration = previousExecutionStart === undefined
|
|
1032
|
-
? task.result?.duration ?? 0
|
|
1033
|
-
: performance.now() - previousExecutionStart
|
|
1034
|
-
const retryCount = getEfdRetryCount(duration, slowTestRetries)
|
|
1035
|
-
efdDeterminedRetries.set(task, retryCount)
|
|
1036
|
-
task.repeats = retryCount
|
|
1037
|
-
if (retryCount === 0) {
|
|
1038
|
-
efdSlowAbortedTasks.add(task)
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
const efdRetryCount = efdDeterminedRetries.get(task)
|
|
1043
|
-
if (isEfdManagedTask && efdRetryCount !== undefined && numRepetition > efdRetryCount) {
|
|
1044
|
-
if (task.result) {
|
|
1045
|
-
efdSkippedRetryResults.set(task, {
|
|
1046
|
-
...task.result,
|
|
1047
|
-
errors: task.result.errors?.slice(),
|
|
1048
|
-
})
|
|
1049
|
-
}
|
|
1050
|
-
if (vitestSetFn) {
|
|
1051
|
-
const noop = function () {}
|
|
1052
|
-
noop.__ddTraceWrapped = true
|
|
1053
|
-
vitestSetFn(task, noop)
|
|
1054
|
-
}
|
|
1055
|
-
return onBeforeTryTask.apply(this, arguments)
|
|
1056
|
-
}
|
|
1057
|
-
if (isEfdManagedTask) {
|
|
1058
|
-
efdExecutionStartByTask.set(task, performance.now())
|
|
1059
|
-
}
|
|
1060
|
-
|
|
1061
|
-
// We finish the previous test here because we know it has failed already
|
|
1062
|
-
if (numAttempt > 0) {
|
|
1063
|
-
const shouldWaitForHitProbe = isDiEnabled && numAttempt > 1
|
|
1064
|
-
if (shouldWaitForHitProbe) {
|
|
1065
|
-
await waitForHitProbe()
|
|
1066
|
-
}
|
|
1067
|
-
|
|
1068
|
-
const promises = {}
|
|
1069
|
-
const shouldSetProbe = isDiEnabled && numAttempt === 1
|
|
1070
|
-
const ctx = taskToCtx.get(task)
|
|
1071
|
-
const testError = getCurrentAttemptTestError(task, task.result?.errors)
|
|
1072
|
-
if (ctx) {
|
|
1073
|
-
testErrorCh.publish({
|
|
1074
|
-
error: testError,
|
|
1075
|
-
shouldSetProbe,
|
|
1076
|
-
promises,
|
|
1077
|
-
...ctx.currentStore,
|
|
1078
|
-
})
|
|
1079
|
-
// We wait for the probe to be set
|
|
1080
|
-
if (promises.setProbePromise) {
|
|
1081
|
-
await promises.setProbePromise
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
const lastExecutionStatus = task.result.state
|
|
1087
|
-
const isAtf = attemptToFixTasks.has(task)
|
|
1088
|
-
const shouldTrackStatuses = isEarlyFlakeDetectionEnabled || isAtf
|
|
1089
|
-
const shouldFlipStatus = isEarlyFlakeDetectionEnabled || isAtf
|
|
1090
|
-
const statuses = isAtf ? attemptToFixTaskToStatuses.get(task) : taskToStatuses.get(task)
|
|
1091
|
-
|
|
1092
|
-
// These clauses handle task.repeats, whether EFD is enabled or not
|
|
1093
|
-
// The only thing that EFD does is to forcefully pass the test if it has passed at least once
|
|
1094
|
-
if (numRepetition > 0 && numRepetition < task.repeats) { // it may or may have not failed
|
|
1095
|
-
// Here we finish the earlier iteration,
|
|
1096
|
-
// as long as it's not the _last_ iteration (which will be finished normally)
|
|
1097
|
-
|
|
1098
|
-
const ctx = taskToCtx.get(task)
|
|
1099
|
-
if (ctx) {
|
|
1100
|
-
if (lastExecutionStatus === 'fail') {
|
|
1101
|
-
const testError = getCurrentAttemptTestError(task, task.result?.errors)
|
|
1102
|
-
testErrorCh.publish({ error: testError, ...ctx.currentStore })
|
|
1103
|
-
} else {
|
|
1104
|
-
testPassCh.publish({ task, ...ctx.currentStore })
|
|
1105
|
-
}
|
|
1106
|
-
if (shouldTrackStatuses && statuses) {
|
|
1107
|
-
statuses.push(lastExecutionStatus)
|
|
1108
|
-
}
|
|
1109
|
-
if (shouldFlipStatus) {
|
|
1110
|
-
// If we don't "reset" the result.state to "pass", once a repetition fails,
|
|
1111
|
-
// vitest will always consider the test as failed, so we can't read the actual status
|
|
1112
|
-
// This means that we change vitest's behavior:
|
|
1113
|
-
// if the last attempt passes, vitest would consider the test as failed
|
|
1114
|
-
// but after this change, it will consider the test as passed
|
|
1115
|
-
task.result.state = 'pass'
|
|
1116
|
-
}
|
|
1117
|
-
}
|
|
1118
|
-
} else if (numRepetition === task.repeats) {
|
|
1119
|
-
if (shouldTrackStatuses && statuses) {
|
|
1120
|
-
statuses.push(lastExecutionStatus)
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
const ctx = taskToCtx.get(task)
|
|
1124
|
-
if (lastExecutionStatus === 'fail') {
|
|
1125
|
-
const testError = getCurrentAttemptTestError(task, task.result?.errors)
|
|
1126
|
-
testErrorCh.publish({ error: testError, ...ctx.currentStore })
|
|
1127
|
-
} else {
|
|
1128
|
-
testPassCh.publish({ task, ...ctx.currentStore })
|
|
1129
|
-
}
|
|
1130
|
-
if (shouldFlipStatus) {
|
|
1131
|
-
task.result.state = 'pass'
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1134
|
-
|
|
1135
|
-
const isRetryReasonAtr = numAttempt > 0 &&
|
|
1136
|
-
isFlakyTestRetriesEnabled &&
|
|
1137
|
-
!isRetryReasonAttemptToFix &&
|
|
1138
|
-
!isRetryReasonEfd
|
|
1139
|
-
|
|
1140
|
-
const ctx = {
|
|
1141
|
-
testName,
|
|
1142
|
-
testSuiteAbsolutePath: task.file.filepath,
|
|
1143
|
-
isRetry: numAttempt > 0 || numRepetition > 0,
|
|
1144
|
-
isRetryReasonEfd,
|
|
1145
|
-
isRetryReasonAttemptToFix: isRetryReasonAttemptToFix && numRepetition > 0,
|
|
1146
|
-
isNew,
|
|
1147
|
-
hasDynamicName: dynamicNameTasks.has(task),
|
|
1148
|
-
mightHitProbe: isDiEnabled && numAttempt > 0,
|
|
1149
|
-
isAttemptToFix: attemptToFixTasks.has(task),
|
|
1150
|
-
isDisabled: disabledTasks.has(task),
|
|
1151
|
-
isQuarantined,
|
|
1152
|
-
isRetryReasonAtr,
|
|
1153
|
-
isModified: modifiedTasks.has(task),
|
|
1154
|
-
}
|
|
1155
|
-
taskToCtx.set(task, ctx)
|
|
1156
|
-
|
|
1157
|
-
if (attemptToFixTasks.has(task)) {
|
|
1158
|
-
logAttemptToFixTestExecution(
|
|
1159
|
-
getTestSuitePath(task.file.filepath, process.cwd()),
|
|
1160
|
-
testName,
|
|
1161
|
-
loggedAttemptToFixTests
|
|
1162
|
-
)
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
testStartCh.runStores(ctx, () => {})
|
|
1166
|
-
|
|
1167
|
-
// Wrap the test function so it runs inside the test span context.
|
|
1168
|
-
// Without this, HTTP requests during test execution become orphaned root spans.
|
|
1169
|
-
if (vitestGetFn && vitestSetFn) {
|
|
1170
|
-
const originalFn = vitestGetFn(task)
|
|
1171
|
-
if (originalFn && !originalFn.__ddTraceWrapped) {
|
|
1172
|
-
const wrappedFn = wrapTestScopedFn(task, originalFn)
|
|
1173
|
-
wrappedFn.__ddTraceWrapped = true
|
|
1174
|
-
vitestSetFn(task, wrappedFn)
|
|
1175
|
-
}
|
|
1176
|
-
}
|
|
1177
|
-
|
|
1178
|
-
// Wrap beforeEach/afterEach hooks so they also run inside the test span context.
|
|
1179
|
-
// In vitest 4+, hooks are in a WeakMap accessed via getHooks(). In older versions, they're on suite.hooks.
|
|
1180
|
-
let currentSuite = task.suite
|
|
1181
|
-
while (currentSuite) {
|
|
1182
|
-
const hooks = vitestGetHooks ? vitestGetHooks(currentSuite) : currentSuite.hooks
|
|
1183
|
-
if (hooks) {
|
|
1184
|
-
for (const hookType of ['beforeEach', 'afterEach']) {
|
|
1185
|
-
const hookArray = hooks[hookType]
|
|
1186
|
-
if (!hookArray) continue
|
|
1187
|
-
for (let i = 0; i < hookArray.length; i++) {
|
|
1188
|
-
const currentFn = hookArray[i]
|
|
1189
|
-
const originalFn = originalHookFns.get(currentFn) || currentFn
|
|
1190
|
-
const wrappedFn = shimmer.wrapFunction(originalFn, fn => function (...args) {
|
|
1191
|
-
const result = testFnCh.runStores(taskToCtx.get(task), () => fn.apply(this, args))
|
|
1192
|
-
|
|
1193
|
-
if (hookType === 'beforeEach') {
|
|
1194
|
-
return wrapBeforeEachCleanupResult(task, result)
|
|
1195
|
-
}
|
|
1196
|
-
|
|
1197
|
-
return result
|
|
1198
|
-
})
|
|
1199
|
-
originalHookFns.set(wrappedFn, originalFn)
|
|
1200
|
-
hookArray[i] = wrappedFn
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
currentSuite = currentSuite.suite
|
|
1205
|
-
}
|
|
1206
|
-
|
|
1207
|
-
return onBeforeTryTask.apply(this, arguments)
|
|
1208
|
-
})
|
|
1209
|
-
|
|
1210
|
-
// test finish (only passed tests)
|
|
1211
|
-
shimmer.wrap(VitestTestRunner.prototype, 'onAfterTryTask', onAfterTryTask =>
|
|
1212
|
-
async function (task, retryInfo) {
|
|
1213
|
-
if (!testPassCh.hasSubscribers && !testErrorCh.hasSubscribers && !testSkipCh.hasSubscribers) {
|
|
1214
|
-
return onAfterTryTask.apply(this, arguments)
|
|
1215
|
-
}
|
|
1216
|
-
const result = await onAfterTryTask.apply(this, arguments)
|
|
1217
|
-
|
|
1218
|
-
const {
|
|
1219
|
-
isEarlyFlakeDetectionEnabled,
|
|
1220
|
-
testManagementAttemptToFixRetries,
|
|
1221
|
-
slowTestRetries,
|
|
1222
|
-
} = getProvidedContext()
|
|
1223
|
-
|
|
1224
|
-
const status = getVitestTestStatus(task, retryInfo.retry)
|
|
1225
|
-
const ctx = taskToCtx.get(task)
|
|
1226
|
-
|
|
1227
|
-
const { isDiEnabled } = getProvidedContext()
|
|
1228
|
-
|
|
1229
|
-
if (efdSkippedRetryResults.has(task)) {
|
|
1230
|
-
task.result = efdSkippedRetryResults.get(task)
|
|
1231
|
-
efdSkippedRetryResults.delete(task)
|
|
1232
|
-
return result
|
|
1233
|
-
}
|
|
1234
|
-
|
|
1235
|
-
if (isDiEnabled && retryInfo.retry > 1) {
|
|
1236
|
-
await waitForHitProbe()
|
|
1237
|
-
}
|
|
1238
|
-
|
|
1239
|
-
if (
|
|
1240
|
-
isEarlyFlakeDetectionEnabled &&
|
|
1241
|
-
(retryInfo.repeats ?? 0) === 0 &&
|
|
1242
|
-
taskToStatuses.has(task) &&
|
|
1243
|
-
!attemptToFixTasks.has(task) &&
|
|
1244
|
-
!efdDeterminedRetries.has(task)
|
|
1245
|
-
) {
|
|
1246
|
-
const executionStart = efdExecutionStartByTask.get(task)
|
|
1247
|
-
const duration = executionStart === undefined ? task.result?.duration ?? 0 : performance.now() - executionStart
|
|
1248
|
-
const retryCount = getEfdRetryCount(duration, slowTestRetries)
|
|
1249
|
-
efdDeterminedRetries.set(task, retryCount)
|
|
1250
|
-
task.repeats = retryCount
|
|
1251
|
-
if (retryCount === 0) {
|
|
1252
|
-
efdSlowAbortedTasks.add(task)
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
let attemptToFixPassed = false
|
|
1257
|
-
let attemptToFixFailed = false
|
|
1258
|
-
if (attemptToFixTasks.has(task)) {
|
|
1259
|
-
const statuses = attemptToFixTaskToStatuses.get(task)
|
|
1260
|
-
if (statuses.length === testManagementAttemptToFixRetries) {
|
|
1261
|
-
if (status === 'pass' && statuses.every(status => status === 'pass')) {
|
|
1262
|
-
attemptToFixPassed = true
|
|
1263
|
-
} else if (status === 'fail' || statuses.includes('fail')) {
|
|
1264
|
-
attemptToFixFailed = true
|
|
1265
|
-
}
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
|
|
1269
|
-
if (ctx) {
|
|
1270
|
-
// We don't finish here because the test might fail in a later hook (afterEach)
|
|
1271
|
-
ctx.status = status
|
|
1272
|
-
ctx.task = task
|
|
1273
|
-
ctx.attemptToFixPassed = attemptToFixPassed
|
|
1274
|
-
ctx.attemptToFixFailed = attemptToFixFailed
|
|
1275
|
-
testFinishTimeCh.runStores(ctx, () => {})
|
|
1276
|
-
}
|
|
1277
|
-
|
|
1278
|
-
return result
|
|
1279
|
-
})
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
function captureRunnerFunctions (pkg) {
|
|
1283
|
-
if (vitestGetFn) return
|
|
1284
|
-
const getFnExport = findExportByName(pkg, 'getFn')
|
|
1285
|
-
const setFnExport = findExportByName(pkg, 'setFn')
|
|
1286
|
-
if (getFnExport && setFnExport) {
|
|
1287
|
-
vitestGetFn = getFnExport.value
|
|
1288
|
-
vitestSetFn = setFnExport.value
|
|
1289
|
-
}
|
|
1290
|
-
const getHooksExport = findExportByName(pkg, 'getHooks')
|
|
1291
|
-
if (getHooksExport) {
|
|
1292
|
-
vitestGetHooks = getHooksExport.value
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
|
|
1296
|
-
addHook({
|
|
1297
|
-
name: 'vitest',
|
|
1298
|
-
versions: ['>=4.0.0'],
|
|
1299
|
-
filePattern: 'dist/chunks/test.*',
|
|
1300
|
-
}, (testPackage) => {
|
|
1301
|
-
const testRunner = getTestRunnerExport(testPackage)
|
|
1302
|
-
if (!testRunner) {
|
|
1303
|
-
return testPackage
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
captureRunnerFunctions(testPackage)
|
|
1307
|
-
wrapVitestTestRunner(testRunner.value)
|
|
1308
|
-
|
|
1309
|
-
return testPackage
|
|
1310
|
-
})
|
|
1311
|
-
|
|
1312
|
-
addHook({
|
|
1313
|
-
name: '@vitest/runner',
|
|
1314
|
-
versions: ['>=1.6.0'],
|
|
1315
|
-
}, (runnerModule) => {
|
|
1316
|
-
if (!vitestGetFn && runnerModule.getFn && runnerModule.setFn) {
|
|
1317
|
-
vitestGetFn = runnerModule.getFn
|
|
1318
|
-
vitestSetFn = runnerModule.setFn
|
|
1319
|
-
}
|
|
1320
|
-
if (!vitestGetHooks && runnerModule.getHooks) {
|
|
1321
|
-
vitestGetHooks = runnerModule.getHooks
|
|
1322
|
-
}
|
|
1323
|
-
return runnerModule
|
|
1324
|
-
})
|
|
1325
|
-
|
|
1326
|
-
addHook({
|
|
1327
|
-
name: 'vitest',
|
|
1328
|
-
versions: ['>=1.6.0 <4.0.0'],
|
|
1329
|
-
file: 'dist/runners.js',
|
|
1330
|
-
}, (vitestPackage) => {
|
|
1331
|
-
const { VitestTestRunner } = vitestPackage
|
|
1332
|
-
|
|
1333
|
-
wrapVitestTestRunner(VitestTestRunner)
|
|
1334
|
-
|
|
1335
|
-
return vitestPackage
|
|
1336
|
-
})
|
|
1337
|
-
|
|
1338
|
-
// There are multiple index* files across different versions of vitest,
|
|
1339
|
-
// so we check for the existence of BaseSequencer to determine if we are in the right file
|
|
1340
|
-
addHook({
|
|
1341
|
-
name: 'vitest',
|
|
1342
|
-
versions: ['>=1.6.0 <2.0.0'],
|
|
1343
|
-
filePattern: 'dist/vendor/index.*',
|
|
1344
|
-
}, (vitestPackage) => {
|
|
1345
|
-
if (isReporterPackage(vitestPackage)) {
|
|
1346
|
-
shimmer.wrap(vitestPackage.B.prototype, 'sort', getSortWrapper)
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
return vitestPackage
|
|
1350
|
-
})
|
|
1351
|
-
|
|
1352
|
-
addHook({
|
|
1353
|
-
name: 'vitest',
|
|
1354
|
-
versions: ['>=2.0.0 <2.0.5'],
|
|
1355
|
-
filePattern: 'dist/vendor/index.*',
|
|
1356
|
-
}, (vitestPackage) => {
|
|
1357
|
-
if (isReporterPackageNew(vitestPackage)) {
|
|
1358
|
-
shimmer.wrap(vitestPackage.e.prototype, 'sort', getSortWrapper)
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
return vitestPackage
|
|
1362
|
-
})
|
|
1363
|
-
|
|
1364
|
-
addHook({
|
|
1365
|
-
name: 'vitest',
|
|
1366
|
-
versions: ['>=2.0.5 <2.1.0'],
|
|
1367
|
-
filePattern: 'dist/chunks/index.*',
|
|
1368
|
-
}, (vitestPackage) => {
|
|
1369
|
-
if (isReporterPackageNewest(vitestPackage)) {
|
|
1370
|
-
shimmer.wrap(vitestPackage.h.prototype, 'sort', getSortWrapper)
|
|
1371
|
-
}
|
|
1372
|
-
|
|
1373
|
-
return vitestPackage
|
|
1374
|
-
})
|
|
1375
|
-
|
|
1376
|
-
addHook({
|
|
1377
|
-
name: 'vitest',
|
|
1378
|
-
versions: ['>=2.1.0 <3.0.0'],
|
|
1379
|
-
filePattern: 'dist/chunks/RandomSequencer.*',
|
|
1380
|
-
}, (randomSequencerPackage) => {
|
|
1381
|
-
shimmer.wrap(randomSequencerPackage.B.prototype, 'sort', getSortWrapper)
|
|
1382
|
-
return randomSequencerPackage
|
|
1383
|
-
})
|
|
1384
|
-
|
|
1385
|
-
addHook({
|
|
1386
|
-
name: 'vitest',
|
|
1387
|
-
versions: ['>=3.0.9'],
|
|
1388
|
-
filePattern: 'dist/chunks/coverage.*',
|
|
1389
|
-
}, (coveragePackage) => {
|
|
1390
|
-
const baseSequencer = getBaseSequencerExport(coveragePackage)
|
|
1391
|
-
if (baseSequencer) {
|
|
1392
|
-
shimmer.wrap(baseSequencer.value.prototype, 'sort', getSortWrapper)
|
|
1393
|
-
}
|
|
1394
|
-
return coveragePackage
|
|
1395
|
-
})
|
|
1396
|
-
|
|
1397
|
-
addHook({
|
|
1398
|
-
name: 'vitest',
|
|
1399
|
-
versions: ['>=3.0.0 <3.0.9'],
|
|
1400
|
-
filePattern: 'dist/chunks/resolveConfig.*',
|
|
1401
|
-
}, (resolveConfigPackage) => {
|
|
1402
|
-
shimmer.wrap(resolveConfigPackage.B.prototype, 'sort', getSortWrapper)
|
|
1403
|
-
return resolveConfigPackage
|
|
1404
|
-
})
|
|
1405
|
-
|
|
1406
|
-
// Can't specify file because compiled vitest includes hashes in their files
|
|
1407
|
-
// Following 3 wrappers are for test session start
|
|
1408
|
-
addHook({
|
|
1409
|
-
name: 'vitest',
|
|
1410
|
-
versions: ['>=1.6.0 <2.0.5'],
|
|
1411
|
-
filePattern: 'dist/vendor/cac.*',
|
|
1412
|
-
}, getCreateCliWrapper)
|
|
1413
|
-
|
|
1414
|
-
addHook({
|
|
1415
|
-
name: 'vitest',
|
|
1416
|
-
versions: ['>=2.0.5'],
|
|
1417
|
-
filePattern: 'dist/chunks/cac.*',
|
|
1418
|
-
}, getCreateCliWrapper)
|
|
1419
|
-
|
|
1420
|
-
addHook({
|
|
1421
|
-
name: 'vitest',
|
|
1422
|
-
versions: ['>=1.6.0 <2.0.5'],
|
|
1423
|
-
filePattern: 'dist/vendor/cli-api.*',
|
|
1424
|
-
}, getStartVitestWrapper)
|
|
1425
|
-
|
|
1426
|
-
addHook({
|
|
1427
|
-
name: 'vitest',
|
|
1428
|
-
versions: ['>=2.0.5'],
|
|
1429
|
-
filePattern: 'dist/chunks/cli-api.*',
|
|
1430
|
-
}, getStartVitestWrapper)
|
|
1431
|
-
|
|
1432
|
-
// test suite start and finish
|
|
1433
|
-
// only relevant for workers
|
|
1434
|
-
addHook({
|
|
1435
|
-
name: '@vitest/runner',
|
|
1436
|
-
versions: ['>=1.6.0'],
|
|
1437
|
-
}, (vitestPackage, frameworkVersion) => {
|
|
1438
|
-
shimmer.wrap(vitestPackage, 'startTests', startTests => async function (testPaths) {
|
|
1439
|
-
let testSuiteError = null
|
|
1440
|
-
if (!testSuiteFinishCh.hasSubscribers) {
|
|
1441
|
-
return startTests.apply(this, arguments)
|
|
1442
|
-
}
|
|
1443
|
-
// From >=3.0.1, the first arguments changes from a string to an object containing the filepath
|
|
1444
|
-
const testSuiteAbsolutePath = testPaths[0]?.filepath || testPaths[0]
|
|
1445
|
-
const providedContext = getProvidedContext()
|
|
1446
|
-
|
|
1447
|
-
const testSuiteCtx = {
|
|
1448
|
-
testSuiteAbsolutePath,
|
|
1449
|
-
frameworkVersion,
|
|
1450
|
-
testSessionId: providedContext.testSessionId,
|
|
1451
|
-
testModuleId: providedContext.testModuleId,
|
|
1452
|
-
testCommand: providedContext.testCommand,
|
|
1453
|
-
repositoryRoot: providedContext.repositoryRoot,
|
|
1454
|
-
codeOwnersEntries: providedContext.codeOwnersEntries,
|
|
1455
|
-
}
|
|
1456
|
-
testSuiteStartCh.runStores(testSuiteCtx, () => {})
|
|
1457
|
-
const startTestsResponse = await startTests.apply(this, arguments)
|
|
1458
|
-
|
|
1459
|
-
let onFinish = null
|
|
1460
|
-
const onFinishPromise = new Promise(resolve => {
|
|
1461
|
-
onFinish = resolve
|
|
1462
|
-
})
|
|
1463
|
-
|
|
1464
|
-
const testTasks = getTypeTasks(startTestsResponse[0].tasks)
|
|
1465
|
-
|
|
1466
|
-
// Only one test task per test, even if there are retries
|
|
1467
|
-
for (const task of testTasks) {
|
|
1468
|
-
const testCtx = taskToCtx.get(task)
|
|
1469
|
-
const { result } = task
|
|
1470
|
-
// We have to trick vitest into thinking that the test has passed
|
|
1471
|
-
// but we want to report it as failed if it did fail
|
|
1472
|
-
const isSwitchedStatus = switchedStatuses.has(task)
|
|
1473
|
-
|
|
1474
|
-
if (result) {
|
|
1475
|
-
const { state, duration, errors } = result
|
|
1476
|
-
const testError = getCurrentAttemptTestError(task, errors)
|
|
1477
|
-
if (attemptToFixTasks.has(task)) {
|
|
1478
|
-
const status = getFinalAttemptToFixStatus(task, state, isSwitchedStatus, testCtx)
|
|
1479
|
-
recordFinalAttemptToFixExecution(task, status, providedContext)
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
if (state === 'skip') { // programmatic skip
|
|
1483
|
-
testSkipCh.publish({
|
|
1484
|
-
testName: getTestName(task),
|
|
1485
|
-
testSuiteAbsolutePath: task.file.filepath,
|
|
1486
|
-
isNew: newTasks.has(task),
|
|
1487
|
-
isDisabled: disabledTasks.has(task),
|
|
1488
|
-
})
|
|
1489
|
-
} else if (state === 'pass' && !isSwitchedStatus) {
|
|
1490
|
-
if (testCtx) {
|
|
1491
|
-
const isSkippedByTestManagement =
|
|
1492
|
-
!attemptToFixTasks.has(task) && (disabledTasks.has(task) || quarantinedTasks.has(task))
|
|
1493
|
-
testPassCh.publish({
|
|
1494
|
-
task,
|
|
1495
|
-
finalStatus: isSkippedByTestManagement ? 'skip' : 'pass',
|
|
1496
|
-
earlyFlakeAbortReason: efdSlowAbortedTasks.has(task) ? 'slow' : undefined,
|
|
1497
|
-
...testCtx.currentStore,
|
|
1498
|
-
})
|
|
1499
|
-
}
|
|
1500
|
-
} else if (state === 'fail' || isSwitchedStatus) {
|
|
1501
|
-
let hasFailedAllRetries = false
|
|
1502
|
-
let attemptToFixFailed = false
|
|
1503
|
-
if (attemptToFixTasks.has(task)) {
|
|
1504
|
-
const statuses = attemptToFixTaskToStatuses.get(task)
|
|
1505
|
-
if (statuses.includes('fail')) {
|
|
1506
|
-
attemptToFixFailed = true
|
|
1507
|
-
}
|
|
1508
|
-
if (statuses.every(status => status === 'fail')) {
|
|
1509
|
-
hasFailedAllRetries = true
|
|
1510
|
-
}
|
|
1511
|
-
}
|
|
1512
|
-
|
|
1513
|
-
// Check if all EFD retries failed
|
|
1514
|
-
const isEfdRetry =
|
|
1515
|
-
providedContext.isEarlyFlakeDetectionEnabled && (newTasks.has(task) || modifiedTasks.has(task))
|
|
1516
|
-
if (isEfdRetry) {
|
|
1517
|
-
const statuses = taskToStatuses.get(task)
|
|
1518
|
-
const efdRetryCount = efdDeterminedRetries.get(task) ?? providedContext.numRepeats
|
|
1519
|
-
// statuses only includes repetitions (not the initial run), so we check against retry count (not +1)
|
|
1520
|
-
if (efdRetryCount > 0 && statuses && statuses.length === efdRetryCount &&
|
|
1521
|
-
statuses.every(status => status === 'fail')) {
|
|
1522
|
-
hasFailedAllRetries = true
|
|
1523
|
-
}
|
|
1524
|
-
}
|
|
1525
|
-
|
|
1526
|
-
// ATR: set hasFailedAllRetries when all auto test retries were exhausted and every attempt failed
|
|
1527
|
-
const isAtrRetry = providedContext.isFlakyTestRetriesEnabled && !attemptToFixTasks.has(task) &&
|
|
1528
|
-
!newTasks.has(task) && !modifiedTasks.has(task)
|
|
1529
|
-
if (isAtrRetry) {
|
|
1530
|
-
const maxRetries = providedContext.flakyTestRetriesCount ?? 0
|
|
1531
|
-
if (maxRetries > 0 && task.result?.retryCount === maxRetries) {
|
|
1532
|
-
hasFailedAllRetries = true
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
|
|
1536
|
-
if (testCtx) {
|
|
1537
|
-
const isRetry = task.result?.retryCount > 0
|
|
1538
|
-
// `duration` is the duration of all the retries, so it can't be used if there are retries
|
|
1539
|
-
|
|
1540
|
-
let finalStatus
|
|
1541
|
-
if (isSwitchedStatus) {
|
|
1542
|
-
if (!attemptToFixTasks.has(task) && (disabledTasks.has(task) || quarantinedTasks.has(task))) {
|
|
1543
|
-
finalStatus = 'skip'
|
|
1544
|
-
} else if (isAtrRetry || isEfdRetry) {
|
|
1545
|
-
finalStatus = hasFailedAllRetries ? 'fail' : 'pass'
|
|
1546
|
-
} else if (attemptToFixTasks.has(task)) {
|
|
1547
|
-
finalStatus = attemptToFixFailed ? 'fail' : 'pass'
|
|
1548
|
-
} else {
|
|
1549
|
-
finalStatus = undefined
|
|
1550
|
-
}
|
|
1551
|
-
} else {
|
|
1552
|
-
finalStatus = 'fail'
|
|
1553
|
-
}
|
|
1554
|
-
|
|
1555
|
-
testErrorCh.publish({
|
|
1556
|
-
duration: isRetry ? undefined : duration,
|
|
1557
|
-
error: testError,
|
|
1558
|
-
hasFailedAllRetries,
|
|
1559
|
-
attemptToFixFailed,
|
|
1560
|
-
finalStatus,
|
|
1561
|
-
earlyFlakeAbortReason: efdSlowAbortedTasks.has(task) ? 'slow' : undefined,
|
|
1562
|
-
...testCtx.currentStore,
|
|
1563
|
-
})
|
|
1564
|
-
}
|
|
1565
|
-
if (errors?.length) {
|
|
1566
|
-
testSuiteError = testError // we store the error to bubble it up to the suite
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
|
-
} else { // test.skip or test.todo
|
|
1570
|
-
testSkipCh.publish({
|
|
1571
|
-
testName: getTestName(task),
|
|
1572
|
-
testSuiteAbsolutePath: task.file.filepath,
|
|
1573
|
-
isNew: newTasks.has(task),
|
|
1574
|
-
isDisabled: disabledTasks.has(task),
|
|
1575
|
-
})
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
const testSuiteResult = startTestsResponse[0].result
|
|
1580
|
-
|
|
1581
|
-
if (testSuiteResult.errors?.length) { // Errors from root level hooks
|
|
1582
|
-
testSuiteError = testSuiteResult.errors[0]
|
|
1583
|
-
} else if (testSuiteResult.state === 'fail') { // Errors from `describe` level hooks
|
|
1584
|
-
const suiteTasks = getTypeTasks(startTestsResponse[0].tasks, 'suite')
|
|
1585
|
-
const failedSuites = suiteTasks.filter(task => task.result?.state === 'fail')
|
|
1586
|
-
if (failedSuites.length && failedSuites[0].result?.errors?.length) {
|
|
1587
|
-
testSuiteError = failedSuites[0].result.errors[0]
|
|
1588
|
-
}
|
|
1589
|
-
}
|
|
1590
|
-
|
|
1591
|
-
if (testSuiteError) {
|
|
1592
|
-
testSuiteCtx.error = testSuiteError
|
|
1593
|
-
testSuiteErrorCh.runStores(testSuiteCtx, () => {})
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
testSuiteFinishCh.publish({ status: testSuiteResult.state, onFinish, ...testSuiteCtx.currentStore })
|
|
1597
|
-
|
|
1598
|
-
await onFinishPromise
|
|
1599
|
-
|
|
1600
|
-
return startTestsResponse
|
|
1601
|
-
})
|
|
1602
|
-
|
|
1603
|
-
return vitestPackage
|
|
1604
|
-
})
|