dd-trace 6.8.0 → 6.9.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/ci/diagnose.js +27 -25
- package/ci/init.js +6 -6
- package/ci/runbook.md +114 -20
- package/ci/test-optimization-validation/approval-artifacts.js +36 -3
- package/ci/test-optimization-validation/approval.js +78 -22
- package/ci/test-optimization-validation/blocker-category.js +24 -0
- package/ci/test-optimization-validation/ci-discovery.js +23 -17
- package/ci/test-optimization-validation/ci-package-scripts.js +34 -6
- package/ci/test-optimization-validation/ci-remediation.js +1 -1
- package/ci/test-optimization-validation/cli.js +280 -52
- package/ci/test-optimization-validation/command-blocker.js +159 -20
- package/ci/test-optimization-validation/command-output-policy.js +9 -3
- package/ci/test-optimization-validation/command-runner.js +9 -7
- package/ci/test-optimization-validation/environment.js +4 -2
- package/ci/test-optimization-validation/executable.js +15 -13
- package/ci/test-optimization-validation/execution-lock.js +70 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber-validation.json +3 -0
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +657 -5
- package/ci/test-optimization-validation/framework-adapters/vitest.js +517 -0
- package/ci/test-optimization-validation/generated-files.js +38 -28
- package/ci/test-optimization-validation/generated-test-contract.js +8 -4
- package/ci/test-optimization-validation/generated-verifier.js +24 -17
- package/ci/test-optimization-validation/literal-glob.js +52 -0
- package/ci/test-optimization-validation/manifest-loader.js +3 -0
- package/ci/test-optimization-validation/manifest-scaffold.js +300 -50
- package/ci/test-optimization-validation/manifest-schema.js +80 -17
- package/ci/test-optimization-validation/offline-fixtures.js +13 -4
- package/ci/test-optimization-validation/package-check.js +94 -0
- package/ci/test-optimization-validation/plan-writer.js +183 -10
- package/ci/test-optimization-validation/preflight-runner.js +110 -31
- package/ci/test-optimization-validation/project-build-artifact.js +31 -0
- package/ci/test-optimization-validation/redaction.js +18 -27
- package/ci/test-optimization-validation/report-writer.js +182 -48
- package/ci/test-optimization-validation/result-semantics.js +20 -3
- package/ci/test-optimization-validation/runner-command.js +40 -20
- package/ci/test-optimization-validation/runner-contract.js +79 -43
- package/ci/test-optimization-validation/scenarios/basic-reporting.js +59 -5
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +141 -35
- package/ci/test-optimization-validation/scenarios/helpers.js +30 -6
- package/ci/test-optimization-validation/static-diagnosis.js +10 -3
- package/ci/test-optimization-validation/test-output.js +2 -1
- package/ci/test-optimization-validation/validation-blocker.js +21 -0
- package/ci/vitest-no-worker-init-setup.mjs +30 -34
- package/index.d.ts +98 -1
- package/init.js +1 -17
- package/initialize.mjs +11 -0
- package/loader-hook.mjs +6 -4
- package/package.json +5 -3
- package/packages/datadog-core/src/utils/src/parse-tags.js +0 -1
- package/packages/datadog-esbuild/src/utils.js +16 -1
- package/packages/datadog-instrumentations/src/ai.js +61 -34
- package/packages/datadog-instrumentations/src/bluebird.js +6 -6
- package/packages/datadog-instrumentations/src/claude-agent-sdk.js +81 -5
- package/packages/datadog-instrumentations/src/cucumber.js +124 -47
- package/packages/datadog-instrumentations/src/dns.js +21 -2
- package/packages/datadog-instrumentations/src/electron.js +1 -0
- package/packages/datadog-instrumentations/src/fastify.js +3 -1
- package/packages/datadog-instrumentations/src/grpc/client.js +4 -2
- package/packages/datadog-instrumentations/src/helpers/callback-instrumentor.js +3 -1
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +33 -9
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +118 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/loader.mjs +20 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.js +32 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +61 -0
- package/packages/datadog-instrumentations/src/helpers/router-helper.js +37 -6
- package/packages/datadog-instrumentations/src/jest/coverage-backfill.js +1 -1
- package/packages/datadog-instrumentations/src/jest.js +979 -233
- package/packages/datadog-instrumentations/src/mocha/main.js +107 -9
- package/packages/datadog-instrumentations/src/mocha/utils.js +64 -42
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +25 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -18
- package/packages/datadog-instrumentations/src/mysql.js +1 -1
- package/packages/datadog-instrumentations/src/next.js +155 -23
- package/packages/datadog-instrumentations/src/openai.js +14 -0
- package/packages/datadog-instrumentations/src/pg.js +180 -6
- package/packages/datadog-instrumentations/src/playwright.js +173 -79
- package/packages/datadog-instrumentations/src/promise-js.js +4 -4
- package/packages/datadog-instrumentations/src/promise.js +3 -3
- package/packages/datadog-instrumentations/src/router.js +195 -19
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +43 -10
- package/packages/datadog-instrumentations/src/vitest-main.js +360 -56
- package/packages/datadog-instrumentations/src/vitest-util.js +42 -7
- package/packages/datadog-instrumentations/src/vitest-worker.js +234 -17
- package/packages/datadog-instrumentations/src/webdriverio.js +194 -18
- package/packages/datadog-instrumentations/src/when.js +3 -3
- package/packages/datadog-instrumentations/src/ws.js +5 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +59 -48
- package/packages/datadog-plugin-child_process/src/scrub-cmd-params.js +1 -3
- package/packages/datadog-plugin-cucumber/src/index.js +5 -1
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +50 -46
- package/packages/datadog-plugin-cypress/src/support.js +7 -4
- package/packages/datadog-plugin-electron/src/net.js +10 -4
- package/packages/datadog-plugin-http/src/client.js +1 -1
- package/packages/datadog-plugin-http2/src/client.js +1 -1
- package/packages/datadog-plugin-jest/src/index.js +1 -18
- package/packages/datadog-plugin-jest/src/util.js +2 -0
- package/packages/datadog-plugin-kafkajs/src/producer.js +2 -1
- package/packages/datadog-plugin-mocha/src/index.js +318 -1
- package/packages/datadog-plugin-next/src/index.js +57 -9
- package/packages/datadog-plugin-openai/src/tracing.js +4 -0
- package/packages/datadog-plugin-pg/src/index.js +65 -1
- package/packages/datadog-plugin-playwright/src/index.js +1 -1
- package/packages/datadog-plugin-vitest/src/index.js +95 -7
- package/packages/datadog-shimmer/src/shimmer.js +6 -2
- package/packages/dd-trace/src/aiguard/sdk.js +3 -0
- package/packages/dd-trace/src/appsec/{blocking.js → blocking/index.js} +26 -6
- package/packages/dd-trace/src/appsec/handlers/auth.js +62 -0
- package/packages/dd-trace/src/appsec/handlers/http-request.js +215 -0
- package/packages/dd-trace/src/appsec/handlers/http-response.js +78 -0
- package/packages/dd-trace/src/appsec/handlers/http-shared.js +24 -0
- package/packages/dd-trace/src/appsec/handlers/payments.js +104 -0
- package/packages/dd-trace/src/appsec/iast/analyzers/hsts-header-missing-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +1 -1
- package/packages/dd-trace/src/appsec/index.js +48 -476
- package/packages/dd-trace/src/appsec/sdk/track_event.js +9 -0
- package/packages/dd-trace/src/ci-visibility/coverage-report-discovery.js +46 -14
- package/packages/dd-trace/src/ci-visibility/efd-retry-policy.js +89 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +27 -13
- package/packages/dd-trace/src/ci-visibility/exporters/git/git_metadata.js +17 -0
- package/packages/dd-trace/src/ci-visibility/exporters/test-worker/index.js +21 -1
- package/packages/dd-trace/src/ci-visibility/intelligent-test-runner/get-skippable-suites.js +22 -7
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +9 -14
- package/packages/dd-trace/src/ci-visibility/requests/upload-coverage-report.js +21 -5
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache-schema.js +1 -1
- package/packages/dd-trace/src/ci-visibility/test-optimization-http-cache.js +5 -1
- package/packages/dd-trace/src/config/generated-config-types.d.ts +2 -0
- package/packages/dd-trace/src/config/helper.js +2 -0
- package/packages/dd-trace/src/config/index.js +16 -5
- package/packages/dd-trace/src/config/remote_config.js +1 -1
- package/packages/dd-trace/src/config/supported-configurations.json +8 -0
- package/packages/dd-trace/src/debugger/devtools_client/status.js +1 -1
- package/packages/dd-trace/src/encode/coverage-ci-visibility.js +0 -1
- package/packages/dd-trace/src/guardrails/apply-pm2-env.js +16 -0
- package/packages/dd-trace/src/guardrails/log.js +1 -10
- package/packages/dd-trace/src/llmobs/eval-metric.js +249 -0
- package/packages/dd-trace/src/llmobs/experiments/index.js +33 -25
- package/packages/dd-trace/src/llmobs/experiments/util.js +4 -2
- package/packages/dd-trace/src/llmobs/noop.js +2 -0
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/util.js +1 -1
- package/packages/dd-trace/src/llmobs/plugins/bedrockruntime.js +5 -5
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +1 -1
- package/packages/dd-trace/src/llmobs/sdk.js +149 -68
- package/packages/dd-trace/src/llmobs/telemetry.js +18 -0
- package/packages/dd-trace/src/llmobs/writers/base.js +3 -0
- package/packages/dd-trace/src/llmobs/writers/util.js +2 -0
- package/packages/dd-trace/src/log/channels.js +1 -9
- package/packages/dd-trace/src/log/levels.js +12 -0
- package/packages/dd-trace/src/openfeature/writers/exposures.js +1 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +1 -1
- package/packages/dd-trace/src/opentelemetry/metrics/meter.js +8 -4
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +1 -1
- package/packages/dd-trace/src/opentelemetry/span.js +4 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +21 -5
- package/packages/dd-trace/src/plugins/index.js +1 -0
- package/packages/dd-trace/src/plugins/util/ci.js +6 -0
- package/packages/dd-trace/src/plugins/util/git.js +6 -4
- package/packages/dd-trace/src/plugins/util/http-otel-semantics.js +2 -2
- package/packages/dd-trace/src/plugins/util/ip_extractor.js +1 -1
- package/packages/dd-trace/src/plugins/util/stacktrace.js +3 -0
- package/packages/dd-trace/src/plugins/util/test.js +319 -75
- package/packages/dd-trace/src/plugins/util/url.js +1 -1
- package/packages/dd-trace/src/plugins/util/web.js +23 -1
- package/packages/dd-trace/src/profiler.js +1 -1
- package/packages/dd-trace/src/profiling/exporters/event_serializer.js +1 -1
- package/packages/dd-trace/src/profiling/profilers/events.js +2 -2
- package/packages/dd-trace/src/proxy.js +1 -0
- package/packages/dd-trace/src/ritm.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +4 -2
- package/packages/dd-trace/src/sampling_rule.js +4 -2
- package/packages/dd-trace/src/service-naming/schemas/v0/storage.js +1 -1
- package/packages/dd-trace/src/service-naming/schemas/v1/storage.js +1 -1
- package/packages/dd-trace/src/telemetry/logs/index.js +3 -0
- package/packages/dd-trace/src/telemetry/telemetry.js +1 -1
- package/vendor/dist/pprof-format/index.js +1 -1
- package/version.js +10 -8
- /package/packages/dd-trace/src/appsec/{blocked_templates.js → blocking/templates.js} +0 -0
|
@@ -8,14 +8,20 @@ const { performance } = require('node:perf_hooks')
|
|
|
8
8
|
const satisfies = require('../../../vendor/dist/semifies')
|
|
9
9
|
|
|
10
10
|
const shimmer = require('../../datadog-shimmer')
|
|
11
|
+
const {
|
|
12
|
+
EMPTY_EFD_RETRY_POLICY,
|
|
13
|
+
getEfdRetryCountForDuration,
|
|
14
|
+
hasEfdRetries,
|
|
15
|
+
shouldSkipEfdRetry,
|
|
16
|
+
} = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
|
|
11
17
|
const {
|
|
12
18
|
parseAnnotations,
|
|
13
19
|
getTestSuitePath,
|
|
20
|
+
PLAYWRIGHT_WORKER_TELEMETRY_PAYLOAD_CODE,
|
|
14
21
|
PLAYWRIGHT_WORKER_TRACE_PAYLOAD_CODE,
|
|
15
22
|
getIsFaultyEarlyFlakeDetection,
|
|
16
23
|
DYNAMIC_NAME_RE,
|
|
17
|
-
|
|
18
|
-
getMaxEfdRetryCount,
|
|
24
|
+
recordTestManagementExecution,
|
|
19
25
|
recordAttemptToFixExecution,
|
|
20
26
|
logAttemptToFixTestExecution,
|
|
21
27
|
logTestOptimizationSummary,
|
|
@@ -50,6 +56,7 @@ const testSuiteStartCh = channel('ci:playwright:test-suite:start')
|
|
|
50
56
|
const testSuiteFinishCh = channel('ci:playwright:test-suite:finish')
|
|
51
57
|
|
|
52
58
|
const workerReportCh = channel('ci:playwright:worker:report')
|
|
59
|
+
const workerReportTelemetryCh = channel('ci:playwright:worker-report:telemetry')
|
|
53
60
|
const testPageGotoCh = channel('ci:playwright:test:page-goto')
|
|
54
61
|
|
|
55
62
|
const dispatcherRunCh = tracingChannel('orchestrion:playwright:Dispatcher_run')
|
|
@@ -66,6 +73,7 @@ const testSuiteToCtx = new Map()
|
|
|
66
73
|
const testSuiteToTestStatuses = new Map()
|
|
67
74
|
const testSuiteToErrors = new Map()
|
|
68
75
|
const testsToTestStatuses = new Map()
|
|
76
|
+
const activeRumPages = new Set()
|
|
69
77
|
|
|
70
78
|
const RUM_FLUSH_WAIT_TIME = getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')
|
|
71
79
|
const DD_PROPERTIES_TIMEOUT = 5000
|
|
@@ -89,8 +97,7 @@ const STATUS_TO_TEST_STATUS = {
|
|
|
89
97
|
let remainingTestsByFile = {}
|
|
90
98
|
let isKnownTestsEnabled = false
|
|
91
99
|
let isEarlyFlakeDetectionEnabled = false
|
|
92
|
-
let
|
|
93
|
-
let earlyFlakeDetectionSlowTestRetries = {}
|
|
100
|
+
let earlyFlakeDetectionRetryPolicy = EMPTY_EFD_RETRY_POLICY
|
|
94
101
|
let isEarlyFlakeDetectionFaulty = false
|
|
95
102
|
let earlyFlakeDetectionFaultyThreshold = 0
|
|
96
103
|
let isFlakyTestRetriesEnabled = false
|
|
@@ -102,6 +109,7 @@ let testManagementTests = {}
|
|
|
102
109
|
let isImpactedTestsEnabled = false
|
|
103
110
|
let modifiedFiles = {}
|
|
104
111
|
let quarantinedButNotAttemptToFixFqns = new Set()
|
|
112
|
+
let recordedTestOptimizationExecutions = new Set()
|
|
105
113
|
let testsReportedInGenerateSummary = new Set()
|
|
106
114
|
const newTestsWithDynamicNames = new Set()
|
|
107
115
|
const attemptToFixExecutions = new Map()
|
|
@@ -205,16 +213,8 @@ function getTestEfdKey (test) {
|
|
|
205
213
|
return [projectKey, repeatEachKey, testFqn].filter(Boolean).join(' ')
|
|
206
214
|
}
|
|
207
215
|
|
|
208
|
-
function getConfiguredEfdRetryCount () {
|
|
209
|
-
if (!earlyFlakeDetectionSlowTestRetries || !Object.keys(earlyFlakeDetectionSlowTestRetries).length) {
|
|
210
|
-
return earlyFlakeDetectionNumRetries
|
|
211
|
-
}
|
|
212
|
-
return getMaxEfdRetryCount(earlyFlakeDetectionSlowTestRetries)
|
|
213
|
-
}
|
|
214
|
-
|
|
215
216
|
function markEfdManagedTest (test) {
|
|
216
217
|
test._ddIsEfdManagedTest = true
|
|
217
|
-
test._ddEfdSlowTestRetries = earlyFlakeDetectionSlowTestRetries
|
|
218
218
|
efdManagedTestKeys.add(getTestEfdKey(test))
|
|
219
219
|
}
|
|
220
220
|
|
|
@@ -237,15 +237,18 @@ function registerEfdRetryTest (test) {
|
|
|
237
237
|
})
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
240
|
+
/**
|
|
241
|
+
* @returns {boolean}
|
|
242
|
+
*/
|
|
243
|
+
function shouldRunEarlyFlakeDetection () {
|
|
244
|
+
return isEarlyFlakeDetectionEnabled && hasEfdRetries(earlyFlakeDetectionRetryPolicy)
|
|
242
245
|
}
|
|
243
246
|
|
|
244
247
|
function isTestEfdManaged (test) {
|
|
245
248
|
return !!test._ddIsEfdManagedTest || (
|
|
246
249
|
(test._ddIsNew || test._ddIsModified) &&
|
|
247
250
|
!test._ddIsAttemptToFix &&
|
|
248
|
-
|
|
251
|
+
shouldRunEarlyFlakeDetection()
|
|
249
252
|
)
|
|
250
253
|
}
|
|
251
254
|
|
|
@@ -261,7 +264,8 @@ function getEfdRetryRepeatEachIndex (fileSuite, projectSuite, retryIndex, retryC
|
|
|
261
264
|
}
|
|
262
265
|
|
|
263
266
|
function getEfdRetryCountForTest (test) {
|
|
264
|
-
return efdRetryCountByTestKey.get(getTestEfdKey(test)) ??
|
|
267
|
+
return efdRetryCountByTestKey.get(getTestEfdKey(test)) ??
|
|
268
|
+
earlyFlakeDetectionRetryPolicy.schedulingRetryCount
|
|
265
269
|
}
|
|
266
270
|
|
|
267
271
|
function setEfdRetryCountForTest (test, retryCount) {
|
|
@@ -395,12 +399,12 @@ function waitForEfdRetryCount (test) {
|
|
|
395
399
|
})
|
|
396
400
|
}
|
|
397
401
|
|
|
398
|
-
function
|
|
402
|
+
function shouldSkipEfdRetryTest (test) {
|
|
399
403
|
if (!test._ddIsEfdRetry) {
|
|
400
404
|
return false
|
|
401
405
|
}
|
|
402
406
|
const retryCount = test._ddEfdRetryCount ?? efdRetryCountByTestKey.get(getTestEfdKey(test))
|
|
403
|
-
return
|
|
407
|
+
return shouldSkipEfdRetry(test._ddEfdRetryIndex, retryCount)
|
|
404
408
|
}
|
|
405
409
|
|
|
406
410
|
function getTestProperties (test) {
|
|
@@ -682,7 +686,7 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
682
686
|
if (_type === 'beforeAll' || _type === 'afterAll') {
|
|
683
687
|
return
|
|
684
688
|
}
|
|
685
|
-
if (
|
|
689
|
+
if (shouldSkipEfdRetryTest(test)) {
|
|
686
690
|
test._ddShouldSkipEfdRetry = true
|
|
687
691
|
return
|
|
688
692
|
}
|
|
@@ -731,6 +735,33 @@ function testBeginHandler (test, browserName, shouldCreateTestSpan) {
|
|
|
731
735
|
}
|
|
732
736
|
}
|
|
733
737
|
|
|
738
|
+
/**
|
|
739
|
+
* Records a skipped managed test for the end-of-session report.
|
|
740
|
+
*
|
|
741
|
+
* @param {object} test
|
|
742
|
+
* @param {string} testSuiteAbsolutePath
|
|
743
|
+
* @returns {void}
|
|
744
|
+
*/
|
|
745
|
+
function recordSkippedTestOptimizationExecution (test, testSuiteAbsolutePath) {
|
|
746
|
+
if (recordedTestOptimizationExecutions.has(test) ||
|
|
747
|
+
!test._ddIsAttemptToFix && !test._ddIsDisabled) return
|
|
748
|
+
|
|
749
|
+
const execution = {
|
|
750
|
+
testSuite: getTestSuitePath(testSuiteAbsolutePath, rootDir),
|
|
751
|
+
testName: getTestFullname(test),
|
|
752
|
+
status: 'skip',
|
|
753
|
+
isDisabled: test._ddIsDisabled,
|
|
754
|
+
isQuarantined: test._ddIsQuarantined,
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
if (test._ddIsAttemptToFix) {
|
|
758
|
+
recordAttemptToFixExecution(attemptToFixExecutions, execution)
|
|
759
|
+
} else {
|
|
760
|
+
recordTestManagementExecution(execution)
|
|
761
|
+
}
|
|
762
|
+
recordedTestOptimizationExecutions.add(test)
|
|
763
|
+
}
|
|
764
|
+
|
|
734
765
|
function finishTestSuiteIfDone (testSuiteAbsolutePath, projects) {
|
|
735
766
|
if (!shouldFinishTestSuite(testSuiteAbsolutePath)) {
|
|
736
767
|
return
|
|
@@ -741,6 +772,7 @@ function finishTestSuiteIfDone (testSuiteAbsolutePath, projects) {
|
|
|
741
772
|
|
|
742
773
|
for (const test of skippedTests) {
|
|
743
774
|
const browserName = getBrowserNameFromProjects(projects, test)
|
|
775
|
+
recordSkippedTestOptimizationExecution(test, testSuiteAbsolutePath)
|
|
744
776
|
testSkipCh.publish({
|
|
745
777
|
testName: getTestFullname(test),
|
|
746
778
|
testSuiteAbsolutePath,
|
|
@@ -799,7 +831,7 @@ function testEndHandler ({
|
|
|
799
831
|
return
|
|
800
832
|
}
|
|
801
833
|
|
|
802
|
-
if (test._ddShouldSkipEfdRetry ||
|
|
834
|
+
if (test._ddShouldSkipEfdRetry || shouldSkipEfdRetryTest(test)) {
|
|
803
835
|
test._ddShouldSkipEfdRetry = true
|
|
804
836
|
remainingTestsByFile[testSuiteAbsolutePath] = remainingTestsByFile[testSuiteAbsolutePath]
|
|
805
837
|
.filter(currentTest => currentTest !== test)
|
|
@@ -825,7 +857,7 @@ function testEndHandler ({
|
|
|
825
857
|
if (isEfdManagedTest && !test._ddIsEfdRetry && !efdRetryCountByTestKey.has(testEfdKey)) {
|
|
826
858
|
const testResult = results.at(-1)
|
|
827
859
|
const duration = testResult?.duration > 0 ? testResult.duration : performance.now() - test._ddStartTime
|
|
828
|
-
const retryCount =
|
|
860
|
+
const retryCount = getEfdRetryCountForDuration(duration, earlyFlakeDetectionRetryPolicy)
|
|
829
861
|
setEfdRetryCountForTest(test, retryCount)
|
|
830
862
|
if (retryCount === 0) {
|
|
831
863
|
efdSlowAbortedTests.add(testEfdKey)
|
|
@@ -833,21 +865,40 @@ function testEndHandler ({
|
|
|
833
865
|
}
|
|
834
866
|
|
|
835
867
|
const testProperties = getTestProperties(test)
|
|
868
|
+
const hasRecordedTestOptimizationExecution = recordedTestOptimizationExecutions.has(test)
|
|
836
869
|
|
|
837
|
-
if (
|
|
838
|
-
|
|
839
|
-
test._ddHasFailedAllRetries = false
|
|
840
|
-
test._ddHasPassedAttemptToFixRetries = false
|
|
841
|
-
|
|
842
|
-
recordAttemptToFixExecution(attemptToFixExecutions, {
|
|
870
|
+
if (!hasRecordedTestOptimizationExecution) {
|
|
871
|
+
recordTestManagementExecution({
|
|
843
872
|
testSuite: getTestSuitePath(test._requireFile, rootDir),
|
|
844
873
|
testName: getTestFullname(test),
|
|
845
874
|
status: testStatus,
|
|
875
|
+
isAttemptToFix: testProperties.attemptToFix,
|
|
846
876
|
isDisabled: testProperties.disabled,
|
|
847
877
|
isQuarantined: testProperties.quarantined,
|
|
848
878
|
})
|
|
849
879
|
}
|
|
850
880
|
|
|
881
|
+
if (testProperties.attemptToFix) {
|
|
882
|
+
test._ddHasFailedAttemptToFixRetries = false
|
|
883
|
+
test._ddHasFailedAllRetries = false
|
|
884
|
+
test._ddHasPassedAttemptToFixRetries = false
|
|
885
|
+
|
|
886
|
+
if (!hasRecordedTestOptimizationExecution) {
|
|
887
|
+
recordAttemptToFixExecution(attemptToFixExecutions, {
|
|
888
|
+
testSuite: getTestSuitePath(test._requireFile, rootDir),
|
|
889
|
+
testName: getTestFullname(test),
|
|
890
|
+
status: testStatus,
|
|
891
|
+
isDisabled: testProperties.disabled,
|
|
892
|
+
isQuarantined: testProperties.quarantined,
|
|
893
|
+
})
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
if (!hasRecordedTestOptimizationExecution &&
|
|
898
|
+
(testProperties.attemptToFix || testProperties.disabled || testProperties.quarantined)) {
|
|
899
|
+
recordedTestOptimizationExecutions.add(test)
|
|
900
|
+
}
|
|
901
|
+
|
|
851
902
|
if (testStatuses.length === testManagementAttemptToFixRetries + 1 && testProperties.attemptToFix) {
|
|
852
903
|
if (testStatuses.includes('fail')) {
|
|
853
904
|
test._ddHasFailedAttemptToFixRetries = true
|
|
@@ -1000,7 +1051,7 @@ function prepareDispatcherRun (dispatcher, args) {
|
|
|
1000
1051
|
testGroups = testGroups.filter(group => group.tests.length > 0)
|
|
1001
1052
|
}
|
|
1002
1053
|
|
|
1003
|
-
if (
|
|
1054
|
+
if (shouldRunEarlyFlakeDetection()) {
|
|
1004
1055
|
testGroups = deferEfdRetryGroups(testGroups)
|
|
1005
1056
|
}
|
|
1006
1057
|
|
|
@@ -1221,8 +1272,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1221
1272
|
if (!err) {
|
|
1222
1273
|
isKnownTestsEnabled = libraryConfig.isKnownTestsEnabled
|
|
1223
1274
|
isEarlyFlakeDetectionEnabled = libraryConfig.isEarlyFlakeDetectionEnabled
|
|
1224
|
-
|
|
1225
|
-
earlyFlakeDetectionSlowTestRetries = libraryConfig.earlyFlakeDetectionSlowTestRetries ?? {}
|
|
1275
|
+
earlyFlakeDetectionRetryPolicy = libraryConfig.earlyFlakeDetectionRetryPolicy ?? EMPTY_EFD_RETRY_POLICY
|
|
1226
1276
|
earlyFlakeDetectionFaultyThreshold = libraryConfig.earlyFlakeDetectionFaultyThreshold
|
|
1227
1277
|
isFlakyTestRetriesEnabled = libraryConfig.isFlakyTestRetriesEnabled
|
|
1228
1278
|
flakyTestRetriesCount = libraryConfig.flakyTestRetriesCount
|
|
@@ -1382,6 +1432,7 @@ function runAllTestsWrapper (runAllTests, playwrightVersion) {
|
|
|
1382
1432
|
startedSuites = []
|
|
1383
1433
|
remainingTestsByFile = {}
|
|
1384
1434
|
quarantinedButNotAttemptToFixFqns = new Set()
|
|
1435
|
+
recordedTestOptimizationExecutions = new Set()
|
|
1385
1436
|
testsReportedInGenerateSummary = new Set()
|
|
1386
1437
|
efdManagedTestKeys.clear()
|
|
1387
1438
|
efdRetryCountByTestKey.clear()
|
|
@@ -1589,6 +1640,30 @@ addHook({
|
|
|
1589
1640
|
return suiteUtilsPackage
|
|
1590
1641
|
})
|
|
1591
1642
|
|
|
1643
|
+
/**
|
|
1644
|
+
* @typedef {Record<string, unknown>} PlaywrightTest
|
|
1645
|
+
*/
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* @typedef {Record<string, unknown>} PlaywrightSuite
|
|
1649
|
+
*/
|
|
1650
|
+
|
|
1651
|
+
/**
|
|
1652
|
+
* @typedef {{ _fullProject: unknown, _addSuite: (suite: unknown) => void }} PlaywrightProjectSuite
|
|
1653
|
+
*/
|
|
1654
|
+
|
|
1655
|
+
/**
|
|
1656
|
+
* @typedef {(
|
|
1657
|
+
* copiedTest: PlaywrightTest, originalTest: PlaywrightTest, repeatEachIndex: number
|
|
1658
|
+
* ) => void} ConfigureCopiedTest
|
|
1659
|
+
*/
|
|
1660
|
+
|
|
1661
|
+
/**
|
|
1662
|
+
* @typedef {(
|
|
1663
|
+
* fileSuite: PlaywrightSuite, projectSuite: PlaywrightProjectSuite, repeatEachIndex: number, numRetries: number
|
|
1664
|
+
* ) => number} GetRetryRepeatEachIndex
|
|
1665
|
+
*/
|
|
1666
|
+
|
|
1592
1667
|
/**
|
|
1593
1668
|
* We could repeat the logic of `applyRepeatEachIndex` here, but it'd be more risky
|
|
1594
1669
|
* as playwright could change it at any time.
|
|
@@ -1602,6 +1677,13 @@ addHook({
|
|
|
1602
1677
|
* - we clone each of these file suites for each repeat index
|
|
1603
1678
|
* - we execute `applyRepeatEachIndex` for each of these cloned file suites
|
|
1604
1679
|
* - we add the cloned file suites to the project suite
|
|
1680
|
+
*
|
|
1681
|
+
* @param {Map<PlaywrightSuite, PlaywrightProjectSuite>} fileSuitesWithTestsToRetry
|
|
1682
|
+
* @param {(test: PlaywrightTest) => unknown} filterTest
|
|
1683
|
+
* @param {Array<string | ((test: PlaywrightTest) => unknown)>} tagsToApply
|
|
1684
|
+
* @param {number} numRetries
|
|
1685
|
+
* @param {ConfigureCopiedTest} [configureCopiedTest]
|
|
1686
|
+
* @param {GetRetryRepeatEachIndex} [getRetryRepeatEachIndex]
|
|
1605
1687
|
*/
|
|
1606
1688
|
function applyRetriesToTests (
|
|
1607
1689
|
fileSuitesWithTestsToRetry,
|
|
@@ -1709,7 +1791,7 @@ function processRootSuite (createRootSuiteReturnValue) {
|
|
|
1709
1791
|
const fileSuitesWithImpactedTestsToProjects = new Map()
|
|
1710
1792
|
for (const impactedTest of impactedTests) {
|
|
1711
1793
|
impactedTest._ddIsModified = true
|
|
1712
|
-
if (
|
|
1794
|
+
if (shouldRunEarlyFlakeDetection() && impactedTest.expectedStatus !== 'skipped') {
|
|
1713
1795
|
markEfdManagedTest(impactedTest)
|
|
1714
1796
|
const fileSuite = getSuiteType(impactedTest, 'file')
|
|
1715
1797
|
if (!fileSuitesWithImpactedTestsToProjects.has(fileSuite)) {
|
|
@@ -1726,7 +1808,7 @@ function processRootSuite (createRootSuiteReturnValue) {
|
|
|
1726
1808
|
'_ddIsEfdRetry',
|
|
1727
1809
|
(test) => (isKnownTestsEnabled && isNewTest(test) ? '_ddIsNew' : null),
|
|
1728
1810
|
],
|
|
1729
|
-
|
|
1811
|
+
earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
|
|
1730
1812
|
(copiedTest, originalTest, retryIndex) => {
|
|
1731
1813
|
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
1732
1814
|
markEfdManagedTest(copiedTest)
|
|
@@ -1752,8 +1834,8 @@ function processRootSuite (createRootSuiteReturnValue) {
|
|
|
1752
1834
|
const fileSuitesWithNewTestsToProjects = new Map()
|
|
1753
1835
|
for (const newTest of newTests) {
|
|
1754
1836
|
newTest._ddIsNew = true
|
|
1755
|
-
if (
|
|
1756
|
-
// Prevent ATR or `--retries` from retrying
|
|
1837
|
+
if (shouldRunEarlyFlakeDetection() && newTest.expectedStatus !== 'skipped' && !newTest._ddIsModified) {
|
|
1838
|
+
// Prevent ATR or `--retries` from retrying tests that EFD manages.
|
|
1757
1839
|
newTest.retries = 0
|
|
1758
1840
|
markEfdManagedTest(newTest)
|
|
1759
1841
|
const fileSuite = getSuiteType(newTest, 'file')
|
|
@@ -1767,7 +1849,7 @@ function processRootSuite (createRootSuiteReturnValue) {
|
|
|
1767
1849
|
fileSuitesWithNewTestsToProjects,
|
|
1768
1850
|
isNewTest,
|
|
1769
1851
|
['_ddIsNew', '_ddIsEfdRetry'],
|
|
1770
|
-
|
|
1852
|
+
earlyFlakeDetectionRetryPolicy.schedulingRetryCount,
|
|
1771
1853
|
(copiedTest, originalTest, retryIndex) => {
|
|
1772
1854
|
markEfdRetryTest(copiedTest, retryIndex, originalTest)
|
|
1773
1855
|
markEfdManagedTest(copiedTest)
|
|
@@ -1831,6 +1913,8 @@ function finishProcessHostStartRunner (processHost) {
|
|
|
1831
1913
|
serializedTraces: message[1],
|
|
1832
1914
|
screenshots: processHost[kDdPlaywrightFailureScreenshots]?.shift(),
|
|
1833
1915
|
})
|
|
1916
|
+
} else if (Array.isArray(message) && message[0] === PLAYWRIGHT_WORKER_TELEMETRY_PAYLOAD_CODE) {
|
|
1917
|
+
workerReportTelemetryCh.publish(message[1])
|
|
1834
1918
|
}
|
|
1835
1919
|
})
|
|
1836
1920
|
}
|
|
@@ -1879,6 +1963,8 @@ async function handlePageGoto (page) {
|
|
|
1879
1963
|
return
|
|
1880
1964
|
}
|
|
1881
1965
|
|
|
1966
|
+
activeRumPages.add(page)
|
|
1967
|
+
|
|
1882
1968
|
let browserVersion
|
|
1883
1969
|
try {
|
|
1884
1970
|
browserVersion = page.context().browser()?.version()
|
|
@@ -1915,6 +2001,54 @@ async function handlePageGoto (page) {
|
|
|
1915
2001
|
}
|
|
1916
2002
|
}
|
|
1917
2003
|
|
|
2004
|
+
/**
|
|
2005
|
+
* Stops a RUM session and expires its correlation cookie.
|
|
2006
|
+
*
|
|
2007
|
+
* @param {import('playwright-core').Page} page
|
|
2008
|
+
* @returns {Promise<void>}
|
|
2009
|
+
*/
|
|
2010
|
+
async function cleanupRumPage (page) {
|
|
2011
|
+
try {
|
|
2012
|
+
const isRumActive = await page.evaluate(stopRumSession)
|
|
2013
|
+
|
|
2014
|
+
if (!isRumActive) {
|
|
2015
|
+
return
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
// Give some time RUM to flush data, similar to what we do in selenium
|
|
2019
|
+
await new Promise(resolve => realSetTimeout(resolve, RUM_FLUSH_WAIT_TIME))
|
|
2020
|
+
const url = page.url()
|
|
2021
|
+
if (url) {
|
|
2022
|
+
const domain = new URL(url).hostname
|
|
2023
|
+
await page.context().addCookies([{
|
|
2024
|
+
name: RUM_COOKIE_NAME,
|
|
2025
|
+
value: '',
|
|
2026
|
+
domain,
|
|
2027
|
+
path: '/',
|
|
2028
|
+
expires: 0,
|
|
2029
|
+
}])
|
|
2030
|
+
} else {
|
|
2031
|
+
log.error('RUM is active but page.url() is not available')
|
|
2032
|
+
}
|
|
2033
|
+
} catch (error) {
|
|
2034
|
+
// Cleanup must not change the test result.
|
|
2035
|
+
log.error('afterEach hook error', error)
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
/**
|
|
2040
|
+
* Stops RUM sessions and expires correlation cookies for pages visited by the current test.
|
|
2041
|
+
*
|
|
2042
|
+
* @returns {Promise<void[]>|undefined}
|
|
2043
|
+
*/
|
|
2044
|
+
function cleanupRumPages () {
|
|
2045
|
+
if (activeRumPages.size === 0) return
|
|
2046
|
+
|
|
2047
|
+
const cleanupPromises = Array.from(activeRumPages, cleanupRumPage)
|
|
2048
|
+
activeRumPages.clear()
|
|
2049
|
+
return Promise.all(cleanupPromises)
|
|
2050
|
+
}
|
|
2051
|
+
|
|
1918
2052
|
addHook({
|
|
1919
2053
|
name: 'playwright-core',
|
|
1920
2054
|
file: 'lib/client/page.js',
|
|
@@ -1955,7 +2089,7 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
1955
2089
|
test.expectedStatus = 'skipped'
|
|
1956
2090
|
}
|
|
1957
2091
|
await waitForEfdRetryCount(test)
|
|
1958
|
-
if (
|
|
2092
|
+
if (shouldSkipEfdRetryTest(test)) {
|
|
1959
2093
|
test._ddShouldSkipEfdRetry = true
|
|
1960
2094
|
test.expectedStatus = 'skipped'
|
|
1961
2095
|
}
|
|
@@ -1964,6 +2098,7 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
1964
2098
|
}
|
|
1965
2099
|
test._ddStartTime = performance.now()
|
|
1966
2100
|
steps = []
|
|
2101
|
+
activeRumPages.clear()
|
|
1967
2102
|
|
|
1968
2103
|
const {
|
|
1969
2104
|
_requireFile: testSuiteAbsolutePath,
|
|
@@ -2027,34 +2162,7 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2027
2162
|
if (!existAfterEachHook) {
|
|
2028
2163
|
test.parent._hooks.push({
|
|
2029
2164
|
type: 'afterEach',
|
|
2030
|
-
fn:
|
|
2031
|
-
try {
|
|
2032
|
-
if (page) {
|
|
2033
|
-
const isRumActive = await page.evaluate(stopRumSession)
|
|
2034
|
-
|
|
2035
|
-
if (isRumActive) {
|
|
2036
|
-
// Give some time RUM to flush data, similar to what we do in selenium
|
|
2037
|
-
await new Promise(resolve => realSetTimeout(resolve, RUM_FLUSH_WAIT_TIME))
|
|
2038
|
-
const url = page.url()
|
|
2039
|
-
if (url) {
|
|
2040
|
-
const domain = new URL(url).hostname
|
|
2041
|
-
await page.context().addCookies([{
|
|
2042
|
-
name: RUM_COOKIE_NAME,
|
|
2043
|
-
value: '',
|
|
2044
|
-
domain,
|
|
2045
|
-
path: '/',
|
|
2046
|
-
expires: 0,
|
|
2047
|
-
}])
|
|
2048
|
-
} else {
|
|
2049
|
-
log.error('RUM is active but page.url() is not available')
|
|
2050
|
-
}
|
|
2051
|
-
}
|
|
2052
|
-
}
|
|
2053
|
-
} catch (e) {
|
|
2054
|
-
// ignore errors
|
|
2055
|
-
log.error('afterEach hook error', e)
|
|
2056
|
-
}
|
|
2057
|
-
},
|
|
2165
|
+
fn: cleanupRumPages,
|
|
2058
2166
|
title: 'afterEach hook',
|
|
2059
2167
|
_ddHook: true,
|
|
2060
2168
|
})
|
|
@@ -2067,21 +2175,6 @@ function instrumentWorkerMainMethods (workerMain) {
|
|
|
2067
2175
|
await res
|
|
2068
2176
|
|
|
2069
2177
|
const { status, error, annotations, retry, testId } = testInfo
|
|
2070
|
-
const testEfdKey = getTestEfdKey(test)
|
|
2071
|
-
const isEfdManagedTest = isTestEfdManaged(test)
|
|
2072
|
-
if (isEfdManagedTest && !test._ddIsEfdRetry && !efdRetryCountByTestKey.has(testEfdKey)) {
|
|
2073
|
-
const duration = test.results?.at(-1)?.duration > 0
|
|
2074
|
-
? test.results.at(-1).duration
|
|
2075
|
-
: performance.now() - test._ddStartTime
|
|
2076
|
-
const retryCount = getEfdRetryCount(
|
|
2077
|
-
duration,
|
|
2078
|
-
getTestEfdSlowTestRetries(test)
|
|
2079
|
-
)
|
|
2080
|
-
setEfdRetryCountForTest(test, retryCount)
|
|
2081
|
-
if (retryCount === 0) {
|
|
2082
|
-
efdSlowAbortedTests.add(testEfdKey)
|
|
2083
|
-
}
|
|
2084
|
-
}
|
|
2085
2178
|
|
|
2086
2179
|
if (!hasDdProperties && process.send) {
|
|
2087
2180
|
process.send({
|
|
@@ -2213,6 +2306,7 @@ function generateSummaryWrapper (generateSummary) {
|
|
|
2213
2306
|
} = test
|
|
2214
2307
|
const browserName = getBrowserNameFromProjects(sessionProjects, test)
|
|
2215
2308
|
|
|
2309
|
+
recordSkippedTestOptimizationExecution(test, testSuiteAbsolutePath)
|
|
2216
2310
|
testSkipCh.publish({
|
|
2217
2311
|
testName: getTestFullname(test),
|
|
2218
2312
|
testSuiteAbsolutePath,
|
|
@@ -7,9 +7,9 @@ const { wrapThen } = require('./helpers/promise')
|
|
|
7
7
|
addHook({
|
|
8
8
|
name: 'promise-js',
|
|
9
9
|
versions: ['>=0.0.3'],
|
|
10
|
-
},
|
|
11
|
-
if (
|
|
12
|
-
shimmer.wrap(
|
|
10
|
+
}, LibraryPromise => {
|
|
11
|
+
if (LibraryPromise !== global.Promise) {
|
|
12
|
+
shimmer.wrap(LibraryPromise.prototype, 'then', wrapThen)
|
|
13
13
|
}
|
|
14
|
-
return
|
|
14
|
+
return LibraryPromise
|
|
15
15
|
})
|
|
@@ -8,7 +8,7 @@ addHook({
|
|
|
8
8
|
name: 'promise',
|
|
9
9
|
file: 'lib/core.js',
|
|
10
10
|
versions: ['>=7'],
|
|
11
|
-
},
|
|
12
|
-
shimmer.wrap(
|
|
13
|
-
return
|
|
11
|
+
}, LibraryPromise => {
|
|
12
|
+
shimmer.wrap(LibraryPromise.prototype, 'then', wrapThen)
|
|
13
|
+
return LibraryPromise
|
|
14
14
|
})
|