dd-trace 6.12.0 → 6.14.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 +3 -1
- package/ci/init.js +8 -3
- package/ci/test-optimization-validation/ci-package-scripts.js +2 -2
- package/ci/test-optimization-validation/ci-remediation.js +6 -6
- package/ci/test-optimization-validation/command-blocker.js +7 -5
- package/ci/test-optimization-validation/framework-adapters/cucumber.js +1 -1
- package/ci/test-optimization-validation/framework-adapters/cypress.js +9 -11
- package/ci/test-optimization-validation/framework-adapters/vitest.js +3 -3
- package/ci/test-optimization-validation/package-check.js +1 -1
- package/ci/test-optimization-validation/report-writer.js +7 -5
- package/ci/test-optimization-validation/scenarios/ci-wiring.js +5 -4
- package/ci/vitest-no-worker-init-runner-setup.mjs +8 -0
- package/ci/vitest-no-worker-init-setup.mjs +13 -13
- package/ci/vitest-no-worker-init-v5-setup.mjs +4 -0
- package/index.d.ts +127 -26
- package/package.json +22 -19
- package/packages/datadog-esbuild/index.js +3 -1
- package/packages/datadog-instrumentations/src/bunyan.js +4 -2
- package/packages/datadog-instrumentations/src/cypress-config.js +19 -17
- package/packages/datadog-instrumentations/src/fs.js +24 -20
- package/packages/datadog-instrumentations/src/google-cloud-vertexai.js +2 -2
- package/packages/datadog-instrumentations/src/graphql.js +16 -16
- package/packages/datadog-instrumentations/src/helpers/bunyan.js +12 -3
- package/packages/datadog-instrumentations/src/helpers/graphql-jit-runtime.js +504 -0
- package/packages/datadog-instrumentations/src/helpers/hooks.js +2 -0
- package/packages/datadog-instrumentations/src/helpers/pool-acquire.js +266 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/compiler.js +1 -2
- package/packages/datadog-instrumentations/src/helpers/rewriter/index.js +95 -30
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/ai.js +0 -25
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql-jit.js +83 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/graphql.js +85 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/index.js +1 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/mercurius.js +30 -12
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/playwright.js +13 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/instrumentations/webdriverio.js +279 -0
- package/packages/datadog-instrumentations/src/helpers/rewriter/targets.json +12 -1
- package/packages/datadog-instrumentations/src/helpers/rewriter/transforms.js +562 -27
- package/packages/datadog-instrumentations/src/jest.js +165 -38
- package/packages/datadog-instrumentations/src/knex.js +76 -0
- package/packages/datadog-instrumentations/src/mariadb-bundle.js +1597 -0
- package/packages/datadog-instrumentations/src/mariadb.js +13 -0
- package/packages/datadog-instrumentations/src/mocha/common.js +59 -2
- package/packages/datadog-instrumentations/src/mocha/main.js +25 -15
- package/packages/datadog-instrumentations/src/mocha/utils.js +2 -1
- package/packages/datadog-instrumentations/src/mocha/webdriverio-protocol.js +98 -0
- package/packages/datadog-instrumentations/src/mocha/worker.js +12 -8
- package/packages/datadog-instrumentations/src/net.js +14 -17
- package/packages/datadog-instrumentations/src/next.js +212 -62
- package/packages/datadog-instrumentations/src/openai.js +42 -40
- package/packages/datadog-instrumentations/src/pg.js +15 -0
- package/packages/datadog-instrumentations/src/pino.js +15 -4
- package/packages/datadog-instrumentations/src/playwright.js +137 -27
- package/packages/datadog-instrumentations/src/rum-browser-scripts.js +40 -0
- package/packages/datadog-instrumentations/src/sequelize.js +94 -2
- package/packages/datadog-instrumentations/src/undici.js +53 -4
- package/packages/datadog-instrumentations/src/vitest-main-no-worker-init.js +44 -7
- package/packages/datadog-instrumentations/src/vitest-main.js +92 -9
- package/packages/datadog-instrumentations/src/vitest-worker.js +82 -14
- package/packages/datadog-instrumentations/src/webdriverio.js +986 -1
- package/packages/datadog-instrumentations/src/winston.js +6 -3
- package/packages/datadog-plugin-amqp10/src/consumer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/producer.js +3 -1
- package/packages/datadog-plugin-amqp10/src/util.js +2 -1
- package/packages/datadog-plugin-avsc/src/schema_iterator.js +6 -1
- package/packages/datadog-plugin-aws-sdk/src/services/bedrockruntime/utils.js +16 -5
- package/packages/datadog-plugin-cassandra-driver/src/index.js +8 -3
- package/packages/datadog-plugin-cucumber/src/index.js +0 -2
- package/packages/datadog-plugin-cypress/src/cypress-plugin.js +262 -48
- package/packages/datadog-plugin-cypress/src/support.js +1 -0
- package/packages/datadog-plugin-google-cloud-pubsub/src/client.js +4 -1
- package/packages/datadog-plugin-graphql/src/execute.js +998 -196
- package/packages/datadog-plugin-graphql/src/index.js +27 -7
- package/packages/datadog-plugin-graphql/src/jit.js +136 -0
- package/packages/datadog-plugin-graphql/src/request.js +18 -27
- package/packages/datadog-plugin-graphql/src/resolve-error.js +108 -0
- package/packages/datadog-plugin-graphql/src/tools/signature.js +1 -2
- package/packages/datadog-plugin-graphql/src/tools/transforms.js +45 -3
- package/packages/datadog-plugin-graphql/src/utils.js +208 -120
- package/packages/datadog-plugin-graphql/src/validate.js +10 -23
- package/packages/datadog-plugin-http/src/client.js +2 -15
- package/packages/datadog-plugin-http2/src/client.js +2 -15
- package/packages/datadog-plugin-jest/src/index.js +0 -6
- package/packages/datadog-plugin-langchain/src/tracing.js +7 -0
- package/packages/datadog-plugin-mocha/src/index.js +403 -75
- package/packages/datadog-plugin-mongodb-core/src/query.js +6 -0
- package/packages/datadog-plugin-mysql/src/index.js +41 -15
- package/packages/datadog-plugin-net/src/tcp.js +3 -1
- package/packages/datadog-plugin-next/src/index.js +63 -29
- package/packages/datadog-plugin-openai-agents/src/integration.js +15 -11
- package/packages/datadog-plugin-playwright/src/index.js +176 -28
- package/packages/datadog-plugin-undici/src/index.js +29 -56
- package/packages/datadog-plugin-vitest/src/index.js +6 -12
- package/packages/dd-trace/src/aiguard/evaluation.js +96 -21
- package/packages/dd-trace/src/aiguard/integrations/vercel-ai.js +2 -3
- package/packages/dd-trace/src/aiguard/messages/anthropic.js +29 -10
- package/packages/dd-trace/src/aiguard/messages/openai.js +8 -1
- package/packages/dd-trace/src/aiguard/messages/vercel-ai.js +20 -6
- package/packages/dd-trace/src/aiguard/noop.js +9 -1
- package/packages/dd-trace/src/aiguard/redaction.js +183 -0
- package/packages/dd-trace/src/aiguard/sdk.js +7 -2
- package/packages/dd-trace/src/aiguard/tags.js +2 -0
- package/packages/dd-trace/src/appsec/api_security/index.js +23 -6
- package/packages/dd-trace/src/appsec/api_security/sampler.js +99 -40
- package/packages/dd-trace/src/appsec/graphql.js +6 -2
- package/packages/dd-trace/src/appsec/iast/taint-tracking/plugin.js +3 -3
- package/packages/dd-trace/src/appsec/iast/taint-tracking/taint-tracking-impl.js +2 -1
- package/packages/dd-trace/src/appsec/iast/telemetry/span-tags.js +8 -2
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/sql-sensitive-analyzer.js +11 -36
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/evidence-redaction/sensitive-analyzers/url-sensitive-analyzer.js +1 -1
- package/packages/dd-trace/src/appsec/iast/vulnerabilities-formatter/utils.js +10 -9
- package/packages/dd-trace/src/appsec/lambda.js +44 -26
- package/packages/dd-trace/src/appsec/rule_manager.js +2 -0
- package/packages/dd-trace/src/ci-visibility/encode/json-encoder.js +32 -2
- package/packages/dd-trace/src/ci-visibility/exporters/agent-proxy/index.js +8 -8
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/coverage-writer.js +6 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/di-logs-writer.js +20 -4
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/request-tracker.js +7 -150
- package/packages/dd-trace/src/ci-visibility/exporters/agentless/writer.js +7 -6
- package/packages/dd-trace/src/ci-visibility/exporters/agents.js +54 -0
- package/packages/dd-trace/src/ci-visibility/exporters/ci-validation/writer.js +1 -2
- package/packages/dd-trace/src/ci-visibility/exporters/ci-visibility-exporter.js +397 -234
- package/packages/dd-trace/src/ci-visibility/exporters/limits.js +9 -0
- package/packages/dd-trace/src/ci-visibility/exporters/request.js +323 -31
- package/packages/dd-trace/src/ci-visibility/exporters/settings-cache-key.js +42 -0
- package/packages/dd-trace/src/ci-visibility/final-flush.js +21 -0
- package/packages/dd-trace/src/ci-visibility/requests/fs-cache.js +57 -18
- package/packages/dd-trace/src/ci-visibility/requests/get-library-configuration.js +0 -3
- package/packages/dd-trace/src/ci-visibility/requests/upload-test-screenshot.js +164 -71
- package/packages/dd-trace/src/ci-visibility/requests/video-request.js +176 -0
- package/packages/dd-trace/src/ci-visibility/telemetry.js +10 -7
- package/packages/dd-trace/src/ci-visibility/test-video.js +61 -0
- package/packages/dd-trace/src/config/generated-config-types.d.ts +10 -0
- package/packages/dd-trace/src/config/index.js +75 -14
- package/packages/dd-trace/src/config/parsers.js +11 -1
- package/packages/dd-trace/src/config/stable.js +3 -3
- package/packages/dd-trace/src/config/supported-configurations.json +47 -2
- package/packages/dd-trace/src/crashtracking/crashtracker.js +82 -18
- package/packages/dd-trace/src/debugger/config.js +12 -1
- package/packages/dd-trace/src/debugger/constants.js +1 -0
- package/packages/dd-trace/src/debugger/devtools_client/breakpoints.js +2 -1
- package/packages/dd-trace/src/debugger/devtools_client/request-options.js +33 -0
- package/packages/dd-trace/src/debugger/devtools_client/send.js +7 -39
- package/packages/dd-trace/src/debugger/devtools_client/status.js +11 -6
- package/packages/dd-trace/src/debugger/devtools_client/tags.js +41 -0
- package/packages/dd-trace/src/debugger/index.js +23 -3
- package/packages/dd-trace/src/dogstatsd.js +532 -71
- package/packages/dd-trace/src/encode/0.4-cross-payload.js +170 -0
- package/packages/dd-trace/src/encode/0.4.js +5 -5
- package/packages/dd-trace/src/encode/agentless-ci-visibility.js +9 -8
- package/packages/dd-trace/src/evp_proxy/direct.js +27 -23
- package/packages/dd-trace/src/exporters/agent/index.js +24 -3
- package/packages/dd-trace/src/exporters/agent/writer.js +28 -11
- package/packages/dd-trace/src/exporters/agentless/index.js +7 -9
- package/packages/dd-trace/src/exporters/agentless/writer.js +100 -118
- package/packages/dd-trace/src/exporters/common/agents.js +28 -6
- package/packages/dd-trace/src/exporters/common/buffering-exporter.js +2 -2
- package/packages/dd-trace/src/exporters/common/final-flush-request-tracker.js +191 -0
- package/packages/dd-trace/src/exporters/common/request.js +31 -11
- package/packages/dd-trace/src/exporters/common/url.js +40 -1
- package/packages/dd-trace/src/exporters/common/writer.js +31 -7
- package/packages/dd-trace/src/exporters/span-stats/index.js +23 -4
- package/packages/dd-trace/src/external-logger/src/index.js +4 -3
- package/packages/dd-trace/src/flush.js +105 -0
- package/packages/dd-trace/src/id.js +14 -0
- package/packages/dd-trace/src/index.js +5 -2
- package/packages/dd-trace/src/llmobs/experiments/client.js +4 -0
- package/packages/dd-trace/src/llmobs/experiments/dataset.js +52 -2
- package/packages/dd-trace/src/llmobs/experiments/experiment.js +420 -105
- package/packages/dd-trace/src/llmobs/experiments/index.js +54 -42
- package/packages/dd-trace/src/llmobs/experiments/noop.js +39 -8
- package/packages/dd-trace/src/llmobs/experiments/result.js +1 -0
- package/packages/dd-trace/src/llmobs/experiments/util.js +21 -0
- package/packages/dd-trace/src/llmobs/index.js +111 -48
- package/packages/dd-trace/src/llmobs/noop.js +2 -1
- package/packages/dd-trace/src/llmobs/plugins/ai/ddTelemetry.js +22 -71
- package/packages/dd-trace/src/llmobs/plugins/ai/vercelTelemetry.js +23 -13
- package/packages/dd-trace/src/llmobs/plugins/anthropic/util.js +8 -4
- package/packages/dd-trace/src/llmobs/plugins/claude-agent-sdk/index.js +6 -3
- package/packages/dd-trace/src/llmobs/plugins/langchain/index.js +7 -0
- package/packages/dd-trace/src/llmobs/plugins/openai/utils.js +13 -6
- package/packages/dd-trace/src/llmobs/plugins/openai-agents/utils.js +5 -7
- package/packages/dd-trace/src/llmobs/util.js +0 -33
- package/packages/dd-trace/src/llmobs/writers/base.js +106 -53
- package/packages/dd-trace/src/log/index.js +7 -1
- package/packages/dd-trace/src/log-submission/log-submission-plugin.js +249 -0
- package/packages/dd-trace/src/noop/span.js +5 -0
- package/packages/dd-trace/src/openfeature/writers/base.js +14 -12
- package/packages/dd-trace/src/openfeature/writers/exposures.js +8 -1
- package/packages/dd-trace/src/openfeature/writers/util.js +1 -1
- package/packages/dd-trace/src/opentelemetry/logs/batch_log_processor.js +45 -3
- package/packages/dd-trace/src/opentelemetry/logs/index.js +9 -25
- package/packages/dd-trace/src/opentelemetry/logs/logger_provider.js +7 -4
- package/packages/dd-trace/src/opentelemetry/logs/otlp_http_log_exporter.js +14 -4
- package/packages/dd-trace/src/opentelemetry/metrics/index.js +18 -23
- package/packages/dd-trace/src/opentelemetry/metrics/meter_provider.js +8 -0
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_http_metric_exporter.js +18 -6
- package/packages/dd-trace/src/opentelemetry/metrics/otlp_span_stats_exporter.js +14 -2
- package/packages/dd-trace/src/opentelemetry/metrics/periodic_metric_reader.js +25 -5
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_http_exporter_base.js +61 -25
- package/packages/dd-trace/src/opentelemetry/otlp/otlp_transformer_base.js +19 -4
- package/packages/dd-trace/src/opentelemetry/resource-attributes.js +55 -0
- package/packages/dd-trace/src/opentelemetry/trace/index.js +6 -1
- package/packages/dd-trace/src/opentelemetry/trace/otlp_http_trace_exporter.js +10 -0
- package/packages/dd-trace/src/opentelemetry/tracer.js +26 -2
- package/packages/dd-trace/src/opentracing/propagation/text_map.js +494 -351
- package/packages/dd-trace/src/opentracing/span.js +28 -0
- package/packages/dd-trace/src/otel-thread-ctx.js +23 -5
- package/packages/dd-trace/src/payload-tagging/index.js +2 -1
- package/packages/dd-trace/src/plugin_manager.js +2 -0
- package/packages/dd-trace/src/plugins/ci_plugin.js +1 -6
- package/packages/dd-trace/src/plugins/tracing.js +12 -10
- package/packages/dd-trace/src/plugins/util/ci.js +3 -1
- package/packages/dd-trace/src/plugins/util/inferred_proxy.js +16 -15
- package/packages/dd-trace/src/plugins/util/status-validator.js +98 -0
- package/packages/dd-trace/src/plugins/util/test.js +157 -41
- package/packages/dd-trace/src/plugins/util/url.js +2 -2
- package/packages/dd-trace/src/plugins/util/web.js +6 -74
- package/packages/dd-trace/src/profiling/config.js +1 -0
- package/packages/dd-trace/src/profiling/oom.js +11 -5
- package/packages/dd-trace/src/profiling/profiler.js +30 -13
- package/packages/dd-trace/src/profiling/profilers/events.js +3 -3
- package/packages/dd-trace/src/profiling/profilers/space.js +38 -11
- package/packages/dd-trace/src/profiling/profilers/wall.js +14 -14
- package/packages/dd-trace/src/profiling/webspan-utils.js +7 -4
- package/packages/dd-trace/src/proxy.js +108 -10
- package/packages/dd-trace/src/remote_config/index.js +319 -101
- package/packages/dd-trace/src/remote_config/scheduler.js +37 -12
- package/packages/dd-trace/src/runtime_metrics/client.js +37 -8
- package/packages/dd-trace/src/runtime_metrics/index.js +5 -0
- package/packages/dd-trace/src/runtime_metrics/otlp_runtime_metrics.js +10 -1
- package/packages/dd-trace/src/runtime_metrics/runtime_metrics.js +22 -7
- package/packages/dd-trace/src/sampling_rule.js +36 -9
- package/packages/dd-trace/src/serverless/telemetry-delivery-tracker.js +51 -0
- package/packages/dd-trace/src/serverless/vercel.js +155 -0
- package/packages/dd-trace/src/serverless.js +45 -21
- package/packages/dd-trace/src/service-naming/schemas/util.js +1 -1
- package/packages/dd-trace/src/span_stats.js +64 -31
- package/packages/dd-trace/src/startup-log.js +3 -2
- package/packages/dd-trace/src/telemetry/agentless-url.js +28 -0
- package/packages/dd-trace/src/telemetry/send-data.js +18 -16
- package/packages/dd-trace/src/telemetry/session-propagation.js +3 -2
- package/packages/dd-trace/src/tracer.js +26 -5
- package/packages/dd-trace/src/tracer_metadata.js +11 -3
- package/packages/dd-trace/src/web-tags-cache.js +67 -13
- package/vendor/dist/@datadog/openfeature-node-server/index.js +1 -1
- 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/datadog-instrumentations/src/playwright-browser-scripts.js +0 -27
- package/packages/dd-trace/src/ci-visibility/log-submission/log-submission-plugin.js +0 -53
- package/packages/dd-trace/src/encode/agentless-json.js +0 -209
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
// Capture real timers at module load time before tests can install fake timers.
|
|
4
|
+
const realSetTimeout = setTimeout
|
|
5
|
+
|
|
3
6
|
const { AsyncResource } = require('node:async_hooks')
|
|
4
7
|
const { fileURLToPath } = require('node:url')
|
|
5
8
|
|
|
6
9
|
const { EMPTY_EFD_RETRY_POLICY } = require('../../dd-trace/src/ci-visibility/efd-retry-policy')
|
|
7
|
-
const {
|
|
10
|
+
const { RUM_TEST_EXECUTION_ID_COOKIE_NAME } = require('../../dd-trace/src/ci-visibility/rum')
|
|
11
|
+
const { getEnvironmentVariable, getValueFromEnvSources } = require('../../dd-trace/src/config/helper')
|
|
8
12
|
const log = require('../../dd-trace/src/log')
|
|
13
|
+
const shimmer = require('../../datadog-shimmer')
|
|
9
14
|
const {
|
|
10
15
|
collectTestOptimizationSummariesFromTraces,
|
|
11
16
|
getIsFaultyEarlyFlakeDetection,
|
|
@@ -16,10 +21,13 @@ const {
|
|
|
16
21
|
MOCHA_WORKER_TRACE_PAYLOAD_CODE,
|
|
17
22
|
TEST_SUITE_EXECUTION_ID,
|
|
18
23
|
} = require('../../dd-trace/src/plugins/util/test')
|
|
24
|
+
const { publishWithCompletion } = require('./helpers/channel')
|
|
19
25
|
const { addHook, channel, tracingChannel } = require('./helpers/instrument')
|
|
20
26
|
const {
|
|
21
27
|
CONFIGURATION_REQUEST,
|
|
22
28
|
CONFIGURATION_RESPONSE,
|
|
29
|
+
SCREENSHOT_UPLOAD,
|
|
30
|
+
SCREENSHOT_UPLOAD_RESPONSE,
|
|
23
31
|
sendWebdriverioWorkerMessage,
|
|
24
32
|
SUITE_FINISH,
|
|
25
33
|
WEBDRIVERIO_WORKER_ENV,
|
|
@@ -28,8 +36,10 @@ const {
|
|
|
28
36
|
WORKER_READY,
|
|
29
37
|
WORKER_READY_RESPONSE,
|
|
30
38
|
} = require('./mocha/webdriverio-protocol')
|
|
39
|
+
const { detectRum, stopRumSessionAndReportActivity } = require('./rum-browser-scripts')
|
|
31
40
|
|
|
32
41
|
const jasmineDoneCh = channel('ci:webdriverio:jasmine:done')
|
|
42
|
+
const rumPageNavigateCh = channel('ci:webdriverio:rum:page-navigate')
|
|
33
43
|
const testFinishCh = channel('ci:mocha:test:finish')
|
|
34
44
|
const testSessionStartCh = channel('ci:mocha:session:start')
|
|
35
45
|
const testSessionFinishCh = channel('ci:mocha:session:finish')
|
|
@@ -38,7 +48,10 @@ const testSuiteStartCh = channel('ci:mocha:test-suite:start')
|
|
|
38
48
|
const testSuiteFinishCh = channel('ci:mocha:test-suite:finish')
|
|
39
49
|
const knownTestsCh = channel('ci:mocha:known-tests')
|
|
40
50
|
const libraryConfigurationCh = channel('ci:mocha:library-configuration')
|
|
51
|
+
const logSubmissionFlushCh = channel('ci:log-submission:flush')
|
|
41
52
|
const modifiedFilesCh = channel('ci:mocha:modified-files')
|
|
53
|
+
const screenshotCapabilitiesCh = channel('ci:webdriverio:screenshot:capabilities')
|
|
54
|
+
const screenshotUploadCh = channel('ci:webdriverio:screenshot:upload')
|
|
42
55
|
const testManagementTestsCh = channel('ci:mocha:test-management-tests')
|
|
43
56
|
const workerConfigurationCh = channel('ci:mocha:worker:configuration')
|
|
44
57
|
const workerReportLogsCh = channel('ci:mocha:worker-report:logs')
|
|
@@ -46,15 +59,25 @@ const workerReportTelemetryCh = channel('ci:mocha:worker-report:telemetry')
|
|
|
46
59
|
const workerReportTraceCh = channel('ci:mocha:worker-report:trace')
|
|
47
60
|
|
|
48
61
|
const jasmineAdapterInitCh = tracingChannel('orchestrion:@wdio/jasmine-framework:JasmineAdapter_init')
|
|
62
|
+
const baseReporterWaitForSyncCh = tracingChannel('orchestrion:@wdio/runner:BaseReporter_waitForSync')
|
|
63
|
+
const runnerRunCh = tracingChannel('orchestrion:@wdio/runner:Runner_run')
|
|
64
|
+
const executeAsyncCh = tracingChannel('orchestrion:@wdio/utils:executeAsync')
|
|
49
65
|
const launcherStartInstanceCh = tracingChannel('orchestrion:@wdio/cli:Launcher_startInstance')
|
|
50
66
|
const localRunnerRunCh = tracingChannel('orchestrion:@wdio/local-runner:LocalRunner_run')
|
|
51
67
|
const localRunnerShutdownCh = tracingChannel('orchestrion:@wdio/local-runner:LocalRunner_shutdown')
|
|
68
|
+
const testFrameworkFnWrapperCh = tracingChannel('orchestrion:@wdio/utils:testFrameworkFnWrapper')
|
|
69
|
+
const newWindowCh = tracingChannel('orchestrion:webdriverio:newWindow')
|
|
70
|
+
const webdriverBidiInitCh = tracingChannel('orchestrion:webdriver:initiateBidi')
|
|
71
|
+
const webdriverCommandCh = tracingChannel('orchestrion:webdriver:command')
|
|
72
|
+
const urlCh = tracingChannel('orchestrion:webdriverio:url')
|
|
52
73
|
|
|
53
74
|
const NODE_OPTIONS_SEPARATOR_RE = /\s/
|
|
54
75
|
const JASMINE_FRAMEWORK_ADAPTER = 'jasmine'
|
|
55
76
|
const MOCHA_FRAMEWORK_ADAPTER = 'mocha'
|
|
56
77
|
const SUPPORTED_FRAMEWORK_ADAPTERS = new Set([JASMINE_FRAMEWORK_ADAPTER, MOCHA_FRAMEWORK_ADAPTER])
|
|
57
78
|
const TEST_FRAMEWORK = 'webdriverio'
|
|
79
|
+
const SET_RUM_COOKIE_SCRIPT = 'cookie => { globalThis.document.cookie = cookie }'
|
|
80
|
+
const RUM_FLUSH_WAIT_TIME = getValueFromEnvSources('DD_CIVISIBILITY_RUM_FLUSH_WAIT_MILLIS')
|
|
58
81
|
const isWebdriverioWorker = !!getEnvironmentVariable(WEBDRIVERIO_WORKER_ENV)
|
|
59
82
|
let jasmineWorkerRequestId = 0
|
|
60
83
|
|
|
@@ -65,6 +88,22 @@ if (loadCh.hasSubscribers) {
|
|
|
65
88
|
|
|
66
89
|
const coordinatorStates = new WeakMap()
|
|
67
90
|
const localRunnerVersions = new WeakMap()
|
|
91
|
+
const rumBrowsers = new Set()
|
|
92
|
+
const rumCorrelationBrowsers = new Set()
|
|
93
|
+
const rumRunnerBrowsers = new WeakSet()
|
|
94
|
+
const rumBrowserPreloadScripts = new WeakMap()
|
|
95
|
+
const rumBrowserSessionEndHandlers = new WeakMap()
|
|
96
|
+
const rumBrowserTestExecutionIds = new WeakMap()
|
|
97
|
+
let isRumCleanupPending = false
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Returns the current BiDi browsing context, including an iframe context when one is selected.
|
|
101
|
+
*
|
|
102
|
+
* @returns {object}
|
|
103
|
+
*/
|
|
104
|
+
function getCurrentRumContext () {
|
|
105
|
+
return globalThis.window
|
|
106
|
+
}
|
|
68
107
|
|
|
69
108
|
addHook({
|
|
70
109
|
name: '@wdio/local-runner',
|
|
@@ -76,6 +115,834 @@ addHook({
|
|
|
76
115
|
return LocalRunner
|
|
77
116
|
})
|
|
78
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Returns whether this browser can preload and clean correlation across every browsing context.
|
|
120
|
+
*
|
|
121
|
+
* @param {object} browser
|
|
122
|
+
* @returns {boolean}
|
|
123
|
+
*/
|
|
124
|
+
function canCorrelateRumBrowser (browser) {
|
|
125
|
+
return browser?.isBidi &&
|
|
126
|
+
typeof browser.scriptAddPreloadScript === 'function' &&
|
|
127
|
+
typeof browser.scriptRemovePreloadScript === 'function' &&
|
|
128
|
+
typeof browser.storageDeleteCookies === 'function'
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Releases retained state when a WebDriver session ends.
|
|
133
|
+
*
|
|
134
|
+
* @param {object} browser
|
|
135
|
+
* @returns {void}
|
|
136
|
+
*/
|
|
137
|
+
function releaseRumBrowser (browser) {
|
|
138
|
+
rumBrowsers.delete(browser)
|
|
139
|
+
rumCorrelationBrowsers.delete(browser)
|
|
140
|
+
rumRunnerBrowsers.delete(browser)
|
|
141
|
+
rumBrowserPreloadScripts.delete(browser)
|
|
142
|
+
rumBrowserTestExecutionIds.delete(browser)
|
|
143
|
+
|
|
144
|
+
const sessionEndHandler = rumBrowserSessionEndHandlers.get(browser)
|
|
145
|
+
rumBrowserSessionEndHandlers.delete(browser)
|
|
146
|
+
if (sessionEndHandler && typeof browser.removeListener === 'function') {
|
|
147
|
+
browser.removeListener('result', sessionEndHandler)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Retains a browser and watches for WebDriver's deleteSession result event.
|
|
153
|
+
*
|
|
154
|
+
* @param {object} browser
|
|
155
|
+
* @returns {void}
|
|
156
|
+
*/
|
|
157
|
+
function retainRumBrowser (browser) {
|
|
158
|
+
rumBrowsers.add(browser)
|
|
159
|
+
if (rumBrowserSessionEndHandlers.has(browser) || typeof browser.on !== 'function') return
|
|
160
|
+
|
|
161
|
+
const sessionEndHandler = ({ command, result }) => {
|
|
162
|
+
const closedFinalWindow = command === 'closeWindow' &&
|
|
163
|
+
Array.isArray(result?.value) && result.value.length === 0
|
|
164
|
+
if (command === 'deleteSession' || closedFinalWindow) releaseRumBrowser(browser)
|
|
165
|
+
}
|
|
166
|
+
rumBrowserSessionEndHandlers.set(browser, sessionEndHandler)
|
|
167
|
+
browser.on('result', sessionEndHandler)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Returns whether RUM is inactive because its configured sampling rate is below 100%.
|
|
172
|
+
*
|
|
173
|
+
* @param {{isRumActive: boolean, isRumInstrumented: boolean, rumSamplingRate: number|null}} rumState
|
|
174
|
+
* @returns {boolean}
|
|
175
|
+
*/
|
|
176
|
+
function isRumSampledOut (rumState) {
|
|
177
|
+
return rumState.isRumInstrumented && !rumState.isRumActive &&
|
|
178
|
+
rumState.rumSamplingRate !== null && rumState.rumSamplingRate < 100
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Installs RUM correlation on every subsequent document in the BiDi session.
|
|
183
|
+
*
|
|
184
|
+
* @param {object} browser
|
|
185
|
+
* @param {string} testExecutionId
|
|
186
|
+
* @returns {Promise<void>}
|
|
187
|
+
*/
|
|
188
|
+
async function installRumPreloadScript (browser, testExecutionId) {
|
|
189
|
+
if (!browser.isBidi ||
|
|
190
|
+
typeof browser.scriptAddPreloadScript !== 'function') return
|
|
191
|
+
|
|
192
|
+
try {
|
|
193
|
+
const preloadScript = rumBrowserPreloadScripts.get(browser)
|
|
194
|
+
if (preloadScript && preloadScript.sessionId === browser.sessionId &&
|
|
195
|
+
preloadScript.testExecutionId === testExecutionId) return
|
|
196
|
+
if (preloadScript && preloadScript.sessionId === browser.sessionId) {
|
|
197
|
+
if (!await removeRumPreloadScript(browser)) return
|
|
198
|
+
} else {
|
|
199
|
+
rumBrowserPreloadScripts.delete(browser)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const cookie = `${RUM_TEST_EXECUTION_ID_COOKIE_NAME}=${testExecutionId}; path=/`
|
|
203
|
+
const { script } = await browser.scriptAddPreloadScript({
|
|
204
|
+
arguments: [{ type: 'string', value: cookie }],
|
|
205
|
+
functionDeclaration: SET_RUM_COOKIE_SCRIPT,
|
|
206
|
+
})
|
|
207
|
+
rumBrowserPreloadScripts.set(browser, { script, sessionId: browser.sessionId, testExecutionId })
|
|
208
|
+
} catch (error) {
|
|
209
|
+
log.error('WebdriverIO RUM correlation preload error', error)
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Removes a browser-wide RUM correlation preload.
|
|
215
|
+
*
|
|
216
|
+
* @param {object} browser
|
|
217
|
+
* @returns {Promise<boolean>}
|
|
218
|
+
*/
|
|
219
|
+
async function removeRumPreloadScript (browser) {
|
|
220
|
+
const preloadScript = rumBrowserPreloadScripts.get(browser)
|
|
221
|
+
if (!preloadScript) return true
|
|
222
|
+
if (preloadScript.sessionId !== browser.sessionId) {
|
|
223
|
+
if (rumBrowserPreloadScripts.get(browser) === preloadScript) rumBrowserPreloadScripts.delete(browser)
|
|
224
|
+
return true
|
|
225
|
+
}
|
|
226
|
+
if (typeof browser.scriptRemovePreloadScript !== 'function') return false
|
|
227
|
+
|
|
228
|
+
try {
|
|
229
|
+
await browser.scriptRemovePreloadScript({ script: preloadScript.script })
|
|
230
|
+
if (rumBrowserPreloadScripts.get(browser) === preloadScript) rumBrowserPreloadScripts.delete(browser)
|
|
231
|
+
return true
|
|
232
|
+
} catch (error) {
|
|
233
|
+
log.error('WebdriverIO RUM correlation preload cleanup error', error)
|
|
234
|
+
return false
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Returns every descendant browsing context in a BiDi context tree.
|
|
240
|
+
*
|
|
241
|
+
* @param {object[]} contexts
|
|
242
|
+
* @returns {string[]}
|
|
243
|
+
*/
|
|
244
|
+
function getRumChildContexts (contexts) {
|
|
245
|
+
const childContexts = []
|
|
246
|
+
const pendingContexts = [...contexts]
|
|
247
|
+
while (pendingContexts.length) {
|
|
248
|
+
const context = pendingContexts.shift()
|
|
249
|
+
if (!context?.children) continue
|
|
250
|
+
for (const child of context.children) {
|
|
251
|
+
childContexts.push(child.context)
|
|
252
|
+
pendingContexts.push(child)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return childContexts
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Gets the active test execution ID and applies browser metadata to its span.
|
|
260
|
+
*
|
|
261
|
+
* @param {object} browser
|
|
262
|
+
* @param {boolean|undefined} isRumActive
|
|
263
|
+
* @returns {string|undefined}
|
|
264
|
+
*/
|
|
265
|
+
function getRumTestExecutionId (browser, isRumActive) {
|
|
266
|
+
const correlationContext = {
|
|
267
|
+
browserName: browser.capabilities?.browserName,
|
|
268
|
+
browserVersion: browser.capabilities?.browserVersion,
|
|
269
|
+
isRumActive,
|
|
270
|
+
isTestOptimizationRunner: isWebdriverioWorker,
|
|
271
|
+
testExecutionId: undefined,
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
rumPageNavigateCh.publish(correlationContext)
|
|
275
|
+
} catch (error) {
|
|
276
|
+
log.error('WebdriverIO RUM correlation channel error', error)
|
|
277
|
+
}
|
|
278
|
+
if (correlationContext.isTestOptimizationRunner || correlationContext.testExecutionId) {
|
|
279
|
+
rumRunnerBrowsers.add(browser)
|
|
280
|
+
}
|
|
281
|
+
return correlationContext.testExecutionId
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Installs RUM correlation before a BiDi navigation starts.
|
|
286
|
+
*
|
|
287
|
+
* @this {object}
|
|
288
|
+
* @returns {Promise<void>}
|
|
289
|
+
*/
|
|
290
|
+
async function preloadRumNavigation () {
|
|
291
|
+
try {
|
|
292
|
+
const browser = this
|
|
293
|
+
const testExecutionId = getRumTestExecutionId(browser)
|
|
294
|
+
if (!canCorrelateRumBrowser(browser)) return
|
|
295
|
+
if (!rumRunnerBrowsers.has(browser)) return
|
|
296
|
+
|
|
297
|
+
retainRumBrowser(browser)
|
|
298
|
+
if (!testExecutionId) return
|
|
299
|
+
|
|
300
|
+
rumCorrelationBrowsers.add(browser)
|
|
301
|
+
rumBrowserTestExecutionIds.set(browser, testExecutionId)
|
|
302
|
+
await installRumPreloadScript(browser, testExecutionId)
|
|
303
|
+
} catch (error) {
|
|
304
|
+
log.error('WebdriverIO RUM correlation preload error', error)
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Correlates the current browser window with one test execution.
|
|
310
|
+
*
|
|
311
|
+
* @param {object} browser
|
|
312
|
+
* @param {string} testExecutionId
|
|
313
|
+
* @returns {Promise<void>}
|
|
314
|
+
*/
|
|
315
|
+
async function correlateRumWindow (browser, testExecutionId) {
|
|
316
|
+
if (!canCorrelateRumBrowser(browser)) return
|
|
317
|
+
|
|
318
|
+
try {
|
|
319
|
+
await browser.setCookies({
|
|
320
|
+
name: RUM_TEST_EXECUTION_ID_COOKIE_NAME,
|
|
321
|
+
value: testExecutionId,
|
|
322
|
+
})
|
|
323
|
+
} catch (error) {
|
|
324
|
+
log.error('WebdriverIO RUM correlation cookie error', error)
|
|
325
|
+
}
|
|
326
|
+
await installRumPreloadScript(browser, testExecutionId)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Detects RUM after a browser navigation and correlates it with the active test.
|
|
331
|
+
*
|
|
332
|
+
* @param {{self?: object}} context
|
|
333
|
+
* @returns {Promise<void>}
|
|
334
|
+
*/
|
|
335
|
+
async function handleRumNavigation (context) {
|
|
336
|
+
const browser = context.self
|
|
337
|
+
try {
|
|
338
|
+
if (!browser) return
|
|
339
|
+
|
|
340
|
+
const canCorrelateRum = canCorrelateRumBrowser(browser)
|
|
341
|
+
if (!canCorrelateRum) {
|
|
342
|
+
getRumTestExecutionId(browser, false)
|
|
343
|
+
return
|
|
344
|
+
}
|
|
345
|
+
if (!rumRunnerBrowsers.has(browser)) {
|
|
346
|
+
getRumTestExecutionId(browser)
|
|
347
|
+
if (!rumRunnerBrowsers.has(browser)) return
|
|
348
|
+
}
|
|
349
|
+
retainRumBrowser(browser)
|
|
350
|
+
if (typeof browser.execute !== 'function') return
|
|
351
|
+
|
|
352
|
+
let rumState
|
|
353
|
+
try {
|
|
354
|
+
rumState = await browser.execute(detectRum)
|
|
355
|
+
} catch (error) {
|
|
356
|
+
log.error('WebdriverIO RUM detection error', error)
|
|
357
|
+
return
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
if (!rumState) return
|
|
361
|
+
|
|
362
|
+
const { isRumActive } = rumState
|
|
363
|
+
const sampledOut = isRumSampledOut(rumState)
|
|
364
|
+
if (sampledOut) {
|
|
365
|
+
log.debug("RUM was detected on the page, but it isn't active because the sampling rate is below 100%")
|
|
366
|
+
}
|
|
367
|
+
const testExecutionId = getRumTestExecutionId(browser, isRumActive)
|
|
368
|
+
if (sampledOut) return
|
|
369
|
+
|
|
370
|
+
if (!testExecutionId) {
|
|
371
|
+
return
|
|
372
|
+
}
|
|
373
|
+
if (!isRumActive && browser.isBidi) return
|
|
374
|
+
|
|
375
|
+
await correlateRumBrowser(browser, testExecutionId)
|
|
376
|
+
} catch (error) {
|
|
377
|
+
log.error('WebdriverIO RUM correlation error', error)
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Stops RUM in the current window.
|
|
383
|
+
*
|
|
384
|
+
* @param {object} browser
|
|
385
|
+
* @returns {Promise<boolean>}
|
|
386
|
+
*/
|
|
387
|
+
async function stopRumWindow (browser) {
|
|
388
|
+
let isRumActive
|
|
389
|
+
try {
|
|
390
|
+
isRumActive = await browser.execute(stopRumSessionAndReportActivity)
|
|
391
|
+
} catch (error) {
|
|
392
|
+
log.error('WebdriverIO RUM cleanup error', error)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (isRumActive) {
|
|
396
|
+
getRumTestExecutionId(browser, true)
|
|
397
|
+
}
|
|
398
|
+
return !!isRumActive
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Removes the RUM correlation cookie from the current window.
|
|
403
|
+
*
|
|
404
|
+
* @param {object} browser
|
|
405
|
+
* @returns {Promise<void>}
|
|
406
|
+
*/
|
|
407
|
+
async function deleteRumCookie (browser) {
|
|
408
|
+
try {
|
|
409
|
+
await browser.deleteCookies([RUM_TEST_EXECUTION_ID_COOKIE_NAME])
|
|
410
|
+
} catch (error) {
|
|
411
|
+
log.error('WebdriverIO RUM correlation cookie cleanup error', error)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Removes every RUM correlation cookie without loading its application origin.
|
|
417
|
+
*
|
|
418
|
+
* @param {object} browser
|
|
419
|
+
* @returns {Promise<void>}
|
|
420
|
+
*/
|
|
421
|
+
async function cleanupRumCookies (browser) {
|
|
422
|
+
if (!browser.isBidi || typeof browser.storageDeleteCookies !== 'function') return
|
|
423
|
+
|
|
424
|
+
try {
|
|
425
|
+
await browser.storageDeleteCookies({
|
|
426
|
+
filter: { name: RUM_TEST_EXECUTION_ID_COOKIE_NAME },
|
|
427
|
+
})
|
|
428
|
+
} catch (error) {
|
|
429
|
+
log.error('WebdriverIO RUM cross-origin cookie cleanup error', error)
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Runs one RUM operation in every open browser window and restores the original window.
|
|
435
|
+
*
|
|
436
|
+
* @param {object} browser
|
|
437
|
+
* @template T
|
|
438
|
+
* @param {(browser: object, value?: T) => Promise<void>} operation
|
|
439
|
+
* @param {T} [value]
|
|
440
|
+
* @returns {Promise<void>}
|
|
441
|
+
*/
|
|
442
|
+
async function forEachRumWindow (browser, operation, value) {
|
|
443
|
+
if (typeof browser.getWindowHandle !== 'function' ||
|
|
444
|
+
typeof browser.getWindowHandles !== 'function' ||
|
|
445
|
+
typeof browser.switchToWindow !== 'function') {
|
|
446
|
+
await operation(browser, value)
|
|
447
|
+
return
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
let currentWindowHandle
|
|
451
|
+
let windowHandles
|
|
452
|
+
try {
|
|
453
|
+
currentWindowHandle = await browser.getWindowHandle()
|
|
454
|
+
windowHandles = await browser.getWindowHandles()
|
|
455
|
+
} catch (error) {
|
|
456
|
+
log.error('WebdriverIO RUM window discovery error', error)
|
|
457
|
+
await operation(browser, value)
|
|
458
|
+
return
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
let currentFrameContext
|
|
462
|
+
if (browser.isBidi &&
|
|
463
|
+
typeof browser.execute === 'function' && typeof browser.switchFrame === 'function') {
|
|
464
|
+
try {
|
|
465
|
+
const currentContext = await browser.execute(getCurrentRumContext)
|
|
466
|
+
if (currentContext?.context && currentContext.context !== currentWindowHandle) {
|
|
467
|
+
currentFrameContext = currentContext.context
|
|
468
|
+
}
|
|
469
|
+
} catch (error) {
|
|
470
|
+
log.error('WebdriverIO RUM frame discovery error', error)
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
let activeWindowHandle = currentWindowHandle
|
|
475
|
+
let isTopLevel = !currentFrameContext
|
|
476
|
+
for (const windowHandle of windowHandles) {
|
|
477
|
+
if (windowHandle !== activeWindowHandle || !isTopLevel) {
|
|
478
|
+
try {
|
|
479
|
+
// WebDriver window commands must run in order because each one changes the active window.
|
|
480
|
+
// eslint-disable-next-line no-await-in-loop
|
|
481
|
+
await browser.switchToWindow(windowHandle)
|
|
482
|
+
activeWindowHandle = windowHandle
|
|
483
|
+
isTopLevel = true
|
|
484
|
+
} catch (error) {
|
|
485
|
+
log.error('WebdriverIO RUM window switch error', error)
|
|
486
|
+
continue
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// eslint-disable-next-line no-await-in-loop
|
|
490
|
+
await operation(browser, value)
|
|
491
|
+
|
|
492
|
+
if (browser.isBidi &&
|
|
493
|
+
typeof browser.browsingContextGetTree === 'function' && typeof browser.switchFrame === 'function') {
|
|
494
|
+
let childContexts = []
|
|
495
|
+
try {
|
|
496
|
+
// eslint-disable-next-line no-await-in-loop
|
|
497
|
+
const contextTree = await browser.browsingContextGetTree({ root: windowHandle })
|
|
498
|
+
childContexts = getRumChildContexts(contextTree.contexts || [])
|
|
499
|
+
} catch (error) {
|
|
500
|
+
log.error('WebdriverIO RUM frame discovery error', error)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
for (const childContext of childContexts) {
|
|
504
|
+
try {
|
|
505
|
+
// Reset to the window root so WebdriverIO can resolve nested frame paths consistently.
|
|
506
|
+
// eslint-disable-next-line no-await-in-loop
|
|
507
|
+
await browser.switchToWindow(windowHandle)
|
|
508
|
+
// eslint-disable-next-line no-await-in-loop
|
|
509
|
+
await browser.switchFrame(childContext)
|
|
510
|
+
isTopLevel = false
|
|
511
|
+
} catch (error) {
|
|
512
|
+
isTopLevel = true
|
|
513
|
+
log.error('WebdriverIO RUM frame switch error', error)
|
|
514
|
+
continue
|
|
515
|
+
}
|
|
516
|
+
// eslint-disable-next-line no-await-in-loop
|
|
517
|
+
await operation(browser, value)
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const canRestoreWindow = windowHandles.includes(currentWindowHandle)
|
|
523
|
+
if (canRestoreWindow && (activeWindowHandle !== currentWindowHandle || !isTopLevel || currentFrameContext)) {
|
|
524
|
+
try {
|
|
525
|
+
if (activeWindowHandle !== currentWindowHandle || !isTopLevel) await browser.switchToWindow(currentWindowHandle)
|
|
526
|
+
if (currentFrameContext) await browser.switchFrame(currentFrameContext)
|
|
527
|
+
} catch (error) {
|
|
528
|
+
log.error('WebdriverIO RUM browsing context restore error', error)
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Stops RUM in every window before a browser session is closed.
|
|
535
|
+
*
|
|
536
|
+
* @param {object} browser
|
|
537
|
+
* @returns {Promise<boolean>}
|
|
538
|
+
*/
|
|
539
|
+
async function stopRumBrowser (browser) {
|
|
540
|
+
await removeRumPreloadScript(browser)
|
|
541
|
+
let isRumActive = false
|
|
542
|
+
await forEachRumWindow(browser, async (browser) => {
|
|
543
|
+
if (await stopRumWindow(browser)) isRumActive = true
|
|
544
|
+
})
|
|
545
|
+
return isRumActive
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Removes the current test's RUM correlation while keeping reusable sessions active.
|
|
550
|
+
*
|
|
551
|
+
* @param {object} browser
|
|
552
|
+
* @returns {Promise<void>}
|
|
553
|
+
*/
|
|
554
|
+
async function cleanupRumBrowser (browser) {
|
|
555
|
+
await removeRumPreloadScript(browser)
|
|
556
|
+
await forEachRumWindow(browser, deleteRumCookie)
|
|
557
|
+
await cleanupRumCookies(browser)
|
|
558
|
+
rumBrowserTestExecutionIds.delete(browser)
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* Removes the current test's RUM correlation from every prepared browser.
|
|
563
|
+
*
|
|
564
|
+
* @returns {Promise<void>}
|
|
565
|
+
*/
|
|
566
|
+
async function cleanupRumBrowsers () {
|
|
567
|
+
isRumCleanupPending = false
|
|
568
|
+
const browsers = [...rumCorrelationBrowsers]
|
|
569
|
+
rumCorrelationBrowsers.clear()
|
|
570
|
+
for (const browser of browsers) {
|
|
571
|
+
// Cleanup is intentionally sequential to avoid overlapping WebDriver commands.
|
|
572
|
+
// eslint-disable-next-line no-await-in-loop
|
|
573
|
+
await cleanupRumBrowser(browser)
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Cleans up every retained RUM browser before the WebdriverIO worker exits.
|
|
579
|
+
*
|
|
580
|
+
* @returns {Promise<void>}
|
|
581
|
+
*/
|
|
582
|
+
async function cleanupAllRumBrowsers () {
|
|
583
|
+
isRumCleanupPending = false
|
|
584
|
+
const browsers = [...rumBrowsers]
|
|
585
|
+
rumBrowsers.clear()
|
|
586
|
+
rumCorrelationBrowsers.clear()
|
|
587
|
+
let isRumActive = false
|
|
588
|
+
for (const browser of browsers) {
|
|
589
|
+
// WebDriver commands are intentionally sequential for each browser.
|
|
590
|
+
// eslint-disable-next-line no-await-in-loop
|
|
591
|
+
if (await stopRumBrowser(browser)) isRumActive = true
|
|
592
|
+
}
|
|
593
|
+
if (isRumActive) {
|
|
594
|
+
await new Promise(resolve => realSetTimeout(resolve, RUM_FLUSH_WAIT_TIME))
|
|
595
|
+
}
|
|
596
|
+
for (const browser of browsers) {
|
|
597
|
+
// WebDriver commands are intentionally sequential for each browser.
|
|
598
|
+
// eslint-disable-next-line no-await-in-loop
|
|
599
|
+
await cleanupRumCookies(browser)
|
|
600
|
+
releaseRumBrowser(browser)
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/**
|
|
605
|
+
* Stops and flushes RUM before WebDriver deletes one retained session.
|
|
606
|
+
*
|
|
607
|
+
* @param {object} browser
|
|
608
|
+
* @returns {Promise<void>}
|
|
609
|
+
*/
|
|
610
|
+
async function cleanupRumBrowserBeforeSessionEnd (browser) {
|
|
611
|
+
if (!rumBrowsers.has(browser)) return
|
|
612
|
+
|
|
613
|
+
if (await stopRumBrowser(browser)) {
|
|
614
|
+
await new Promise(resolve => realSetTimeout(resolve, RUM_FLUSH_WAIT_TIME))
|
|
615
|
+
}
|
|
616
|
+
await cleanupRumCookies(browser)
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/**
|
|
620
|
+
* Stops and flushes RUM before closing the last window terminates its WebDriver session.
|
|
621
|
+
*
|
|
622
|
+
* @param {object} browser
|
|
623
|
+
* @returns {Promise<void>}
|
|
624
|
+
*/
|
|
625
|
+
async function cleanupRumBrowserBeforeLastWindowClose (browser) {
|
|
626
|
+
if (!rumBrowsers.has(browser) || typeof browser.getWindowHandles !== 'function') return
|
|
627
|
+
|
|
628
|
+
let windowHandles
|
|
629
|
+
try {
|
|
630
|
+
windowHandles = await browser.getWindowHandles()
|
|
631
|
+
} catch (error) {
|
|
632
|
+
log.error('WebdriverIO RUM window discovery error', error)
|
|
633
|
+
return
|
|
634
|
+
}
|
|
635
|
+
if (windowHandles.length === 1) await cleanupRumBrowserBeforeSessionEnd(browser)
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* Prepares RUM around low-level WebDriver commands that bypass WebdriverIO's navigation helpers.
|
|
640
|
+
*
|
|
641
|
+
* @this {object}
|
|
642
|
+
* @param {string} command
|
|
643
|
+
* @returns {Promise<void>|undefined}
|
|
644
|
+
*/
|
|
645
|
+
function handleRumProtocolCommand (command) {
|
|
646
|
+
if (command === 'navigateTo') return preloadRumNavigation.call(this)
|
|
647
|
+
if (command === 'deleteSession') return cleanupRumBrowserBeforeSessionEnd(this)
|
|
648
|
+
if (command === 'closeWindow') return cleanupRumBrowserBeforeLastWindowClose(this)
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Wraps only WebDriver commands that need to wait for RUM work before their request starts.
|
|
653
|
+
*
|
|
654
|
+
* @param {{arguments?: unknown[], result?: Function}} context
|
|
655
|
+
* @returns {void}
|
|
656
|
+
*/
|
|
657
|
+
function wrapRumProtocolCommand (context) {
|
|
658
|
+
const command = context.arguments?.[2]?.command
|
|
659
|
+
const protocolCommand = context.result
|
|
660
|
+
if ((command !== 'navigateTo' && command !== 'deleteSession' && command !== 'closeWindow') ||
|
|
661
|
+
typeof protocolCommand !== 'function') return
|
|
662
|
+
|
|
663
|
+
context.result = async function (...args) {
|
|
664
|
+
try {
|
|
665
|
+
await handleRumProtocolCommand.call(this, command)
|
|
666
|
+
} catch (error) {
|
|
667
|
+
log.error('WebdriverIO RUM protocol command error', error)
|
|
668
|
+
}
|
|
669
|
+
return protocolCommand.apply(this, args)
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Wraps the runtime-created BiDi navigation handler so RUM preparation can finish before it sends the command.
|
|
675
|
+
* A tracing-channel start subscriber is synchronous and cannot delay the static async handler itself.
|
|
676
|
+
*
|
|
677
|
+
* @param {{result?: {_bidiHandler?: {value?: object}}}} context
|
|
678
|
+
* @returns {void}
|
|
679
|
+
*/
|
|
680
|
+
function wrapRumBidiNavigation (context) {
|
|
681
|
+
const bidiHandler = context.result?._bidiHandler?.value
|
|
682
|
+
if (typeof bidiHandler?.browsingContextNavigate !== 'function') return
|
|
683
|
+
|
|
684
|
+
try {
|
|
685
|
+
shimmer.wrap(bidiHandler, 'browsingContextNavigate', navigate => async function (...args) {
|
|
686
|
+
try {
|
|
687
|
+
await preloadRumNavigation.call(this.client)
|
|
688
|
+
} catch (error) {
|
|
689
|
+
log.error('WebdriverIO RUM BiDi navigation error', error)
|
|
690
|
+
}
|
|
691
|
+
return navigate.apply(this, args)
|
|
692
|
+
})
|
|
693
|
+
} catch (error) {
|
|
694
|
+
log.error('WebdriverIO RUM BiDi instrumentation error', error)
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Reapplies RUM correlation to every open browser window for a retry.
|
|
700
|
+
*
|
|
701
|
+
* @param {object} browser
|
|
702
|
+
* @param {string} testExecutionId
|
|
703
|
+
* @returns {Promise<void>}
|
|
704
|
+
*/
|
|
705
|
+
async function correlateRumBrowser (browser, testExecutionId) {
|
|
706
|
+
if (!canCorrelateRumBrowser(browser)) return
|
|
707
|
+
|
|
708
|
+
retainRumBrowser(browser)
|
|
709
|
+
rumCorrelationBrowsers.add(browser)
|
|
710
|
+
rumBrowserTestExecutionIds.set(browser, testExecutionId)
|
|
711
|
+
await forEachRumWindow(browser, correlateRumWindow, testExecutionId)
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/**
|
|
715
|
+
* Reapplies RUM correlation to browsers retained across a native retry.
|
|
716
|
+
*
|
|
717
|
+
* @param {object[]|undefined} browsers
|
|
718
|
+
* @param {string|undefined} testExecutionId
|
|
719
|
+
* @returns {Promise<void>}
|
|
720
|
+
*/
|
|
721
|
+
async function correlateRumBrowsers (browsers, testExecutionId) {
|
|
722
|
+
if (!browsers || !testExecutionId) return
|
|
723
|
+
|
|
724
|
+
for (const browser of browsers) {
|
|
725
|
+
// WebDriver commands are intentionally sequential for each browser.
|
|
726
|
+
// eslint-disable-next-line no-await-in-loop
|
|
727
|
+
await correlateRumBrowser(browser, testExecutionId)
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
/**
|
|
732
|
+
* Correlates RUM browsers discovered outside the current test before its next function runs.
|
|
733
|
+
*
|
|
734
|
+
* @returns {Promise<void>}
|
|
735
|
+
*/
|
|
736
|
+
async function startRumTest () {
|
|
737
|
+
for (const browser of rumBrowsers) {
|
|
738
|
+
let isRumActive = false
|
|
739
|
+
let shouldCorrelateRum = false
|
|
740
|
+
// WebDriver commands are intentionally sequential for each browser.
|
|
741
|
+
// eslint-disable-next-line no-await-in-loop
|
|
742
|
+
await forEachRumWindow(browser, async (browser) => {
|
|
743
|
+
try {
|
|
744
|
+
const rumState = await browser.execute(detectRum)
|
|
745
|
+
if (!rumState) return
|
|
746
|
+
if (rumState.isRumActive) isRumActive = true
|
|
747
|
+
shouldCorrelateRum = true
|
|
748
|
+
} catch (error) {
|
|
749
|
+
log.error('WebdriverIO RUM detection error', error)
|
|
750
|
+
}
|
|
751
|
+
})
|
|
752
|
+
if (!shouldCorrelateRum) continue
|
|
753
|
+
|
|
754
|
+
const testExecutionId = getRumTestExecutionId(browser, isRumActive || undefined)
|
|
755
|
+
if (!testExecutionId || rumBrowserTestExecutionIds.get(browser) === testExecutionId) continue
|
|
756
|
+
|
|
757
|
+
// eslint-disable-next-line no-await-in-loop
|
|
758
|
+
await correlateRumBrowser(browser, testExecutionId)
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
/**
|
|
763
|
+
* Replaces the completed test's RUM correlation before WebdriverIO runs callbacks for the next test or hook.
|
|
764
|
+
*
|
|
765
|
+
* @returns {Promise<void>}
|
|
766
|
+
*/
|
|
767
|
+
async function prepareNextRumTest () {
|
|
768
|
+
await cleanupRumBrowsers()
|
|
769
|
+
await startRumTest()
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
/**
|
|
773
|
+
* Detects active RUM browsers and optionally updates the active test without ending their sessions.
|
|
774
|
+
*
|
|
775
|
+
* @param {boolean} [updateTest]
|
|
776
|
+
* @returns {Promise<object|undefined>}
|
|
777
|
+
*/
|
|
778
|
+
async function detectActiveRumBrowsers (updateTest = true) {
|
|
779
|
+
let activeRumBrowser
|
|
780
|
+
for (const browser of rumCorrelationBrowsers) {
|
|
781
|
+
let isBrowserRumActive = false
|
|
782
|
+
// WebDriver commands are intentionally sequential for each browser.
|
|
783
|
+
// eslint-disable-next-line no-await-in-loop
|
|
784
|
+
await forEachRumWindow(browser, async (browser) => {
|
|
785
|
+
try {
|
|
786
|
+
const rumState = await browser.execute(detectRum)
|
|
787
|
+
if (rumState?.isRumActive) isBrowserRumActive = true
|
|
788
|
+
} catch (error) {
|
|
789
|
+
log.error('WebdriverIO RUM detection error', error)
|
|
790
|
+
}
|
|
791
|
+
})
|
|
792
|
+
if (isBrowserRumActive) {
|
|
793
|
+
activeRumBrowser ??= browser
|
|
794
|
+
if (updateTest) getRumTestExecutionId(browser, true)
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return activeRumBrowser
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
/**
|
|
801
|
+
* Preserves the active test's RUM correlation across a native WebdriverIO retry.
|
|
802
|
+
*
|
|
803
|
+
* @returns {Promise<void>}
|
|
804
|
+
*/
|
|
805
|
+
async function retryRumTest () {
|
|
806
|
+
const browsers = [...rumCorrelationBrowsers]
|
|
807
|
+
if (browsers.length === 0) return
|
|
808
|
+
|
|
809
|
+
const activeRumBrowser = await detectActiveRumBrowsers(false)
|
|
810
|
+
const testExecutionId = getRumTestExecutionId(activeRumBrowser || browsers[0], !!activeRumBrowser)
|
|
811
|
+
if (!testExecutionId) return
|
|
812
|
+
|
|
813
|
+
await correlateRumBrowsers(browsers, testExecutionId)
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Reapplies RUM correlation for a native Jasmine retry and reports whether RUM is active.
|
|
818
|
+
*
|
|
819
|
+
* @param {string|undefined} testExecutionId
|
|
820
|
+
* @returns {Promise<{
|
|
821
|
+
* browserName: string|undefined,
|
|
822
|
+
* browserVersion: string|undefined,
|
|
823
|
+
* isRumActive: boolean
|
|
824
|
+
* }>}
|
|
825
|
+
*/
|
|
826
|
+
async function retryRumBrowsers (testExecutionId) {
|
|
827
|
+
const browsers = [...rumCorrelationBrowsers]
|
|
828
|
+
let activeRumBrowser
|
|
829
|
+
if (testExecutionId) {
|
|
830
|
+
await correlateRumBrowsers(browsers, testExecutionId)
|
|
831
|
+
} else {
|
|
832
|
+
activeRumBrowser = await detectActiveRumBrowsers(false)
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
const browser = activeRumBrowser || browsers[0]
|
|
836
|
+
return {
|
|
837
|
+
browserName: browser?.capabilities?.browserName,
|
|
838
|
+
browserVersion: browser?.capabilities?.browserVersion,
|
|
839
|
+
isRumActive: !!activeRumBrowser,
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* Delays BiDi navigation until its RUM correlation preload is installed.
|
|
845
|
+
*
|
|
846
|
+
* @param {{rumPreloadCallback?: () => Promise<void>}} context
|
|
847
|
+
* @returns {void}
|
|
848
|
+
*/
|
|
849
|
+
function waitForRumNavigationStart (context) {
|
|
850
|
+
if (!rumPageNavigateCh.hasSubscribers) return
|
|
851
|
+
context.rumPreloadCallback = preloadRumNavigation
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
/**
|
|
855
|
+
* Makes a promise-returning RUM operation compatible with the rewriter's completion callbacks.
|
|
856
|
+
*
|
|
857
|
+
* @param {{
|
|
858
|
+
* resolveCallback?: (onDone: () => void) => void,
|
|
859
|
+
* rejectCallback?: (onDone: () => void) => void
|
|
860
|
+
* }} context
|
|
861
|
+
* @param {() => Promise<unknown>} operation
|
|
862
|
+
* @param {boolean} [waitForResolve]
|
|
863
|
+
* @returns {void}
|
|
864
|
+
*/
|
|
865
|
+
function setRumWaitCallbacks (context, operation, waitForResolve = true) {
|
|
866
|
+
const waitForOperation = onDone => operation().then(onDone, onDone)
|
|
867
|
+
if (waitForResolve) context.resolveCallback = waitForOperation
|
|
868
|
+
context.rejectCallback = waitForOperation
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Delays URL completion until RUM detection and correlation settle.
|
|
873
|
+
*
|
|
874
|
+
* @param {object} context
|
|
875
|
+
* @returns {void}
|
|
876
|
+
*/
|
|
877
|
+
function waitForRumNavigation (context) {
|
|
878
|
+
if (!rumPageNavigateCh.hasSubscribers) return
|
|
879
|
+
setRumWaitCallbacks(context, () => handleRumNavigation(context))
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
/**
|
|
883
|
+
* Detects delayed RUM at test and afterEach completion.
|
|
884
|
+
* Correlation remains active until every afterEach hook has run.
|
|
885
|
+
*
|
|
886
|
+
* @param {{arguments?: unknown[]}} context
|
|
887
|
+
* @returns {void}
|
|
888
|
+
*/
|
|
889
|
+
function waitForRumCleanup (context) {
|
|
890
|
+
const type = context.arguments?.[1]
|
|
891
|
+
const hookName = context.arguments?.[7]
|
|
892
|
+
if (rumCorrelationBrowsers.size === 0) return
|
|
893
|
+
|
|
894
|
+
if (type === 'Test') {
|
|
895
|
+
isRumCleanupPending = true
|
|
896
|
+
setRumWaitCallbacks(context, detectActiveRumBrowsers)
|
|
897
|
+
} else if (type === 'Hook' && hookName === 'afterEach') {
|
|
898
|
+
setRumWaitCallbacks(context, detectActiveRumBrowsers)
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
/**
|
|
903
|
+
* Detects delayed RUM after test or afterEach failures.
|
|
904
|
+
* Correlation remains active until every afterEach hook has run.
|
|
905
|
+
*
|
|
906
|
+
* @param {{arguments?: unknown[]}} context
|
|
907
|
+
* @returns {void}
|
|
908
|
+
*/
|
|
909
|
+
function waitForFailedRumCleanup (context) {
|
|
910
|
+
const type = context.arguments?.[1]
|
|
911
|
+
const hookName = context.arguments?.[7]
|
|
912
|
+
if (rumCorrelationBrowsers.size === 0) return
|
|
913
|
+
|
|
914
|
+
if (type === 'Test') {
|
|
915
|
+
isRumCleanupPending = true
|
|
916
|
+
setRumWaitCallbacks(context, detectActiveRumBrowsers, false)
|
|
917
|
+
} else if (type === 'Hook' && hookName === 'afterEach') {
|
|
918
|
+
setRumWaitCallbacks(context, detectActiveRumBrowsers, false)
|
|
919
|
+
} else {
|
|
920
|
+
isRumCleanupPending = true
|
|
921
|
+
if (type === 'Hook' && hookName === 'beforeEach') {
|
|
922
|
+
setRumWaitCallbacks(context, detectActiveRumBrowsers, false)
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
/**
|
|
928
|
+
* Prepares retained RUM state before the next test or non-afterEach hook starts.
|
|
929
|
+
*
|
|
930
|
+
* @param {{arguments?: unknown[], rumCleanupCallback?: () => Promise<void>}} context
|
|
931
|
+
* @returns {void}
|
|
932
|
+
*/
|
|
933
|
+
function waitForRumTestStart (context) {
|
|
934
|
+
const type = context.arguments?.[1]
|
|
935
|
+
const hookName = context.arguments?.[7]
|
|
936
|
+
if (type === 'Hook' && hookName === 'afterEach') return
|
|
937
|
+
|
|
938
|
+
const startsTest = type === 'Test' || (type === 'Hook' && hookName === 'beforeEach')
|
|
939
|
+
if (startsTest) {
|
|
940
|
+
context.rumCleanupCallback = isRumCleanupPending ? prepareNextRumTest : startRumTest
|
|
941
|
+
} else if (isRumCleanupPending) {
|
|
942
|
+
context.rumCleanupCallback = cleanupRumBrowsers
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
79
946
|
/**
|
|
80
947
|
* @typedef {object} WebdriverioRunnerConfig
|
|
81
948
|
* @property {string} framework
|
|
@@ -158,6 +1025,7 @@ function createWorkerConfiguration () {
|
|
|
158
1025
|
isImpactedTestsEnabled: false,
|
|
159
1026
|
isItrEnabled: false,
|
|
160
1027
|
isKnownTestsEnabled: false,
|
|
1028
|
+
isTestFailureScreenshotsEnabled: false,
|
|
161
1029
|
isSuitesSkippingEnabled: false,
|
|
162
1030
|
isTestDynamicInstrumentationEnabled: false,
|
|
163
1031
|
isTestManagementTestsEnabled: false,
|
|
@@ -476,6 +1344,10 @@ function configureCoordinator (state, response) {
|
|
|
476
1344
|
repositoryRoot,
|
|
477
1345
|
} = response || {}
|
|
478
1346
|
|
|
1347
|
+
const screenshotCapabilities = {}
|
|
1348
|
+
screenshotCapabilitiesCh.publish(screenshotCapabilities)
|
|
1349
|
+
configuration.isTestFailureScreenshotsEnabled = screenshotCapabilities.enabled === true
|
|
1350
|
+
|
|
479
1351
|
configuration.repositoryRoot = repositoryRoot
|
|
480
1352
|
if (err || !libraryConfig) {
|
|
481
1353
|
completeCoordinatorInitialization(state)
|
|
@@ -841,6 +1713,28 @@ function handleWorkerMessage (state, workerRecord, message) {
|
|
|
841
1713
|
handleConfigurationRequest(state, workerRecord, message)
|
|
842
1714
|
return
|
|
843
1715
|
}
|
|
1716
|
+
if (message.name === SCREENSHOT_UPLOAD) {
|
|
1717
|
+
const { requestId, ...content } = message.content || {}
|
|
1718
|
+
const respond = (error) => sendWorkerMessage(workerRecord, {
|
|
1719
|
+
origin: 'datadog',
|
|
1720
|
+
name: SCREENSHOT_UPLOAD_RESPONSE,
|
|
1721
|
+
content: {
|
|
1722
|
+
error: error?.message,
|
|
1723
|
+
requestId,
|
|
1724
|
+
},
|
|
1725
|
+
})
|
|
1726
|
+
if (!requestId || !screenshotUploadCh.hasSubscribers) {
|
|
1727
|
+
respond(new Error('WebdriverIO screenshot upload is not available'))
|
|
1728
|
+
} else {
|
|
1729
|
+
try {
|
|
1730
|
+
screenshotUploadCh.publish({ ...content, onDone: respond })
|
|
1731
|
+
} catch (error) {
|
|
1732
|
+
log.error('WebdriverIO screenshot upload error: %s', error?.message || String(error))
|
|
1733
|
+
respond(error)
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
return
|
|
1737
|
+
}
|
|
844
1738
|
if (message.name === SUITE_FINISH) {
|
|
845
1739
|
handleSuiteResults(workerRecord, message)
|
|
846
1740
|
return
|
|
@@ -1000,8 +1894,99 @@ function finishCoordinator (state, error, onDone) {
|
|
|
1000
1894
|
})
|
|
1001
1895
|
}
|
|
1002
1896
|
|
|
1897
|
+
/**
|
|
1898
|
+
* Delays WebdriverIO worker exit until pending log-submission requests settle.
|
|
1899
|
+
*
|
|
1900
|
+
* @param {{
|
|
1901
|
+
* resolveCallback?: (onDone: () => void) => void,
|
|
1902
|
+
* rejectCallback?: (onDone: () => void) => void
|
|
1903
|
+
* }} context
|
|
1904
|
+
* @returns {void}
|
|
1905
|
+
*/
|
|
1906
|
+
function waitForLogSubmissionAtWorkerExit (context) {
|
|
1907
|
+
const shouldCleanRum = rumBrowsers.size > 0
|
|
1908
|
+
const shouldFlushLogs = isWebdriverioWorker && logSubmissionFlushCh.hasSubscribers
|
|
1909
|
+
if (!shouldCleanRum && !shouldFlushLogs) return
|
|
1910
|
+
|
|
1911
|
+
const waitForExit = onDone => {
|
|
1912
|
+
const finish = () => shouldFlushLogs
|
|
1913
|
+
? publishWithCompletion(logSubmissionFlushCh, {}, onDone)
|
|
1914
|
+
: onDone()
|
|
1915
|
+
if (shouldCleanRum) {
|
|
1916
|
+
cleanupAllRumBrowsers().then(finish, finish)
|
|
1917
|
+
} else {
|
|
1918
|
+
finish()
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
context.resolveCallback = waitForExit
|
|
1922
|
+
context.rejectCallback = waitForExit
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1925
|
+
/**
|
|
1926
|
+
* Cleans retained RUM browsers before WebdriverIO deletes the worker session.
|
|
1927
|
+
*
|
|
1928
|
+
* @param {{rumCleanupCallback?: () => Promise<void>}} context
|
|
1929
|
+
* @returns {void}
|
|
1930
|
+
*/
|
|
1931
|
+
function waitForRumCleanupBeforeSessionEnd (context) {
|
|
1932
|
+
context.rumCleanupCallback = cleanupAllRumBrowsers
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
baseReporterWaitForSyncCh.asyncEnd.subscribe(
|
|
1936
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (waitForLogSubmissionAtWorkerExit)
|
|
1937
|
+
)
|
|
1938
|
+
|
|
1939
|
+
runnerRunCh.start.subscribe(
|
|
1940
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (waitForRumCleanupBeforeSessionEnd)
|
|
1941
|
+
)
|
|
1942
|
+
|
|
1943
|
+
urlCh.start.subscribe(
|
|
1944
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (waitForRumNavigationStart)
|
|
1945
|
+
)
|
|
1946
|
+
|
|
1947
|
+
urlCh.asyncEnd.subscribe(
|
|
1948
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (waitForRumNavigation)
|
|
1949
|
+
)
|
|
1950
|
+
|
|
1951
|
+
newWindowCh.start.subscribe(
|
|
1952
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (waitForRumNavigationStart)
|
|
1953
|
+
)
|
|
1954
|
+
|
|
1955
|
+
newWindowCh.asyncEnd.subscribe(
|
|
1956
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (waitForRumNavigation)
|
|
1957
|
+
)
|
|
1958
|
+
|
|
1959
|
+
webdriverCommandCh.end.subscribe(
|
|
1960
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (wrapRumProtocolCommand)
|
|
1961
|
+
)
|
|
1962
|
+
|
|
1963
|
+
webdriverBidiInitCh.end.subscribe(
|
|
1964
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (wrapRumBidiNavigation)
|
|
1965
|
+
)
|
|
1966
|
+
|
|
1967
|
+
testFrameworkFnWrapperCh.asyncEnd.subscribe(
|
|
1968
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (waitForRumCleanup)
|
|
1969
|
+
)
|
|
1970
|
+
|
|
1971
|
+
testFrameworkFnWrapperCh.start.subscribe(
|
|
1972
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (waitForRumTestStart)
|
|
1973
|
+
)
|
|
1974
|
+
|
|
1975
|
+
testFrameworkFnWrapperCh.error.subscribe(
|
|
1976
|
+
/** @type {import('node:diagnostics_channel').ChannelListener} */ (waitForFailedRumCleanup)
|
|
1977
|
+
)
|
|
1978
|
+
|
|
1003
1979
|
// dc-polyfill supports partial tracing-channel subscribers, unlike the Node.js type definition.
|
|
1004
1980
|
// @ts-expect-error
|
|
1981
|
+
executeAsyncCh.subscribe({
|
|
1982
|
+
start (context) {
|
|
1983
|
+
context.rumStartCallback = startRumTest
|
|
1984
|
+
context.rumRetryCallback = retryRumBrowsers
|
|
1985
|
+
context.retryCallback ??= retryRumTest
|
|
1986
|
+
},
|
|
1987
|
+
})
|
|
1988
|
+
|
|
1989
|
+
// @ts-expect-error See the partial tracing-channel subscriber above.
|
|
1005
1990
|
jasmineAdapterInitCh.subscribe({
|
|
1006
1991
|
asyncEnd (context) {
|
|
1007
1992
|
initializeJasmineWorker(context.result || context.self, context)
|