dd-trace 5.104.0 → 5.105.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-3rdparty.csv +90 -102
- package/index.d.ts +82 -3
- package/package.json +15 -15
- package/packages/datadog-core/src/storage.js +1 -1
- package/packages/datadog-instrumentations/src/aerospike.js +1 -1
- package/packages/datadog-instrumentations/src/ai.js +8 -7
- package/packages/datadog-instrumentations/src/aws-sdk.js +13 -0
- package/packages/datadog-instrumentations/src/azure-cosmos.js +7 -0
- package/packages/datadog-instrumentations/src/azure-functions.js +3 -0
- package/packages/datadog-instrumentations/src/cucumber.js +78 -5
- package/packages/datadog-instrumentations/src/dns.js +54 -18
- package/packages/datadog-instrumentations/src/fastify.js +142 -82
- package/packages/datadog-instrumentations/src/graphql.js +188 -62
- package/packages/datadog-instrumentations/src/helpers/ai-messages.js +322 -14
- package/packages/datadog-instrumentations/src/helpers/hooks.js +4 -0
- package/packages/datadog-instrumentations/src/helpers/instrument.js +2 -1
- package/packages/datadog-instrumentations/src/helpers/openai-ai-guard.js +269 -0
- package/packages/datadog-instrumentations/src/helpers/promise-instrumentor.js +42 -0
- package/packages/datadog-instrumentations/src/helpers/register.js +1 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +2 -3
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/azure-cosmos.js +50 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/langgraph.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +85 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +37 -236
- package/packages/datadog-instrumentations/src/hono.js +54 -3
- package/packages/datadog-instrumentations/src/http/server.js +9 -4
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +163 -0
- package/packages/datadog-instrumentations/src/jest.js +360 -150
- package/packages/datadog-instrumentations/src/kafkajs.js +120 -16
- package/packages/datadog-instrumentations/src/mocha/main.js +128 -17
- package/packages/datadog-instrumentations/src/nats.js +182 -0
- package/packages/datadog-instrumentations/src/nyc.js +38 -1
- package/packages/datadog-instrumentations/src/openai.js +33 -18
- package/packages/datadog-instrumentations/src/oracledb.js +6 -1
- package/packages/datadog-instrumentations/src/pino.js +17 -5
- package/packages/datadog-instrumentations/src/playwright.js +515 -292
- package/packages/datadog-instrumentations/src/router.js +76 -32
- package/packages/datadog-instrumentations/src/stripe.js +1 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-azure-cosmos/src/index.js +144 -0
- package/packages/datadog-plugin-azure-event-hubs/src/producer.js +1 -1
- package/packages/datadog-plugin-azure-functions/src/index.js +5 -2
- package/packages/datadog-plugin-azure-service-bus/src/producer.js +1 -1
- package/packages/datadog-plugin-bunyan/src/index.js +28 -0
- package/packages/datadog-plugin-cucumber/src/index.js +17 -3
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +199 -28
- package/packages/datadog-plugin-cypress/src/support.js +69 -1
- package/packages/datadog-plugin-dns/src/lookup.js +8 -6
- package/packages/datadog-plugin-google-cloud-pubsub/src/pubsub-push-subscription.js +1 -1
- package/packages/datadog-plugin-graphql/src/execute.js +2 -0
- package/packages/datadog-plugin-graphql/src/resolve.js +64 -67
- package/packages/datadog-plugin-http/src/server.js +40 -15
- package/packages/datadog-plugin-jest/src/index.js +11 -3
- package/packages/datadog-plugin-jest/src/util.js +15 -8
- package/packages/datadog-plugin-kafkajs/src/batch-consumer.js +1 -1
- package/packages/datadog-plugin-kafkajs/src/producer.js +3 -0
- package/packages/datadog-plugin-langgraph/src/stream.js +1 -1
- package/packages/datadog-plugin-mocha/src/index.js +19 -4
- package/packages/datadog-plugin-mongodb-core/src/index.js +281 -40
- package/packages/datadog-plugin-nats/src/consumer.js +43 -0
- package/packages/datadog-plugin-nats/src/index.js +20 -0
- package/packages/datadog-plugin-nats/src/producer.js +62 -0
- package/packages/datadog-plugin-nats/src/util.js +33 -0
- package/packages/datadog-plugin-next/src/index.js +5 -3
- package/packages/datadog-plugin-openai/src/tracing.js +15 -2
- package/packages/datadog-plugin-oracledb/src/index.js +13 -2
- package/packages/datadog-plugin-pino/src/index.js +42 -0
- package/packages/datadog-plugin-playwright/src/index.js +4 -4
- package/packages/datadog-plugin-protobufjs/src/schema_iterator.js +1 -1
- package/packages/datadog-plugin-rhea/src/producer.js +1 -1
- package/packages/datadog-plugin-router/src/index.js +33 -44
- package/packages/datadog-plugin-selenium/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +5 -13
- package/packages/datadog-plugin-winston/src/index.js +30 -0
- package/packages/datadog-shimmer/src/shimmer.js +33 -40
- package/packages/dd-trace/src/aiguard/index.js +1 -1
- package/packages/dd-trace/src/aiguard/sdk.js +1 -1
- package/packages/dd-trace/src/appsec/api_security_sampler.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +1 -1
- package/packages/dd-trace/src/appsec/reporter.js +5 -6
- package/packages/dd-trace/src/appsec/sdk/user_blocking.js +1 -1
- package/packages/dd-trace/src/appsec/sdk/utils.js +1 -1
- package/packages/dd-trace/src/appsec/user_tracking.js +5 -4
- package/packages/dd-trace/src/baggage.js +7 -1
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +0 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +25 -13
- package/packages/dd-trace/src/ci-visibility/test-optimization-cache.js +70 -6
- package/packages/dd-trace/src/config/generated-config-types.d.ts +6 -2
- package/packages/dd-trace/src/config/supported-configurations.json +27 -8
- package/packages/dd-trace/src/datastreams/writer.js +2 -4
- package/packages/dd-trace/src/debugger/devtools_client/condition.js +5 -8
- package/packages/dd-trace/src/encode/0.4.js +124 -108
- package/packages/dd-trace/src/encode/0.5.js +114 -26
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +31 -23
- package/packages/dd-trace/src/encode/agentless-json.js +4 -2
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +32 -13
- package/packages/dd-trace/src/encode/span-stats.js +16 -16
- package/packages/dd-trace/src/encode/tags-processors.js +16 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/genai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/handlers/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +9 -7
- package/packages/dd-trace/src/llmobs/plugins/langgraph/index.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/openai/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +0 -16
- package/packages/dd-trace/src/llmobs/span_processor.js +3 -3
- package/packages/dd-trace/src/llmobs/tagger.js +9 -1
- package/packages/dd-trace/src/llmobs/telemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/util.js +66 -3
- package/packages/dd-trace/src/log/index.js +1 -1
- package/packages/dd-trace/src/msgpack/chunk.js +394 -10
- package/packages/dd-trace/src/msgpack/index.js +96 -2
- package/packages/dd-trace/src/openfeature/encoding.js +70 -0
- package/packages/dd-trace/src/openfeature/flagging_provider.js +20 -0
- package/packages/dd-trace/src/openfeature/span-enrichment-hook.js +143 -0
- package/packages/dd-trace/src/openfeature/span-enrichment.js +149 -0
- package/packages/dd-trace/src/opentelemetry/span-helpers.js +4 -3
- package/packages/dd-trace/src/opentelemetry/span.js +1 -1
- package/packages/dd-trace/src/opentracing/propagation/log.js +18 -7
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +62 -67
- package/packages/dd-trace/src/opentracing/span.js +59 -19
- package/packages/dd-trace/src/opentracing/span_context.js +49 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +20 -20
- package/packages/dd-trace/src/plugins/database.js +7 -6
- package/packages/dd-trace/src/plugins/index.js +4 -0
- package/packages/dd-trace/src/plugins/log_injection.js +56 -0
- package/packages/dd-trace/src/plugins/log_plugin.js +3 -48
- package/packages/dd-trace/src/plugins/outbound.js +1 -1
- package/packages/dd-trace/src/plugins/plugin.js +15 -17
- package/packages/dd-trace/src/plugins/tracing.js +43 -5
- package/packages/dd-trace/src/plugins/util/test.js +236 -13
- package/packages/dd-trace/src/plugins/util/web.js +79 -65
- package/packages/dd-trace/src/priority_sampler.js +2 -2
- package/packages/dd-trace/src/profiling/profiler.js +2 -2
- package/packages/dd-trace/src/profiling/profilers/wall.js +10 -4
- package/packages/dd-trace/src/sampling_rule.js +7 -7
- package/packages/dd-trace/src/service-naming/schemas/v0/messaging.js +10 -0
- package/packages/dd-trace/src/service-naming/schemas/v1/messaging.js +8 -0
- package/packages/dd-trace/src/service-naming/source-resolver.js +46 -0
- package/packages/dd-trace/src/span_format.js +190 -58
- package/packages/dd-trace/src/spanleak.js +1 -1
- package/packages/dd-trace/src/standalone/index.js +3 -3
- package/packages/dd-trace/src/tagger.js +0 -2
- package/vendor/dist/@apm-js-collab/code-transformer/index.js +70 -39
- package/vendor/dist/@datadog/sketches-js/LICENSE +10 -36
- package/vendor/dist/@datadog/sketches-js/index.js +1 -1
- package/vendor/dist/protobufjs/index.js +1 -1
- package/vendor/dist/protobufjs/minimal/index.js +1 -1
- package/packages/dd-trace/src/msgpack/encoder.js +0 -308
- package/packages/dd-trace/src/plugins/structured_log_plugin.js +0 -9
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// Capture real timers at module load time, before any test can install fake timers.
|
|
4
4
|
const realSetTimeout = setTimeout
|
|
5
|
+
const realClearTimeout = clearTimeout
|
|
5
6
|
|
|
6
7
|
const { performance } = require('node:perf_hooks')
|
|
7
8
|
const satisfies = require('../../../vendor/dist/semifies')
|
|
@@ -25,7 +26,7 @@ const {
|
|
|
25
26
|
getValueFromEnvSources,
|
|
26
27
|
} = require('../../dd-trace/src/config/helper')
|
|
27
28
|
const { DD_MAJOR } = require('../../../version')
|
|
28
|
-
const { addHook, channel } = require('./helpers/instrument')
|
|
29
|
+
const { addHook, channel, tracingChannel } = require('./helpers/instrument')
|
|
29
30
|
|
|
30
31
|
const testStartCh = channel('ci:playwright:test:start')
|
|
31
32
|
const testFinishCh = channel('ci:playwright:test:finish')
|
|
@@ -46,6 +47,12 @@ const testSuiteFinishCh = channel('ci:playwright:test-suite:finish')
|
|
|
46
47
|
const workerReportCh = channel('ci:playwright:worker:report')
|
|
47
48
|
const testPageGotoCh = channel('ci:playwright:test:page-goto')
|
|
48
49
|
|
|
50
|
+
const dispatcherRunCh = tracingChannel('orchestrion:playwright:Dispatcher_run')
|
|
51
|
+
const dispatcherCreateWorkerCh = tracingChannel('orchestrion:playwright:Dispatcher_createWorker')
|
|
52
|
+
const processHostStartRunnerCh = tracingChannel('orchestrion:playwright:ProcessHost_startRunner')
|
|
53
|
+
const createRootSuiteCh = tracingChannel('orchestrion:playwright:createRootSuite')
|
|
54
|
+
const pageGotoCh = tracingChannel('orchestrion:playwright-core:Page_goto')
|
|
55
|
+
|
|
49
56
|
const testToCtx = new WeakMap()
|
|
50
57
|
const testSuiteToCtx = new Map()
|
|
51
58
|
const testSuiteToTestStatuses = new Map()
|
|
@@ -53,6 +60,7 @@ const testSuiteToErrors = new Map()
|
|
|
53
60
|
const testsToTestStatuses = new Map()
|
|
54
61
|
|
|
55
62
|
const RUM_FLUSH_WAIT_TIME = Number(getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')) || 500
|
|
63
|
+
const DD_PROPERTIES_TIMEOUT = 5000
|
|
56
64
|
|
|
57
65
|
let applyRepeatEachIndex = null
|
|
58
66
|
|
|
@@ -95,12 +103,17 @@ const efdRetryTestsById = new Map()
|
|
|
95
103
|
const efdScheduledOriginalTestKeys = new Set()
|
|
96
104
|
const efdStartedOriginalTestKeys = new Set()
|
|
97
105
|
const efdSlowAbortedTests = new Set()
|
|
106
|
+
const ddPropertiesByTestId = new Map()
|
|
107
|
+
const ddPropertiesRequestsByTestId = new Map()
|
|
98
108
|
let rootDir = ''
|
|
99
109
|
let sessionProjects = []
|
|
100
110
|
|
|
101
111
|
const MINIMUM_SUPPORTED_VERSION_RANGE_EFD = '>=1.38.0' // TODO: remove this once we drop support for v5
|
|
102
112
|
const EFD_RETRY_COUNT_REQUEST = 'ddEfdRetryCountRequest'
|
|
103
113
|
const EFD_RETRY_COUNT_RESPONSE = 'ddEfdRetryCountResponse'
|
|
114
|
+
const DD_PROPERTIES_REQUEST = 'ddPropertiesRequest'
|
|
115
|
+
const DD_PROPERTIES_RESPONSE = 'ddProperties'
|
|
116
|
+
const kDdPlaywrightWorkerInstrumented = Symbol('ddPlaywrightWorkerInstrumented')
|
|
104
117
|
|
|
105
118
|
function isValidKnownTests (receivedKnownTests) {
|
|
106
119
|
return !!receivedKnownTests.playwright
|
|
@@ -281,6 +294,43 @@ function sendEfdRetryCountToWorkerWhenAvailable (workerProcess, testId) {
|
|
|
281
294
|
})
|
|
282
295
|
}
|
|
283
296
|
|
|
297
|
+
function sendDdPropertiesToWorker (workerProcess, testId, properties) {
|
|
298
|
+
workerProcess.send({
|
|
299
|
+
type: DD_PROPERTIES_RESPONSE,
|
|
300
|
+
testId,
|
|
301
|
+
properties,
|
|
302
|
+
})
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function setDdPropertiesForTest (workerProcess, testId, properties) {
|
|
306
|
+
ddPropertiesByTestId.set(testId, properties)
|
|
307
|
+
|
|
308
|
+
const requests = ddPropertiesRequestsByTestId.get(testId)
|
|
309
|
+
if (requests) {
|
|
310
|
+
ddPropertiesRequestsByTestId.delete(testId)
|
|
311
|
+
for (const resolveRequest of requests) {
|
|
312
|
+
resolveRequest(properties)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
sendDdPropertiesToWorker(workerProcess, testId, properties)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function sendDdPropertiesToWorkerWhenAvailable (workerProcess, testId) {
|
|
320
|
+
const properties = ddPropertiesByTestId.get(testId)
|
|
321
|
+
if (properties) {
|
|
322
|
+
sendDdPropertiesToWorker(workerProcess, testId, properties)
|
|
323
|
+
return
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (!ddPropertiesRequestsByTestId.has(testId)) {
|
|
327
|
+
ddPropertiesRequestsByTestId.set(testId, [])
|
|
328
|
+
}
|
|
329
|
+
ddPropertiesRequestsByTestId.get(testId).push((properties) => {
|
|
330
|
+
sendDdPropertiesToWorker(workerProcess, testId, properties)
|
|
331
|
+
})
|
|
332
|
+
}
|
|
333
|
+
|
|
284
334
|
/**
|
|
285
335
|
* @param {object} test
|
|
286
336
|
* @returns {boolean}
|
|
@@ -354,11 +404,15 @@ function getSuiteType (test, type) {
|
|
|
354
404
|
return suite
|
|
355
405
|
}
|
|
356
406
|
|
|
407
|
+
function isSuiteEntry (entry) {
|
|
408
|
+
return entry.constructor.name === 'Suite' || entry.constructor.name === '_Suite'
|
|
409
|
+
}
|
|
410
|
+
|
|
357
411
|
// Copy of Suite#_deepClone but with a function to filter tests
|
|
358
412
|
function deepCloneSuite (suite, filterTest, tags = [], configureCopiedTest) {
|
|
359
413
|
const copy = suite._clone()
|
|
360
414
|
for (const entry of suite._entries) {
|
|
361
|
-
if (entry
|
|
415
|
+
if (isSuiteEntry(entry)) {
|
|
362
416
|
copy._addSuite(deepCloneSuite(entry, filterTest, tags, configureCopiedTest))
|
|
363
417
|
} else {
|
|
364
418
|
if (filterTest(entry)) {
|
|
@@ -445,6 +499,10 @@ function getProjectsFromRunner (runner, configArg) {
|
|
|
445
499
|
}
|
|
446
500
|
|
|
447
501
|
function getProjectsFromDispatcher (dispatcher) {
|
|
502
|
+
const bundledConfig = dispatcher._testRun?.config?.config?.projects
|
|
503
|
+
if (bundledConfig) {
|
|
504
|
+
return bundledConfig
|
|
505
|
+
}
|
|
448
506
|
const newConfig = dispatcher._config?.config?.projects
|
|
449
507
|
if (newConfig) {
|
|
450
508
|
return newConfig
|
|
@@ -888,31 +946,118 @@ function deferEfdRetryGroups (testGroups) {
|
|
|
888
946
|
return [...groupsWithOriginalTests, ...efdRetryOnlyGroups]
|
|
889
947
|
}
|
|
890
948
|
|
|
949
|
+
function prepareDispatcherRun (dispatcher, args) {
|
|
950
|
+
let testGroups = args[0]
|
|
951
|
+
|
|
952
|
+
// Filter out disabled tests from testGroups before they get scheduled,
|
|
953
|
+
// unless they have attemptToFix (in which case they should still run and be retried)
|
|
954
|
+
if (isTestManagementTestsEnabled) {
|
|
955
|
+
for (const group of testGroups) {
|
|
956
|
+
group.tests = group.tests.filter(test => !test._ddIsDisabled || test._ddIsAttemptToFix)
|
|
957
|
+
}
|
|
958
|
+
// Remove empty groups
|
|
959
|
+
testGroups = testGroups.filter(group => group.tests.length > 0)
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
if (isEarlyFlakeDetectionEnabled) {
|
|
963
|
+
testGroups = deferEfdRetryGroups(testGroups)
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
if (!dispatcher._allTests) {
|
|
967
|
+
// Removed in https://github.com/microsoft/playwright/commit/1e52c37b254a441cccf332520f60225a5acc14c7
|
|
968
|
+
// Not available from >=1.44.0
|
|
969
|
+
dispatcher._ddAllTests = testGroups.flatMap(g => g.tests)
|
|
970
|
+
}
|
|
971
|
+
remainingTestsByFile = getTestsBySuiteFromTestGroups(testGroups)
|
|
972
|
+
args[0] = testGroups
|
|
973
|
+
}
|
|
974
|
+
|
|
891
975
|
function dispatcherRunWrapperNew (run) {
|
|
892
|
-
return function (
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
976
|
+
return function () {
|
|
977
|
+
prepareDispatcherRun(this, arguments)
|
|
978
|
+
return run.apply(this, arguments)
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
function onDispatcherCreateWorker (dispatcher, worker) {
|
|
983
|
+
if (!worker) {
|
|
984
|
+
return worker
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const projects = getProjectsFromDispatcher(dispatcher)
|
|
988
|
+
sessionProjects = projects
|
|
989
|
+
|
|
990
|
+
worker.on('testBegin', ({ testId }) => {
|
|
991
|
+
const test = getTestByTestId(dispatcher, testId)
|
|
992
|
+
const browser = getBrowserNameFromProjects(projects, test)
|
|
993
|
+
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
|
|
994
|
+
testBeginHandler(test, browser, shouldCreateTestSpan)
|
|
995
|
+
})
|
|
996
|
+
worker.on('testEnd', ({ testId, status, errors, annotations }) => {
|
|
997
|
+
const test = getTestByTestId(dispatcher, testId)
|
|
998
|
+
|
|
999
|
+
const isTimeout = status === 'timedOut'
|
|
1000
|
+
const testStatus = STATUS_TO_TEST_STATUS[status]
|
|
1001
|
+
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
|
|
1002
|
+
if (shouldCreateTestSpan && !testToCtx.has(test)) {
|
|
1003
|
+
testBeginHandler(test, getBrowserNameFromProjects(projects, test), true)
|
|
901
1004
|
}
|
|
1005
|
+
testEndHandler(
|
|
1006
|
+
{
|
|
1007
|
+
test,
|
|
1008
|
+
annotations,
|
|
1009
|
+
testStatus,
|
|
1010
|
+
error: errors && errors[0],
|
|
1011
|
+
isTimeout,
|
|
1012
|
+
shouldCreateTestSpan,
|
|
1013
|
+
projects,
|
|
1014
|
+
}
|
|
1015
|
+
)
|
|
1016
|
+
const testResult = test.results.at(-1)
|
|
1017
|
+
const isAtrRetry = testResult?.retry > 0 &&
|
|
1018
|
+
isFlakyTestRetriesEnabled &&
|
|
1019
|
+
!test._ddIsAttemptToFix &&
|
|
1020
|
+
!test._ddIsEfdRetry
|
|
902
1021
|
|
|
903
|
-
|
|
904
|
-
|
|
1022
|
+
// EFD retries (new or modified tests) are implemented as clones with retries=0,
|
|
1023
|
+
// so testWillRetry always returns false for them. Instead, we track how many
|
|
1024
|
+
// executions have been reported via testsToTestStatuses (updated by testEndHandler
|
|
1025
|
+
// above) and mark the execution final once the count reaches the expected total.
|
|
1026
|
+
// This mirrors how ATF finality is detected and centralizes the decision in the
|
|
1027
|
+
// main process, so workers only need to act on the _ddIsFinalExecution flag.
|
|
1028
|
+
const isEfdManagedTest = isTestEfdManaged(test)
|
|
1029
|
+
let isFinalExecution
|
|
1030
|
+
if (isEfdManagedTest) {
|
|
1031
|
+
const efdTestStatuses = testsToTestStatuses.get(getTestEfdKey(test)) || []
|
|
1032
|
+
isFinalExecution = efdTestStatuses.length === getEfdRetryCountForTest(test) + 1
|
|
1033
|
+
} else if (test._ddIsAttemptToFix) {
|
|
1034
|
+
isFinalExecution = !!(test._ddHasPassedAttemptToFixRetries || test._ddHasFailedAttemptToFixRetries)
|
|
1035
|
+
} else {
|
|
1036
|
+
isFinalExecution = !testWillRetry(test, testStatus)
|
|
905
1037
|
}
|
|
906
1038
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
1039
|
+
const ddProperties = {
|
|
1040
|
+
_ddIsDisabled: test._ddIsDisabled,
|
|
1041
|
+
_ddIsQuarantined: test._ddIsQuarantined,
|
|
1042
|
+
_ddIsAttemptToFix: test._ddIsAttemptToFix,
|
|
1043
|
+
_ddIsAttemptToFixRetry: test._ddIsAttemptToFixRetry,
|
|
1044
|
+
_ddIsNew: test._ddIsNew,
|
|
1045
|
+
_ddIsEfdRetry: test._ddIsEfdRetry,
|
|
1046
|
+
_ddHasFailedAllRetries: test._ddHasFailedAllRetries,
|
|
1047
|
+
_ddHasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
|
|
1048
|
+
_ddHasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
|
|
1049
|
+
_ddIsAtrRetry: isAtrRetry,
|
|
1050
|
+
_ddIsModified: test._ddIsModified,
|
|
1051
|
+
_ddIsFinalExecution: isFinalExecution,
|
|
1052
|
+
_ddIsEfdManagedTest: isEfdManagedTest,
|
|
1053
|
+
_ddEarlyFlakeAbortReason: efdSlowAbortedTests.has(getTestEfdKey(test)) ? 'slow' : undefined,
|
|
1054
|
+
_ddHasPassedAnyEfdAttempt: (testsToTestStatuses.get(getTestEfdKey(test)) || []).includes('pass'),
|
|
911
1055
|
}
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
1056
|
+
|
|
1057
|
+
setDdPropertiesForTest(worker.process, test.id, ddProperties)
|
|
1058
|
+
})
|
|
1059
|
+
|
|
1060
|
+
return worker
|
|
916
1061
|
}
|
|
917
1062
|
|
|
918
1063
|
function dispatcherHook (dispatcherExport) {
|
|
@@ -960,82 +1105,7 @@ function dispatcherHookNew (dispatcherExport, runWrapper) {
|
|
|
960
1105
|
shimmer.wrap(dispatcherExport.Dispatcher.prototype, '_createWorker', createWorker => function (...args) {
|
|
961
1106
|
const dispatcher = this
|
|
962
1107
|
const worker = createWorker.apply(this, args)
|
|
963
|
-
|
|
964
|
-
sessionProjects = projects
|
|
965
|
-
|
|
966
|
-
worker.on('testBegin', ({ testId }) => {
|
|
967
|
-
const test = getTestByTestId(dispatcher, testId)
|
|
968
|
-
const browser = getBrowserNameFromProjects(projects, test)
|
|
969
|
-
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
|
|
970
|
-
testBeginHandler(test, browser, shouldCreateTestSpan)
|
|
971
|
-
})
|
|
972
|
-
worker.on('testEnd', ({ testId, status, errors, annotations }) => {
|
|
973
|
-
const test = getTestByTestId(dispatcher, testId)
|
|
974
|
-
|
|
975
|
-
const isTimeout = status === 'timedOut'
|
|
976
|
-
const testStatus = STATUS_TO_TEST_STATUS[status]
|
|
977
|
-
const shouldCreateTestSpan = test.expectedStatus === 'skipped'
|
|
978
|
-
if (shouldCreateTestSpan && !testToCtx.has(test)) {
|
|
979
|
-
testBeginHandler(test, getBrowserNameFromProjects(projects, test), true)
|
|
980
|
-
}
|
|
981
|
-
testEndHandler(
|
|
982
|
-
{
|
|
983
|
-
test,
|
|
984
|
-
annotations,
|
|
985
|
-
testStatus,
|
|
986
|
-
error: errors && errors[0],
|
|
987
|
-
isTimeout,
|
|
988
|
-
shouldCreateTestSpan,
|
|
989
|
-
projects,
|
|
990
|
-
}
|
|
991
|
-
)
|
|
992
|
-
const testResult = test.results.at(-1)
|
|
993
|
-
const isAtrRetry = testResult?.retry > 0 &&
|
|
994
|
-
isFlakyTestRetriesEnabled &&
|
|
995
|
-
!test._ddIsAttemptToFix &&
|
|
996
|
-
!test._ddIsEfdRetry
|
|
997
|
-
|
|
998
|
-
// EFD retries (new or modified tests) are implemented as clones with retries=0,
|
|
999
|
-
// so testWillRetry always returns false for them. Instead, we track how many
|
|
1000
|
-
// executions have been reported via testsToTestStatuses (updated by testEndHandler
|
|
1001
|
-
// above) and mark the execution final once the count reaches the expected total.
|
|
1002
|
-
// This mirrors how ATF finality is detected and centralizes the decision in the
|
|
1003
|
-
// main process, so workers only need to act on the _ddIsFinalExecution flag.
|
|
1004
|
-
const isEfdManagedTest = isTestEfdManaged(test)
|
|
1005
|
-
let isFinalExecution
|
|
1006
|
-
if (isEfdManagedTest) {
|
|
1007
|
-
const efdTestStatuses = testsToTestStatuses.get(getTestEfdKey(test)) || []
|
|
1008
|
-
isFinalExecution = efdTestStatuses.length === getEfdRetryCountForTest(test) + 1
|
|
1009
|
-
} else if (test._ddIsAttemptToFix) {
|
|
1010
|
-
isFinalExecution = !!(test._ddHasPassedAttemptToFixRetries || test._ddHasFailedAttemptToFixRetries)
|
|
1011
|
-
} else {
|
|
1012
|
-
isFinalExecution = !testWillRetry(test, testStatus)
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
// We want to send the ddProperties to the worker
|
|
1016
|
-
worker.process.send({
|
|
1017
|
-
type: 'ddProperties',
|
|
1018
|
-
testId: test.id,
|
|
1019
|
-
properties: {
|
|
1020
|
-
_ddIsDisabled: test._ddIsDisabled,
|
|
1021
|
-
_ddIsQuarantined: test._ddIsQuarantined,
|
|
1022
|
-
_ddIsAttemptToFix: test._ddIsAttemptToFix,
|
|
1023
|
-
_ddIsAttemptToFixRetry: test._ddIsAttemptToFixRetry,
|
|
1024
|
-
_ddIsNew: test._ddIsNew,
|
|
1025
|
-
_ddIsEfdRetry: test._ddIsEfdRetry,
|
|
1026
|
-
_ddHasFailedAllRetries: test._ddHasFailedAllRetries,
|
|
1027
|
-
_ddHasPassedAttemptToFixRetries: test._ddHasPassedAttemptToFixRetries,
|
|
1028
|
-
_ddHasFailedAttemptToFixRetries: test._ddHasFailedAttemptToFixRetries,
|
|
1029
|
-
_ddIsAtrRetry: isAtrRetry,
|
|
1030
|
-
_ddIsModified: test._ddIsModified,
|
|
1031
|
-
_ddIsFinalExecution: isFinalExecution,
|
|
1032
|
-
_ddIsEfdManagedTest: isEfdManagedTest,
|
|
1033
|
-
_ddEarlyFlakeAbortReason: efdSlowAbortedTests.has(getTestEfdKey(test)) ? 'slow' : undefined,
|
|
1034
|
-
_ddHasPassedAnyEfdAttempt: (testsToTestStatuses.get(getTestEfdKey(test)) || []).includes('pass'),
|
|
1035
|
-
},
|
|
1036
|
-
})
|
|
1037
|
-
})
|
|
1038
|
-
return worker
|
|
1108
|
+
return onDispatcherCreateWorker(dispatcher, worker)
|
|
1039
1109
|
})
|
|
1040
1110
|
return dispatcherExport
|
|
1041
1111
|
}
|
|
@@ -1046,7 +1116,6 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1046
1116
|
let onDone
|
|
1047
1117
|
|
|
1048
1118
|
rootDir = getRootDir(this, config)
|
|
1049
|
-
|
|
1050
1119
|
const processArgv = process.argv.slice(2).join(' ')
|
|
1051
1120
|
const command = `playwright ${processArgv}`
|
|
1052
1121
|
testSessionStartCh.publish({ command, frameworkVersion: playwrightVersion, rootDir })
|
|
@@ -1233,6 +1302,8 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1233
1302
|
efdScheduledOriginalTestKeys.clear()
|
|
1234
1303
|
efdStartedOriginalTestKeys.clear()
|
|
1235
1304
|
efdSlowAbortedTests.clear()
|
|
1305
|
+
ddPropertiesByTestId.clear()
|
|
1306
|
+
ddPropertiesRequestsByTestId.clear()
|
|
1236
1307
|
|
|
1237
1308
|
// TODO: we can trick playwright into thinking the session passed by returning
|
|
1238
1309
|
// 'passed' here. We might be able to use this for both EFD and Test Management tests.
|
|
@@ -1259,6 +1330,85 @@ function runnerHookNew (runnerExport, playwrightVersion) {
|
|
|
1259
1330
|
return runnerExport
|
|
1260
1331
|
}
|
|
1261
1332
|
|
|
1333
|
+
function runnerIndexHook (runnerExport, playwrightVersion) {
|
|
1334
|
+
let wrappedTestRunner
|
|
1335
|
+
runnerExport = shimmer.wrap(runnerExport, 'testRunner', function (originalGetter) {
|
|
1336
|
+
return function () {
|
|
1337
|
+
if (!wrappedTestRunner) {
|
|
1338
|
+
wrappedTestRunner = runnerHookNew(originalGetter.call(this), playwrightVersion)
|
|
1339
|
+
}
|
|
1340
|
+
return wrappedTestRunner
|
|
1341
|
+
}
|
|
1342
|
+
})
|
|
1343
|
+
|
|
1344
|
+
const baseReporter = runnerExport.base?.TerminalReporter
|
|
1345
|
+
if (baseReporter) {
|
|
1346
|
+
shimmer.wrap(baseReporter.prototype, 'generateSummary', generateSummaryWrapper)
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
return runnerExport
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
function commonIndexHook (commonExport) {
|
|
1353
|
+
applyRepeatEachIndex = commonExport.suiteUtils?.applyRepeatEachIndex
|
|
1354
|
+
|
|
1355
|
+
let wrappedStartProcessRunner
|
|
1356
|
+
commonExport = shimmer.wrap(commonExport, 'startProcessRunner', function (originalGetter) {
|
|
1357
|
+
return function () {
|
|
1358
|
+
if (!wrappedStartProcessRunner) {
|
|
1359
|
+
const startProcessRunner = originalGetter.call(this)
|
|
1360
|
+
wrappedStartProcessRunner = function (create) {
|
|
1361
|
+
return startProcessRunner.call(this, function () {
|
|
1362
|
+
const processRunner = create.apply(this, arguments)
|
|
1363
|
+
instrumentWorkerMainMethods(processRunner)
|
|
1364
|
+
return processRunner
|
|
1365
|
+
})
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
return wrappedStartProcessRunner
|
|
1369
|
+
}
|
|
1370
|
+
})
|
|
1371
|
+
|
|
1372
|
+
return commonExport
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
dispatcherRunCh.subscribe({
|
|
1376
|
+
start (ctx) {
|
|
1377
|
+
prepareDispatcherRun(ctx.self, ctx.arguments)
|
|
1378
|
+
},
|
|
1379
|
+
})
|
|
1380
|
+
|
|
1381
|
+
dispatcherCreateWorkerCh.subscribe({
|
|
1382
|
+
end (ctx) {
|
|
1383
|
+
onDispatcherCreateWorker(ctx.self, ctx.result)
|
|
1384
|
+
},
|
|
1385
|
+
})
|
|
1386
|
+
|
|
1387
|
+
processHostStartRunnerCh.subscribe({
|
|
1388
|
+
start (ctx) {
|
|
1389
|
+
prepareProcessHostStartRunner(ctx.self)
|
|
1390
|
+
},
|
|
1391
|
+
asyncEnd (ctx) {
|
|
1392
|
+
finishProcessHostStartRunner(ctx.self)
|
|
1393
|
+
},
|
|
1394
|
+
})
|
|
1395
|
+
|
|
1396
|
+
createRootSuiteCh.subscribe({
|
|
1397
|
+
asyncEnd (ctx) {
|
|
1398
|
+
if (ctx.error) {
|
|
1399
|
+
return
|
|
1400
|
+
}
|
|
1401
|
+
processRootSuite(ctx.result || ctx.arguments?.[0])
|
|
1402
|
+
},
|
|
1403
|
+
})
|
|
1404
|
+
|
|
1405
|
+
pageGotoCh.subscribe({
|
|
1406
|
+
asyncEnd (ctx) {
|
|
1407
|
+
// The Page.goto rewriter waits for this so tests closing immediately after navigation still get RUM tags.
|
|
1408
|
+
ctx.asyncEndPromise = handlePageGoto(ctx.self)
|
|
1409
|
+
},
|
|
1410
|
+
})
|
|
1411
|
+
|
|
1262
1412
|
if (DD_MAJOR < 6) { // <1.38.0 is only supported up to version 5
|
|
1263
1413
|
addHook({
|
|
1264
1414
|
name: '@playwright/test',
|
|
@@ -1291,28 +1441,40 @@ if (DD_MAJOR < 6) { // <1.38.0 is only supported up to version 5
|
|
|
1291
1441
|
}, runnerHook)
|
|
1292
1442
|
}
|
|
1293
1443
|
|
|
1444
|
+
addHook({
|
|
1445
|
+
name: 'playwright',
|
|
1446
|
+
file: 'lib/runner/index.js',
|
|
1447
|
+
versions: ['>=1.60.0'],
|
|
1448
|
+
}, runnerIndexHook)
|
|
1449
|
+
|
|
1450
|
+
addHook({
|
|
1451
|
+
name: 'playwright',
|
|
1452
|
+
file: 'lib/common/index.js',
|
|
1453
|
+
versions: ['>=1.60.0'],
|
|
1454
|
+
}, commonIndexHook)
|
|
1455
|
+
|
|
1294
1456
|
addHook({
|
|
1295
1457
|
name: 'playwright',
|
|
1296
1458
|
file: 'lib/runner/runner.js',
|
|
1297
|
-
versions: ['>=1.38.0'],
|
|
1459
|
+
versions: ['>=1.38.0 <1.60.0'],
|
|
1298
1460
|
}, runnerHook)
|
|
1299
1461
|
|
|
1300
1462
|
addHook({
|
|
1301
1463
|
name: 'playwright',
|
|
1302
1464
|
file: 'lib/runner/testRunner.js',
|
|
1303
|
-
versions: ['>=1.55.0'],
|
|
1465
|
+
versions: ['>=1.55.0 <1.60.0'],
|
|
1304
1466
|
}, runnerHookNew)
|
|
1305
1467
|
|
|
1306
1468
|
addHook({
|
|
1307
1469
|
name: 'playwright',
|
|
1308
1470
|
file: 'lib/runner/dispatcher.js',
|
|
1309
|
-
versions: ['>=1.38.0'],
|
|
1471
|
+
versions: ['>=1.38.0 <1.60.0'],
|
|
1310
1472
|
}, (dispatcher) => dispatcherHookNew(dispatcher, dispatcherRunWrapperNew))
|
|
1311
1473
|
|
|
1312
1474
|
addHook({
|
|
1313
1475
|
name: 'playwright',
|
|
1314
1476
|
file: 'lib/common/suiteUtils.js',
|
|
1315
|
-
versions: ['>=1.38.0'],
|
|
1477
|
+
versions: ['>=1.38.0 <1.60.0'],
|
|
1316
1478
|
}, suiteUtilsPackage => {
|
|
1317
1479
|
// We grab `applyRepeatEachIndex` to use it later
|
|
1318
1480
|
// `applyRepeatEachIndex` needs to be applied to a cloned suite
|
|
@@ -1361,102 +1523,142 @@ function applyRetriesToTests (
|
|
|
1361
1523
|
}
|
|
1362
1524
|
}
|
|
1363
1525
|
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
}, (loadUtilsPackage) => {
|
|
1369
|
-
const oldCreateRootSuite = loadUtilsPackage.createRootSuite
|
|
1526
|
+
function processRootSuite (createRootSuiteReturnValue) {
|
|
1527
|
+
if (!isKnownTestsEnabled && !isTestManagementTestsEnabled && !isImpactedTestsEnabled) {
|
|
1528
|
+
return createRootSuiteReturnValue
|
|
1529
|
+
}
|
|
1370
1530
|
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
}
|
|
1531
|
+
if (!createRootSuiteReturnValue) {
|
|
1532
|
+
return createRootSuiteReturnValue
|
|
1533
|
+
}
|
|
1375
1534
|
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1535
|
+
// From v1.56.0 on, createRootSuite returns `{ rootSuite, topLevelProjects }`
|
|
1536
|
+
const rootSuite = createRootSuiteReturnValue.rootSuite || createRootSuiteReturnValue
|
|
1537
|
+
if (typeof rootSuite?.allTests !== 'function') {
|
|
1538
|
+
return createRootSuiteReturnValue
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
const allTests = rootSuite.allTests()
|
|
1542
|
+
|
|
1543
|
+
if (isTestManagementTestsEnabled) {
|
|
1544
|
+
const fileSuitesWithManagedTestsToProjects = new Map()
|
|
1545
|
+
for (const test of allTests) {
|
|
1546
|
+
const testProperties = getTestProperties(test)
|
|
1547
|
+
// Disabled tests are skipped unless they have attemptToFix
|
|
1548
|
+
if (testProperties.disabled) {
|
|
1549
|
+
test._ddIsDisabled = true
|
|
1550
|
+
if (!testProperties.attemptToFix) {
|
|
1551
|
+
test.expectedStatus = 'skipped'
|
|
1552
|
+
// setting test.expectedStatus to 'skipped' does not work for every case,
|
|
1553
|
+
// so we need to filter out disabled tests in dispatcherRunWrapperNew,
|
|
1554
|
+
// so they don't get to the workers
|
|
1555
|
+
continue
|
|
1396
1556
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1557
|
+
}
|
|
1558
|
+
if (testProperties.quarantined) {
|
|
1559
|
+
test._ddIsQuarantined = true
|
|
1560
|
+
if (!testProperties.attemptToFix) {
|
|
1561
|
+
// Do not skip quarantined tests, let them run and overwrite results post-run if they fail
|
|
1562
|
+
const testFqn = getTestFullyQualifiedName(test)
|
|
1563
|
+
quarantinedButNotAttemptToFixFqns.add(testFqn)
|
|
1404
1564
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1565
|
+
}
|
|
1566
|
+
if (testProperties.attemptToFix) {
|
|
1567
|
+
test._ddIsAttemptToFix = true
|
|
1568
|
+
// Prevent ATR or `--retries` from retrying attemptToFix tests
|
|
1569
|
+
test.retries = 0
|
|
1570
|
+
const fileSuite = getSuiteType(test, 'file')
|
|
1571
|
+
|
|
1572
|
+
if (!fileSuitesWithManagedTestsToProjects.has(fileSuite)) {
|
|
1573
|
+
fileSuitesWithManagedTestsToProjects.set(fileSuite, getSuiteType(test, 'project'))
|
|
1414
1574
|
}
|
|
1415
1575
|
}
|
|
1416
|
-
applyRetriesToTests(
|
|
1417
|
-
fileSuitesWithManagedTestsToProjects,
|
|
1418
|
-
(test) => test._ddIsAttemptToFix,
|
|
1419
|
-
[
|
|
1420
|
-
(test) => test._ddIsQuarantined && '_ddIsQuarantined',
|
|
1421
|
-
(test) => test._ddIsDisabled && '_ddIsDisabled',
|
|
1422
|
-
'_ddIsAttemptToFix',
|
|
1423
|
-
'_ddIsAttemptToFixRetry',
|
|
1424
|
-
],
|
|
1425
|
-
testManagementAttemptToFixRetries
|
|
1426
|
-
)
|
|
1427
1576
|
}
|
|
1577
|
+
applyRetriesToTests(
|
|
1578
|
+
fileSuitesWithManagedTestsToProjects,
|
|
1579
|
+
(test) => test._ddIsAttemptToFix,
|
|
1580
|
+
[
|
|
1581
|
+
(test) => test._ddIsQuarantined && '_ddIsQuarantined',
|
|
1582
|
+
(test) => test._ddIsDisabled && '_ddIsDisabled',
|
|
1583
|
+
'_ddIsAttemptToFix',
|
|
1584
|
+
'_ddIsAttemptToFixRetry',
|
|
1585
|
+
],
|
|
1586
|
+
testManagementAttemptToFixRetries
|
|
1587
|
+
)
|
|
1588
|
+
}
|
|
1428
1589
|
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
})
|
|
1437
|
-
return isImpacted
|
|
1590
|
+
if (isImpactedTestsEnabled) {
|
|
1591
|
+
const impactedTests = allTests.filter(test => {
|
|
1592
|
+
let isImpacted = false
|
|
1593
|
+
isModifiedCh.publish({
|
|
1594
|
+
filePath: test._requireFile,
|
|
1595
|
+
modifiedFiles,
|
|
1596
|
+
onDone: (isModified) => { isImpacted = isModified },
|
|
1438
1597
|
})
|
|
1598
|
+
return isImpacted
|
|
1599
|
+
})
|
|
1439
1600
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1601
|
+
const fileSuitesWithImpactedTestsToProjects = new Map()
|
|
1602
|
+
for (const impactedTest of impactedTests) {
|
|
1603
|
+
impactedTest._ddIsModified = true
|
|
1604
|
+
if (isEarlyFlakeDetectionEnabled && impactedTest.expectedStatus !== 'skipped') {
|
|
1605
|
+
markEfdManagedTest(impactedTest)
|
|
1606
|
+
const fileSuite = getSuiteType(impactedTest, 'file')
|
|
1607
|
+
if (!fileSuitesWithImpactedTestsToProjects.has(fileSuite)) {
|
|
1608
|
+
fileSuitesWithImpactedTestsToProjects.set(fileSuite, getSuiteType(impactedTest, 'project'))
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
// If something change in the file, all tests in the file are impacted, hence the () => true filter
|
|
1613
|
+
applyRetriesToTests(
|
|
1614
|
+
fileSuitesWithImpactedTestsToProjects,
|
|
1615
|
+
() => true,
|
|
1616
|
+
[
|
|
1617
|
+
'_ddIsModified',
|
|
1618
|
+
'_ddIsEfdRetry',
|
|
1619
|
+
(test) => (isKnownTestsEnabled && isNewTest(test) ? '_ddIsNew' : null),
|
|
1620
|
+
],
|
|
1621
|
+
getConfiguredEfdRetryCount(),
|
|
1622
|
+
(copiedTest, originalTest, retryIndex) => {
|
|
1623
|
+
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
1624
|
+
markEfdManagedTest(copiedTest)
|
|
1625
|
+
},
|
|
1626
|
+
getEfdRetryRepeatEachIndex
|
|
1627
|
+
)
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
if (isKnownTestsEnabled) {
|
|
1631
|
+
const newTests = allTests.filter(isNewTest)
|
|
1632
|
+
|
|
1633
|
+
const isFaulty = getIsFaultyEarlyFlakeDetection(
|
|
1634
|
+
allTests.map(test => getTestSuitePath(test._requireFile, rootDir)),
|
|
1635
|
+
knownTests.playwright,
|
|
1636
|
+
earlyFlakeDetectionFaultyThreshold
|
|
1637
|
+
)
|
|
1638
|
+
|
|
1639
|
+
if (isFaulty) {
|
|
1640
|
+
isEarlyFlakeDetectionEnabled = false
|
|
1641
|
+
isKnownTestsEnabled = false
|
|
1642
|
+
isEarlyFlakeDetectionFaulty = true
|
|
1643
|
+
} else {
|
|
1644
|
+
const fileSuitesWithNewTestsToProjects = new Map()
|
|
1645
|
+
for (const newTest of newTests) {
|
|
1646
|
+
newTest._ddIsNew = true
|
|
1647
|
+
if (isEarlyFlakeDetectionEnabled && newTest.expectedStatus !== 'skipped' && !newTest._ddIsModified) {
|
|
1648
|
+
// Prevent ATR or `--retries` from retrying new tests if EFD is enabled
|
|
1649
|
+
newTest.retries = 0
|
|
1650
|
+
markEfdManagedTest(newTest)
|
|
1651
|
+
const fileSuite = getSuiteType(newTest, 'file')
|
|
1652
|
+
if (!fileSuitesWithNewTestsToProjects.has(fileSuite)) {
|
|
1653
|
+
fileSuitesWithNewTestsToProjects.set(fileSuite, getSuiteType(newTest, 'project'))
|
|
1448
1654
|
}
|
|
1449
1655
|
}
|
|
1450
1656
|
}
|
|
1451
|
-
|
|
1657
|
+
|
|
1452
1658
|
applyRetriesToTests(
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
[
|
|
1456
|
-
'_ddIsModified',
|
|
1457
|
-
'_ddIsEfdRetry',
|
|
1458
|
-
(test) => (isKnownTestsEnabled && isNewTest(test) ? '_ddIsNew' : null),
|
|
1459
|
-
],
|
|
1659
|
+
fileSuitesWithNewTestsToProjects,
|
|
1660
|
+
isNewTest,
|
|
1661
|
+
['_ddIsNew', '_ddIsEfdRetry'],
|
|
1460
1662
|
getConfiguredEfdRetryCount(),
|
|
1461
1663
|
(copiedTest, originalTest, retryIndex) => {
|
|
1462
1664
|
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
@@ -1465,50 +1667,20 @@ addHook({
|
|
|
1465
1667
|
getEfdRetryRepeatEachIndex
|
|
1466
1668
|
)
|
|
1467
1669
|
}
|
|
1670
|
+
}
|
|
1468
1671
|
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
const isFaulty = getIsFaultyEarlyFlakeDetection(
|
|
1473
|
-
allTests.map(test => getTestSuitePath(test._requireFile, rootDir)),
|
|
1474
|
-
knownTests.playwright,
|
|
1475
|
-
earlyFlakeDetectionFaultyThreshold
|
|
1476
|
-
)
|
|
1477
|
-
|
|
1478
|
-
if (isFaulty) {
|
|
1479
|
-
isEarlyFlakeDetectionEnabled = false
|
|
1480
|
-
isKnownTestsEnabled = false
|
|
1481
|
-
isEarlyFlakeDetectionFaulty = true
|
|
1482
|
-
} else {
|
|
1483
|
-
const fileSuitesWithNewTestsToProjects = new Map()
|
|
1484
|
-
for (const newTest of newTests) {
|
|
1485
|
-
newTest._ddIsNew = true
|
|
1486
|
-
if (isEarlyFlakeDetectionEnabled && newTest.expectedStatus !== 'skipped' && !newTest._ddIsModified) {
|
|
1487
|
-
// Prevent ATR or `--retries` from retrying new tests if EFD is enabled
|
|
1488
|
-
newTest.retries = 0
|
|
1489
|
-
markEfdManagedTest(newTest)
|
|
1490
|
-
const fileSuite = getSuiteType(newTest, 'file')
|
|
1491
|
-
if (!fileSuitesWithNewTestsToProjects.has(fileSuite)) {
|
|
1492
|
-
fileSuitesWithNewTestsToProjects.set(fileSuite, getSuiteType(newTest, 'project'))
|
|
1493
|
-
}
|
|
1494
|
-
}
|
|
1495
|
-
}
|
|
1672
|
+
return createRootSuiteReturnValue
|
|
1673
|
+
}
|
|
1496
1674
|
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
1504
|
-
markEfdManagedTest(copiedTest)
|
|
1505
|
-
},
|
|
1506
|
-
getEfdRetryRepeatEachIndex
|
|
1507
|
-
)
|
|
1508
|
-
}
|
|
1509
|
-
}
|
|
1675
|
+
addHook({
|
|
1676
|
+
name: 'playwright',
|
|
1677
|
+
file: 'lib/runner/loadUtils.js',
|
|
1678
|
+
versions: ['>=1.38.0 <1.60.0'],
|
|
1679
|
+
}, (loadUtilsPackage) => {
|
|
1680
|
+
const oldCreateRootSuite = loadUtilsPackage.createRootSuite
|
|
1510
1681
|
|
|
1511
|
-
|
|
1682
|
+
async function newCreateRootSuite () {
|
|
1683
|
+
return processRootSuite(await oldCreateRootSuite.apply(this, arguments))
|
|
1512
1684
|
}
|
|
1513
1685
|
|
|
1514
1686
|
// We need to proxy the createRootSuite function because the function is not configurable
|
|
@@ -1522,32 +1694,47 @@ addHook({
|
|
|
1522
1694
|
})
|
|
1523
1695
|
})
|
|
1524
1696
|
|
|
1697
|
+
function prepareProcessHostStartRunner (processHost) {
|
|
1698
|
+
processHost._extraEnv = {
|
|
1699
|
+
...processHost._extraEnv,
|
|
1700
|
+
// Used to detect that we're in a playwright worker
|
|
1701
|
+
DD_PLAYWRIGHT_WORKER: '1',
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
function finishProcessHostStartRunner (processHost) {
|
|
1706
|
+
if (!processHost.process) {
|
|
1707
|
+
return
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
// We add a new listener to `processHost.process`, which represents the worker
|
|
1711
|
+
processHost.process.on('message', (message) => {
|
|
1712
|
+
if (message?.type === EFD_RETRY_COUNT_REQUEST) {
|
|
1713
|
+
sendEfdRetryCountToWorkerWhenAvailable(processHost.process, message.testId)
|
|
1714
|
+
return
|
|
1715
|
+
}
|
|
1716
|
+
if (message?.type === DD_PROPERTIES_REQUEST) {
|
|
1717
|
+
sendDdPropertiesToWorkerWhenAvailable(processHost.process, message.testId)
|
|
1718
|
+
return
|
|
1719
|
+
}
|
|
1720
|
+
// These messages are [code, payload]. The payload is test data
|
|
1721
|
+
if (Array.isArray(message) && message[0] === PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE) {
|
|
1722
|
+
workerReportCh.publish(message[1])
|
|
1723
|
+
}
|
|
1724
|
+
})
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1525
1727
|
// main process hook
|
|
1526
1728
|
addHook({
|
|
1527
1729
|
name: 'playwright',
|
|
1528
1730
|
file: 'lib/runner/processHost.js',
|
|
1529
|
-
versions: ['>=1.38.0'],
|
|
1731
|
+
versions: ['>=1.38.0 <1.60.0'],
|
|
1530
1732
|
}, (processHostPackage) => {
|
|
1531
1733
|
shimmer.wrap(processHostPackage.ProcessHost.prototype, 'startRunner', startRunner => async function () {
|
|
1532
|
-
this
|
|
1533
|
-
...this._extraEnv,
|
|
1534
|
-
// Used to detect that we're in a playwright worker
|
|
1535
|
-
DD_PLAYWRIGHT_WORKER: '1',
|
|
1536
|
-
}
|
|
1734
|
+
prepareProcessHostStartRunner(this)
|
|
1537
1735
|
|
|
1538
1736
|
const res = await startRunner.apply(this, arguments)
|
|
1539
|
-
|
|
1540
|
-
// We add a new listener to `this.process`, which is represents the worker
|
|
1541
|
-
this.process.on('message', (message) => {
|
|
1542
|
-
if (message?.type === EFD_RETRY_COUNT_REQUEST) {
|
|
1543
|
-
sendEfdRetryCountToWorkerWhenAvailable(this.process, message.testId)
|
|
1544
|
-
return
|
|
1545
|
-
}
|
|
1546
|
-
// These messages are [code, payload]. The payload is test data
|
|
1547
|
-
if (Array.isArray(message) && message[0] === PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE) {
|
|
1548
|
-
workerReportCh.publish(message[1])
|
|
1549
|
-
}
|
|
1550
|
-
})
|
|
1737
|
+
finishProcessHostStartRunner(this)
|
|
1551
1738
|
|
|
1552
1739
|
return res
|
|
1553
1740
|
})
|
|
@@ -1555,34 +1742,36 @@ addHook({
|
|
|
1555
1742
|
return processHostPackage
|
|
1556
1743
|
})
|
|
1557
1744
|
|
|
1745
|
+
async function handlePageGoto (page) {
|
|
1746
|
+
try {
|
|
1747
|
+
if (page && typeof page.evaluate === 'function') {
|
|
1748
|
+
const { isRumInstrumented, isRumActive, rumSamplingRate } = await page.evaluate(detectRum)
|
|
1749
|
+
if (isRumInstrumented && rumSamplingRate < 100 && !isRumActive) {
|
|
1750
|
+
log.debug("RUM was detected on the page, but it isn't active because the sampling rate is below 100%")
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
if (isRumActive) {
|
|
1754
|
+
testPageGotoCh.publish({
|
|
1755
|
+
isRumActive,
|
|
1756
|
+
page,
|
|
1757
|
+
})
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
} catch (e) {
|
|
1761
|
+
// ignore errors such as redirects, context destroyed, etc
|
|
1762
|
+
log.error('goto hook error', e)
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1558
1766
|
addHook({
|
|
1559
1767
|
name: 'playwright-core',
|
|
1560
1768
|
file: 'lib/client/page.js',
|
|
1561
|
-
versions: ['>=1.38.0'],
|
|
1769
|
+
versions: ['>=1.38.0 <1.60.0'],
|
|
1562
1770
|
}, (pagePackage) => {
|
|
1563
1771
|
shimmer.wrap(pagePackage.Page.prototype, 'goto', goto => async function (url, options) {
|
|
1564
1772
|
const response = await goto.apply(this, arguments)
|
|
1565
1773
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
try {
|
|
1569
|
-
if (page) {
|
|
1570
|
-
const { isRumInstrumented, isRumActive, rumSamplingRate } = await page.evaluate(detectRum)
|
|
1571
|
-
if (isRumInstrumented && rumSamplingRate < 100 && !isRumActive) {
|
|
1572
|
-
log.debug("RUM was detected on the page, but it isn't active because the sampling rate is below 100%")
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
if (isRumActive) {
|
|
1576
|
-
testPageGotoCh.publish({
|
|
1577
|
-
isRumActive,
|
|
1578
|
-
page,
|
|
1579
|
-
})
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
} catch (e) {
|
|
1583
|
-
// ignore errors such as redirects, context destroyed, etc
|
|
1584
|
-
log.error('goto hook error', e)
|
|
1585
|
-
}
|
|
1774
|
+
await handlePageGoto(this)
|
|
1586
1775
|
|
|
1587
1776
|
return response
|
|
1588
1777
|
})
|
|
@@ -1590,17 +1779,19 @@ addHook({
|
|
|
1590
1779
|
return pagePackage
|
|
1591
1780
|
})
|
|
1592
1781
|
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1782
|
+
function instrumentWorkerMainMethods (workerMain) {
|
|
1783
|
+
if (!workerMain || workerMain[kDdPlaywrightWorkerInstrumented] ||
|
|
1784
|
+
typeof workerMain._runTest !== 'function' || typeof workerMain.dispatchEvent !== 'function') {
|
|
1785
|
+
return workerMain
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
Object.defineProperty(workerMain, kDdPlaywrightWorkerInstrumented, { value: true })
|
|
1789
|
+
|
|
1599
1790
|
// we assume there's only a test running at a time
|
|
1600
1791
|
let steps = []
|
|
1601
1792
|
const stepInfoByStepId = {}
|
|
1602
1793
|
|
|
1603
|
-
shimmer.wrap(
|
|
1794
|
+
shimmer.wrap(workerMain, '_runTest', _runTest => async function (test) {
|
|
1604
1795
|
await waitForEfdRetryCount(test)
|
|
1605
1796
|
if (shouldSkipEfdRetry(test)) {
|
|
1606
1797
|
test._ddShouldSkipEfdRetry = true
|
|
@@ -1636,6 +1827,27 @@ addHook({
|
|
|
1636
1827
|
browserName,
|
|
1637
1828
|
}
|
|
1638
1829
|
testToCtx.set(test, testCtx)
|
|
1830
|
+
|
|
1831
|
+
// Wait for ddProperties to be received and processed. The main process sends
|
|
1832
|
+
// this during Playwright's testEnd event, which can happen before _runTest
|
|
1833
|
+
// resolves in 1.60 when retry clones run across multiple workers.
|
|
1834
|
+
let hasDdProperties = false
|
|
1835
|
+
const ddPropertiesDeferred = {}
|
|
1836
|
+
const ddPropertiesPromise = new Promise(resolve => {
|
|
1837
|
+
ddPropertiesDeferred.resolve = resolve
|
|
1838
|
+
})
|
|
1839
|
+
const ddPropertiesMessageHandler = ({ type, testId, properties }) => {
|
|
1840
|
+
if (type === DD_PROPERTIES_RESPONSE && testId === test.id) {
|
|
1841
|
+
hasDdProperties = true
|
|
1842
|
+
if (properties) {
|
|
1843
|
+
Object.assign(test, properties)
|
|
1844
|
+
}
|
|
1845
|
+
process.removeListener('message', ddPropertiesMessageHandler)
|
|
1846
|
+
ddPropertiesDeferred.resolve()
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
process.on('message', ddPropertiesMessageHandler)
|
|
1850
|
+
|
|
1639
1851
|
// TODO - In the future we may need to implement a mechanism to send test properties
|
|
1640
1852
|
// to the worker process before _runTest is called
|
|
1641
1853
|
testStartCh.runStores(testCtx, () => {
|
|
@@ -1708,6 +1920,16 @@ addHook({
|
|
|
1708
1920
|
}
|
|
1709
1921
|
}
|
|
1710
1922
|
|
|
1923
|
+
if (!hasDdProperties && process.send) {
|
|
1924
|
+
process.send({
|
|
1925
|
+
type: DD_PROPERTIES_REQUEST,
|
|
1926
|
+
testId: test.id,
|
|
1927
|
+
})
|
|
1928
|
+
} else if (!hasDdProperties) {
|
|
1929
|
+
process.removeListener('message', ddPropertiesMessageHandler)
|
|
1930
|
+
ddPropertiesDeferred.resolve()
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1711
1933
|
// testInfo.errors could be better than "error",
|
|
1712
1934
|
// which will only include timeout error (even though the test failed because of a different error)
|
|
1713
1935
|
|
|
@@ -1722,26 +1944,17 @@ addHook({
|
|
|
1722
1944
|
onDone = resolve
|
|
1723
1945
|
})
|
|
1724
1946
|
|
|
1725
|
-
// Wait for
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
process.removeListener('message', messageHandler)
|
|
1735
|
-
resolve()
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
|
|
1739
|
-
// Add the listener
|
|
1740
|
-
process.on('message', messageHandler)
|
|
1947
|
+
// Wait for the properties to be received, but do not block the worker forever if IPC fails.
|
|
1948
|
+
const ddPropertiesTimeoutPromise = new Promise(resolve => {
|
|
1949
|
+
const ddPropertiesTimeout = realSetTimeout(() => {
|
|
1950
|
+
process.removeListener('message', ddPropertiesMessageHandler)
|
|
1951
|
+
resolve()
|
|
1952
|
+
}, DD_PROPERTIES_TIMEOUT)
|
|
1953
|
+
ddPropertiesPromise.then(() => {
|
|
1954
|
+
realClearTimeout(ddPropertiesTimeout)
|
|
1955
|
+
})
|
|
1741
1956
|
})
|
|
1742
|
-
|
|
1743
|
-
// Wait for the properties to be received
|
|
1744
|
-
await ddPropertiesPromise
|
|
1957
|
+
await Promise.race([ddPropertiesPromise, ddPropertiesTimeoutPromise])
|
|
1745
1958
|
|
|
1746
1959
|
const finalStatus = getFinalStatus({
|
|
1747
1960
|
isFinalExecution: test._ddIsFinalExecution,
|
|
@@ -1787,7 +2000,7 @@ addHook({
|
|
|
1787
2000
|
|
|
1788
2001
|
// We reproduce what happens in `Dispatcher#_onStepBegin` and `Dispatcher#_onStepEnd`,
|
|
1789
2002
|
// since `startTime` and `duration` are not available directly in the worker process
|
|
1790
|
-
shimmer.wrap(
|
|
2003
|
+
shimmer.wrap(workerMain, 'dispatchEvent', dispatchEvent => function (event, payload) {
|
|
1791
2004
|
if (event === 'stepBegin') {
|
|
1792
2005
|
stepInfoByStepId[payload.stepId] = {
|
|
1793
2006
|
startTime: payload.wallTime,
|
|
@@ -1808,6 +2021,16 @@ addHook({
|
|
|
1808
2021
|
return dispatchEvent.apply(this, arguments)
|
|
1809
2022
|
})
|
|
1810
2023
|
|
|
2024
|
+
return workerMain
|
|
2025
|
+
}
|
|
2026
|
+
|
|
2027
|
+
// Only in worker
|
|
2028
|
+
addHook({
|
|
2029
|
+
name: 'playwright',
|
|
2030
|
+
file: 'lib/worker/workerMain.js',
|
|
2031
|
+
versions: ['>=1.38.0 <1.60.0'],
|
|
2032
|
+
}, (workerPackage) => {
|
|
2033
|
+
instrumentWorkerMainMethods(workerPackage.WorkerMain.prototype)
|
|
1811
2034
|
return workerPackage
|
|
1812
2035
|
})
|
|
1813
2036
|
|
|
@@ -1856,7 +2079,7 @@ function generateSummaryWrapper (generateSummary) {
|
|
|
1856
2079
|
addHook({
|
|
1857
2080
|
name: 'playwright',
|
|
1858
2081
|
file: 'lib/reporters/base.js',
|
|
1859
|
-
versions: ['>=1.38.0'],
|
|
2082
|
+
versions: ['>=1.38.0 <1.60.0'],
|
|
1860
2083
|
}, (reportersPackage) => {
|
|
1861
2084
|
// v1.50.0 changed the name of the base reporter from BaseReporter to TerminalReporter
|
|
1862
2085
|
if (reportersPackage.TerminalReporter) {
|