dd-trace 6.11.0 → 6.13.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 +5 -0
- package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/index.d.ts +143 -25
- package/package.json +26 -25
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/cucumber.js +4 -4
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +28 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +1 -0
- package/packages/datadog-instrumentations/src/jest.js +140 -18
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +7 -0
- package/packages/datadog-instrumentations/src/mocha/utils.js +5 -2
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright-reporter.js +149 -1
- package/packages/datadog-instrumentations/src/playwright.js +67 -53
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +13 -12
- package/packages/datadog-instrumentations/src/vitest-worker.js +11 -0
- package/packages/datadog-instrumentations/src/webdriverio.js +26 -0
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +13 -1
- package/packages/datadog-plugin-undici/src/index.js +2 -15
- package/packages/dd-trace/src/aiguard/evaluation.js +94 -20
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +21 -9
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/reporter.js +2 -0
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/appsec/telemetry/index.js +13 -0
- package/packages/dd-trace/src/appsec/telemetry/rasp.js +16 -0
- package/packages/dd-trace/src/appsec/telemetry/waf.js +24 -0
- package/packages/dd-trace/src/carrier.js +107 -48
- package/packages/dd-trace/src/ci-visibility/early-flake-detection/get-known-tests.js +5 -5
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +2 -0
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +5 -3
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +51 -26
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +4 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +5 -11
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +6 -4
- package/packages/dd-trace/src/ci-visibility/final-flush.js +20 -0
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +5 -3
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +196 -29
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +4 -2
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +4 -2
- package/packages/dd-trace/src/ci-visibility/test-management/get-test-management-tests.js +5 -5
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -0
- package/packages/dd-trace/src/config/index.js +2 -1
- package/packages/dd-trace/src/config/supported-configurations.json +27 -0
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/index.js +3 -10
- package/packages/dd-trace/src/debugger/devtools_client/send.js +4 -3
- package/packages/dd-trace/src/debugger/index.js +4 -2
- package/packages/dd-trace/src/debugger/inspect-segment.js +154 -0
- package/packages/dd-trace/src/dogstatsd.js +45 -18
- package/packages/dd-trace/src/encode/agentless-json.js +5 -4
- package/packages/dd-trace/src/evp_proxy/constants.js +8 -0
- package/packages/dd-trace/src/evp_proxy/direct.js +64 -0
- package/packages/dd-trace/src/evp_proxy/discovery.js +133 -0
- package/packages/dd-trace/src/evp_proxy/path.js +22 -0
- package/packages/dd-trace/src/exporters/agent/index.js +22 -2
- package/packages/dd-trace/src/exporters/agent/writer.js +4 -4
- package/packages/dd-trace/src/exporters/common/request.js +5 -2
- package/packages/dd-trace/src/exporters/common/writer.js +24 -2
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +102 -0
- package/packages/dd-trace/src/llmobs/experiments/client.js +10 -2
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +301 -14
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +440 -112
- package/packages/dd-trace/src/llmobs/experiments/index.js +68 -44
- package/packages/dd-trace/src/llmobs/experiments/noop.js +103 -12
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +81 -4
- package/packages/dd-trace/src/llmobs/index.js +80 -17
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/writers/base.js +60 -24
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/constants/constants.js +0 -18
- package/packages/dd-trace/src/openfeature/index.js +7 -4
- package/packages/dd-trace/src/openfeature/writers/base.js +153 -11
- package/packages/dd-trace/src/openfeature/writers/exposures.js +70 -15
- package/packages/dd-trace/src/openfeature/writers/util.js +97 -15
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +4 -1
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +3 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +4 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +429 -352
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +74 -21
- package/packages/dd-trace/src/plugins/util/web.js +2 -65
- package/packages/dd-trace/src/profiling/oom.js +4 -3
- package/packages/dd-trace/src/profiling/profiler.js +7 -0
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/proxy.js +36 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +17 -6
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +55 -0
- package/packages/dd-trace/src/serverless/vercel.js +122 -0
- package/packages/dd-trace/src/serverless.js +37 -20
- package/packages/dd-trace/src/span_stats.js +40 -3
- package/packages/dd-trace/src/telemetry/metrics.js +62 -8
- package/packages/dd-trace/src/telemetry/send-data.js +1 -0
- package/packages/dd-trace/src/tracer.js +22 -0
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/https-proxy-agent/LICENSE +22 -0
- package/vendor/dist/https-proxy-agent/index.js +4 -0
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/vendor/dist/proxy-from-env/LICENSE +20 -0
- package/vendor/dist/proxy-from-env/index.js +1 -0
|
@@ -14,6 +14,7 @@ const {
|
|
|
14
14
|
takePoolWaitTime,
|
|
15
15
|
wrapPoolQueryMethod,
|
|
16
16
|
} = require('./helpers/pool-acquire')
|
|
17
|
+
const { wrapCallbackBundle, wrapPromiseBundle } = require('./mariadb-bundle')
|
|
17
18
|
|
|
18
19
|
const commandAddCh = channel('apm:mariadb:command:add')
|
|
19
20
|
const connectionStartCh = channel('apm:mariadb:connection:start')
|
|
@@ -443,3 +444,9 @@ addHook({ name, file: 'lib/connection.js', versions: ['>=2.0.4 <=2.5.1'] }, (Con
|
|
|
443
444
|
addHook({ name, file: 'lib/pool-base.js', versions: ['>=2.0.4 <3'] }, (PoolBase) => {
|
|
444
445
|
return shimmer.wrapFunction(PoolBase, wrapPoolBase)
|
|
445
446
|
})
|
|
447
|
+
|
|
448
|
+
// MariaDB 3.5.3 added single-file CommonJS bundles that do not load the original source modules at runtime.
|
|
449
|
+
// Matching their generated, minified internals would couple instrumentation to unstable bundle output, so wrap
|
|
450
|
+
// the runtime objects returned by the public factories instead.
|
|
451
|
+
addHook({ name, versions: ['>=3.5.3'] }, wrapPromiseBundle)
|
|
452
|
+
addHook({ name, file: 'dist/callback.cjs', versions: ['>=3.5.3'] }, wrapCallbackBundle)
|
|
@@ -299,6 +299,9 @@ function retryTest (test, numRetries, tags, retryPolicy) {
|
|
|
299
299
|
clonedTest[tag] = true
|
|
300
300
|
}
|
|
301
301
|
}
|
|
302
|
+
if (clonedTest._ddIsQuarantined && !clonedTest._ddIsAttemptToFix) {
|
|
303
|
+
testsQuarantined.add(clonedTest)
|
|
304
|
+
}
|
|
302
305
|
}
|
|
303
306
|
}
|
|
304
307
|
|
|
@@ -1146,7 +1149,7 @@ function getRunTestsWrapper (runTests, config) {
|
|
|
1146
1149
|
retryTest(
|
|
1147
1150
|
test,
|
|
1148
1151
|
config.earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
|
|
1149
|
-
['_ddIsModified', '_ddIsEfdRetry'],
|
|
1152
|
+
['_ddIsModified', '_ddIsEfdRetry', test._ddIsQuarantined && '_ddIsQuarantined'],
|
|
1150
1153
|
config.earlyFlakeDetectionRetryPolicy
|
|
1151
1154
|
)
|
|
1152
1155
|
}
|
|
@@ -1165,7 +1168,7 @@ function getRunTestsWrapper (runTests, config) {
|
|
|
1165
1168
|
retryTest(
|
|
1166
1169
|
test,
|
|
1167
1170
|
config.earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
|
|
1168
|
-
['_ddIsNew', '_ddIsEfdRetry'],
|
|
1171
|
+
['_ddIsNew', '_ddIsEfdRetry', test._ddIsQuarantined && '_ddIsQuarantined'],
|
|
1169
1172
|
config.earlyFlakeDetectionRetryPolicy
|
|
1170
1173
|
)
|
|
1171
1174
|
}
|
|
@@ -6,6 +6,8 @@ const {
|
|
|
6
6
|
addHook,
|
|
7
7
|
} = require('./helpers/instrument')
|
|
8
8
|
|
|
9
|
+
const logSubmissionCh = channel('ci:log-submission:log')
|
|
10
|
+
|
|
9
11
|
/**
|
|
10
12
|
* @param {string} symbol
|
|
11
13
|
* @param {(original: Function) => Function} wrapper
|
|
@@ -36,13 +38,22 @@ function wrapAsJson (asJson) {
|
|
|
36
38
|
obj = arguments[0] = obj || {}
|
|
37
39
|
|
|
38
40
|
// Caller-provided `dd` wins -- skip the splice so a bespoke `dd` survives.
|
|
41
|
+
let line
|
|
39
42
|
if (!jsonCh.hasSubscribers || Object.hasOwn(obj, 'dd')) {
|
|
40
|
-
|
|
43
|
+
line = asJson.apply(this, arguments)
|
|
44
|
+
} else {
|
|
45
|
+
const payload = { line: asJson.apply(this, arguments) }
|
|
46
|
+
jsonCh.publish(payload)
|
|
47
|
+
line = payload.line
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Submit the serialized line for agentless log collection only when trace
|
|
51
|
+
// correlation is active, matching the Bunyan contract.
|
|
52
|
+
if (jsonCh.hasSubscribers && logSubmissionCh.hasSubscribers) {
|
|
53
|
+
logSubmissionCh.publish({ source: 'pino', message: line })
|
|
41
54
|
}
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
jsonCh.publish(payload)
|
|
45
|
-
return payload.line
|
|
56
|
+
return line
|
|
46
57
|
}
|
|
47
58
|
}
|
|
48
59
|
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
const { channel } = require('dc-polyfill')
|
|
4
4
|
|
|
5
5
|
const reporterErrorCh = channel('ci:playwright:reporter:error')
|
|
6
|
+
const reporterRunSummaryCh = channel('ci:playwright:reporter:run-summary')
|
|
7
|
+
const reporterSuiteHookErrorCh = channel('ci:playwright:reporter:suite-hook-error')
|
|
8
|
+
const suitesWithHookErrors = new Set()
|
|
6
9
|
const PLAYWRIGHT_REPORTER_ERROR_MESSAGE = 'Error in reporter'
|
|
7
10
|
const PLAYWRIGHT_REPORTER_ERROR_CALLER_RE =
|
|
8
11
|
/^\s*at wrapAsync .*?[\\/]playwright[\\/]lib[\\/]runner[\\/]index\.js:\d+:\d+\)?$/
|
|
@@ -27,7 +30,53 @@ function isPlaywrightReporterError (message) {
|
|
|
27
30
|
}
|
|
28
31
|
}
|
|
29
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Returns whether a finalized Playwright result contains a failed suite hook.
|
|
35
|
+
*
|
|
36
|
+
* @param {Array<object>} steps
|
|
37
|
+
* @returns {boolean}
|
|
38
|
+
*/
|
|
39
|
+
function hasFailedSuiteHook (steps) {
|
|
40
|
+
if (!steps) return false
|
|
41
|
+
|
|
42
|
+
for (const step of steps) {
|
|
43
|
+
if (step.error && (step.title === 'beforeAll hook' || step.title === 'afterAll hook')) return true
|
|
44
|
+
if (hasFailedSuiteHook(step.steps)) return true
|
|
45
|
+
}
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
|
|
30
49
|
class DatadogPlaywrightReporter {
|
|
50
|
+
/**
|
|
51
|
+
* Creates a reporter that observes the finalized Playwright result independently of user reporters.
|
|
52
|
+
*
|
|
53
|
+
* @param {object} [options]
|
|
54
|
+
* @param {boolean} [options.captureReporterErrors]
|
|
55
|
+
*/
|
|
56
|
+
constructor ({ captureReporterErrors = true } = {}) {
|
|
57
|
+
this.captureReporterErrors = captureReporterErrors
|
|
58
|
+
this.fatalErrorCount = 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Identifies this reporter as implementing Playwright's reporter v2 contract.
|
|
63
|
+
*
|
|
64
|
+
* @returns {'v2'}
|
|
65
|
+
*/
|
|
66
|
+
version () {
|
|
67
|
+
return 'v2'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Implements the reporter v2 lifecycle hook required by older Playwright versions.
|
|
72
|
+
*
|
|
73
|
+
* @param {object} config
|
|
74
|
+
* @returns {void}
|
|
75
|
+
*/
|
|
76
|
+
onConfigure (config) {
|
|
77
|
+
this.failOnFlakyTests = config.failOnFlakyTests
|
|
78
|
+
}
|
|
79
|
+
|
|
31
80
|
/**
|
|
32
81
|
* Restores console error after Playwright completes the reporter lifecycle.
|
|
33
82
|
*
|
|
@@ -45,12 +94,96 @@ class DatadogPlaywrightReporter {
|
|
|
45
94
|
DatadogPlaywrightReporter.originalConsoleError = undefined
|
|
46
95
|
}
|
|
47
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Records the suite so finalized test outcomes can be counted in onEnd.
|
|
99
|
+
*
|
|
100
|
+
* @param {object} configOrSuite
|
|
101
|
+
* @param {object} [suite]
|
|
102
|
+
* @returns {void}
|
|
103
|
+
*/
|
|
104
|
+
onBegin (configOrSuite, suite) {
|
|
105
|
+
this.suite = suite || configOrSuite
|
|
106
|
+
if (suite) this.failOnFlakyTests = configOrSuite.failOnFlakyTests
|
|
107
|
+
suitesWithHookErrors.clear()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Implements the reporter v2 lifecycle hook required by older Playwright versions.
|
|
112
|
+
*
|
|
113
|
+
* @returns {void}
|
|
114
|
+
*/
|
|
115
|
+
onTestBegin () {}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Implements the reporter v2 lifecycle hook required by older Playwright versions.
|
|
119
|
+
*
|
|
120
|
+
* @returns {void}
|
|
121
|
+
*/
|
|
122
|
+
onStdOut () {}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Implements the reporter v2 lifecycle hook required by older Playwright versions.
|
|
126
|
+
*
|
|
127
|
+
* @returns {void}
|
|
128
|
+
*/
|
|
129
|
+
onStdErr () {}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Implements the reporter v2 lifecycle hook required by older Playwright versions.
|
|
133
|
+
*
|
|
134
|
+
* @returns {void}
|
|
135
|
+
*/
|
|
136
|
+
onTestEnd () {}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Implements the reporter v2 lifecycle hook required by older Playwright versions.
|
|
140
|
+
*
|
|
141
|
+
* @returns {void}
|
|
142
|
+
*/
|
|
143
|
+
onStepBegin () {}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Implements the reporter v2 lifecycle hook required by older Playwright versions.
|
|
147
|
+
*
|
|
148
|
+
* @returns {void}
|
|
149
|
+
*/
|
|
150
|
+
onStepEnd () {}
|
|
151
|
+
|
|
48
152
|
/**
|
|
49
153
|
* Marks the beginning of reporter finalization so later reporter errors can be identified.
|
|
50
154
|
*
|
|
51
155
|
* @returns {void}
|
|
52
156
|
*/
|
|
53
157
|
onEnd () {
|
|
158
|
+
let failureCount = this.fatalErrorCount
|
|
159
|
+
let quarantinedFailureCount = 0
|
|
160
|
+
let hasIncompleteTests = suitesWithHookErrors.size > 0
|
|
161
|
+
const tests = this.suite?.allTests?.()
|
|
162
|
+
if (tests) {
|
|
163
|
+
for (const test of tests) {
|
|
164
|
+
const outcome = test.outcome()
|
|
165
|
+
const finalResult = test.results.at(-1)
|
|
166
|
+
hasIncompleteTests ||= hasFailedSuiteHook(finalResult?.steps)
|
|
167
|
+
if (outcome === 'unexpected' || (outcome === 'flaky' && this.failOnFlakyTests)) {
|
|
168
|
+
failureCount += 1
|
|
169
|
+
const finalResultStatus = finalResult?.status
|
|
170
|
+
const hasFailed = finalResultStatus === 'failed' || finalResultStatus === 'timedOut'
|
|
171
|
+
if (outcome === 'unexpected' && hasFailed && test._ddIsQuarantined && !test._ddIsAttemptToFix) {
|
|
172
|
+
quarantinedFailureCount += 1
|
|
173
|
+
}
|
|
174
|
+
} else if (outcome === 'skipped' && !test._ddShouldSkipEfdRetry &&
|
|
175
|
+
(!test._ddIsDisabled || test._ddIsAttemptToFix)) {
|
|
176
|
+
const { results } = test
|
|
177
|
+
hasIncompleteTests ||= results.some(result => result.status === 'interrupted') ||
|
|
178
|
+
!results.length || test.expectedStatus !== 'skipped'
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
reporterRunSummaryCh.publish({ failureCount, quarantinedFailureCount, hasIncompleteTests })
|
|
183
|
+
suitesWithHookErrors.clear()
|
|
184
|
+
|
|
185
|
+
if (!this.captureReporterErrors) return
|
|
186
|
+
|
|
54
187
|
this.isFinalizing = true
|
|
55
188
|
// Playwright 1.60 and 1.61 only expose reporter errors through this exact console call.
|
|
56
189
|
// eslint-disable-next-line no-console
|
|
@@ -76,6 +209,13 @@ class DatadogPlaywrightReporter {
|
|
|
76
209
|
DatadogPlaywrightReporter.consoleError = consoleError
|
|
77
210
|
}
|
|
78
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Implements the reporter v2 lifecycle hook required by older Playwright versions.
|
|
214
|
+
*
|
|
215
|
+
* @returns {void}
|
|
216
|
+
*/
|
|
217
|
+
onExit () {}
|
|
218
|
+
|
|
79
219
|
/**
|
|
80
220
|
* Reports errors emitted by Playwright while later reporters are finalizing.
|
|
81
221
|
*
|
|
@@ -83,7 +223,11 @@ class DatadogPlaywrightReporter {
|
|
|
83
223
|
* @returns {void}
|
|
84
224
|
*/
|
|
85
225
|
onError (error) {
|
|
86
|
-
if (this.isFinalizing)
|
|
226
|
+
if (this.isFinalizing) {
|
|
227
|
+
reporterErrorCh.publish(error)
|
|
228
|
+
} else {
|
|
229
|
+
this.fatalErrorCount += 1
|
|
230
|
+
}
|
|
87
231
|
}
|
|
88
232
|
|
|
89
233
|
/**
|
|
@@ -96,4 +240,8 @@ class DatadogPlaywrightReporter {
|
|
|
96
240
|
}
|
|
97
241
|
}
|
|
98
242
|
|
|
243
|
+
reporterSuiteHookErrorCh.subscribe((testSuiteAbsolutePath) => {
|
|
244
|
+
suitesWithHookErrors.add(testSuiteAbsolutePath)
|
|
245
|
+
})
|
|
246
|
+
|
|
99
247
|
module.exports = DatadogPlaywrightReporter
|
|
@@ -46,6 +46,8 @@ const testSessionStartCh = channel('ci:playwright:session:start')
|
|
|
46
46
|
const testSessionConfigurationCh = channel('ci:playwright:session:configuration')
|
|
47
47
|
const testSessionFinishCh = channel('ci:playwright:session:finish')
|
|
48
48
|
const reporterErrorCh = channel('ci:playwright:reporter:error')
|
|
49
|
+
const reporterRunSummaryCh = channel('ci:playwright:reporter:run-summary')
|
|
50
|
+
const reporterSuiteHookErrorCh = channel('ci:playwright:reporter:suite-hook-error')
|
|
49
51
|
|
|
50
52
|
const libraryConfigurationCh = channel('ci:playwright:library-configuration')
|
|
51
53
|
const knownTestsCh = channel('ci:playwright:known-tests')
|
|
@@ -57,6 +59,7 @@ const testSuiteStartCh = channel('ci:playwright:test-suite:start')
|
|
|
57
59
|
const testSuiteFinishCh = channel('ci:playwright:test-suite:finish')
|
|
58
60
|
|
|
59
61
|
const workerReportCh = channel('ci:playwright:worker:report')
|
|
62
|
+
const logSubmissionFlushCh = channel('ci:log-submission:flush')
|
|
60
63
|
const workerReportTelemetryCh = channel('ci:playwright:worker-report:telemetry')
|
|
61
64
|
const testPageGotoCh = channel('ci:playwright:test:page-goto')
|
|
62
65
|
|
|
@@ -111,7 +114,7 @@ let testManagementAttemptToFixRetries = 0
|
|
|
111
114
|
let testManagementTests = {}
|
|
112
115
|
let isImpactedTestsEnabled = false
|
|
113
116
|
let modifiedFiles = {}
|
|
114
|
-
let
|
|
117
|
+
let playwrightRunSummary
|
|
115
118
|
let recordedTestOptimizationExecutions = new Set()
|
|
116
119
|
let testsReportedInGenerateSummary = new Set()
|
|
117
120
|
const newTestsWithDynamicNames = new Set()
|
|
@@ -785,6 +788,7 @@ function finishTestSuiteIfDone (testSuiteAbsolutePath, projects) {
|
|
|
785
788
|
testSourceLine: test.location.line,
|
|
786
789
|
browserName,
|
|
787
790
|
isNew: test._ddIsNew,
|
|
791
|
+
isAttemptToFix: test._ddIsAttemptToFix,
|
|
788
792
|
isDisabled: test._ddIsDisabled,
|
|
789
793
|
isModified: test._ddIsModified,
|
|
790
794
|
isQuarantined: test._ddIsQuarantined,
|
|
@@ -832,6 +836,7 @@ function testEndHandler ({
|
|
|
832
836
|
|
|
833
837
|
if (hookError) {
|
|
834
838
|
addErrorToTestSuite(testSuiteAbsolutePath, hookError)
|
|
839
|
+
reporterSuiteHookErrorCh.publish(testSuiteAbsolutePath)
|
|
835
840
|
}
|
|
836
841
|
return
|
|
837
842
|
}
|
|
@@ -917,9 +922,7 @@ function testEndHandler ({
|
|
|
917
922
|
|
|
918
923
|
// Check if all EFD retries failed
|
|
919
924
|
const efdRetryCount = getEfdRetryCountForTest(test)
|
|
920
|
-
if (efdRetryCount > 0 && testStatuses.length === efdRetryCount + 1 &&
|
|
921
|
-
(test._ddIsNew || test._ddIsModified) &&
|
|
922
|
-
isEarlyFlakeDetectionEnabled &&
|
|
925
|
+
if (isEfdManagedTest && efdRetryCount > 0 && testStatuses.length === efdRetryCount + 1 &&
|
|
923
926
|
testStatuses.every(status => status === 'fail')) {
|
|
924
927
|
test._ddHasFailedAllRetries = true
|
|
925
928
|
}
|
|
@@ -1083,7 +1086,7 @@ function onDispatcherCreateWorker (dispatcher, worker) {
|
|
|
1083
1086
|
|
|
1084
1087
|
const projects = getProjectsFromDispatcher(dispatcher)
|
|
1085
1088
|
sessionProjects = projects
|
|
1086
|
-
const
|
|
1089
|
+
const automaticFailureScreenshotsByTestId = new Map()
|
|
1087
1090
|
|
|
1088
1091
|
if (disabledTestIds.size && !worker[kDdPlaywrightWorkerHostInstrumented] &&
|
|
1089
1092
|
typeof worker.runTestGroup === 'function') {
|
|
@@ -1108,19 +1111,21 @@ function onDispatcherCreateWorker (dispatcher, worker) {
|
|
|
1108
1111
|
const test = getTestByTestId(dispatcher, testId)
|
|
1109
1112
|
if (!test) return
|
|
1110
1113
|
|
|
1114
|
+
automaticFailureScreenshotsByTestId.clear()
|
|
1111
1115
|
const browser = getBrowserNameFromProjects(projects, test)
|
|
1112
1116
|
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
|
|
1113
1117
|
testBeginHandler(test, browser, shouldCreateTestSpan)
|
|
1114
1118
|
})
|
|
1115
|
-
worker.on('attach', (
|
|
1119
|
+
worker.on('attach', (attachment) => {
|
|
1120
|
+
const { testId, _ddIsAutomaticFailureScreenshot } = attachment
|
|
1116
1121
|
if (!_ddIsAutomaticFailureScreenshot) return
|
|
1117
1122
|
|
|
1118
|
-
let
|
|
1119
|
-
if (!
|
|
1120
|
-
|
|
1121
|
-
|
|
1123
|
+
let screenshots = automaticFailureScreenshotsByTestId.get(testId)
|
|
1124
|
+
if (!screenshots) {
|
|
1125
|
+
screenshots = []
|
|
1126
|
+
automaticFailureScreenshotsByTestId.set(testId, screenshots)
|
|
1122
1127
|
}
|
|
1123
|
-
|
|
1128
|
+
screenshots.push(attachment)
|
|
1124
1129
|
})
|
|
1125
1130
|
worker.on('testEnd', ({ testId, status, errors, annotations }) => {
|
|
1126
1131
|
const test = getTestByTestId(dispatcher, testId)
|
|
@@ -1144,19 +1149,20 @@ function onDispatcherCreateWorker (dispatcher, worker) {
|
|
|
1144
1149
|
}
|
|
1145
1150
|
)
|
|
1146
1151
|
const testResult = test.results.at(-1)
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1152
|
+
if (isFailureScreenshotUploadEnabled &&
|
|
1153
|
+
!shouldCreateTestSpan &&
|
|
1154
|
+
!test._ddShouldSkipEfdRetry &&
|
|
1155
|
+
!disabledTestIds.has(testId)) {
|
|
1156
|
+
let screenshots
|
|
1157
|
+
if (testStatus === 'fail') {
|
|
1158
|
+
screenshots = automaticFailureScreenshotsByTestId.get(testId)
|
|
1159
|
+
if (!screenshots) {
|
|
1160
|
+
screenshots = []
|
|
1161
|
+
automaticFailureScreenshotsByTestId.set(testId, screenshots)
|
|
1154
1162
|
}
|
|
1155
1163
|
}
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
worker[kDdPlaywrightFailureScreenshots].push(screenshots)
|
|
1159
|
-
}
|
|
1164
|
+
worker[kDdPlaywrightFailureScreenshots] ??= []
|
|
1165
|
+
worker[kDdPlaywrightFailureScreenshots].push(screenshots)
|
|
1160
1166
|
}
|
|
1161
1167
|
const isAtrRetry = testResult?.retry > 0 &&
|
|
1162
1168
|
isFlakyTestRetriesEnabled &&
|
|
@@ -1259,6 +1265,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1259
1265
|
return async function (config) {
|
|
1260
1266
|
reporterError = undefined
|
|
1261
1267
|
hasReporterError = false
|
|
1268
|
+
playwrightRunSummary = undefined
|
|
1262
1269
|
let restoreReporterConsoleError
|
|
1263
1270
|
if (satisfies(playwrightVersion, '>=1.60.0') && config?.config) {
|
|
1264
1271
|
const DatadogPlaywrightReporter = require('./playwright-reporter')
|
|
@@ -1433,23 +1440,8 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1433
1440
|
const sessionStatus = runAllTestsReturn.status || runAllTestsReturn
|
|
1434
1441
|
|
|
1435
1442
|
if (isTestManagementTestsEnabled && sessionStatus === 'failed') {
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
for (const [fqn, testStatuses] of testsToTestStatuses) {
|
|
1440
|
-
// Only count as failed if the final status (after retries) is 'fail'
|
|
1441
|
-
const lastStatus = testStatuses.at(-1)
|
|
1442
|
-
if (lastStatus === 'fail') {
|
|
1443
|
-
totalFailedTestCount += 1
|
|
1444
|
-
if (quarantinedButNotAttemptToFixFqns.has(fqn)) {
|
|
1445
|
-
totalPureQuarantinedFailedTestCount += 1
|
|
1446
|
-
}
|
|
1447
|
-
}
|
|
1448
|
-
}
|
|
1449
|
-
|
|
1450
|
-
const totalIgnorableFailures = totalPureQuarantinedFailedTestCount
|
|
1451
|
-
|
|
1452
|
-
if (totalFailedTestCount > 0 && totalFailedTestCount === totalIgnorableFailures) {
|
|
1443
|
+
const { failureCount, quarantinedFailureCount, hasIncompleteTests } = playwrightRunSummary || {}
|
|
1444
|
+
if (!hasIncompleteTests && quarantinedFailureCount > 0 && quarantinedFailureCount === failureCount) {
|
|
1453
1445
|
runAllTestsReturn = 'passed'
|
|
1454
1446
|
preventedToFail = true
|
|
1455
1447
|
}
|
|
@@ -1476,7 +1468,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1476
1468
|
|
|
1477
1469
|
startedSuites = []
|
|
1478
1470
|
remainingTestsByFile = {}
|
|
1479
|
-
|
|
1471
|
+
playwrightRunSummary = undefined
|
|
1480
1472
|
recordedTestOptimizationExecutions = new Set()
|
|
1481
1473
|
testsReportedInGenerateSummary = new Set()
|
|
1482
1474
|
efdManagedTestKeys.clear()
|
|
@@ -1507,6 +1499,8 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1507
1499
|
function reportersHook (reportersPackage) {
|
|
1508
1500
|
return shimmer.wrap(reportersPackage, 'createReporters', createReporters => async function () {
|
|
1509
1501
|
const reporters = await createReporters.apply(this, arguments)
|
|
1502
|
+
const DatadogPlaywrightReporter = require('./playwright-reporter')
|
|
1503
|
+
reporters.unshift(new DatadogPlaywrightReporter({ captureReporterErrors: false }))
|
|
1510
1504
|
for (const reporter of reporters) {
|
|
1511
1505
|
if (instrumentedPlaywrightReporters.has(reporter)) continue
|
|
1512
1506
|
|
|
@@ -1663,6 +1657,10 @@ reporterErrorCh.subscribe((error) => {
|
|
|
1663
1657
|
recordReporterError(error)
|
|
1664
1658
|
})
|
|
1665
1659
|
|
|
1660
|
+
reporterRunSummaryCh.subscribe((runSummary) => {
|
|
1661
|
+
playwrightRunSummary = runSummary
|
|
1662
|
+
})
|
|
1663
|
+
|
|
1666
1664
|
/**
|
|
1667
1665
|
* Records a reporter failure even when the reporter throws a falsy value.
|
|
1668
1666
|
*
|
|
@@ -1894,11 +1892,6 @@ function processRootSuite (createRootSuiteReturnValue) {
|
|
|
1894
1892
|
}
|
|
1895
1893
|
if (testProperties.quarantined) {
|
|
1896
1894
|
test._ddIsQuarantined = true
|
|
1897
|
-
if (!testProperties.attemptToFix) {
|
|
1898
|
-
// Do not skip quarantined tests, let them run and overwrite results post-run if they fail
|
|
1899
|
-
const testFqn = getTestFullyQualifiedName(test)
|
|
1900
|
-
quarantinedButNotAttemptToFixFqns.add(testFqn)
|
|
1901
|
-
}
|
|
1902
1895
|
}
|
|
1903
1896
|
if (testProperties.attemptToFix) {
|
|
1904
1897
|
test._ddIsAttemptToFix = true
|
|
@@ -1938,7 +1931,10 @@ function processRootSuite (createRootSuiteReturnValue) {
|
|
|
1938
1931
|
const fileSuitesWithImpactedTestsToProjects = new Map()
|
|
1939
1932
|
for (const impactedTest of impactedTests) {
|
|
1940
1933
|
impactedTest._ddIsModified = true
|
|
1941
|
-
if (shouldRunEarlyFlakeDetection() && impactedTest.expectedStatus !== 'skipped'
|
|
1934
|
+
if (shouldRunEarlyFlakeDetection() && impactedTest.expectedStatus !== 'skipped' &&
|
|
1935
|
+
!impactedTest._ddIsAttemptToFix) {
|
|
1936
|
+
// Prevent ATR or `--retries` from retrying tests that EFD manages.
|
|
1937
|
+
impactedTest.retries = 0
|
|
1942
1938
|
markEfdManagedTest(impactedTest)
|
|
1943
1939
|
const fileSuite = getSuiteType(impactedTest, 'file')
|
|
1944
1940
|
if (!fileSuitesWithImpactedTestsToProjects.has(fileSuite)) {
|
|
@@ -1946,14 +1942,15 @@ function processRootSuite (createRootSuiteReturnValue) {
|
|
|
1946
1942
|
}
|
|
1947
1943
|
}
|
|
1948
1944
|
}
|
|
1949
|
-
// If something
|
|
1945
|
+
// If something changes in the file, all tests in the file are impacted, except attempt-to-fix tests managed above.
|
|
1950
1946
|
applyRetriesToTests(
|
|
1951
1947
|
fileSuitesWithImpactedTestsToProjects,
|
|
1952
|
-
() =>
|
|
1948
|
+
(test) => !test._ddIsAttemptToFix,
|
|
1953
1949
|
[
|
|
1954
1950
|
'_ddIsModified',
|
|
1955
1951
|
'_ddIsEfdRetry',
|
|
1956
1952
|
(test) => (isKnownTestsEnabled && isNewTest(test) ? '_ddIsNew' : null),
|
|
1953
|
+
(test) => test._ddIsQuarantined && '_ddIsQuarantined',
|
|
1957
1954
|
],
|
|
1958
1955
|
earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
|
|
1959
1956
|
(copiedTest, originalTest, retryIndex) => {
|
|
@@ -1981,7 +1978,8 @@ function processRootSuite (createRootSuiteReturnValue) {
|
|
|
1981
1978
|
const fileSuitesWithNewTestsToProjects = new Map()
|
|
1982
1979
|
for (const newTest of newTests) {
|
|
1983
1980
|
newTest._ddIsNew = true
|
|
1984
|
-
if (shouldRunEarlyFlakeDetection() && newTest.expectedStatus !== 'skipped' &&
|
|
1981
|
+
if (shouldRunEarlyFlakeDetection() && newTest.expectedStatus !== 'skipped' &&
|
|
1982
|
+
!newTest._ddIsModified && !newTest._ddIsAttemptToFix) {
|
|
1985
1983
|
// Prevent ATR or `--retries` from retrying tests that EFD manages.
|
|
1986
1984
|
newTest.retries = 0
|
|
1987
1985
|
markEfdManagedTest(newTest)
|
|
@@ -1994,8 +1992,12 @@ function processRootSuite (createRootSuiteReturnValue) {
|
|
|
1994
1992
|
|
|
1995
1993
|
applyRetriesToTests(
|
|
1996
1994
|
fileSuitesWithNewTestsToProjects,
|
|
1997
|
-
isNewTest,
|
|
1998
|
-
[
|
|
1995
|
+
(test) => !test._ddIsAttemptToFix && isNewTest(test),
|
|
1996
|
+
[
|
|
1997
|
+
'_ddIsNew',
|
|
1998
|
+
'_ddIsEfdRetry',
|
|
1999
|
+
(test) => test._ddIsQuarantined && '_ddIsQuarantined',
|
|
2000
|
+
],
|
|
1999
2001
|
earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
|
|
2000
2002
|
(copiedTest, originalTest, retryIndex) => {
|
|
2001
2003
|
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
@@ -2214,7 +2216,8 @@ addHook({
|
|
|
2214
2216
|
|
|
2215
2217
|
function instrumentWorkerMainMethods (workerMain) {
|
|
2216
2218
|
if (!workerMain || workerMain[kDdPlaywrightWorkerInstrumented] ||
|
|
2217
|
-
typeof workerMain._runTest !== 'function' || typeof workerMain.dispatchEvent !== 'function'
|
|
2219
|
+
typeof workerMain._runTest !== 'function' || typeof workerMain.dispatchEvent !== 'function' ||
|
|
2220
|
+
typeof workerMain.gracefullyClose !== 'function') {
|
|
2218
2221
|
return workerMain
|
|
2219
2222
|
}
|
|
2220
2223
|
|
|
@@ -2230,6 +2233,15 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2230
2233
|
return runTestGroup.apply(this, arguments)
|
|
2231
2234
|
})
|
|
2232
2235
|
|
|
2236
|
+
// Playwright >=1.60 creates WorkerMain through a runtime factory, which Orchestrion cannot wrap statically.
|
|
2237
|
+
shimmer.wrap(workerMain, 'gracefullyClose', gracefullyClose => async function () {
|
|
2238
|
+
try {
|
|
2239
|
+
return await gracefullyClose.apply(this, arguments)
|
|
2240
|
+
} finally {
|
|
2241
|
+
await getChannelPromise(logSubmissionFlushCh)
|
|
2242
|
+
}
|
|
2243
|
+
})
|
|
2244
|
+
|
|
2233
2245
|
shimmer.wrap(workerMain, '_runTest', _runTest => async function (test) {
|
|
2234
2246
|
if (this[kDdPlaywrightDisabledTestIds]?.has(test.id)) {
|
|
2235
2247
|
test._ddIsDisabled = true
|
|
@@ -2395,7 +2407,7 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2395
2407
|
// We reproduce what happens in `Dispatcher#_onStepBegin` and `Dispatcher#_onStepEnd`,
|
|
2396
2408
|
// since `startTime` and `duration` are not available directly in the worker process
|
|
2397
2409
|
shimmer.wrap(workerMain, 'dispatchEvent', dispatchEvent => function (event, payload) {
|
|
2398
|
-
if (event === 'testBegin'
|
|
2410
|
+
if (event === 'testBegin') {
|
|
2399
2411
|
automaticFailureScreenshotPaths.clear()
|
|
2400
2412
|
} else if (event === 'stepBegin') {
|
|
2401
2413
|
stepInfoByStepId[payload.stepId] = {
|
|
@@ -2447,6 +2459,7 @@ function generateSummaryWrapper (generateSummary) {
|
|
|
2447
2459
|
line: testSourceLine,
|
|
2448
2460
|
},
|
|
2449
2461
|
_ddIsNew: isNew,
|
|
2462
|
+
_ddIsAttemptToFix: isAttemptToFix,
|
|
2450
2463
|
_ddIsDisabled: isDisabled,
|
|
2451
2464
|
_ddIsModified: isModified,
|
|
2452
2465
|
_ddIsQuarantined: isQuarantined,
|
|
@@ -2461,6 +2474,7 @@ function generateSummaryWrapper (generateSummary) {
|
|
|
2461
2474
|
testSourceLine,
|
|
2462
2475
|
browserName,
|
|
2463
2476
|
isNew,
|
|
2477
|
+
isAttemptToFix,
|
|
2464
2478
|
isDisabled,
|
|
2465
2479
|
isModified,
|
|
2466
2480
|
isQuarantined,
|
|
@@ -1058,7 +1058,7 @@ function getRepeatedTestReport (task, testName, testSuiteAbsolutePath, testPrope
|
|
|
1058
1058
|
const errors = browserEnvironment
|
|
1059
1059
|
? normalizeVitestBrowserErrors(result?.errors)
|
|
1060
1060
|
: result?.errors || []
|
|
1061
|
-
const finalAttemptStatus = finalStatus(statuses)
|
|
1061
|
+
const finalAttemptStatus = finalStatus(statuses, testProperties)
|
|
1062
1062
|
const hasFailure = finalAttemptStatus === 'fail'
|
|
1063
1063
|
const attempts = []
|
|
1064
1064
|
const attemptCount = getRepeatedAttemptCount(task, statuses)
|
|
@@ -1095,8 +1095,7 @@ function getRepeatedTestReport (task, testName, testSuiteAbsolutePath, testPrope
|
|
|
1095
1095
|
testProperties,
|
|
1096
1096
|
type,
|
|
1097
1097
|
statuses,
|
|
1098
|
-
isFinalAttempt
|
|
1099
|
-
hasFailure
|
|
1098
|
+
isFinalAttempt
|
|
1100
1099
|
),
|
|
1101
1100
|
isRetry: index > 0,
|
|
1102
1101
|
status: attemptStatus,
|
|
@@ -1136,11 +1135,10 @@ function getRepeatedTestReport (task, testName, testSuiteAbsolutePath, testPrope
|
|
|
1136
1135
|
* @param {'attempt_to_fix'|'early_flake_detection'|'external'} type
|
|
1137
1136
|
* @param {string[]} statuses
|
|
1138
1137
|
* @param {boolean} isFinalAttempt
|
|
1139
|
-
* @param {boolean} hasFailure
|
|
1140
1138
|
* @returns {boolean}
|
|
1141
1139
|
*/
|
|
1142
|
-
function hasFailedAllManagedRetries (task, testProperties, type, statuses, isFinalAttempt
|
|
1143
|
-
if (!isFinalAttempt ||
|
|
1140
|
+
function hasFailedAllManagedRetries (task, testProperties, type, statuses, isFinalAttempt) {
|
|
1141
|
+
if (!isFinalAttempt || statuses.length === 0 || !statuses.every(status => status === 'fail')) {
|
|
1144
1142
|
return false
|
|
1145
1143
|
}
|
|
1146
1144
|
|
|
@@ -1165,7 +1163,8 @@ function getAttemptToFixFinalStatus (statuses) {
|
|
|
1165
1163
|
return statuses.includes('fail') ? 'fail' : 'pass'
|
|
1166
1164
|
}
|
|
1167
1165
|
|
|
1168
|
-
function getEarlyFlakeDetectionFinalStatus (statuses) {
|
|
1166
|
+
function getEarlyFlakeDetectionFinalStatus (statuses, testProperties) {
|
|
1167
|
+
if (testProperties.isDisabled || testProperties.isQuarantined) return 'skip'
|
|
1169
1168
|
return statuses.includes('pass') ? 'pass' : 'fail'
|
|
1170
1169
|
}
|
|
1171
1170
|
|
|
@@ -1274,7 +1273,6 @@ function reportFinalTestAttempt (testReport) {
|
|
|
1274
1273
|
testProperties,
|
|
1275
1274
|
'external',
|
|
1276
1275
|
['fail'],
|
|
1277
|
-
true,
|
|
1278
1276
|
true
|
|
1279
1277
|
),
|
|
1280
1278
|
isRetry: (result?.retryCount || 0) > 0 || (result?.repeatCount || 0) > 0,
|
|
@@ -1604,15 +1602,18 @@ function splitNodeOptions (nodeOptions) {
|
|
|
1604
1602
|
}
|
|
1605
1603
|
|
|
1606
1604
|
function serializeNodeOptions (tokens) {
|
|
1607
|
-
|
|
1605
|
+
let serializedTokens = ''
|
|
1606
|
+
let hasSerializedToken = false
|
|
1608
1607
|
for (const token of tokens) {
|
|
1608
|
+
if (hasSerializedToken) serializedTokens += ' '
|
|
1609
1609
|
if (NODE_OPTIONS_QUOTE_RE.test(token)) {
|
|
1610
|
-
serializedTokens
|
|
1610
|
+
serializedTokens += JSON.stringify(token)
|
|
1611
1611
|
} else {
|
|
1612
|
-
serializedTokens
|
|
1612
|
+
serializedTokens += token
|
|
1613
1613
|
}
|
|
1614
|
+
hasSerializedToken = true
|
|
1614
1615
|
}
|
|
1615
|
-
return serializedTokens
|
|
1616
|
+
return serializedTokens
|
|
1616
1617
|
}
|
|
1617
1618
|
|
|
1618
1619
|
function getTestSpecificationProject (testSpecification) {
|
|
@@ -5,6 +5,7 @@ const { performance } = require('node:perf_hooks')
|
|
|
5
5
|
const { fileURLToPath } = require('node:url')
|
|
6
6
|
const { isMainThread, parentPort } = require('node:worker_threads')
|
|
7
7
|
|
|
8
|
+
const { channel } = require('dc-polyfill')
|
|
8
9
|
const shimmer = require('../../datadog-shimmer')
|
|
9
10
|
const log = require('../../dd-trace/src/log')
|
|
10
11
|
const { getEfdRetryCountForDuration } = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
|
|
@@ -40,10 +41,12 @@ const {
|
|
|
40
41
|
} = require('./vitest-util')
|
|
41
42
|
|
|
42
43
|
const EFD_SUITE_ADMISSION_TIMEOUT_MS = 5000
|
|
44
|
+
const logSubmissionFlushCh = channel('ci:log-submission:flush')
|
|
43
45
|
const taskToCtx = new WeakMap()
|
|
44
46
|
const taskToTestProperties = new WeakMap()
|
|
45
47
|
const taskToStatuses = new WeakMap()
|
|
46
48
|
const taskToReportedErrorCount = new WeakMap()
|
|
49
|
+
const runnersWithLogSubmissionCleanup = new WeakSet()
|
|
47
50
|
const attemptToFixTaskToStatuses = new WeakMap()
|
|
48
51
|
const fileToHasConcurrentTests = new WeakMap()
|
|
49
52
|
const fileToEfdSuiteAdmission = new WeakMap()
|
|
@@ -954,6 +957,14 @@ addHook({
|
|
|
954
957
|
if (!testSuiteFinishCh.hasSubscribers) {
|
|
955
958
|
return startTests.apply(this, arguments)
|
|
956
959
|
}
|
|
960
|
+
const runner = arguments[1]
|
|
961
|
+
// Vitest 3+ exposes the only awaited worker-shutdown boundary; older versions keep timer/before-exit behavior.
|
|
962
|
+
if (logSubmissionFlushCh.hasSubscribers &&
|
|
963
|
+
typeof runner?.onCleanupWorkerContext === 'function' &&
|
|
964
|
+
!runnersWithLogSubmissionCleanup.has(runner)) {
|
|
965
|
+
runnersWithLogSubmissionCleanup.add(runner)
|
|
966
|
+
runner.onCleanupWorkerContext(() => getChannelPromise(logSubmissionFlushCh))
|
|
967
|
+
}
|
|
957
968
|
// From >=3.0.1, the first arguments changes from a string to an object containing the filepath
|
|
958
969
|
const testSuiteAbsolutePath = testPaths[0]?.filepath || testPaths[0]
|
|
959
970
|
const providedContext = getProvidedContext()
|