dd-trace 5.102.0 → 5.104.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/ext/exporters.js +1 -0
- package/index.d.ts +25 -3
- package/package.json +15 -13
- package/packages/datadog-esbuild/src/utils.js +2 -2
- package/packages/datadog-instrumentations/src/ai.js +1 -1
- package/packages/datadog-instrumentations/src/aws-sdk.js +2 -2
- package/packages/datadog-instrumentations/src/cassandra-driver.js +5 -2
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +32 -15
- package/packages/datadog-instrumentations/src/couchbase.js +69 -220
- package/packages/datadog-instrumentations/src/cucumber.js +104 -31
- package/packages/datadog-instrumentations/src/elasticsearch.js +4 -4
- package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
- package/packages/datadog-instrumentations/src/electron.js +240 -0
- package/packages/datadog-instrumentations/src/fetch.js +5 -5
- package/packages/datadog-instrumentations/src/graphql.js +13 -17
- package/packages/datadog-instrumentations/src/grpc/client.js +48 -32
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/hook.js +4 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/kafka.js +58 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +3 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +19 -5
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +14 -13
- package/packages/datadog-instrumentations/src/http/client.js +2 -2
- package/packages/datadog-instrumentations/src/ioredis.js +18 -14
- package/packages/datadog-instrumentations/src/jest.js +382 -84
- package/packages/datadog-instrumentations/src/kafkajs.js +184 -174
- package/packages/datadog-instrumentations/src/mariadb.js +1 -1
- package/packages/datadog-instrumentations/src/memcached.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/main.js +309 -56
- package/packages/datadog-instrumentations/src/mocha/utils.js +48 -8
- package/packages/datadog-instrumentations/src/mongodb-core.js +34 -9
- package/packages/datadog-instrumentations/src/mongoose.js +10 -12
- package/packages/datadog-instrumentations/src/mysql.js +2 -2
- package/packages/datadog-instrumentations/src/mysql2.js +1 -1
- package/packages/datadog-instrumentations/src/pg.js +25 -11
- package/packages/datadog-instrumentations/src/playwright.js +449 -60
- package/packages/datadog-instrumentations/src/redis.js +19 -10
- package/packages/datadog-instrumentations/src/router.js +4 -2
- package/packages/datadog-instrumentations/src/vitest.js +246 -149
- package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
- package/packages/datadog-plugin-aws-sdk/src/base.js +18 -24
- package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/redshift.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/s3.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/sns.js +2 -2
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +1 -1
- package/packages/datadog-plugin-couchbase/src/index.js +58 -52
- package/packages/datadog-plugin-cucumber/src/index.js +1 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +239 -40
- package/packages/datadog-plugin-cypress/src/support.js +13 -1
- package/packages/datadog-plugin-elasticsearch/src/index.js +28 -8
- package/packages/datadog-plugin-electron/src/index.js +17 -0
- package/packages/datadog-plugin-electron/src/ipc.js +143 -0
- package/packages/datadog-plugin-electron/src/net.js +82 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
- package/packages/datadog-plugin-graphql/src/execute.js +6 -28
- package/packages/datadog-plugin-graphql/src/resolve.js +30 -35
- package/packages/datadog-plugin-graphql/src/tools/signature.js +32 -7
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +118 -100
- package/packages/datadog-plugin-graphql/src/utils.js +33 -1
- package/packages/datadog-plugin-grpc/src/client.js +6 -7
- package/packages/datadog-plugin-grpc/src/util.js +57 -22
- package/packages/datadog-plugin-http/src/client.js +2 -2
- package/packages/datadog-plugin-jest/src/index.js +92 -50
- package/packages/datadog-plugin-kafkajs/src/producer.js +32 -0
- package/packages/datadog-plugin-mocha/src/index.js +1 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +70 -69
- package/packages/datadog-plugin-mysql/src/index.js +1 -1
- package/packages/datadog-plugin-openai/src/services.js +2 -1
- package/packages/datadog-plugin-pg/src/index.js +3 -3
- package/packages/datadog-plugin-playwright/src/index.js +4 -0
- package/packages/datadog-plugin-redis/src/index.js +54 -24
- package/packages/datadog-plugin-undici/src/index.js +19 -0
- package/packages/datadog-plugin-vitest/src/index.js +19 -7
- package/packages/datadog-shimmer/src/shimmer.js +35 -0
- package/packages/dd-trace/src/aiguard/index.js +3 -1
- package/packages/dd-trace/src/aiguard/sdk.js +36 -30
- package/packages/dd-trace/src/aiguard/tags.js +20 -11
- package/packages/dd-trace/src/appsec/blocking.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +10 -3
- package/packages/dd-trace/src/appsec/reporter.js +19 -5
- package/packages/dd-trace/src/azure_metadata.js +17 -6
- package/packages/dd-trace/src/ci-visibility/dynamic-instrumentation/index.js +4 -4
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +4 -2
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +6 -4
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +1 -1
- package/packages/dd-trace/src/ci-visibility/requests/request.js +3 -1
- package/packages/dd-trace/src/ci-visibility/test-api-manual/test-api-manual-plugin.js +5 -3
- package/packages/dd-trace/src/config/defaults.js +3 -14
- package/packages/dd-trace/src/config/generated-config-types.d.ts +4 -1
- package/packages/dd-trace/src/config/helper.js +4 -0
- package/packages/dd-trace/src/config/index.js +2 -2
- package/packages/dd-trace/src/config/major-overrides.js +98 -0
- package/packages/dd-trace/src/config/parsers.js +7 -1
- package/packages/dd-trace/src/config/supported-configurations.json +60 -38
- package/packages/dd-trace/src/crashtracking/crashtracker.js +15 -3
- package/packages/dd-trace/src/datastreams/checkpointer.js +2 -2
- package/packages/dd-trace/src/datastreams/context.js +4 -2
- package/packages/dd-trace/src/datastreams/manager.js +1 -1
- package/packages/dd-trace/src/datastreams/processor.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/source-maps.js +1 -1
- package/packages/dd-trace/src/debugger/devtools_client/state.js +2 -1
- package/packages/dd-trace/src/debugger/index.js +7 -7
- package/packages/dd-trace/src/dogstatsd.js +2 -2
- package/packages/dd-trace/src/encode/0.4.js +45 -54
- package/packages/dd-trace/src/encode/0.5.js +34 -3
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +26 -19
- package/packages/dd-trace/src/encode/agentless-json.js +1 -1
- package/packages/dd-trace/src/exporter.js +2 -0
- package/packages/dd-trace/src/exporters/agent/index.js +2 -1
- package/packages/dd-trace/src/exporters/agentless/index.js +3 -2
- package/packages/dd-trace/src/exporters/agentless/writer.js +2 -2
- package/packages/dd-trace/src/exporters/common/agents.js +3 -1
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -1
- package/packages/dd-trace/src/exporters/common/request.js +4 -2
- package/packages/dd-trace/src/exporters/electron/index.js +49 -0
- package/packages/dd-trace/src/external-logger/src/index.js +2 -1
- package/packages/dd-trace/src/git_metadata.js +10 -8
- package/packages/dd-trace/src/id.js +17 -4
- package/packages/dd-trace/src/lambda/handler-paths.js +52 -0
- package/packages/dd-trace/src/lambda/handler.js +2 -4
- package/packages/dd-trace/src/lambda/index.js +62 -14
- package/packages/dd-trace/src/lambda/runtime/patch.js +21 -46
- package/packages/dd-trace/src/llmobs/index.js +13 -2
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
- package/packages/dd-trace/src/llmobs/sdk.js +10 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
- package/packages/dd-trace/src/log/writer.js +3 -1
- package/packages/dd-trace/src/noop/span.js +3 -1
- package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
- package/packages/dd-trace/src/openfeature/writers/exposures.js +51 -20
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +3 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
- package/packages/dd-trace/src/payload-tagging/config/index.js +2 -2
- package/packages/dd-trace/src/plugins/apollo.js +3 -1
- package/packages/dd-trace/src/plugins/ci_plugin.js +52 -17
- package/packages/dd-trace/src/plugins/database.js +54 -12
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -1
- package/packages/dd-trace/src/plugins/plugin.js +2 -4
- package/packages/dd-trace/src/plugins/tracing.js +5 -3
- package/packages/dd-trace/src/plugins/util/ci.js +8 -8
- package/packages/dd-trace/src/plugins/util/git-cache.js +20 -18
- package/packages/dd-trace/src/plugins/util/git.js +3 -1
- package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
- package/packages/dd-trace/src/plugins/util/test.js +119 -5
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -15
- package/packages/dd-trace/src/plugins/util/web.js +11 -0
- package/packages/dd-trace/src/priority_sampler.js +1 -1
- package/packages/dd-trace/src/profiling/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/wall.js +1 -1
- package/packages/dd-trace/src/profiling/ssi-heuristics.js +1 -1
- package/packages/dd-trace/src/rate_limiter.js +1 -1
- package/packages/dd-trace/src/remote_config/scheduler.js +1 -1
- package/packages/dd-trace/src/ritm.js +2 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
- package/packages/dd-trace/src/scope.js +7 -5
- package/packages/dd-trace/src/serverless.js +5 -2
- package/packages/dd-trace/src/service-naming/extra-services.js +14 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +20 -0
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +20 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/web.js +4 -0
- package/packages/dd-trace/src/span_stats.js +1 -1
- package/packages/dd-trace/src/telemetry/dependencies.js +1 -1
- package/packages/dd-trace/src/telemetry/endpoints.js +1 -1
- package/packages/dd-trace/src/telemetry/telemetry.js +2 -2
- package/packages/dd-trace/src/lambda/runtime/ritm.js +0 -133
- package/vendor/dist/opentracing/LICENSE +0 -201
- package/vendor/dist/opentracing/binary_carrier.d.ts +0 -11
- package/vendor/dist/opentracing/constants.d.ts +0 -61
- package/vendor/dist/opentracing/examples/demo/demo.d.ts +0 -2
- package/vendor/dist/opentracing/ext/tags.d.ts +0 -90
- package/vendor/dist/opentracing/functions.d.ts +0 -20
- package/vendor/dist/opentracing/global_tracer.d.ts +0 -14
- package/vendor/dist/opentracing/index.d.ts +0 -12
- package/vendor/dist/opentracing/index.js +0 -1
- package/vendor/dist/opentracing/mock_tracer/index.d.ts +0 -5
- package/vendor/dist/opentracing/mock_tracer/mock_context.d.ts +0 -13
- package/vendor/dist/opentracing/mock_tracer/mock_report.d.ts +0 -16
- package/vendor/dist/opentracing/mock_tracer/mock_span.d.ts +0 -50
- package/vendor/dist/opentracing/mock_tracer/mock_tracer.d.ts +0 -26
- package/vendor/dist/opentracing/noop.d.ts +0 -8
- package/vendor/dist/opentracing/reference.d.ts +0 -33
- package/vendor/dist/opentracing/span.d.ts +0 -147
- package/vendor/dist/opentracing/span_context.d.ts +0 -26
- package/vendor/dist/opentracing/test/api_compatibility.d.ts +0 -16
- package/vendor/dist/opentracing/test/mocktracer_implemenation.d.ts +0 -3
- package/vendor/dist/opentracing/test/noop_implementation.d.ts +0 -4
- package/vendor/dist/opentracing/test/opentracing_api.d.ts +0 -3
- package/vendor/dist/opentracing/test/unittest.d.ts +0 -2
- package/vendor/dist/opentracing/tracer.d.ts +0 -127
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// Capture real timers at module load time, before any test can install fake timers.
|
|
4
4
|
const realSetTimeout = setTimeout
|
|
5
5
|
|
|
6
|
+
const { performance } = require('node:perf_hooks')
|
|
6
7
|
const satisfies = require('../../../vendor/dist/semifies')
|
|
7
8
|
|
|
8
9
|
const shimmer = require('../../datadog-shimmer')
|
|
@@ -12,9 +13,12 @@ const {
|
|
|
12
13
|
PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE,
|
|
13
14
|
getIsFaultyEarlyFlakeDetection,
|
|
14
15
|
DYNAMIC_NAME_RE,
|
|
16
|
+
getEfdRetryCount,
|
|
17
|
+
getMaxEfdRetryCount,
|
|
15
18
|
recordAttemptToFixExecution,
|
|
16
19
|
logAttemptToFixTestExecution,
|
|
17
20
|
logTestOptimizationSummary,
|
|
21
|
+
getTestOptimizationRequestResults,
|
|
18
22
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
19
23
|
const log = require('../../dd-trace/src/log')
|
|
20
24
|
const {
|
|
@@ -68,6 +72,7 @@ let remainingTestsByFile = {}
|
|
|
68
72
|
let isKnownTestsEnabled = false
|
|
69
73
|
let isEarlyFlakeDetectionEnabled = false
|
|
70
74
|
let earlyFlakeDetectionNumRetries = 0
|
|
75
|
+
let earlyFlakeDetectionSlowTestRetries = {}
|
|
71
76
|
let isEarlyFlakeDetectionFaulty = false
|
|
72
77
|
let earlyFlakeDetectionFaultyThreshold = 0
|
|
73
78
|
let isFlakyTestRetriesEnabled = false
|
|
@@ -83,10 +88,19 @@ let testsReportedInGenerateSummary = new Set()
|
|
|
83
88
|
const newTestsWithDynamicNames = new Set()
|
|
84
89
|
const attemptToFixExecutions = new Map()
|
|
85
90
|
const loggedAttemptToFixTests = new Set()
|
|
91
|
+
const efdManagedTestKeys = new Set()
|
|
92
|
+
const efdRetryCountByTestKey = new Map()
|
|
93
|
+
const efdRetryCountRequestsByTestKey = new Map()
|
|
94
|
+
const efdRetryTestsById = new Map()
|
|
95
|
+
const efdScheduledOriginalTestKeys = new Set()
|
|
96
|
+
const efdStartedOriginalTestKeys = new Set()
|
|
97
|
+
const efdSlowAbortedTests = new Set()
|
|
86
98
|
let rootDir = ''
|
|
87
99
|
let sessionProjects = []
|
|
88
100
|
|
|
89
101
|
const MINIMUM_SUPPORTED_VERSION_RANGE_EFD = '>=1.38.0' // TODO: remove this once we drop support for v5
|
|
102
|
+
const EFD_RETRY_COUNT_REQUEST = 'ddEfdRetryCountRequest'
|
|
103
|
+
const EFD_RETRY_COUNT_RESPONSE = 'ddEfdRetryCountResponse'
|
|
90
104
|
|
|
91
105
|
function isValidKnownTests (receivedKnownTests) {
|
|
92
106
|
return !!receivedKnownTests.playwright
|
|
@@ -97,6 +111,222 @@ function getTestFullyQualifiedName (test) {
|
|
|
97
111
|
return `${test._requireFile} ${fullname}`
|
|
98
112
|
}
|
|
99
113
|
|
|
114
|
+
/**
|
|
115
|
+
* @param {object} test
|
|
116
|
+
* @returns {string|undefined}
|
|
117
|
+
*/
|
|
118
|
+
function getTestProjectKey (test) {
|
|
119
|
+
const { _projectIndex, _projectId } = test
|
|
120
|
+
if (_projectIndex !== undefined) {
|
|
121
|
+
return `index:${_projectIndex}`
|
|
122
|
+
}
|
|
123
|
+
if (_projectId !== undefined) {
|
|
124
|
+
return `id:${_projectId}`
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const projectSuite = getSuiteType(test, 'project')
|
|
128
|
+
const projectName = projectSuite?._fullProject?.project?.name ||
|
|
129
|
+
projectSuite?._fullProject?.name ||
|
|
130
|
+
projectSuite?.title
|
|
131
|
+
if (projectName) {
|
|
132
|
+
return `name:${projectName}`
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @param {object} test
|
|
138
|
+
* @returns {number|undefined}
|
|
139
|
+
*/
|
|
140
|
+
function getTestEfdRepeatEachIndex (test) {
|
|
141
|
+
if (Object.hasOwn(test, '_ddEfdOriginalRepeatEachIndex')) {
|
|
142
|
+
return test._ddEfdOriginalRepeatEachIndex
|
|
143
|
+
}
|
|
144
|
+
return test.repeatEachIndex
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* @param {object} test
|
|
149
|
+
* @returns {string|undefined}
|
|
150
|
+
*/
|
|
151
|
+
function getTestRepeatEachKey (test) {
|
|
152
|
+
const repeatEachIndex = getTestEfdRepeatEachIndex(test)
|
|
153
|
+
if (repeatEachIndex !== undefined) {
|
|
154
|
+
return `repeat:${repeatEachIndex}`
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {object} test
|
|
160
|
+
* @returns {string}
|
|
161
|
+
*/
|
|
162
|
+
function getTestEfdKey (test) {
|
|
163
|
+
const projectKey = getTestProjectKey(test)
|
|
164
|
+
const repeatEachKey = getTestRepeatEachKey(test)
|
|
165
|
+
const testFqn = getTestFullyQualifiedName(test)
|
|
166
|
+
return [projectKey, repeatEachKey, testFqn].filter(Boolean).join(' ')
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function getConfiguredEfdRetryCount () {
|
|
170
|
+
if (!earlyFlakeDetectionSlowTestRetries || !Object.keys(earlyFlakeDetectionSlowTestRetries).length) {
|
|
171
|
+
return earlyFlakeDetectionNumRetries
|
|
172
|
+
}
|
|
173
|
+
return getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function markEfdManagedTest (test) {
|
|
177
|
+
test._ddIsEfdManagedTest = true
|
|
178
|
+
test._ddEfdSlowTestRetries = earlyFlakeDetectionSlowTestRetries
|
|
179
|
+
efdManagedTestKeys.add(getTestEfdKey(test))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function markEfdRetryTest (test, retryIndex, originalTest) {
|
|
183
|
+
test._ddIsEfdRetry = true
|
|
184
|
+
test._ddEfdRetryIndex = retryIndex
|
|
185
|
+
if (originalTest) {
|
|
186
|
+
test._ddEfdOriginalRepeatEachIndex = getTestEfdRepeatEachIndex(originalTest)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function registerEfdRetryTest (test) {
|
|
191
|
+
if (!test._ddIsEfdRetry) {
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
efdRetryTestsById.set(test.id, {
|
|
196
|
+
retryIndex: test._ddEfdRetryIndex,
|
|
197
|
+
testEfdKey: getTestEfdKey(test),
|
|
198
|
+
})
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function getTestEfdSlowTestRetries (test) {
|
|
202
|
+
return test._ddEfdSlowTestRetries || earlyFlakeDetectionSlowTestRetries
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function isTestEfdManaged (test) {
|
|
206
|
+
return !!test._ddIsEfdManagedTest || (
|
|
207
|
+
(test._ddIsNew || test._ddIsModified) &&
|
|
208
|
+
!test._ddIsAttemptToFix &&
|
|
209
|
+
isEarlyFlakeDetectionEnabled
|
|
210
|
+
)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function getFileSuiteRepeatEachIndex (fileSuite) {
|
|
214
|
+
const test = fileSuite.allTests()[0]
|
|
215
|
+
return test ? getTestEfdRepeatEachIndex(test) || 0 : 0
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function getEfdRetryRepeatEachIndex (fileSuite, projectSuite, retryIndex, retryCount) {
|
|
219
|
+
const nativeRepeatEach = projectSuite._fullProject?.project?.repeatEach || 1
|
|
220
|
+
const originalRepeatEachIndex = getFileSuiteRepeatEachIndex(fileSuite)
|
|
221
|
+
return nativeRepeatEach + (originalRepeatEachIndex * retryCount) + retryIndex - 1
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function getEfdRetryCountForTest (test) {
|
|
225
|
+
return efdRetryCountByTestKey.get(getTestEfdKey(test)) ?? getConfiguredEfdRetryCount()
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function setEfdRetryCountForTest (test, retryCount) {
|
|
229
|
+
const testEfdKey = getTestEfdKey(test)
|
|
230
|
+
efdRetryCountByTestKey.set(testEfdKey, retryCount)
|
|
231
|
+
|
|
232
|
+
const requests = efdRetryCountRequestsByTestKey.get(testEfdKey)
|
|
233
|
+
if (requests) {
|
|
234
|
+
efdRetryCountRequestsByTestKey.delete(testEfdKey)
|
|
235
|
+
for (const resolveRequest of requests) {
|
|
236
|
+
resolveRequest(retryCount)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function sendEfdRetryCountToWorker (workerProcess, testId, retryIndex, retryCount) {
|
|
242
|
+
workerProcess.send({
|
|
243
|
+
type: EFD_RETRY_COUNT_RESPONSE,
|
|
244
|
+
testId,
|
|
245
|
+
isEfdRetry: retryIndex !== undefined,
|
|
246
|
+
retryIndex,
|
|
247
|
+
retryCount,
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function sendEfdRetryCountToWorkerWhenAvailable (workerProcess, testId) {
|
|
252
|
+
const efdRetryTest = efdRetryTestsById.get(testId)
|
|
253
|
+
if (!efdRetryTest) {
|
|
254
|
+
sendEfdRetryCountToWorker(workerProcess, testId)
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const { retryIndex, testEfdKey } = efdRetryTest
|
|
259
|
+
|
|
260
|
+
if (!testEfdKey || !efdManagedTestKeys.has(testEfdKey)) {
|
|
261
|
+
sendEfdRetryCountToWorker(workerProcess, testId)
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const retryCount = efdRetryCountByTestKey.get(testEfdKey)
|
|
266
|
+
if (retryCount !== undefined) {
|
|
267
|
+
sendEfdRetryCountToWorker(workerProcess, testId, retryIndex, retryCount)
|
|
268
|
+
return
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if (!efdStartedOriginalTestKeys.has(testEfdKey) && !efdScheduledOriginalTestKeys.has(testEfdKey)) {
|
|
272
|
+
sendEfdRetryCountToWorker(workerProcess, testId, retryIndex, 0)
|
|
273
|
+
return
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (!efdRetryCountRequestsByTestKey.has(testEfdKey)) {
|
|
277
|
+
efdRetryCountRequestsByTestKey.set(testEfdKey, [])
|
|
278
|
+
}
|
|
279
|
+
efdRetryCountRequestsByTestKey.get(testEfdKey).push((retryCount) => {
|
|
280
|
+
sendEfdRetryCountToWorker(workerProcess, testId, retryIndex, retryCount)
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @param {object} test
|
|
286
|
+
* @returns {boolean}
|
|
287
|
+
*/
|
|
288
|
+
function shouldRequestEfdRetryCount (test) {
|
|
289
|
+
// The main process remains the source of truth. repeatEachIndex is only used as
|
|
290
|
+
// a cheap worker-side filter so first executions do not block on coordination.
|
|
291
|
+
return test._ddIsEfdRetry || test.repeatEachIndex > 0
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function waitForEfdRetryCount (test) {
|
|
295
|
+
if (!process.send || !shouldRequestEfdRetryCount(test)) {
|
|
296
|
+
return Promise.resolve()
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const testEfdKey = getTestEfdKey(test)
|
|
300
|
+
return new Promise(resolve => {
|
|
301
|
+
const messageHandler = (message) => {
|
|
302
|
+
if (message?.type === EFD_RETRY_COUNT_RESPONSE && message.testId === test.id) {
|
|
303
|
+
if (message.isEfdRetry) {
|
|
304
|
+
test._ddIsEfdRetry = true
|
|
305
|
+
test._ddEfdRetryIndex = message.retryIndex
|
|
306
|
+
test._ddEfdRetryCount = message.retryCount
|
|
307
|
+
efdRetryCountByTestKey.set(testEfdKey, message.retryCount)
|
|
308
|
+
}
|
|
309
|
+
process.removeListener('message', messageHandler)
|
|
310
|
+
resolve()
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
process.on('message', messageHandler)
|
|
315
|
+
process.send({
|
|
316
|
+
type: EFD_RETRY_COUNT_REQUEST,
|
|
317
|
+
testId: test.id,
|
|
318
|
+
})
|
|
319
|
+
})
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function shouldSkipEfdRetry (test) {
|
|
323
|
+
if (!test._ddIsEfdRetry) {
|
|
324
|
+
return false
|
|
325
|
+
}
|
|
326
|
+
const retryCount = test._ddEfdRetryCount ?? efdRetryCountByTestKey.get(getTestEfdKey(test))
|
|
327
|
+
return retryCount !== undefined && test._ddEfdRetryIndex > retryCount
|
|
328
|
+
}
|
|
329
|
+
|
|
100
330
|
function getTestProperties (test) {
|
|
101
331
|
const testName = getTestFullname(test)
|
|
102
332
|
const testSuite = getTestSuitePath(test._requireFile, rootDir)
|
|
@@ -125,14 +355,17 @@ function getSuiteType (test, type) {
|
|
|
125
355
|
}
|
|
126
356
|
|
|
127
357
|
// Copy of Suite#_deepClone but with a function to filter tests
|
|
128
|
-
function deepCloneSuite (suite, filterTest, tags = []) {
|
|
358
|
+
function deepCloneSuite (suite, filterTest, tags = [], configureCopiedTest) {
|
|
129
359
|
const copy = suite._clone()
|
|
130
360
|
for (const entry of suite._entries) {
|
|
131
361
|
if (entry.constructor.name === 'Suite') {
|
|
132
|
-
copy._addSuite(deepCloneSuite(entry, filterTest, tags))
|
|
362
|
+
copy._addSuite(deepCloneSuite(entry, filterTest, tags, configureCopiedTest))
|
|
133
363
|
} else {
|
|
134
364
|
if (filterTest(entry)) {
|
|
135
365
|
const copiedTest = entry._clone()
|
|
366
|
+
if (configureCopiedTest) {
|
|
367
|
+
configureCopiedTest(copiedTest, entry)
|
|
368
|
+
}
|
|
136
369
|
for (const tag of tags) {
|
|
137
370
|
const resolvedTag = typeof tag === 'function' ? tag(entry) : tag
|
|
138
371
|
|
|
@@ -303,6 +536,7 @@ function getFinalStatus ({
|
|
|
303
536
|
isAttemptToFix,
|
|
304
537
|
hasFailedAllRetries,
|
|
305
538
|
hasFailedAttemptToFixRetries,
|
|
539
|
+
hasPassedAnyEfdAttempt,
|
|
306
540
|
testStatus,
|
|
307
541
|
}) {
|
|
308
542
|
if (!isFinalExecution) {
|
|
@@ -311,9 +545,12 @@ function getFinalStatus ({
|
|
|
311
545
|
if (isDisabled || isQuarantined || testStatus === 'skip') {
|
|
312
546
|
return 'skip'
|
|
313
547
|
}
|
|
314
|
-
if (isAtrRetry
|
|
548
|
+
if (isAtrRetry) {
|
|
315
549
|
return hasFailedAllRetries ? 'fail' : 'pass'
|
|
316
550
|
}
|
|
551
|
+
if (isEfdManagedTest) {
|
|
552
|
+
return hasPassedAnyEfdAttempt ? 'pass' : 'fail'
|
|
553
|
+
}
|
|
317
554
|
if (isAttemptToFix) {
|
|
318
555
|
return hasFailedAttemptToFixRetries ? 'fail' : 'pass'
|
|
319
556
|
}
|
|
@@ -350,6 +587,14 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
350
587
|
if (_type === 'beforeAll' || _type === 'afterAll') {
|
|
351
588
|
return
|
|
352
589
|
}
|
|
590
|
+
if (shouldSkipEfdRetry(test)) {
|
|
591
|
+
test._ddShouldSkipEfdRetry = true
|
|
592
|
+
return
|
|
593
|
+
}
|
|
594
|
+
test._ddStartTime = performance.now()
|
|
595
|
+
if (isTestEfdManaged(test) && !test._ddIsEfdRetry) {
|
|
596
|
+
efdStartedOriginalTestKeys.add(getTestEfdKey(test))
|
|
597
|
+
}
|
|
353
598
|
// this means that a skipped test is being handled
|
|
354
599
|
if (!remainingTestsByFile[testSuiteAbsolutePath].length) {
|
|
355
600
|
return
|
|
@@ -391,6 +636,45 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
391
636
|
}
|
|
392
637
|
}
|
|
393
638
|
|
|
639
|
+
function finishTestSuiteIfDone (testSuiteAbsolutePath, projects) {
|
|
640
|
+
if (!shouldFinishTestSuite(testSuiteAbsolutePath)) {
|
|
641
|
+
return
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
const skippedTests = remainingTestsByFile[testSuiteAbsolutePath]
|
|
645
|
+
.filter(test => test.expectedStatus === 'skipped')
|
|
646
|
+
|
|
647
|
+
for (const test of skippedTests) {
|
|
648
|
+
const browserName = getBrowserNameFromProjects(projects, test)
|
|
649
|
+
testSkipCh.publish({
|
|
650
|
+
testName: getTestFullname(test),
|
|
651
|
+
testSuiteAbsolutePath,
|
|
652
|
+
testSourceFileAbsolutePath: test.location.file,
|
|
653
|
+
testSourceLine: test.location.line,
|
|
654
|
+
browserName,
|
|
655
|
+
isNew: test._ddIsNew,
|
|
656
|
+
isDisabled: test._ddIsDisabled,
|
|
657
|
+
isModified: test._ddIsModified,
|
|
658
|
+
isQuarantined: test._ddIsQuarantined,
|
|
659
|
+
})
|
|
660
|
+
}
|
|
661
|
+
remainingTestsByFile[testSuiteAbsolutePath] = []
|
|
662
|
+
|
|
663
|
+
const testStatuses = testSuiteToTestStatuses.get(testSuiteAbsolutePath)
|
|
664
|
+
let testSuiteStatus = 'pass'
|
|
665
|
+
if (testStatuses?.includes('fail')) {
|
|
666
|
+
testSuiteStatus = 'fail'
|
|
667
|
+
} else if (testStatuses?.every(status => status === 'skip')) {
|
|
668
|
+
testSuiteStatus = 'skip'
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const suiteError = getTestSuiteError(testSuiteAbsolutePath)
|
|
672
|
+
const testSuiteCtx = testSuiteToCtx.get(testSuiteAbsolutePath)
|
|
673
|
+
if (testSuiteCtx) {
|
|
674
|
+
testSuiteFinishCh.publish({ status: testSuiteStatus, error: suiteError, ...testSuiteCtx.currentStore })
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
394
678
|
function testEndHandler ({
|
|
395
679
|
test,
|
|
396
680
|
annotations,
|
|
@@ -420,11 +704,21 @@ function testEndHandler ({
|
|
|
420
704
|
return
|
|
421
705
|
}
|
|
422
706
|
|
|
707
|
+
if (test._ddShouldSkipEfdRetry || shouldSkipEfdRetry(test)) {
|
|
708
|
+
test._ddShouldSkipEfdRetry = true
|
|
709
|
+
remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath]
|
|
710
|
+
.filter(currentTest => currentTest !== test)
|
|
711
|
+
finishTestSuiteIfDone(testSuiteAbsolutePath, projects)
|
|
712
|
+
return
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const isEfdManagedTest = isTestEfdManaged(test)
|
|
423
716
|
const testFqn = getTestFullyQualifiedName(test)
|
|
424
|
-
const
|
|
717
|
+
const testStatusKey = isEfdManagedTest ? getTestEfdKey(test) : testFqn
|
|
718
|
+
const testStatuses = testsToTestStatuses.get(testStatusKey) || []
|
|
425
719
|
|
|
426
720
|
if (testStatuses.length === 0) {
|
|
427
|
-
testsToTestStatuses.set(
|
|
721
|
+
testsToTestStatuses.set(testStatusKey, [testStatus])
|
|
428
722
|
if (test._ddIsNew && DYNAMIC_NAME_RE.test(getTestFullname(test))) {
|
|
429
723
|
newTestsWithDynamicNames.add(`${getTestSuitePath(test._requireFile, rootDir)} › ${getTestFullname(test)}`)
|
|
430
724
|
}
|
|
@@ -432,6 +726,17 @@ function testEndHandler ({
|
|
|
432
726
|
testStatuses.push(testStatus)
|
|
433
727
|
}
|
|
434
728
|
|
|
729
|
+
const testEfdKey = getTestEfdKey(test)
|
|
730
|
+
if (isEfdManagedTest && !test._ddIsEfdRetry && !efdRetryCountByTestKey.has(testEfdKey)) {
|
|
731
|
+
const testResult = results.at(-1)
|
|
732
|
+
const duration = testResult?.duration > 0 ? testResult.duration : performance.now() - test._ddStartTime
|
|
733
|
+
const retryCount = getEfdRetryCount(duration, getTestEfdSlowTestRetries(test))
|
|
734
|
+
setEfdRetryCountForTest(test, retryCount)
|
|
735
|
+
if (retryCount === 0) {
|
|
736
|
+
efdSlowAbortedTests.add(testEfdKey)
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
435
740
|
const testProperties = getTestProperties(test)
|
|
436
741
|
|
|
437
742
|
if (testProperties.attemptToFix) {
|
|
@@ -460,9 +765,10 @@ function testEndHandler ({
|
|
|
460
765
|
}
|
|
461
766
|
|
|
462
767
|
// Check if all EFD retries failed
|
|
463
|
-
|
|
768
|
+
const efdRetryCount = getEfdRetryCountForTest(test)
|
|
769
|
+
if (efdRetryCount > 0 && testStatuses.length === efdRetryCount + 1 &&
|
|
464
770
|
(test._ddIsNew || test._ddIsModified) &&
|
|
465
|
-
|
|
771
|
+
isEarlyFlakeDetectionEnabled &&
|
|
466
772
|
testStatuses.every(status => status === 'fail')) {
|
|
467
773
|
test._ddHasFailedAllRetries = true
|
|
468
774
|
}
|
|
@@ -480,9 +786,6 @@ function testEndHandler ({
|
|
|
480
786
|
if (shouldCreateTestSpan) {
|
|
481
787
|
const testResult = results.at(-1)
|
|
482
788
|
const testCtx = testToCtx.get(test)
|
|
483
|
-
const isEfdManagedTest = (test._ddIsNew || test._ddIsModified) &&
|
|
484
|
-
!test._ddIsAttemptToFix &&
|
|
485
|
-
isEarlyFlakeDetectionEnabled
|
|
486
789
|
const isAtrRetry = testResult?.retry > 0 &&
|
|
487
790
|
isFlakyTestRetriesEnabled &&
|
|
488
791
|
!test._ddIsAttemptToFix &&
|
|
@@ -497,6 +800,7 @@ function testEndHandler ({
|
|
|
497
800
|
isAttemptToFix: test._ddIsAttemptToFix,
|
|
498
801
|
hasFailedAllRetries: test._ddHasFailedAllRetries,
|
|
499
802
|
hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
|
|
803
|
+
hasPassedAnyEfdAttempt: testStatuses.includes('pass'),
|
|
500
804
|
testStatus,
|
|
501
805
|
})
|
|
502
806
|
|
|
@@ -520,6 +824,7 @@ function testEndHandler ({
|
|
|
520
824
|
isAtrRetry,
|
|
521
825
|
isModified: test._ddIsModified,
|
|
522
826
|
finalStatus,
|
|
827
|
+
earlyFlakeAbortReason: efdSlowAbortedTests.has(testEfdKey) ? 'slow' : undefined,
|
|
523
828
|
...testCtx.currentStore,
|
|
524
829
|
})
|
|
525
830
|
}
|
|
@@ -540,38 +845,7 @@ function testEndHandler ({
|
|
|
540
845
|
.filter(currentTest => currentTest !== test)
|
|
541
846
|
}
|
|
542
847
|
|
|
543
|
-
|
|
544
|
-
const skippedTests = remainingTestsByFile[testSuiteAbsolutePath]
|
|
545
|
-
.filter(test => test.expectedStatus === 'skipped')
|
|
546
|
-
|
|
547
|
-
for (const test of skippedTests) {
|
|
548
|
-
const browserName = getBrowserNameFromProjects(projects, test)
|
|
549
|
-
testSkipCh.publish({
|
|
550
|
-
testName: getTestFullname(test),
|
|
551
|
-
testSuiteAbsolutePath,
|
|
552
|
-
testSourceFileAbsolutePath: test.location.file,
|
|
553
|
-
testSourceLine: test.location.line,
|
|
554
|
-
browserName,
|
|
555
|
-
isNew: test._ddIsNew,
|
|
556
|
-
isDisabled: test._ddIsDisabled,
|
|
557
|
-
isModified: test._ddIsModified,
|
|
558
|
-
isQuarantined: test._ddIsQuarantined,
|
|
559
|
-
})
|
|
560
|
-
}
|
|
561
|
-
remainingTestsByFile[testSuiteAbsolutePath] = []
|
|
562
|
-
|
|
563
|
-
const testStatuses = testSuiteToTestStatuses.get(testSuiteAbsolutePath)
|
|
564
|
-
let testSuiteStatus = 'pass'
|
|
565
|
-
if (testStatuses.includes('fail')) {
|
|
566
|
-
testSuiteStatus = 'fail'
|
|
567
|
-
} else if (testStatuses.every(status => status === 'skip')) {
|
|
568
|
-
testSuiteStatus = 'skip'
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
const suiteError = getTestSuiteError(testSuiteAbsolutePath)
|
|
572
|
-
const testSuiteCtx = testSuiteToCtx.get(testSuiteAbsolutePath)
|
|
573
|
-
testSuiteFinishCh.publish({ status: testSuiteStatus, error: suiteError, ...testSuiteCtx.currentStore })
|
|
574
|
-
}
|
|
848
|
+
finishTestSuiteIfDone(testSuiteAbsolutePath, projects)
|
|
575
849
|
}
|
|
576
850
|
|
|
577
851
|
function dispatcherRunWrapper (run) {
|
|
@@ -581,6 +855,39 @@ function dispatcherRunWrapper (run) {
|
|
|
581
855
|
}
|
|
582
856
|
}
|
|
583
857
|
|
|
858
|
+
function deferEfdRetryGroups (testGroups) {
|
|
859
|
+
const groupsWithOriginalTests = []
|
|
860
|
+
const efdRetryOnlyGroups = []
|
|
861
|
+
|
|
862
|
+
for (const group of testGroups) {
|
|
863
|
+
const originalTests = []
|
|
864
|
+
const efdRetryTests = []
|
|
865
|
+
|
|
866
|
+
for (const test of group.tests) {
|
|
867
|
+
if (test._ddIsEfdRetry) {
|
|
868
|
+
efdRetryTests.push(test)
|
|
869
|
+
} else {
|
|
870
|
+
originalTests.push(test)
|
|
871
|
+
if (isTestEfdManaged(test)) {
|
|
872
|
+
efdScheduledOriginalTestKeys.add(getTestEfdKey(test))
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
if (efdRetryTests.length && originalTests.length) {
|
|
878
|
+
group.tests = [...originalTests, ...efdRetryTests]
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
if (originalTests.length) {
|
|
882
|
+
groupsWithOriginalTests.push(group)
|
|
883
|
+
} else {
|
|
884
|
+
efdRetryOnlyGroups.push(group)
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
return [...groupsWithOriginalTests, ...efdRetryOnlyGroups]
|
|
889
|
+
}
|
|
890
|
+
|
|
584
891
|
function dispatcherRunWrapperNew (run) {
|
|
585
892
|
return function (testGroups) {
|
|
586
893
|
// Filter out disabled tests from testGroups before they get scheduled,
|
|
@@ -593,12 +900,17 @@ function dispatcherRunWrapperNew (run) {
|
|
|
593
900
|
testGroups = testGroups.filter(group => group.tests.length > 0)
|
|
594
901
|
}
|
|
595
902
|
|
|
903
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
904
|
+
testGroups = deferEfdRetryGroups(testGroups)
|
|
905
|
+
}
|
|
906
|
+
|
|
596
907
|
if (!this._allTests) {
|
|
597
908
|
// Removed in https://github.com/microsoft/playwright/commit/1e52c37b254a441cccf332520f60225a5acc14c7
|
|
598
909
|
// Not available from >=1.44.0
|
|
599
910
|
this._ddAllTests = testGroups.flatMap(g => g.tests)
|
|
600
911
|
}
|
|
601
912
|
remainingTestsByFile = getTestsBySuiteFromTestGroups(testGroups)
|
|
913
|
+
arguments[0] = testGroups
|
|
602
914
|
return run.apply(this, arguments)
|
|
603
915
|
}
|
|
604
916
|
}
|
|
@@ -638,7 +950,6 @@ function dispatcherHook (dispatcherExport) {
|
|
|
638
950
|
)
|
|
639
951
|
}
|
|
640
952
|
})
|
|
641
|
-
|
|
642
953
|
return worker
|
|
643
954
|
})
|
|
644
955
|
return dispatcherExport
|
|
@@ -690,14 +1001,11 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
690
1001
|
// above) and mark the execution final once the count reaches the expected total.
|
|
691
1002
|
// This mirrors how ATF finality is detected and centralizes the decision in the
|
|
692
1003
|
// main process, so workers only need to act on the _ddIsFinalExecution flag.
|
|
693
|
-
const isEfdManagedTest = (test
|
|
694
|
-
!test._ddIsAttemptToFix &&
|
|
695
|
-
isEarlyFlakeDetectionEnabled
|
|
1004
|
+
const isEfdManagedTest = isTestEfdManaged(test)
|
|
696
1005
|
let isFinalExecution
|
|
697
1006
|
if (isEfdManagedTest) {
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
isFinalExecution = efdTestStatuses.length === earlyFlakeDetectionNumRetries + 1
|
|
1007
|
+
const efdTestStatuses = testsToTestStatuses.get(getTestEfdKey(test)) || []
|
|
1008
|
+
isFinalExecution = efdTestStatuses.length === getEfdRetryCountForTest(test) + 1
|
|
701
1009
|
} else if (test._ddIsAttemptToFix) {
|
|
702
1010
|
isFinalExecution = !!(test._ddHasPassedAttemptToFixRetries || test._ddHasFailedAttemptToFixRetries)
|
|
703
1011
|
} else {
|
|
@@ -722,10 +1030,11 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
722
1030
|
_ddIsModified: test._ddIsModified,
|
|
723
1031
|
_ddIsFinalExecution: isFinalExecution,
|
|
724
1032
|
_ddIsEfdManagedTest: isEfdManagedTest,
|
|
1033
|
+
_ddEarlyFlakeAbortReason: efdSlowAbortedTests.has(getTestEfdKey(test)) ? 'slow' : undefined,
|
|
1034
|
+
_ddHasPassedAnyEfdAttempt: (testsToTestStatuses.get(getTestEfdKey(test)) || []).includes('pass'),
|
|
725
1035
|
},
|
|
726
1036
|
})
|
|
727
1037
|
})
|
|
728
|
-
|
|
729
1038
|
return worker
|
|
730
1039
|
})
|
|
731
1040
|
return dispatcherExport
|
|
@@ -751,6 +1060,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
751
1060
|
isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
|
|
752
1061
|
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
|
|
753
1062
|
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
|
|
1063
|
+
earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
|
|
754
1064
|
earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
|
|
755
1065
|
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
756
1066
|
flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
|
|
@@ -766,9 +1076,24 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
766
1076
|
log.error('Playwright session start error', e)
|
|
767
1077
|
}
|
|
768
1078
|
|
|
769
|
-
|
|
1079
|
+
const isTestOptimizationSupported = satisfies(playwrightVersion, MINIMUM_SUPPORTED_VERSION_RANGE_EFD)
|
|
1080
|
+
const shouldGetKnownTests = isKnownTestsEnabled && isTestOptimizationSupported
|
|
1081
|
+
const shouldGetTestManagementTests = isTestManagementTestsEnabled && isTestOptimizationSupported
|
|
1082
|
+
|
|
1083
|
+
const {
|
|
1084
|
+
knownTestsResponse,
|
|
1085
|
+
testManagementTestsResponse,
|
|
1086
|
+
} = await getTestOptimizationRequestResults({
|
|
1087
|
+
isKnownTestsEnabled: shouldGetKnownTests,
|
|
1088
|
+
isTestManagementTestsEnabled: shouldGetTestManagementTests,
|
|
1089
|
+
getKnownTests: () => getChannelPromise(knownTestsCh),
|
|
1090
|
+
getTestManagementTests: () => getChannelPromise(testManagementTestsCh),
|
|
1091
|
+
})
|
|
1092
|
+
|
|
1093
|
+
if (shouldGetKnownTests) {
|
|
770
1094
|
try {
|
|
771
|
-
const { err, knownTests: receivedKnownTests } =
|
|
1095
|
+
const { err, knownTests: receivedKnownTests } =
|
|
1096
|
+
knownTestsResponse || await getChannelPromise(knownTestsCh)
|
|
772
1097
|
if (err) {
|
|
773
1098
|
isEarlyFlakeDetectionEnabled = false
|
|
774
1099
|
isKnownTestsEnabled = false
|
|
@@ -787,9 +1112,10 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
787
1112
|
}
|
|
788
1113
|
}
|
|
789
1114
|
|
|
790
|
-
if (
|
|
1115
|
+
if (shouldGetTestManagementTests) {
|
|
791
1116
|
try {
|
|
792
|
-
const { err, testManagementTests: receivedTestManagementTests } =
|
|
1117
|
+
const { err, testManagementTests: receivedTestManagementTests } =
|
|
1118
|
+
testManagementTestsResponse || await getChannelPromise(testManagementTestsCh)
|
|
793
1119
|
if (err) {
|
|
794
1120
|
isTestManagementTestsEnabled = false
|
|
795
1121
|
} else {
|
|
@@ -801,7 +1127,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
801
1127
|
}
|
|
802
1128
|
}
|
|
803
1129
|
|
|
804
|
-
if (isImpactedTestsEnabled &&
|
|
1130
|
+
if (isImpactedTestsEnabled && isTestOptimizationSupported) {
|
|
805
1131
|
try {
|
|
806
1132
|
const { err, modifiedFiles: receivedModifiedFiles } = await getChannelPromise(modifiedFilesCh)
|
|
807
1133
|
if (err) {
|
|
@@ -900,6 +1226,13 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
900
1226
|
remainingTestsByFile = {}
|
|
901
1227
|
quarantinedButNotAttemptToFixFqns = new Set()
|
|
902
1228
|
testsReportedInGenerateSummary = new Set()
|
|
1229
|
+
efdManagedTestKeys.clear()
|
|
1230
|
+
efdRetryCountByTestKey.clear()
|
|
1231
|
+
efdRetryCountRequestsByTestKey.clear()
|
|
1232
|
+
efdRetryTestsById.clear()
|
|
1233
|
+
efdScheduledOriginalTestKeys.clear()
|
|
1234
|
+
efdStartedOriginalTestKeys.clear()
|
|
1235
|
+
efdSlowAbortedTests.clear()
|
|
903
1236
|
|
|
904
1237
|
// TODO: we can trick playwright into thinking the session passed by returning
|
|
905
1238
|
// 'passed' here. We might be able to use this for both EFD and Test Management tests.
|
|
@@ -1001,12 +1334,29 @@ addHook({
|
|
|
1001
1334
|
* - we execute `applyRepeatEachIndex` for each of these cloned file suites
|
|
1002
1335
|
* - we add the cloned file suites to the project suite
|
|
1003
1336
|
*/
|
|
1004
|
-
function applyRetriesToTests (
|
|
1337
|
+
function applyRetriesToTests (
|
|
1338
|
+
fileSuitesWithTestsToRetry,
|
|
1339
|
+
filterTest,
|
|
1340
|
+
tagsToApply,
|
|
1341
|
+
numRetries,
|
|
1342
|
+
configureCopiedTest,
|
|
1343
|
+
getRetryRepeatEachIndex
|
|
1344
|
+
) {
|
|
1005
1345
|
for (const [fileSuite, projectSuite] of fileSuitesWithTestsToRetry.entries()) {
|
|
1006
1346
|
for (let repeatEachIndex = 1; repeatEachIndex <= numRetries; repeatEachIndex++) {
|
|
1007
|
-
const copyFileSuite = deepCloneSuite(fileSuite, filterTest, tagsToApply)
|
|
1008
|
-
|
|
1347
|
+
const copyFileSuite = deepCloneSuite(fileSuite, filterTest, tagsToApply, (copiedTest, originalTest) => {
|
|
1348
|
+
if (configureCopiedTest) {
|
|
1349
|
+
configureCopiedTest(copiedTest, originalTest, repeatEachIndex)
|
|
1350
|
+
}
|
|
1351
|
+
})
|
|
1352
|
+
const retryRepeatEachIndex = getRetryRepeatEachIndex
|
|
1353
|
+
? getRetryRepeatEachIndex(fileSuite, projectSuite, repeatEachIndex, numRetries)
|
|
1354
|
+
: repeatEachIndex + 1
|
|
1355
|
+
applyRepeatEachIndex(projectSuite._fullProject, copyFileSuite, retryRepeatEachIndex)
|
|
1009
1356
|
projectSuite._addSuite(copyFileSuite)
|
|
1357
|
+
for (const copiedTest of copyFileSuite.allTests()) {
|
|
1358
|
+
registerEfdRetryTest(copiedTest)
|
|
1359
|
+
}
|
|
1010
1360
|
}
|
|
1011
1361
|
}
|
|
1012
1362
|
}
|
|
@@ -1091,6 +1441,7 @@ addHook({
|
|
|
1091
1441
|
for (const impactedTest of impactedTests) {
|
|
1092
1442
|
impactedTest._ddIsModified = true
|
|
1093
1443
|
if (isEarlyFlakeDetectionEnabled && impactedTest.expectedStatus !== 'skipped') {
|
|
1444
|
+
markEfdManagedTest(impactedTest)
|
|
1094
1445
|
const fileSuite = getSuiteType(impactedTest, 'file')
|
|
1095
1446
|
if (!fileSuitesWithImpactedTestsToProjects.has(fileSuite)) {
|
|
1096
1447
|
fileSuitesWithImpactedTestsToProjects.set(fileSuite, getSuiteType(impactedTest, 'project'))
|
|
@@ -1106,7 +1457,12 @@ addHook({
|
|
|
1106
1457
|
'_ddIsEfdRetry',
|
|
1107
1458
|
(test) => (isKnownTestsEnabled && isNewTest(test) ? '_ddIsNew' : null),
|
|
1108
1459
|
],
|
|
1109
|
-
|
|
1460
|
+
getConfiguredEfdRetryCount(),
|
|
1461
|
+
(copiedTest, originalTest, retryIndex) => {
|
|
1462
|
+
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
1463
|
+
markEfdManagedTest(copiedTest)
|
|
1464
|
+
},
|
|
1465
|
+
getEfdRetryRepeatEachIndex
|
|
1110
1466
|
)
|
|
1111
1467
|
}
|
|
1112
1468
|
|
|
@@ -1130,6 +1486,7 @@ addHook({
|
|
|
1130
1486
|
if (isEarlyFlakeDetectionEnabled && newTest.expectedStatus !== 'skipped' && !newTest._ddIsModified) {
|
|
1131
1487
|
// Prevent ATR or `--retries` from retrying new tests if EFD is enabled
|
|
1132
1488
|
newTest.retries = 0
|
|
1489
|
+
markEfdManagedTest(newTest)
|
|
1133
1490
|
const fileSuite = getSuiteType(newTest, 'file')
|
|
1134
1491
|
if (!fileSuitesWithNewTestsToProjects.has(fileSuite)) {
|
|
1135
1492
|
fileSuitesWithNewTestsToProjects.set(fileSuite, getSuiteType(newTest, 'project'))
|
|
@@ -1141,7 +1498,12 @@ addHook({
|
|
|
1141
1498
|
fileSuitesWithNewTestsToProjects,
|
|
1142
1499
|
isNewTest,
|
|
1143
1500
|
['_ddIsNew', '_ddIsEfdRetry'],
|
|
1144
|
-
|
|
1501
|
+
getConfiguredEfdRetryCount(),
|
|
1502
|
+
(copiedTest, originalTest, retryIndex) => {
|
|
1503
|
+
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
1504
|
+
markEfdManagedTest(copiedTest)
|
|
1505
|
+
},
|
|
1506
|
+
getEfdRetryRepeatEachIndex
|
|
1145
1507
|
)
|
|
1146
1508
|
}
|
|
1147
1509
|
}
|
|
@@ -1177,6 +1539,10 @@ addHook({
|
|
|
1177
1539
|
|
|
1178
1540
|
// We add a new listener to `this.process`, which is represents the worker
|
|
1179
1541
|
this.process.on('message', (message) => {
|
|
1542
|
+
if (message?.type === EFD_RETRY_COUNT_REQUEST) {
|
|
1543
|
+
sendEfdRetryCountToWorkerWhenAvailable(this.process, message.testId)
|
|
1544
|
+
return
|
|
1545
|
+
}
|
|
1180
1546
|
// These messages are [code, payload]. The payload is test data
|
|
1181
1547
|
if (Array.isArray(message) && message[0] === PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE) {
|
|
1182
1548
|
workerReportCh.publish(message[1])
|
|
@@ -1235,9 +1601,15 @@ addHook({
|
|
|
1235
1601
|
const stepInfoByStepId = {}
|
|
1236
1602
|
|
|
1237
1603
|
shimmer.wrap(workerPackage.WorkerMain.prototype, '_runTest', _runTest => async function (test) {
|
|
1604
|
+
await waitForEfdRetryCount(test)
|
|
1605
|
+
if (shouldSkipEfdRetry(test)) {
|
|
1606
|
+
test._ddShouldSkipEfdRetry = true
|
|
1607
|
+
test.expectedStatus = 'skipped'
|
|
1608
|
+
}
|
|
1238
1609
|
if (test.expectedStatus === 'skipped') {
|
|
1239
1610
|
return _runTest.apply(this, arguments)
|
|
1240
1611
|
}
|
|
1612
|
+
test._ddStartTime = performance.now()
|
|
1241
1613
|
steps = []
|
|
1242
1614
|
|
|
1243
1615
|
const {
|
|
@@ -1320,6 +1692,21 @@ addHook({
|
|
|
1320
1692
|
await res
|
|
1321
1693
|
|
|
1322
1694
|
const { status, error, annotations, retry, testId } = testInfo
|
|
1695
|
+
const testEfdKey = getTestEfdKey(test)
|
|
1696
|
+
const isEfdManagedTest = isTestEfdManaged(test)
|
|
1697
|
+
if (isEfdManagedTest && !test._ddIsEfdRetry && !efdRetryCountByTestKey.has(testEfdKey)) {
|
|
1698
|
+
const duration = test.results?.at(-1)?.duration > 0
|
|
1699
|
+
? test.results.at(-1).duration
|
|
1700
|
+
: performance.now() - test._ddStartTime
|
|
1701
|
+
const retryCount = getEfdRetryCount(
|
|
1702
|
+
duration,
|
|
1703
|
+
getTestEfdSlowTestRetries(test)
|
|
1704
|
+
)
|
|
1705
|
+
setEfdRetryCountForTest(test, retryCount)
|
|
1706
|
+
if (retryCount === 0) {
|
|
1707
|
+
efdSlowAbortedTests.add(testEfdKey)
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1323
1710
|
|
|
1324
1711
|
// testInfo.errors could be better than "error",
|
|
1325
1712
|
// which will only include timeout error (even though the test failed because of a different error)
|
|
@@ -1365,6 +1752,7 @@ addHook({
|
|
|
1365
1752
|
isAttemptToFix: test._ddIsAttemptToFix,
|
|
1366
1753
|
hasFailedAllRetries: test._ddHasFailedAllRetries,
|
|
1367
1754
|
hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
|
|
1755
|
+
hasPassedAnyEfdAttempt: test._ddHasPassedAnyEfdAttempt,
|
|
1368
1756
|
testStatus: STATUS_TO_TEST_STATUS[status],
|
|
1369
1757
|
})
|
|
1370
1758
|
|
|
@@ -1388,6 +1776,7 @@ addHook({
|
|
|
1388
1776
|
isModified: test._ddIsModified,
|
|
1389
1777
|
onDone,
|
|
1390
1778
|
finalStatus,
|
|
1779
|
+
earlyFlakeAbortReason: test._ddEarlyFlakeAbortReason,
|
|
1391
1780
|
...testCtx.currentStore,
|
|
1392
1781
|
})
|
|
1393
1782
|
|