dd-trace 5.101.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 +20 -17
- package/packages/datadog-esbuild/src/utils.js +2 -2
- package/packages/datadog-instrumentations/src/aerospike.js +2 -2
- package/packages/datadog-instrumentations/src/ai.js +9 -9
- package/packages/datadog-instrumentations/src/amqplib.js +6 -7
- package/packages/datadog-instrumentations/src/anthropic.js +10 -10
- package/packages/datadog-instrumentations/src/apollo-server-core.js +3 -3
- package/packages/datadog-instrumentations/src/apollo-server.js +5 -5
- package/packages/datadog-instrumentations/src/avsc.js +6 -6
- package/packages/datadog-instrumentations/src/aws-sdk.js +151 -67
- package/packages/datadog-instrumentations/src/azure-durable-functions.js +8 -8
- package/packages/datadog-instrumentations/src/bluebird.js +2 -2
- package/packages/datadog-instrumentations/src/body-parser.js +2 -2
- package/packages/datadog-instrumentations/src/cassandra-driver.js +7 -7
- package/packages/datadog-instrumentations/src/child_process.js +12 -12
- package/packages/datadog-instrumentations/src/confluentinc-kafka-javascript.js +41 -24
- package/packages/datadog-instrumentations/src/connect.js +7 -7
- package/packages/datadog-instrumentations/src/cookie-parser.js +4 -4
- package/packages/datadog-instrumentations/src/cookie.js +2 -2
- package/packages/datadog-instrumentations/src/couchbase.js +73 -238
- package/packages/datadog-instrumentations/src/crypto.js +4 -4
- package/packages/datadog-instrumentations/src/cucumber.js +78 -17
- package/packages/datadog-instrumentations/src/dns.js +0 -3
- package/packages/datadog-instrumentations/src/elasticsearch.js +8 -11
- package/packages/datadog-instrumentations/src/electron/preload.js +42 -0
- package/packages/datadog-instrumentations/src/electron.js +240 -0
- package/packages/datadog-instrumentations/src/express-mongo-sanitize.js +6 -6
- package/packages/datadog-instrumentations/src/express-session.js +4 -4
- package/packages/datadog-instrumentations/src/express.js +10 -11
- package/packages/datadog-instrumentations/src/fastify.js +2 -2
- package/packages/datadog-instrumentations/src/fetch.js +5 -5
- package/packages/datadog-instrumentations/src/fs.js +14 -14
- package/packages/datadog-instrumentations/src/google-cloud-pubsub.js +5 -7
- package/packages/datadog-instrumentations/src/google-genai.js +4 -4
- package/packages/datadog-instrumentations/src/graphql.js +13 -12
- package/packages/datadog-instrumentations/src/grpc/server.js +2 -2
- package/packages/datadog-instrumentations/src/hapi.js +2 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +9 -9
- 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/helpers/promise.js +2 -2
- package/packages/datadog-instrumentations/src/hono.js +2 -2
- package/packages/datadog-instrumentations/src/http/client.js +6 -6
- package/packages/datadog-instrumentations/src/http/server.js +9 -9
- package/packages/datadog-instrumentations/src/ioredis.js +16 -12
- package/packages/datadog-instrumentations/src/jest.js +382 -81
- package/packages/datadog-instrumentations/src/kafkajs.js +165 -174
- package/packages/datadog-instrumentations/src/knex.js +17 -17
- package/packages/datadog-instrumentations/src/koa.js +12 -12
- package/packages/datadog-instrumentations/src/ldapjs.js +5 -5
- package/packages/datadog-instrumentations/src/light-my-request.js +2 -2
- package/packages/datadog-instrumentations/src/limitd-client.js +4 -4
- package/packages/datadog-instrumentations/src/lodash.js +4 -4
- package/packages/datadog-instrumentations/src/mariadb.js +13 -13
- package/packages/datadog-instrumentations/src/memcached.js +2 -2
- package/packages/datadog-instrumentations/src/microgateway-core.js +2 -2
- package/packages/datadog-instrumentations/src/mocha/common.js +3 -3
- package/packages/datadog-instrumentations/src/mocha/main.js +85 -11
- package/packages/datadog-instrumentations/src/mocha/utils.js +133 -16
- package/packages/datadog-instrumentations/src/mocha/worker.js +7 -5
- package/packages/datadog-instrumentations/src/mongodb-core.js +42 -30
- package/packages/datadog-instrumentations/src/mongodb.js +5 -5
- package/packages/datadog-instrumentations/src/mongoose.js +21 -21
- package/packages/datadog-instrumentations/src/mquery.js +5 -5
- package/packages/datadog-instrumentations/src/multer.js +4 -4
- package/packages/datadog-instrumentations/src/mysql.js +16 -16
- package/packages/datadog-instrumentations/src/mysql2.js +4 -4
- package/packages/datadog-instrumentations/src/net.js +14 -8
- package/packages/datadog-instrumentations/src/nyc.js +5 -5
- package/packages/datadog-instrumentations/src/openai.js +19 -19
- package/packages/datadog-instrumentations/src/oracledb.js +6 -6
- package/packages/datadog-instrumentations/src/passport-utils.js +5 -5
- package/packages/datadog-instrumentations/src/pg.js +39 -25
- package/packages/datadog-instrumentations/src/pino.js +6 -10
- package/packages/datadog-instrumentations/src/playwright.js +445 -68
- package/packages/datadog-instrumentations/src/protobufjs.js +16 -16
- package/packages/datadog-instrumentations/src/redis.js +20 -12
- package/packages/datadog-instrumentations/src/restify.js +2 -2
- package/packages/datadog-instrumentations/src/router.js +12 -12
- package/packages/datadog-instrumentations/src/stripe.js +12 -12
- package/packages/datadog-instrumentations/src/vitest.js +107 -26
- package/packages/datadog-instrumentations/src/winston.js +4 -4
- package/packages/datadog-instrumentations/src/ws.js +7 -7
- package/packages/datadog-plugin-apollo/src/gateway/request.js +1 -21
- package/packages/datadog-plugin-aws-sdk/src/base.js +70 -28
- package/packages/datadog-plugin-aws-sdk/src/services/cloudwatchlogs.js +1 -1
- package/packages/datadog-plugin-aws-sdk/src/services/eventbridge.js +20 -13
- package/packages/datadog-plugin-aws-sdk/src/services/kinesis.js +46 -36
- package/packages/datadog-plugin-aws-sdk/src/services/lambda.js +34 -23
- 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 +14 -15
- package/packages/datadog-plugin-aws-sdk/src/services/sqs.js +74 -55
- package/packages/datadog-plugin-aws-sdk/src/services/stepfunctions.js +20 -18
- package/packages/datadog-plugin-aws-sdk/src/util.js +22 -0
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +6 -6
- package/packages/datadog-plugin-couchbase/src/index.js +58 -52
- package/packages/datadog-plugin-cucumber/src/index.js +5 -0
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +215 -26
- 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/consumer.js +1 -5
- package/packages/datadog-plugin-google-cloud-pubsub/src/producer.js +27 -18
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +3 -1
- 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 +3 -7
- package/packages/datadog-plugin-jest/src/index.js +92 -50
- package/packages/datadog-plugin-jest/src/util.js +1 -2
- package/packages/datadog-plugin-mocha/src/index.js +5 -0
- package/packages/datadog-plugin-mongodb-core/src/index.js +36 -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-openai/src/tracing.js +12 -23
- package/packages/datadog-plugin-pg/src/index.js +3 -3
- package/packages/datadog-plugin-playwright/src/index.js +5 -1
- package/packages/datadog-plugin-redis/src/index.js +18 -23
- package/packages/datadog-plugin-vitest/src/index.js +8 -1
- package/packages/datadog-shimmer/src/shimmer.js +7 -1
- 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/analyzers/hardcoded-password-rules.js +1 -1
- package/packages/dd-trace/src/appsec/iast/analyzers/hardcoded-secret-rules.js +81 -81
- package/packages/dd-trace/src/appsec/iast/security-controls/index.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugins/kafka.js +2 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/rewriter.js +4 -4
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-handler.js +2 -0
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/index.js +1 -3
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +83 -48
- package/packages/dd-trace/src/appsec/iast/vulnerability-reporter.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +21 -24
- package/packages/dd-trace/src/appsec/reporter.js +3 -1
- package/packages/dd-trace/src/appsec/rule_manager.js +4 -2
- package/packages/dd-trace/src/appsec/waf/waf_context_wrapper.js +31 -16
- 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/git_properties.js +2 -2
- 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/index.js +2 -1
- package/packages/dd-trace/src/datastreams/manager.js +1 -1
- package/packages/dd-trace/src/datastreams/processor.js +3 -4
- package/packages/dd-trace/src/debugger/devtools_client/snapshot/collector.js +2 -2
- package/packages/dd-trace/src/debugger/devtools_client/snapshot-pruner.js +1 -0
- 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 +748 -232
- package/packages/dd-trace/src/encode/0.5.js +47 -10
- 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/ai/util.js +1 -2
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +45 -15
- package/packages/dd-trace/src/llmobs/plugins/genai/util.js +6 -3
- package/packages/dd-trace/src/llmobs/sdk.js +24 -26
- package/packages/dd-trace/src/llmobs/span_processor.js +25 -5
- package/packages/dd-trace/src/llmobs/util.js +1 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +2 -1
- package/packages/dd-trace/src/msgpack/chunk.js +6 -3
- package/packages/dd-trace/src/openfeature/noop.js +40 -36
- package/packages/dd-trace/src/openfeature/writers/base.js +2 -1
- package/packages/dd-trace/src/openfeature/writers/exposures.js +33 -52
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +2 -1
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +1 -2
- package/packages/dd-trace/src/opentelemetry/tracer.js +0 -22
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +20 -9
- package/packages/dd-trace/src/opentracing/propagation/text_map_dsm.js +2 -11
- 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 +9 -9
- package/packages/dd-trace/src/plugins/util/git-cache.js +23 -23
- package/packages/dd-trace/src/plugins/util/stacktrace.js +2 -2
- package/packages/dd-trace/src/plugins/util/test.js +56 -12
- package/packages/dd-trace/src/plugins/util/url.js +1 -3
- package/packages/dd-trace/src/plugins/util/user-provided-git.js +18 -16
- package/packages/dd-trace/src/plugins/util/web.js +5 -7
- 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/events.js +3 -23
- package/packages/dd-trace/src/profiling/profilers/wall.js +5 -6
- 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/index.js +2 -2
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +5 -8
- package/packages/dd-trace/src/scope.js +3 -10
- package/packages/dd-trace/src/serverless.js +6 -6
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +27 -1
- package/packages/dd-trace/src/service-naming/schemas/v0/web.js +4 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +24 -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/tracer.js +7 -7
- 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
|
|
@@ -79,13 +83,23 @@ let testManagementTests = {}
|
|
|
79
83
|
let isImpactedTestsEnabled = false
|
|
80
84
|
let modifiedFiles = {}
|
|
81
85
|
let quarantinedButNotAttemptToFixFqns = new Set()
|
|
86
|
+
let testsReportedInGenerateSummary = new Set()
|
|
82
87
|
const newTestsWithDynamicNames = new Set()
|
|
83
88
|
const attemptToFixExecutions = new Map()
|
|
84
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()
|
|
85
97
|
let rootDir = ''
|
|
86
98
|
let sessionProjects = []
|
|
87
99
|
|
|
88
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'
|
|
89
103
|
|
|
90
104
|
function isValidKnownTests (receivedKnownTests) {
|
|
91
105
|
return !!receivedKnownTests.playwright
|
|
@@ -96,6 +110,222 @@ function getTestFullyQualifiedName (test) {
|
|
|
96
110
|
return `${test._requireFile} ${fullname}`
|
|
97
111
|
}
|
|
98
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
|
+
|
|
99
329
|
function getTestProperties (test) {
|
|
100
330
|
const testName = getTestFullname(test)
|
|
101
331
|
const testSuite = getTestSuitePath(test._requireFile, rootDir)
|
|
@@ -124,14 +354,17 @@ function getSuiteType (test, type) {
|
|
|
124
354
|
}
|
|
125
355
|
|
|
126
356
|
// Copy of Suite#_deepClone but with a function to filter tests
|
|
127
|
-
function deepCloneSuite (suite, filterTest, tags = []) {
|
|
357
|
+
function deepCloneSuite (suite, filterTest, tags = [], configureCopiedTest) {
|
|
128
358
|
const copy = suite._clone()
|
|
129
359
|
for (const entry of suite._entries) {
|
|
130
360
|
if (entry.constructor.name === 'Suite') {
|
|
131
|
-
copy._addSuite(deepCloneSuite(entry, filterTest, tags))
|
|
361
|
+
copy._addSuite(deepCloneSuite(entry, filterTest, tags, configureCopiedTest))
|
|
132
362
|
} else {
|
|
133
363
|
if (filterTest(entry)) {
|
|
134
364
|
const copiedTest = entry._clone()
|
|
365
|
+
if (configureCopiedTest) {
|
|
366
|
+
configureCopiedTest(copiedTest, entry)
|
|
367
|
+
}
|
|
135
368
|
for (const tag of tags) {
|
|
136
369
|
const resolvedTag = typeof tag === 'function' ? tag(entry) : tag
|
|
137
370
|
|
|
@@ -302,6 +535,7 @@ function getFinalStatus ({
|
|
|
302
535
|
isAttemptToFix,
|
|
303
536
|
hasFailedAllRetries,
|
|
304
537
|
hasFailedAttemptToFixRetries,
|
|
538
|
+
hasPassedAnyEfdAttempt,
|
|
305
539
|
testStatus,
|
|
306
540
|
}) {
|
|
307
541
|
if (!isFinalExecution) {
|
|
@@ -310,9 +544,12 @@ function getFinalStatus ({
|
|
|
310
544
|
if (isDisabled || isQuarantined || testStatus === 'skip') {
|
|
311
545
|
return 'skip'
|
|
312
546
|
}
|
|
313
|
-
if (isAtrRetry
|
|
547
|
+
if (isAtrRetry) {
|
|
314
548
|
return hasFailedAllRetries ? 'fail' : 'pass'
|
|
315
549
|
}
|
|
550
|
+
if (isEfdManagedTest) {
|
|
551
|
+
return hasPassedAnyEfdAttempt ? 'pass' : 'fail'
|
|
552
|
+
}
|
|
316
553
|
if (isAttemptToFix) {
|
|
317
554
|
return hasFailedAttemptToFixRetries ? 'fail' : 'pass'
|
|
318
555
|
}
|
|
@@ -349,6 +586,14 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
349
586
|
if (_type === 'beforeAll' || _type === 'afterAll') {
|
|
350
587
|
return
|
|
351
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
|
+
}
|
|
352
597
|
// this means that a skipped test is being handled
|
|
353
598
|
if (!remainingTestsByFile[testSuiteAbsolutePath].length) {
|
|
354
599
|
return
|
|
@@ -390,6 +635,45 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
390
635
|
}
|
|
391
636
|
}
|
|
392
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
|
+
|
|
393
677
|
function testEndHandler ({
|
|
394
678
|
test,
|
|
395
679
|
annotations,
|
|
@@ -419,11 +703,21 @@ function testEndHandler ({
|
|
|
419
703
|
return
|
|
420
704
|
}
|
|
421
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)
|
|
422
715
|
const testFqn = getTestFullyQualifiedName(test)
|
|
423
|
-
const
|
|
716
|
+
const testStatusKey = isEfdManagedTest ? getTestEfdKey(test) : testFqn
|
|
717
|
+
const testStatuses = testsToTestStatuses.get(testStatusKey) || []
|
|
424
718
|
|
|
425
719
|
if (testStatuses.length === 0) {
|
|
426
|
-
testsToTestStatuses.set(
|
|
720
|
+
testsToTestStatuses.set(testStatusKey, [testStatus])
|
|
427
721
|
if (test._ddIsNew && DYNAMIC_NAME_RE.test(getTestFullname(test))) {
|
|
428
722
|
newTestsWithDynamicNames.add(`${getTestSuitePath(test._requireFile, rootDir)} › ${getTestFullname(test)}`)
|
|
429
723
|
}
|
|
@@ -431,6 +725,17 @@ function testEndHandler ({
|
|
|
431
725
|
testStatuses.push(testStatus)
|
|
432
726
|
}
|
|
433
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
|
+
|
|
434
739
|
const testProperties = getTestProperties(test)
|
|
435
740
|
|
|
436
741
|
if (testProperties.attemptToFix) {
|
|
@@ -459,9 +764,10 @@ function testEndHandler ({
|
|
|
459
764
|
}
|
|
460
765
|
|
|
461
766
|
// Check if all EFD retries failed
|
|
462
|
-
|
|
767
|
+
const efdRetryCount = getEfdRetryCountForTest(test)
|
|
768
|
+
if (efdRetryCount > 0 && testStatuses.length === efdRetryCount + 1 &&
|
|
463
769
|
(test._ddIsNew || test._ddIsModified) &&
|
|
464
|
-
|
|
770
|
+
isEarlyFlakeDetectionEnabled &&
|
|
465
771
|
testStatuses.every(status => status === 'fail')) {
|
|
466
772
|
test._ddHasFailedAllRetries = true
|
|
467
773
|
}
|
|
@@ -479,9 +785,6 @@ function testEndHandler ({
|
|
|
479
785
|
if (shouldCreateTestSpan) {
|
|
480
786
|
const testResult = results.at(-1)
|
|
481
787
|
const testCtx = testToCtx.get(test)
|
|
482
|
-
const isEfdManagedTest = (test._ddIsNew || test._ddIsModified) &&
|
|
483
|
-
!test._ddIsAttemptToFix &&
|
|
484
|
-
isEarlyFlakeDetectionEnabled
|
|
485
788
|
const isAtrRetry = testResult?.retry > 0 &&
|
|
486
789
|
isFlakyTestRetriesEnabled &&
|
|
487
790
|
!test._ddIsAttemptToFix &&
|
|
@@ -496,6 +799,7 @@ function testEndHandler ({
|
|
|
496
799
|
isAttemptToFix: test._ddIsAttemptToFix,
|
|
497
800
|
hasFailedAllRetries: test._ddHasFailedAllRetries,
|
|
498
801
|
hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
|
|
802
|
+
hasPassedAnyEfdAttempt: testStatuses.includes('pass'),
|
|
499
803
|
testStatus,
|
|
500
804
|
})
|
|
501
805
|
|
|
@@ -519,6 +823,7 @@ function testEndHandler ({
|
|
|
519
823
|
isAtrRetry,
|
|
520
824
|
isModified: test._ddIsModified,
|
|
521
825
|
finalStatus,
|
|
826
|
+
earlyFlakeAbortReason: efdSlowAbortedTests.has(testEfdKey) ? 'slow' : undefined,
|
|
522
827
|
...testCtx.currentStore,
|
|
523
828
|
})
|
|
524
829
|
}
|
|
@@ -539,45 +844,47 @@ function testEndHandler ({
|
|
|
539
844
|
.filter(currentTest => currentTest !== test)
|
|
540
845
|
}
|
|
541
846
|
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
847
|
+
finishTestSuiteIfDone(testSuiteAbsolutePath, projects)
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
function dispatcherRunWrapper (run) {
|
|
851
|
+
return function (...args) {
|
|
852
|
+
remainingTestsByFile = getTestsBySuiteFromTestsById(this._testById)
|
|
853
|
+
return run.apply(this, args)
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
function deferEfdRetryGroups (testGroups) {
|
|
858
|
+
const groupsWithOriginalTests = []
|
|
859
|
+
const efdRetryOnlyGroups = []
|
|
545
860
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
}
|
|
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
|
+
}
|
|
559
874
|
}
|
|
560
|
-
remainingTestsByFile[testSuiteAbsolutePath] = []
|
|
561
875
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
if (testStatuses.includes('fail')) {
|
|
565
|
-
testSuiteStatus = 'fail'
|
|
566
|
-
} else if (testStatuses.every(status => status === 'skip')) {
|
|
567
|
-
testSuiteStatus = 'skip'
|
|
876
|
+
if (efdRetryTests.length && originalTests.length) {
|
|
877
|
+
group.tests = [...originalTests, ...efdRetryTests]
|
|
568
878
|
}
|
|
569
879
|
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
880
|
+
if (originalTests.length) {
|
|
881
|
+
groupsWithOriginalTests.push(group)
|
|
882
|
+
} else {
|
|
883
|
+
efdRetryOnlyGroups.push(group)
|
|
884
|
+
}
|
|
573
885
|
}
|
|
574
|
-
}
|
|
575
886
|
|
|
576
|
-
|
|
577
|
-
return function () {
|
|
578
|
-
remainingTestsByFile = getTestsBySuiteFromTestsById(this._testById)
|
|
579
|
-
return run.apply(this, arguments)
|
|
580
|
-
}
|
|
887
|
+
return [...groupsWithOriginalTests, ...efdRetryOnlyGroups]
|
|
581
888
|
}
|
|
582
889
|
|
|
583
890
|
function dispatcherRunWrapperNew (run) {
|
|
@@ -592,21 +899,26 @@ function dispatcherRunWrapperNew (run) {
|
|
|
592
899
|
testGroups = testGroups.filter(group => group.tests.length > 0)
|
|
593
900
|
}
|
|
594
901
|
|
|
902
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
903
|
+
testGroups = deferEfdRetryGroups(testGroups)
|
|
904
|
+
}
|
|
905
|
+
|
|
595
906
|
if (!this._allTests) {
|
|
596
907
|
// Removed in https://github.com/microsoft/playwright/commit/1e52c37b254a441cccf332520f60225a5acc14c7
|
|
597
908
|
// Not available from >=1.44.0
|
|
598
909
|
this._ddAllTests = testGroups.flatMap(g => g.tests)
|
|
599
910
|
}
|
|
600
911
|
remainingTestsByFile = getTestsBySuiteFromTestGroups(testGroups)
|
|
912
|
+
arguments[0] = testGroups
|
|
601
913
|
return run.apply(this, arguments)
|
|
602
914
|
}
|
|
603
915
|
}
|
|
604
916
|
|
|
605
917
|
function dispatcherHook (dispatcherExport) {
|
|
606
918
|
shimmer.wrap(dispatcherExport.Dispatcher.prototype, 'run', dispatcherRunWrapper)
|
|
607
|
-
shimmer.wrap(dispatcherExport.Dispatcher.prototype, '_createWorker', createWorker => function () {
|
|
919
|
+
shimmer.wrap(dispatcherExport.Dispatcher.prototype, '_createWorker', createWorker => function (...args) {
|
|
608
920
|
const dispatcher = this
|
|
609
|
-
const worker = createWorker.apply(this,
|
|
921
|
+
const worker = createWorker.apply(this, args)
|
|
610
922
|
const projects = getProjectsFromDispatcher(dispatcher)
|
|
611
923
|
sessionProjects = projects
|
|
612
924
|
|
|
@@ -637,7 +949,6 @@ function dispatcherHook (dispatcherExport) {
|
|
|
637
949
|
)
|
|
638
950
|
}
|
|
639
951
|
})
|
|
640
|
-
|
|
641
952
|
return worker
|
|
642
953
|
})
|
|
643
954
|
return dispatcherExport
|
|
@@ -645,9 +956,9 @@ function dispatcherHook (dispatcherExport) {
|
|
|
645
956
|
|
|
646
957
|
function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
647
958
|
shimmer.wrap(dispatcherExport.Dispatcher.prototype, 'run', runWrapper)
|
|
648
|
-
shimmer.wrap(dispatcherExport.Dispatcher.prototype, '_createWorker', createWorker => function () {
|
|
959
|
+
shimmer.wrap(dispatcherExport.Dispatcher.prototype, '_createWorker', createWorker => function (...args) {
|
|
649
960
|
const dispatcher = this
|
|
650
|
-
const worker = createWorker.apply(this,
|
|
961
|
+
const worker = createWorker.apply(this, args)
|
|
651
962
|
const projects = getProjectsFromDispatcher(dispatcher)
|
|
652
963
|
sessionProjects = projects
|
|
653
964
|
|
|
@@ -661,8 +972,11 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
661
972
|
const test = getTestByTestId(dispatcher, testId)
|
|
662
973
|
|
|
663
974
|
const isTimeout = status === 'timedOut'
|
|
664
|
-
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
|
|
665
975
|
const testStatus = STATUS_TO_TEST_STATUS[status]
|
|
976
|
+
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
|
|
977
|
+
if (shouldCreateTestSpan && !testToCtx.has(test)) {
|
|
978
|
+
testBeginHandler(test, getBrowserNameFromProjects(projects, test), true)
|
|
979
|
+
}
|
|
666
980
|
testEndHandler(
|
|
667
981
|
{
|
|
668
982
|
test,
|
|
@@ -686,14 +1000,11 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
686
1000
|
// above) and mark the execution final once the count reaches the expected total.
|
|
687
1001
|
// This mirrors how ATF finality is detected and centralizes the decision in the
|
|
688
1002
|
// main process, so workers only need to act on the _ddIsFinalExecution flag.
|
|
689
|
-
const isEfdManagedTest = (test
|
|
690
|
-
!test._ddIsAttemptToFix &&
|
|
691
|
-
isEarlyFlakeDetectionEnabled
|
|
1003
|
+
const isEfdManagedTest = isTestEfdManaged(test)
|
|
692
1004
|
let isFinalExecution
|
|
693
1005
|
if (isEfdManagedTest) {
|
|
694
|
-
const
|
|
695
|
-
|
|
696
|
-
isFinalExecution = efdTestStatuses.length === earlyFlakeDetectionNumRetries + 1
|
|
1006
|
+
const efdTestStatuses = testsToTestStatuses.get(getTestEfdKey(test)) || []
|
|
1007
|
+
isFinalExecution = efdTestStatuses.length === getEfdRetryCountForTest(test) + 1
|
|
697
1008
|
} else if (test._ddIsAttemptToFix) {
|
|
698
1009
|
isFinalExecution = !!(test._ddHasPassedAttemptToFixRetries || test._ddHasFailedAttemptToFixRetries)
|
|
699
1010
|
} else {
|
|
@@ -718,10 +1029,11 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
718
1029
|
_ddIsModified: test._ddIsModified,
|
|
719
1030
|
_ddIsFinalExecution: isFinalExecution,
|
|
720
1031
|
_ddIsEfdManagedTest: isEfdManagedTest,
|
|
1032
|
+
_ddEarlyFlakeAbortReason: efdSlowAbortedTests.has(getTestEfdKey(test)) ? 'slow' : undefined,
|
|
1033
|
+
_ddHasPassedAnyEfdAttempt: (testsToTestStatuses.get(getTestEfdKey(test)) || []).includes('pass'),
|
|
721
1034
|
},
|
|
722
1035
|
})
|
|
723
1036
|
})
|
|
724
|
-
|
|
725
1037
|
return worker
|
|
726
1038
|
})
|
|
727
1039
|
return dispatcherExport
|
|
@@ -747,6 +1059,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
747
1059
|
isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
|
|
748
1060
|
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
|
|
749
1061
|
earlyFlakeDetectionNumRetries = libraryConfig.earlyFlakeDetectionNumRetries
|
|
1062
|
+
earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
|
|
750
1063
|
earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
|
|
751
1064
|
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
752
1065
|
flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
|
|
@@ -835,15 +1148,16 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
835
1148
|
// there were tests that did not go through `testBegin` or `testEnd`,
|
|
836
1149
|
// because they were skipped
|
|
837
1150
|
for (const test of tests) {
|
|
1151
|
+
const alreadyReported = testsReportedInGenerateSummary.has(test)
|
|
838
1152
|
const browser = getBrowserNameFromProjects(projects, test)
|
|
839
|
-
testBeginHandler(test, browser,
|
|
1153
|
+
testBeginHandler(test, browser, !alreadyReported)
|
|
840
1154
|
testEndHandler({
|
|
841
1155
|
test,
|
|
842
1156
|
annotations: [],
|
|
843
1157
|
testStatus: 'skip',
|
|
844
1158
|
error: null,
|
|
845
1159
|
isTimeout: false,
|
|
846
|
-
shouldCreateTestSpan:
|
|
1160
|
+
shouldCreateTestSpan: !alreadyReported,
|
|
847
1161
|
projects,
|
|
848
1162
|
})
|
|
849
1163
|
}
|
|
@@ -894,6 +1208,14 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
894
1208
|
startedSuites = []
|
|
895
1209
|
remainingTestsByFile = {}
|
|
896
1210
|
quarantinedButNotAttemptToFixFqns = new Set()
|
|
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()
|
|
897
1219
|
|
|
898
1220
|
// TODO: we can trick playwright into thinking the session passed by returning
|
|
899
1221
|
// 'passed' here. We might be able to use this for both EFD and Test Management tests.
|
|
@@ -995,12 +1317,29 @@ addHook({
|
|
|
995
1317
|
* - we execute `applyRepeatEachIndex` for each of these cloned file suites
|
|
996
1318
|
* - we add the cloned file suites to the project suite
|
|
997
1319
|
*/
|
|
998
|
-
function applyRetriesToTests (
|
|
1320
|
+
function applyRetriesToTests (
|
|
1321
|
+
fileSuitesWithTestsToRetry,
|
|
1322
|
+
filterTest,
|
|
1323
|
+
tagsToApply,
|
|
1324
|
+
numRetries,
|
|
1325
|
+
configureCopiedTest,
|
|
1326
|
+
getRetryRepeatEachIndex
|
|
1327
|
+
) {
|
|
999
1328
|
for (const [fileSuite, projectSuite] of fileSuitesWithTestsToRetry.entries()) {
|
|
1000
1329
|
for (let repeatEachIndex = 1; repeatEachIndex <= numRetries; repeatEachIndex++) {
|
|
1001
|
-
const copyFileSuite = deepCloneSuite(fileSuite, filterTest, tagsToApply)
|
|
1002
|
-
|
|
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)
|
|
1003
1339
|
projectSuite._addSuite(copyFileSuite)
|
|
1340
|
+
for (const copiedTest of copyFileSuite.allTests()) {
|
|
1341
|
+
registerEfdRetryTest(copiedTest)
|
|
1342
|
+
}
|
|
1004
1343
|
}
|
|
1005
1344
|
}
|
|
1006
1345
|
}
|
|
@@ -1085,6 +1424,7 @@ addHook({
|
|
|
1085
1424
|
for (const impactedTest of impactedTests) {
|
|
1086
1425
|
impactedTest._ddIsModified = true
|
|
1087
1426
|
if (isEarlyFlakeDetectionEnabled && impactedTest.expectedStatus !== 'skipped') {
|
|
1427
|
+
markEfdManagedTest(impactedTest)
|
|
1088
1428
|
const fileSuite = getSuiteType(impactedTest, 'file')
|
|
1089
1429
|
if (!fileSuitesWithImpactedTestsToProjects.has(fileSuite)) {
|
|
1090
1430
|
fileSuitesWithImpactedTestsToProjects.set(fileSuite, getSuiteType(impactedTest, 'project'))
|
|
@@ -1100,7 +1440,12 @@ addHook({
|
|
|
1100
1440
|
'_ddIsEfdRetry',
|
|
1101
1441
|
(test) => (isKnownTestsEnabled && isNewTest(test) ? '_ddIsNew' : null),
|
|
1102
1442
|
],
|
|
1103
|
-
|
|
1443
|
+
getConfiguredEfdRetryCount(),
|
|
1444
|
+
(copiedTest, originalTest, retryIndex) => {
|
|
1445
|
+
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
1446
|
+
markEfdManagedTest(copiedTest)
|
|
1447
|
+
},
|
|
1448
|
+
getEfdRetryRepeatEachIndex
|
|
1104
1449
|
)
|
|
1105
1450
|
}
|
|
1106
1451
|
|
|
@@ -1124,6 +1469,7 @@ addHook({
|
|
|
1124
1469
|
if (isEarlyFlakeDetectionEnabled && newTest.expectedStatus !== 'skipped' && !newTest._ddIsModified) {
|
|
1125
1470
|
// Prevent ATR or `--retries` from retrying new tests if EFD is enabled
|
|
1126
1471
|
newTest.retries = 0
|
|
1472
|
+
markEfdManagedTest(newTest)
|
|
1127
1473
|
const fileSuite = getSuiteType(newTest, 'file')
|
|
1128
1474
|
if (!fileSuitesWithNewTestsToProjects.has(fileSuite)) {
|
|
1129
1475
|
fileSuitesWithNewTestsToProjects.set(fileSuite, getSuiteType(newTest, 'project'))
|
|
@@ -1135,7 +1481,12 @@ addHook({
|
|
|
1135
1481
|
fileSuitesWithNewTestsToProjects,
|
|
1136
1482
|
isNewTest,
|
|
1137
1483
|
['_ddIsNew', '_ddIsEfdRetry'],
|
|
1138
|
-
|
|
1484
|
+
getConfiguredEfdRetryCount(),
|
|
1485
|
+
(copiedTest, originalTest, retryIndex) => {
|
|
1486
|
+
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
1487
|
+
markEfdManagedTest(copiedTest)
|
|
1488
|
+
},
|
|
1489
|
+
getEfdRetryRepeatEachIndex
|
|
1139
1490
|
)
|
|
1140
1491
|
}
|
|
1141
1492
|
}
|
|
@@ -1144,7 +1495,7 @@ addHook({
|
|
|
1144
1495
|
}
|
|
1145
1496
|
|
|
1146
1497
|
// We need to proxy the createRootSuite function because the function is not configurable
|
|
1147
|
-
|
|
1498
|
+
return new Proxy(loadUtilsPackage, {
|
|
1148
1499
|
get (target, prop) {
|
|
1149
1500
|
if (prop === 'createRootSuite') {
|
|
1150
1501
|
return newCreateRootSuite
|
|
@@ -1152,8 +1503,6 @@ addHook({
|
|
|
1152
1503
|
return target[prop]
|
|
1153
1504
|
},
|
|
1154
1505
|
})
|
|
1155
|
-
|
|
1156
|
-
return proxy
|
|
1157
1506
|
})
|
|
1158
1507
|
|
|
1159
1508
|
// main process hook
|
|
@@ -1173,6 +1522,10 @@ addHook({
|
|
|
1173
1522
|
|
|
1174
1523
|
// We add a new listener to `this.process`, which is represents the worker
|
|
1175
1524
|
this.process.on('message', (message) => {
|
|
1525
|
+
if (message?.type === EFD_RETRY_COUNT_REQUEST) {
|
|
1526
|
+
sendEfdRetryCountToWorkerWhenAvailable(this.process, message.testId)
|
|
1527
|
+
return
|
|
1528
|
+
}
|
|
1176
1529
|
// These messages are [code, payload]. The payload is test data
|
|
1177
1530
|
if (Array.isArray(message) && message[0] === PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE) {
|
|
1178
1531
|
workerReportCh.publish(message[1])
|
|
@@ -1231,9 +1584,15 @@ addHook({
|
|
|
1231
1584
|
const stepInfoByStepId = {}
|
|
1232
1585
|
|
|
1233
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
|
+
}
|
|
1234
1592
|
if (test.expectedStatus === 'skipped') {
|
|
1235
1593
|
return _runTest.apply(this, arguments)
|
|
1236
1594
|
}
|
|
1595
|
+
test._ddStartTime = performance.now()
|
|
1237
1596
|
steps = []
|
|
1238
1597
|
|
|
1239
1598
|
const {
|
|
@@ -1316,6 +1675,21 @@ addHook({
|
|
|
1316
1675
|
await res
|
|
1317
1676
|
|
|
1318
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
|
+
}
|
|
1319
1693
|
|
|
1320
1694
|
// testInfo.errors could be better than "error",
|
|
1321
1695
|
// which will only include timeout error (even though the test failed because of a different error)
|
|
@@ -1361,6 +1735,7 @@ addHook({
|
|
|
1361
1735
|
isAttemptToFix: test._ddIsAttemptToFix,
|
|
1362
1736
|
hasFailedAllRetries: test._ddHasFailedAllRetries,
|
|
1363
1737
|
hasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
|
|
1738
|
+
hasPassedAnyEfdAttempt: test._ddHasPassedAnyEfdAttempt,
|
|
1364
1739
|
testStatus: STATUS_TO_TEST_STATUS[status],
|
|
1365
1740
|
})
|
|
1366
1741
|
|
|
@@ -1384,6 +1759,7 @@ addHook({
|
|
|
1384
1759
|
isModified: test._ddIsModified,
|
|
1385
1760
|
onDone,
|
|
1386
1761
|
finalStatus,
|
|
1762
|
+
earlyFlakeAbortReason: test._ddEarlyFlakeAbortReason,
|
|
1387
1763
|
...testCtx.currentStore,
|
|
1388
1764
|
})
|
|
1389
1765
|
|
|
@@ -1419,12 +1795,13 @@ addHook({
|
|
|
1419
1795
|
})
|
|
1420
1796
|
|
|
1421
1797
|
function generateSummaryWrapper (generateSummary) {
|
|
1422
|
-
return function () {
|
|
1798
|
+
return function (...args) {
|
|
1423
1799
|
for (const test of this.suite.allTests()) {
|
|
1424
1800
|
// https://github.com/microsoft/playwright/blob/bf92ffecff6f30a292b53430dbaee0207e0c61ad/packages/playwright/src/reporters/base.ts#L279
|
|
1425
1801
|
const didNotRun = test.outcome() === 'skipped' &&
|
|
1426
1802
|
(!test.results.length || test.expectedStatus !== 'skipped')
|
|
1427
|
-
if (didNotRun) {
|
|
1803
|
+
if (didNotRun && !testsReportedInGenerateSummary.has(test)) {
|
|
1804
|
+
testsReportedInGenerateSummary.add(test)
|
|
1428
1805
|
const {
|
|
1429
1806
|
_requireFile: testSuiteAbsolutePath,
|
|
1430
1807
|
location: {
|
|
@@ -1451,7 +1828,7 @@ function generateSummaryWrapper (generateSummary) {
|
|
|
1451
1828
|
})
|
|
1452
1829
|
}
|
|
1453
1830
|
}
|
|
1454
|
-
return generateSummary.apply(this,
|
|
1831
|
+
return generateSummary.apply(this, args)
|
|
1455
1832
|
}
|
|
1456
1833
|
}
|
|
1457
1834
|
|