dd-trace 5.102.0 → 5.103.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/package.json +12 -11
- package/packages/datadog-esbuild/src/utils.js +2 -2
- package/packages/datadog-instrumentations/src/ai.js +1 -1
- 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 +1 -1
- 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 -12
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +1 -1
- 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 +41 -0
- package/packages/datadog-instrumentations/src/ioredis.js +16 -12
- package/packages/datadog-instrumentations/src/jest.js +351 -50
- package/packages/datadog-instrumentations/src/kafkajs.js +164 -173
- package/packages/datadog-instrumentations/src/mocha/main.js +73 -1
- package/packages/datadog-instrumentations/src/mongodb-core.js +33 -8
- package/packages/datadog-instrumentations/src/pg.js +24 -10
- package/packages/datadog-instrumentations/src/playwright.js +427 -55
- package/packages/datadog-instrumentations/src/redis.js +19 -10
- 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 +214 -22
- package/packages/datadog-plugin-cypress/src/support.js +13 -1
- 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 +29 -0
- 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-mocha/src/index.js +1 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +36 -70
- 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 +18 -23
- 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/iast/taint-tracking/rewriter.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
- 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/config/defaults.js +3 -14
- package/packages/dd-trace/src/config/generated-config-types.d.ts +3 -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 +51 -38
- package/packages/dd-trace/src/datastreams/checkpointer.js +2 -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-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/buffering-exporter.js +2 -1
- package/packages/dd-trace/src/exporters/common/request.js +1 -1
- 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/lambda/handler-paths.js +52 -0
- 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/writers/base.js +2 -1
- package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +2 -1
- 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/ci_plugin.js +49 -4
- 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/plugin.js +2 -4
- 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/stacktrace.js +2 -2
- package/packages/dd-trace/src/plugins/util/test.js +37 -5
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +17 -15
- 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/serverless.js +5 -2
- 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
|
@@ -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,6 +13,8 @@ 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,
|
|
@@ -68,6 +71,7 @@ let remainingTestsByFile = {}
|
|
|
68
71
|
let isKnownTestsEnabled = false
|
|
69
72
|
let isEarlyFlakeDetectionEnabled = false
|
|
70
73
|
let earlyFlakeDetectionNumRetries = 0
|
|
74
|
+
let earlyFlakeDetectionSlowTestRetries = {}
|
|
71
75
|
let isEarlyFlakeDetectionFaulty = false
|
|
72
76
|
let earlyFlakeDetectionFaultyThreshold = 0
|
|
73
77
|
let isFlakyTestRetriesEnabled = false
|
|
@@ -83,10 +87,19 @@ let testsReportedInGenerateSummary = new Set()
|
|
|
83
87
|
const newTestsWithDynamicNames = new Set()
|
|
84
88
|
const attemptToFixExecutions = new Map()
|
|
85
89
|
const loggedAttemptToFixTests = new Set()
|
|
90
|
+
const efdManagedTestKeys = new Set()
|
|
91
|
+
const efdRetryCountByTestKey = new Map()
|
|
92
|
+
const efdRetryCountRequestsByTestKey = new Map()
|
|
93
|
+
const efdRetryTestsById = new Map()
|
|
94
|
+
const efdScheduledOriginalTestKeys = new Set()
|
|
95
|
+
const efdStartedOriginalTestKeys = new Set()
|
|
96
|
+
const efdSlowAbortedTests = new Set()
|
|
86
97
|
let rootDir = ''
|
|
87
98
|
let sessionProjects = []
|
|
88
99
|
|
|
89
100
|
const MINIMUM_SUPPORTED_VERSION_RANGE_EFD = '>=1.38.0' // TODO: remove this once we drop support for v5
|
|
101
|
+
const EFD_RETRY_COUNT_REQUEST = 'ddEfdRetryCountRequest'
|
|
102
|
+
const EFD_RETRY_COUNT_RESPONSE = 'ddEfdRetryCountResponse'
|
|
90
103
|
|
|
91
104
|
function isValidKnownTests (receivedKnownTests) {
|
|
92
105
|
return !!receivedKnownTests.playwright
|
|
@@ -97,6 +110,222 @@ function getTestFullyQualifiedName (test) {
|
|
|
97
110
|
return `${test._requireFile} ${fullname}`
|
|
98
111
|
}
|
|
99
112
|
|
|
113
|
+
/**
|
|
114
|
+
* @param {object} test
|
|
115
|
+
* @returns {string|undefined}
|
|
116
|
+
*/
|
|
117
|
+
function getTestProjectKey (test) {
|
|
118
|
+
const { _projectIndex, _projectId } = test
|
|
119
|
+
if (_projectIndex !== undefined) {
|
|
120
|
+
return `index:${_projectIndex}`
|
|
121
|
+
}
|
|
122
|
+
if (_projectId !== undefined) {
|
|
123
|
+
return `id:${_projectId}`
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const projectSuite = getSuiteType(test, 'project')
|
|
127
|
+
const projectName = projectSuite?._fullProject?.project?.name ||
|
|
128
|
+
projectSuite?._fullProject?.name ||
|
|
129
|
+
projectSuite?.title
|
|
130
|
+
if (projectName) {
|
|
131
|
+
return `name:${projectName}`
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @param {object} test
|
|
137
|
+
* @returns {number|undefined}
|
|
138
|
+
*/
|
|
139
|
+
function getTestEfdRepeatEachIndex (test) {
|
|
140
|
+
if (Object.hasOwn(test, '_ddEfdOriginalRepeatEachIndex')) {
|
|
141
|
+
return test._ddEfdOriginalRepeatEachIndex
|
|
142
|
+
}
|
|
143
|
+
return test.repeatEachIndex
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* @param {object} test
|
|
148
|
+
* @returns {string|undefined}
|
|
149
|
+
*/
|
|
150
|
+
function getTestRepeatEachKey (test) {
|
|
151
|
+
const repeatEachIndex = getTestEfdRepeatEachIndex(test)
|
|
152
|
+
if (repeatEachIndex !== undefined) {
|
|
153
|
+
return `repeat:${repeatEachIndex}`
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* @param {object} test
|
|
159
|
+
* @returns {string}
|
|
160
|
+
*/
|
|
161
|
+
function getTestEfdKey (test) {
|
|
162
|
+
const projectKey = getTestProjectKey(test)
|
|
163
|
+
const repeatEachKey = getTestRepeatEachKey(test)
|
|
164
|
+
const testFqn = getTestFullyQualifiedName(test)
|
|
165
|
+
return [projectKey, repeatEachKey, testFqn].filter(Boolean).join(' ')
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function getConfiguredEfdRetryCount () {
|
|
169
|
+
if (!earlyFlakeDetectionSlowTestRetries || !Object.keys(earlyFlakeDetectionSlowTestRetries).length) {
|
|
170
|
+
return earlyFlakeDetectionNumRetries
|
|
171
|
+
}
|
|
172
|
+
return getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function markEfdManagedTest (test) {
|
|
176
|
+
test._ddIsEfdManagedTest = true
|
|
177
|
+
test._ddEfdSlowTestRetries = earlyFlakeDetectionSlowTestRetries
|
|
178
|
+
efdManagedTestKeys.add(getTestEfdKey(test))
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function markEfdRetryTest (test, retryIndex, originalTest) {
|
|
182
|
+
test._ddIsEfdRetry = true
|
|
183
|
+
test._ddEfdRetryIndex = retryIndex
|
|
184
|
+
if (originalTest) {
|
|
185
|
+
test._ddEfdOriginalRepeatEachIndex = getTestEfdRepeatEachIndex(originalTest)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function registerEfdRetryTest (test) {
|
|
190
|
+
if (!test._ddIsEfdRetry) {
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
efdRetryTestsById.set(test.id, {
|
|
195
|
+
retryIndex: test._ddEfdRetryIndex,
|
|
196
|
+
testEfdKey: getTestEfdKey(test),
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function getTestEfdSlowTestRetries (test) {
|
|
201
|
+
return test._ddEfdSlowTestRetries || earlyFlakeDetectionSlowTestRetries
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function isTestEfdManaged (test) {
|
|
205
|
+
return !!test._ddIsEfdManagedTest || (
|
|
206
|
+
(test._ddIsNew || test._ddIsModified) &&
|
|
207
|
+
!test._ddIsAttemptToFix &&
|
|
208
|
+
isEarlyFlakeDetectionEnabled
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function getFileSuiteRepeatEachIndex (fileSuite) {
|
|
213
|
+
const test = fileSuite.allTests()[0]
|
|
214
|
+
return test ? getTestEfdRepeatEachIndex(test) || 0 : 0
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function getEfdRetryRepeatEachIndex (fileSuite, projectSuite, retryIndex, retryCount) {
|
|
218
|
+
const nativeRepeatEach = projectSuite._fullProject?.project?.repeatEach || 1
|
|
219
|
+
const originalRepeatEachIndex = getFileSuiteRepeatEachIndex(fileSuite)
|
|
220
|
+
return nativeRepeatEach + (originalRepeatEachIndex * retryCount) + retryIndex - 1
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function getEfdRetryCountForTest (test) {
|
|
224
|
+
return efdRetryCountByTestKey.get(getTestEfdKey(test)) ?? getConfiguredEfdRetryCount()
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function setEfdRetryCountForTest (test, retryCount) {
|
|
228
|
+
const testEfdKey = getTestEfdKey(test)
|
|
229
|
+
efdRetryCountByTestKey.set(testEfdKey, retryCount)
|
|
230
|
+
|
|
231
|
+
const requests = efdRetryCountRequestsByTestKey.get(testEfdKey)
|
|
232
|
+
if (requests) {
|
|
233
|
+
efdRetryCountRequestsByTestKey.delete(testEfdKey)
|
|
234
|
+
for (const resolveRequest of requests) {
|
|
235
|
+
resolveRequest(retryCount)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function sendEfdRetryCountToWorker (workerProcess, testId, retryIndex, retryCount) {
|
|
241
|
+
workerProcess.send({
|
|
242
|
+
type: EFD_RETRY_COUNT_RESPONSE,
|
|
243
|
+
testId,
|
|
244
|
+
isEfdRetry: retryIndex !== undefined,
|
|
245
|
+
retryIndex,
|
|
246
|
+
retryCount,
|
|
247
|
+
})
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function sendEfdRetryCountToWorkerWhenAvailable (workerProcess, testId) {
|
|
251
|
+
const efdRetryTest = efdRetryTestsById.get(testId)
|
|
252
|
+
if (!efdRetryTest) {
|
|
253
|
+
sendEfdRetryCountToWorker(workerProcess, testId)
|
|
254
|
+
return
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const { retryIndex, testEfdKey } = efdRetryTest
|
|
258
|
+
|
|
259
|
+
if (!testEfdKey || !efdManagedTestKeys.has(testEfdKey)) {
|
|
260
|
+
sendEfdRetryCountToWorker(workerProcess, testId)
|
|
261
|
+
return
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const retryCount = efdRetryCountByTestKey.get(testEfdKey)
|
|
265
|
+
if (retryCount !== undefined) {
|
|
266
|
+
sendEfdRetryCountToWorker(workerProcess, testId, retryIndex, retryCount)
|
|
267
|
+
return
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (!efdStartedOriginalTestKeys.has(testEfdKey) && !efdScheduledOriginalTestKeys.has(testEfdKey)) {
|
|
271
|
+
sendEfdRetryCountToWorker(workerProcess, testId, retryIndex, 0)
|
|
272
|
+
return
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (!efdRetryCountRequestsByTestKey.has(testEfdKey)) {
|
|
276
|
+
efdRetryCountRequestsByTestKey.set(testEfdKey, [])
|
|
277
|
+
}
|
|
278
|
+
efdRetryCountRequestsByTestKey.get(testEfdKey).push((retryCount) => {
|
|
279
|
+
sendEfdRetryCountToWorker(workerProcess, testId, retryIndex, retryCount)
|
|
280
|
+
})
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* @param {object} test
|
|
285
|
+
* @returns {boolean}
|
|
286
|
+
*/
|
|
287
|
+
function shouldRequestEfdRetryCount (test) {
|
|
288
|
+
// The main process remains the source of truth. repeatEachIndex is only used as
|
|
289
|
+
// a cheap worker-side filter so first executions do not block on coordination.
|
|
290
|
+
return test._ddIsEfdRetry || test.repeatEachIndex > 0
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function waitForEfdRetryCount (test) {
|
|
294
|
+
if (!process.send || !shouldRequestEfdRetryCount(test)) {
|
|
295
|
+
return Promise.resolve()
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const testEfdKey = getTestEfdKey(test)
|
|
299
|
+
return new Promise(resolve => {
|
|
300
|
+
const messageHandler = (message) => {
|
|
301
|
+
if (message?.type === EFD_RETRY_COUNT_RESPONSE && message.testId === test.id) {
|
|
302
|
+
if (message.isEfdRetry) {
|
|
303
|
+
test._ddIsEfdRetry = true
|
|
304
|
+
test._ddEfdRetryIndex = message.retryIndex
|
|
305
|
+
test._ddEfdRetryCount = message.retryCount
|
|
306
|
+
efdRetryCountByTestKey.set(testEfdKey, message.retryCount)
|
|
307
|
+
}
|
|
308
|
+
process.removeListener('message', messageHandler)
|
|
309
|
+
resolve()
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
process.on('message', messageHandler)
|
|
314
|
+
process.send({
|
|
315
|
+
type: EFD_RETRY_COUNT_REQUEST,
|
|
316
|
+
testId: test.id,
|
|
317
|
+
})
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function shouldSkipEfdRetry (test) {
|
|
322
|
+
if (!test._ddIsEfdRetry) {
|
|
323
|
+
return false
|
|
324
|
+
}
|
|
325
|
+
const retryCount = test._ddEfdRetryCount ?? efdRetryCountByTestKey.get(getTestEfdKey(test))
|
|
326
|
+
return retryCount !== undefined && test._ddEfdRetryIndex > retryCount
|
|
327
|
+
}
|
|
328
|
+
|
|
100
329
|
function getTestProperties (test) {
|
|
101
330
|
const testName = getTestFullname(test)
|
|
102
331
|
const testSuite = getTestSuitePath(test._requireFile, rootDir)
|
|
@@ -125,14 +354,17 @@ function getSuiteType (test, type) {
|
|
|
125
354
|
}
|
|
126
355
|
|
|
127
356
|
// Copy of Suite#_deepClone but with a function to filter tests
|
|
128
|
-
function deepCloneSuite (suite, filterTest, tags = []) {
|
|
357
|
+
function deepCloneSuite (suite, filterTest, tags = [], configureCopiedTest) {
|
|
129
358
|
const copy = suite._clone()
|
|
130
359
|
for (const entry of suite._entries) {
|
|
131
360
|
if (entry.constructor.name === 'Suite') {
|
|
132
|
-
copy._addSuite(deepCloneSuite(entry, filterTest, tags))
|
|
361
|
+
copy._addSuite(deepCloneSuite(entry, filterTest, tags, configureCopiedTest))
|
|
133
362
|
} else {
|
|
134
363
|
if (filterTest(entry)) {
|
|
135
364
|
const copiedTest = entry._clone()
|
|
365
|
+
if (configureCopiedTest) {
|
|
366
|
+
configureCopiedTest(copiedTest, entry)
|
|
367
|
+
}
|
|
136
368
|
for (const tag of tags) {
|
|
137
369
|
const resolvedTag = typeof tag === 'function' ? tag(entry) : tag
|
|
138
370
|
|
|
@@ -303,6 +535,7 @@ function getFinalStatus ({
|
|
|
303
535
|
isAttemptToFix,
|
|
304
536
|
hasFailedAllRetries,
|
|
305
537
|
hasFailedAttemptToFixRetries,
|
|
538
|
+
hasPassedAnyEfdAttempt,
|
|
306
539
|
testStatus,
|
|
307
540
|
}) {
|
|
308
541
|
if (!isFinalExecution) {
|
|
@@ -311,9 +544,12 @@ function getFinalStatus ({
|
|
|
311
544
|
if (isDisabled || isQuarantined || testStatus === 'skip') {
|
|
312
545
|
return 'skip'
|
|
313
546
|
}
|
|
314
|
-
if (isAtrRetry
|
|
547
|
+
if (isAtrRetry) {
|
|
315
548
|
return hasFailedAllRetries ? 'fail' : 'pass'
|
|
316
549
|
}
|
|
550
|
+
if (isEfdManagedTest) {
|
|
551
|
+
return hasPassedAnyEfdAttempt ? 'pass' : 'fail'
|
|
552
|
+
}
|
|
317
553
|
if (isAttemptToFix) {
|
|
318
554
|
return hasFailedAttemptToFixRetries ? 'fail' : 'pass'
|
|
319
555
|
}
|
|
@@ -350,6 +586,14 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
350
586
|
if (_type === 'beforeAll' || _type === 'afterAll') {
|
|
351
587
|
return
|
|
352
588
|
}
|
|
589
|
+
if (shouldSkipEfdRetry(test)) {
|
|
590
|
+
test._ddShouldSkipEfdRetry = true
|
|
591
|
+
return
|
|
592
|
+
}
|
|
593
|
+
test._ddStartTime = performance.now()
|
|
594
|
+
if (isTestEfdManaged(test) && !test._ddIsEfdRetry) {
|
|
595
|
+
efdStartedOriginalTestKeys.add(getTestEfdKey(test))
|
|
596
|
+
}
|
|
353
597
|
// this means that a skipped test is being handled
|
|
354
598
|
if (!remainingTestsByFile[testSuiteAbsolutePath].length) {
|
|
355
599
|
return
|
|
@@ -391,6 +635,45 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
391
635
|
}
|
|
392
636
|
}
|
|
393
637
|
|
|
638
|
+
function finishTestSuiteIfDone (testSuiteAbsolutePath, projects) {
|
|
639
|
+
if (!shouldFinishTestSuite(testSuiteAbsolutePath)) {
|
|
640
|
+
return
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const skippedTests = remainingTestsByFile[testSuiteAbsolutePath]
|
|
644
|
+
.filter(test => test.expectedStatus === 'skipped')
|
|
645
|
+
|
|
646
|
+
for (const test of skippedTests) {
|
|
647
|
+
const browserName = getBrowserNameFromProjects(projects, test)
|
|
648
|
+
testSkipCh.publish({
|
|
649
|
+
testName: getTestFullname(test),
|
|
650
|
+
testSuiteAbsolutePath,
|
|
651
|
+
testSourceFileAbsolutePath: test.location.file,
|
|
652
|
+
testSourceLine: test.location.line,
|
|
653
|
+
browserName,
|
|
654
|
+
isNew: test._ddIsNew,
|
|
655
|
+
isDisabled: test._ddIsDisabled,
|
|
656
|
+
isModified: test._ddIsModified,
|
|
657
|
+
isQuarantined: test._ddIsQuarantined,
|
|
658
|
+
})
|
|
659
|
+
}
|
|
660
|
+
remainingTestsByFile[testSuiteAbsolutePath] = []
|
|
661
|
+
|
|
662
|
+
const testStatuses = testSuiteToTestStatuses.get(testSuiteAbsolutePath)
|
|
663
|
+
let testSuiteStatus = 'pass'
|
|
664
|
+
if (testStatuses?.includes('fail')) {
|
|
665
|
+
testSuiteStatus = 'fail'
|
|
666
|
+
} else if (testStatuses?.every(status => status === 'skip')) {
|
|
667
|
+
testSuiteStatus = 'skip'
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
const suiteError = getTestSuiteError(testSuiteAbsolutePath)
|
|
671
|
+
const testSuiteCtx = testSuiteToCtx.get(testSuiteAbsolutePath)
|
|
672
|
+
if (testSuiteCtx) {
|
|
673
|
+
testSuiteFinishCh.publish({ status: testSuiteStatus, error: suiteError, ...testSuiteCtx.currentStore })
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
394
677
|
function testEndHandler ({
|
|
395
678
|
test,
|
|
396
679
|
annotations,
|
|
@@ -420,11 +703,21 @@ function testEndHandler ({
|
|
|
420
703
|
return
|
|
421
704
|
}
|
|
422
705
|
|
|
706
|
+
if (test._ddShouldSkipEfdRetry || shouldSkipEfdRetry(test)) {
|
|
707
|
+
test._ddShouldSkipEfdRetry = true
|
|
708
|
+
remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath]
|
|
709
|
+
.filter(currentTest => currentTest !== test)
|
|
710
|
+
finishTestSuiteIfDone(testSuiteAbsolutePath, projects)
|
|
711
|
+
return
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
const isEfdManagedTest = isTestEfdManaged(test)
|
|
423
715
|
const testFqn = getTestFullyQualifiedName(test)
|
|
424
|
-
const
|
|
716
|
+
const testStatusKey = isEfdManagedTest ? getTestEfdKey(test) : testFqn
|
|
717
|
+
const testStatuses = testsToTestStatuses.get(testStatusKey) || []
|
|
425
718
|
|
|
426
719
|
if (testStatuses.length === 0) {
|
|
427
|
-
testsToTestStatuses.set(
|
|
720
|
+
testsToTestStatuses.set(testStatusKey, [testStatus])
|
|
428
721
|
if (test._ddIsNew && DYNAMIC_NAME_RE.test(getTestFullname(test))) {
|
|
429
722
|
newTestsWithDynamicNames.add(`${getTestSuitePath(test._requireFile, rootDir)} › ${getTestFullname(test)}`)
|
|
430
723
|
}
|
|
@@ -432,6 +725,17 @@ function testEndHandler ({
|
|
|
432
725
|
testStatuses.push(testStatus)
|
|
433
726
|
}
|
|
434
727
|
|
|
728
|
+
const testEfdKey = getTestEfdKey(test)
|
|
729
|
+
if (isEfdManagedTest && !test._ddIsEfdRetry && !efdRetryCountByTestKey.has(testEfdKey)) {
|
|
730
|
+
const testResult = results.at(-1)
|
|
731
|
+
const duration = testResult?.duration > 0 ? testResult.duration : performance.now() - test._ddStartTime
|
|
732
|
+
const retryCount = getEfdRetryCount(duration, getTestEfdSlowTestRetries(test))
|
|
733
|
+
setEfdRetryCountForTest(test, retryCount)
|
|
734
|
+
if (retryCount === 0) {
|
|
735
|
+
efdSlowAbortedTests.add(testEfdKey)
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
435
739
|
const testProperties = getTestProperties(test)
|
|
436
740
|
|
|
437
741
|
if (testProperties.attemptToFix) {
|
|
@@ -460,9 +764,10 @@ function testEndHandler ({
|
|
|
460
764
|
}
|
|
461
765
|
|
|
462
766
|
// Check if all EFD retries failed
|
|
463
|
-
|
|
767
|
+
const efdRetryCount = getEfdRetryCountForTest(test)
|
|
768
|
+
if (efdRetryCount > 0 && testStatuses.length === efdRetryCount + 1 &&
|
|
464
769
|
(test._ddIsNew || test._ddIsModified) &&
|
|
465
|
-
|
|
770
|
+
isEarlyFlakeDetectionEnabled &&
|
|
466
771
|
testStatuses.every(status => status === 'fail')) {
|
|
467
772
|
test._ddHasFailedAllRetries = true
|
|
468
773
|
}
|
|
@@ -480,9 +785,6 @@ function testEndHandler ({
|
|
|
480
785
|
if (shouldCreateTestSpan) {
|
|
481
786
|
const testResult = results.at(-1)
|
|
482
787
|
const testCtx = testToCtx.get(test)
|
|
483
|
-
const isEfdManagedTest = (test._ddIsNew || test._ddIsModified) &&
|
|
484
|
-
!test._ddIsAttemptToFix &&
|
|
485
|
-
isEarlyFlakeDetectionEnabled
|
|
486
788
|
const isAtrRetry = testResult?.retry > 0 &&
|
|
487
789
|
isFlakyTestRetriesEnabled &&
|
|
488
790
|
!test._ddIsAttemptToFix &&
|
|
@@ -497,6 +799,7 @@ function testEndHandler ({
|
|
|
497
799
|
isAttemptToFix: test._ddIsAttemptToFix,
|
|
498
800
|
hasFailedAllRetries: test._ddHasFailedAllRetries,
|
|
499
801
|
hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
|
|
802
|
+
hasPassedAnyEfdAttempt: testStatuses.includes('pass'),
|
|
500
803
|
testStatus,
|
|
501
804
|
})
|
|
502
805
|
|
|
@@ -520,6 +823,7 @@ function testEndHandler ({
|
|
|
520
823
|
isAtrRetry,
|
|
521
824
|
isModified: test._ddIsModified,
|
|
522
825
|
finalStatus,
|
|
826
|
+
earlyFlakeAbortReason: efdSlowAbortedTests.has(testEfdKey) ? 'slow' : undefined,
|
|
523
827
|
...testCtx.currentStore,
|
|
524
828
|
})
|
|
525
829
|
}
|
|
@@ -540,38 +844,7 @@ function testEndHandler ({
|
|
|
540
844
|
.filter(currentTest => currentTest !== test)
|
|
541
845
|
}
|
|
542
846
|
|
|
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
|
-
}
|
|
847
|
+
finishTestSuiteIfDone(testSuiteAbsolutePath, projects)
|
|
575
848
|
}
|
|
576
849
|
|
|
577
850
|
function dispatcherRunWrapper (run) {
|
|
@@ -581,6 +854,39 @@ function dispatcherRunWrapper (run) {
|
|
|
581
854
|
}
|
|
582
855
|
}
|
|
583
856
|
|
|
857
|
+
function deferEfdRetryGroups (testGroups) {
|
|
858
|
+
const groupsWithOriginalTests = []
|
|
859
|
+
const efdRetryOnlyGroups = []
|
|
860
|
+
|
|
861
|
+
for (const group of testGroups) {
|
|
862
|
+
const originalTests = []
|
|
863
|
+
const efdRetryTests = []
|
|
864
|
+
|
|
865
|
+
for (const test of group.tests) {
|
|
866
|
+
if (test._ddIsEfdRetry) {
|
|
867
|
+
efdRetryTests.push(test)
|
|
868
|
+
} else {
|
|
869
|
+
originalTests.push(test)
|
|
870
|
+
if (isTestEfdManaged(test)) {
|
|
871
|
+
efdScheduledOriginalTestKeys.add(getTestEfdKey(test))
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
if (efdRetryTests.length && originalTests.length) {
|
|
877
|
+
group.tests = [...originalTests, ...efdRetryTests]
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
if (originalTests.length) {
|
|
881
|
+
groupsWithOriginalTests.push(group)
|
|
882
|
+
} else {
|
|
883
|
+
efdRetryOnlyGroups.push(group)
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
return [...groupsWithOriginalTests, ...efdRetryOnlyGroups]
|
|
888
|
+
}
|
|
889
|
+
|
|
584
890
|
function dispatcherRunWrapperNew (run) {
|
|
585
891
|
return function (testGroups) {
|
|
586
892
|
// Filter out disabled tests from testGroups before they get scheduled,
|
|
@@ -593,12 +899,17 @@ function dispatcherRunWrapperNew (run) {
|
|
|
593
899
|
testGroups = testGroups.filter(group => group.tests.length > 0)
|
|
594
900
|
}
|
|
595
901
|
|
|
902
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
903
|
+
testGroups = deferEfdRetryGroups(testGroups)
|
|
904
|
+
}
|
|
905
|
+
|
|
596
906
|
if (!this._allTests) {
|
|
597
907
|
// Removed in https://github.com/microsoft/playwright/commit/1e52c37b254a441cccf332520f60225a5acc14c7
|
|
598
908
|
// Not available from >=1.44.0
|
|
599
909
|
this._ddAllTests = testGroups.flatMap(g => g.tests)
|
|
600
910
|
}
|
|
601
911
|
remainingTestsByFile = getTestsBySuiteFromTestGroups(testGroups)
|
|
912
|
+
arguments[0] = testGroups
|
|
602
913
|
return run.apply(this, arguments)
|
|
603
914
|
}
|
|
604
915
|
}
|
|
@@ -638,7 +949,6 @@ function dispatcherHook (dispatcherExport) {
|
|
|
638
949
|
)
|
|
639
950
|
}
|
|
640
951
|
})
|
|
641
|
-
|
|
642
952
|
return worker
|
|
643
953
|
})
|
|
644
954
|
return dispatcherExport
|
|
@@ -690,14 +1000,11 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
690
1000
|
// above) and mark the execution final once the count reaches the expected total.
|
|
691
1001
|
// This mirrors how ATF finality is detected and centralizes the decision in the
|
|
692
1002
|
// main process, so workers only need to act on the _ddIsFinalExecution flag.
|
|
693
|
-
const isEfdManagedTest = (test
|
|
694
|
-
!test._ddIsAttemptToFix &&
|
|
695
|
-
isEarlyFlakeDetectionEnabled
|
|
1003
|
+
const isEfdManagedTest = isTestEfdManaged(test)
|
|
696
1004
|
let isFinalExecution
|
|
697
1005
|
if (isEfdManagedTest) {
|
|
698
|
-
const
|
|
699
|
-
|
|
700
|
-
isFinalExecution = efdTestStatuses.length === earlyFlakeDetectionNumRetries + 1
|
|
1006
|
+
const efdTestStatuses = testsToTestStatuses.get(getTestEfdKey(test)) || []
|
|
1007
|
+
isFinalExecution = efdTestStatuses.length === getEfdRetryCountForTest(test) + 1
|
|
701
1008
|
} else if (test._ddIsAttemptToFix) {
|
|
702
1009
|
isFinalExecution = !!(test._ddHasPassedAttemptToFixRetries || test._ddHasFailedAttemptToFixRetries)
|
|
703
1010
|
} else {
|
|
@@ -722,10 +1029,11 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
722
1029
|
_ddIsModified: test._ddIsModified,
|
|
723
1030
|
_ddIsFinalExecution: isFinalExecution,
|
|
724
1031
|
_ddIsEfdManagedTest: isEfdManagedTest,
|
|
1032
|
+
_ddEarlyFlakeAbortReason: efdSlowAbortedTests.has(getTestEfdKey(test)) ? 'slow' : undefined,
|
|
1033
|
+
_ddHasPassedAnyEfdAttempt: (testsToTestStatuses.get(getTestEfdKey(test)) || []).includes('pass'),
|
|
725
1034
|
},
|
|
726
1035
|
})
|
|
727
1036
|
})
|
|
728
|
-
|
|
729
1037
|
return worker
|
|
730
1038
|
})
|
|
731
1039
|
return dispatcherExport
|
|
@@ -751,6 +1059,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
751
1059
|
isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
|
|
752
1060
|
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
|
|
753
1061
|
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
|
|
1062
|
+
earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
|
|
754
1063
|
earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
|
|
755
1064
|
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
756
1065
|
flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
|
|
@@ -900,6 +1209,13 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
900
1209
|
remainingTestsByFile = {}
|
|
901
1210
|
quarantinedButNotAttemptToFixFqns = new Set()
|
|
902
1211
|
testsReportedInGenerateSummary = new Set()
|
|
1212
|
+
efdManagedTestKeys.clear()
|
|
1213
|
+
efdRetryCountByTestKey.clear()
|
|
1214
|
+
efdRetryCountRequestsByTestKey.clear()
|
|
1215
|
+
efdRetryTestsById.clear()
|
|
1216
|
+
efdScheduledOriginalTestKeys.clear()
|
|
1217
|
+
efdStartedOriginalTestKeys.clear()
|
|
1218
|
+
efdSlowAbortedTests.clear()
|
|
903
1219
|
|
|
904
1220
|
// TODO: we can trick playwright into thinking the session passed by returning
|
|
905
1221
|
// 'passed' here. We might be able to use this for both EFD and Test Management tests.
|
|
@@ -1001,12 +1317,29 @@ addHook({
|
|
|
1001
1317
|
* - we execute `applyRepeatEachIndex` for each of these cloned file suites
|
|
1002
1318
|
* - we add the cloned file suites to the project suite
|
|
1003
1319
|
*/
|
|
1004
|
-
function applyRetriesToTests (
|
|
1320
|
+
function applyRetriesToTests (
|
|
1321
|
+
fileSuitesWithTestsToRetry,
|
|
1322
|
+
filterTest,
|
|
1323
|
+
tagsToApply,
|
|
1324
|
+
numRetries,
|
|
1325
|
+
configureCopiedTest,
|
|
1326
|
+
getRetryRepeatEachIndex
|
|
1327
|
+
) {
|
|
1005
1328
|
for (const [fileSuite, projectSuite] of fileSuitesWithTestsToRetry.entries()) {
|
|
1006
1329
|
for (let repeatEachIndex = 1; repeatEachIndex <= numRetries; repeatEachIndex++) {
|
|
1007
|
-
const copyFileSuite = deepCloneSuite(fileSuite, filterTest, tagsToApply)
|
|
1008
|
-
|
|
1330
|
+
const copyFileSuite = deepCloneSuite(fileSuite, filterTest, tagsToApply, (copiedTest, originalTest) => {
|
|
1331
|
+
if (configureCopiedTest) {
|
|
1332
|
+
configureCopiedTest(copiedTest, originalTest, repeatEachIndex)
|
|
1333
|
+
}
|
|
1334
|
+
})
|
|
1335
|
+
const retryRepeatEachIndex = getRetryRepeatEachIndex
|
|
1336
|
+
? getRetryRepeatEachIndex(fileSuite, projectSuite, repeatEachIndex, numRetries)
|
|
1337
|
+
: repeatEachIndex + 1
|
|
1338
|
+
applyRepeatEachIndex(projectSuite._fullProject, copyFileSuite, retryRepeatEachIndex)
|
|
1009
1339
|
projectSuite._addSuite(copyFileSuite)
|
|
1340
|
+
for (const copiedTest of copyFileSuite.allTests()) {
|
|
1341
|
+
registerEfdRetryTest(copiedTest)
|
|
1342
|
+
}
|
|
1010
1343
|
}
|
|
1011
1344
|
}
|
|
1012
1345
|
}
|
|
@@ -1091,6 +1424,7 @@ addHook({
|
|
|
1091
1424
|
for (const impactedTest of impactedTests) {
|
|
1092
1425
|
impactedTest._ddIsModified = true
|
|
1093
1426
|
if (isEarlyFlakeDetectionEnabled && impactedTest.expectedStatus !== 'skipped') {
|
|
1427
|
+
markEfdManagedTest(impactedTest)
|
|
1094
1428
|
const fileSuite = getSuiteType(impactedTest, 'file')
|
|
1095
1429
|
if (!fileSuitesWithImpactedTestsToProjects.has(fileSuite)) {
|
|
1096
1430
|
fileSuitesWithImpactedTestsToProjects.set(fileSuite, getSuiteType(impactedTest, 'project'))
|
|
@@ -1106,7 +1440,12 @@ addHook({
|
|
|
1106
1440
|
'_ddIsEfdRetry',
|
|
1107
1441
|
(test) => (isKnownTestsEnabled && isNewTest(test) ? '_ddIsNew' : null),
|
|
1108
1442
|
],
|
|
1109
|
-
|
|
1443
|
+
getConfiguredEfdRetryCount(),
|
|
1444
|
+
(copiedTest, originalTest, retryIndex) => {
|
|
1445
|
+
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
1446
|
+
markEfdManagedTest(copiedTest)
|
|
1447
|
+
},
|
|
1448
|
+
getEfdRetryRepeatEachIndex
|
|
1110
1449
|
)
|
|
1111
1450
|
}
|
|
1112
1451
|
|
|
@@ -1130,6 +1469,7 @@ addHook({
|
|
|
1130
1469
|
if (isEarlyFlakeDetectionEnabled && newTest.expectedStatus !== 'skipped' && !newTest._ddIsModified) {
|
|
1131
1470
|
// Prevent ATR or `--retries` from retrying new tests if EFD is enabled
|
|
1132
1471
|
newTest.retries = 0
|
|
1472
|
+
markEfdManagedTest(newTest)
|
|
1133
1473
|
const fileSuite = getSuiteType(newTest, 'file')
|
|
1134
1474
|
if (!fileSuitesWithNewTestsToProjects.has(fileSuite)) {
|
|
1135
1475
|
fileSuitesWithNewTestsToProjects.set(fileSuite, getSuiteType(newTest, 'project'))
|
|
@@ -1141,7 +1481,12 @@ addHook({
|
|
|
1141
1481
|
fileSuitesWithNewTestsToProjects,
|
|
1142
1482
|
isNewTest,
|
|
1143
1483
|
['_ddIsNew', '_ddIsEfdRetry'],
|
|
1144
|
-
|
|
1484
|
+
getConfiguredEfdRetryCount(),
|
|
1485
|
+
(copiedTest, originalTest, retryIndex) => {
|
|
1486
|
+
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
1487
|
+
markEfdManagedTest(copiedTest)
|
|
1488
|
+
},
|
|
1489
|
+
getEfdRetryRepeatEachIndex
|
|
1145
1490
|
)
|
|
1146
1491
|
}
|
|
1147
1492
|
}
|
|
@@ -1177,6 +1522,10 @@ addHook({
|
|
|
1177
1522
|
|
|
1178
1523
|
// We add a new listener to `this.process`, which is represents the worker
|
|
1179
1524
|
this.process.on('message', (message) => {
|
|
1525
|
+
if (message?.type === EFD_RETRY_COUNT_REQUEST) {
|
|
1526
|
+
sendEfdRetryCountToWorkerWhenAvailable(this.process, message.testId)
|
|
1527
|
+
return
|
|
1528
|
+
}
|
|
1180
1529
|
// These messages are [code, payload]. The payload is test data
|
|
1181
1530
|
if (Array.isArray(message) && message[0] === PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE) {
|
|
1182
1531
|
workerReportCh.publish(message[1])
|
|
@@ -1235,9 +1584,15 @@ addHook({
|
|
|
1235
1584
|
const stepInfoByStepId = {}
|
|
1236
1585
|
|
|
1237
1586
|
shimmer.wrap(workerPackage.WorkerMain.prototype, '_runTest', _runTest => async function (test) {
|
|
1587
|
+
await waitForEfdRetryCount(test)
|
|
1588
|
+
if (shouldSkipEfdRetry(test)) {
|
|
1589
|
+
test._ddShouldSkipEfdRetry = true
|
|
1590
|
+
test.expectedStatus = 'skipped'
|
|
1591
|
+
}
|
|
1238
1592
|
if (test.expectedStatus === 'skipped') {
|
|
1239
1593
|
return _runTest.apply(this, arguments)
|
|
1240
1594
|
}
|
|
1595
|
+
test._ddStartTime = performance.now()
|
|
1241
1596
|
steps = []
|
|
1242
1597
|
|
|
1243
1598
|
const {
|
|
@@ -1320,6 +1675,21 @@ addHook({
|
|
|
1320
1675
|
await res
|
|
1321
1676
|
|
|
1322
1677
|
const { status, error, annotations, retry, testId } = testInfo
|
|
1678
|
+
const testEfdKey = getTestEfdKey(test)
|
|
1679
|
+
const isEfdManagedTest = isTestEfdManaged(test)
|
|
1680
|
+
if (isEfdManagedTest && !test._ddIsEfdRetry && !efdRetryCountByTestKey.has(testEfdKey)) {
|
|
1681
|
+
const duration = test.results?.at(-1)?.duration > 0
|
|
1682
|
+
? test.results.at(-1).duration
|
|
1683
|
+
: performance.now() - test._ddStartTime
|
|
1684
|
+
const retryCount = getEfdRetryCount(
|
|
1685
|
+
duration,
|
|
1686
|
+
getTestEfdSlowTestRetries(test)
|
|
1687
|
+
)
|
|
1688
|
+
setEfdRetryCountForTest(test, retryCount)
|
|
1689
|
+
if (retryCount === 0) {
|
|
1690
|
+
efdSlowAbortedTests.add(testEfdKey)
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1323
1693
|
|
|
1324
1694
|
// testInfo.errors could be better than "error",
|
|
1325
1695
|
// which will only include timeout error (even though the test failed because of a different error)
|
|
@@ -1365,6 +1735,7 @@ addHook({
|
|
|
1365
1735
|
isAttemptToFix: test._ddIsAttemptToFix,
|
|
1366
1736
|
hasFailedAllRetries: test._ddHasFailedAllRetries,
|
|
1367
1737
|
hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
|
|
1738
|
+
hasPassedAnyEfdAttempt: test._ddHasPassedAnyEfdAttempt,
|
|
1368
1739
|
testStatus: STATUS_TO_TEST_STATUS[status],
|
|
1369
1740
|
})
|
|
1370
1741
|
|
|
@@ -1388,6 +1759,7 @@ addHook({
|
|
|
1388
1759
|
isModified: test._ddIsModified,
|
|
1389
1760
|
onDone,
|
|
1390
1761
|
finalStatus,
|
|
1762
|
+
earlyFlakeAbortReason: test._ddEarlyFlakeAbortReason,
|
|
1391
1763
|
...testCtx.currentStore,
|
|
1392
1764
|
})
|
|
1393
1765
|
|