@wdio/browserstack-service 0.0.0-canary-20260722132416
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-MIT +20 -0
- package/README.md +392 -0
- package/browserstack-service.d.ts +43 -0
- package/build/Percy/Percy-Handler.d.ts +32 -0
- package/build/Percy/Percy-Handler.d.ts.map +1 -0
- package/build/Percy/Percy-Handler.js +165 -0
- package/build/Percy/Percy-Handler.js.map +1 -0
- package/build/Percy/Percy.d.ts +19 -0
- package/build/Percy/Percy.d.ts.map +1 -0
- package/build/Percy/Percy.js +164 -0
- package/build/Percy/Percy.js.map +1 -0
- package/build/Percy/PercyBinary.d.ts +10 -0
- package/build/Percy/PercyBinary.d.ts.map +1 -0
- package/build/Percy/PercyBinary.js +161 -0
- package/build/Percy/PercyBinary.js.map +1 -0
- package/build/Percy/PercyCaptureMap.d.ts +9 -0
- package/build/Percy/PercyCaptureMap.d.ts.map +1 -0
- package/build/Percy/PercyCaptureMap.js +36 -0
- package/build/Percy/PercyCaptureMap.js.map +1 -0
- package/build/Percy/PercyHelper.d.ts +8 -0
- package/build/Percy/PercyHelper.d.ts.map +1 -0
- package/build/Percy/PercyHelper.js +68 -0
- package/build/Percy/PercyHelper.js.map +1 -0
- package/build/Percy/PercyLogger.d.ts +15 -0
- package/build/Percy/PercyLogger.d.ts.map +1 -0
- package/build/Percy/PercyLogger.js +68 -0
- package/build/Percy/PercyLogger.js.map +1 -0
- package/build/Percy/PercySDK.d.ts +4 -0
- package/build/Percy/PercySDK.d.ts.map +1 -0
- package/build/Percy/PercySDK.js +86 -0
- package/build/Percy/PercySDK.js.map +1 -0
- package/build/accessibility-handler.d.ts +52 -0
- package/build/accessibility-handler.d.ts.map +1 -0
- package/build/accessibility-handler.js +361 -0
- package/build/accessibility-handler.js.map +1 -0
- package/build/ai-handler.d.ts +23 -0
- package/build/ai-handler.d.ts.map +1 -0
- package/build/ai-handler.js +184 -0
- package/build/ai-handler.js.map +1 -0
- package/build/bstackLogger.d.ts +16 -0
- package/build/bstackLogger.d.ts.map +1 -0
- package/build/bstackLogger.js +71 -0
- package/build/bstackLogger.js.map +1 -0
- package/build/cleanup.d.ts +9 -0
- package/build/cleanup.d.ts.map +1 -0
- package/build/cleanup.js +101 -0
- package/build/cleanup.js.map +1 -0
- package/build/cli/apiUtils.d.ts +14 -0
- package/build/cli/apiUtils.d.ts.map +1 -0
- package/build/cli/apiUtils.js +25 -0
- package/build/cli/apiUtils.js.map +1 -0
- package/build/cli/cliLogger.d.ts +17 -0
- package/build/cli/cliLogger.d.ts.map +1 -0
- package/build/cli/cliLogger.js +82 -0
- package/build/cli/cliLogger.js.map +1 -0
- package/build/cli/cliUtils.d.ts +64 -0
- package/build/cli/cliUtils.d.ts.map +1 -0
- package/build/cli/cliUtils.js +461 -0
- package/build/cli/cliUtils.js.map +1 -0
- package/build/cli/eventDispatcher.d.ts +28 -0
- package/build/cli/eventDispatcher.d.ts.map +1 -0
- package/build/cli/eventDispatcher.js +49 -0
- package/build/cli/eventDispatcher.js.map +1 -0
- package/build/cli/frameworks/automationFramework.d.ts +89 -0
- package/build/cli/frameworks/automationFramework.d.ts.map +1 -0
- package/build/cli/frameworks/automationFramework.js +132 -0
- package/build/cli/frameworks/automationFramework.js.map +1 -0
- package/build/cli/frameworks/constants/automationFrameworkConstants.d.ts +18 -0
- package/build/cli/frameworks/constants/automationFrameworkConstants.d.ts.map +1 -0
- package/build/cli/frameworks/constants/automationFrameworkConstants.js +18 -0
- package/build/cli/frameworks/constants/automationFrameworkConstants.js.map +1 -0
- package/build/cli/frameworks/constants/testFrameworkConstants.d.ts +43 -0
- package/build/cli/frameworks/constants/testFrameworkConstants.d.ts.map +1 -0
- package/build/cli/frameworks/constants/testFrameworkConstants.js +43 -0
- package/build/cli/frameworks/constants/testFrameworkConstants.js.map +1 -0
- package/build/cli/frameworks/testFramework.d.ts +89 -0
- package/build/cli/frameworks/testFramework.d.ts.map +1 -0
- package/build/cli/frameworks/testFramework.js +126 -0
- package/build/cli/frameworks/testFramework.js.map +1 -0
- package/build/cli/frameworks/wdioAutomationFramework.d.ts +28 -0
- package/build/cli/frameworks/wdioAutomationFramework.d.ts.map +1 -0
- package/build/cli/frameworks/wdioAutomationFramework.js +67 -0
- package/build/cli/frameworks/wdioAutomationFramework.js.map +1 -0
- package/build/cli/frameworks/wdioMochaTestFramework.d.ts +82 -0
- package/build/cli/frameworks/wdioMochaTestFramework.d.ts.map +1 -0
- package/build/cli/frameworks/wdioMochaTestFramework.js +322 -0
- package/build/cli/frameworks/wdioMochaTestFramework.js.map +1 -0
- package/build/cli/grpcClient.d.ts +86 -0
- package/build/cli/grpcClient.d.ts.map +1 -0
- package/build/cli/grpcClient.js +532 -0
- package/build/cli/grpcClient.js.map +1 -0
- package/build/cli/index.d.ts +150 -0
- package/build/cli/index.d.ts.map +1 -0
- package/build/cli/index.js +464 -0
- package/build/cli/index.js.map +1 -0
- package/build/cli/instances/automationFrameworkInstance.d.ts +33 -0
- package/build/cli/instances/automationFrameworkInstance.d.ts.map +1 -0
- package/build/cli/instances/automationFrameworkInstance.js +45 -0
- package/build/cli/instances/automationFrameworkInstance.js.map +1 -0
- package/build/cli/instances/testFrameworkInstance.d.ts +62 -0
- package/build/cli/instances/testFrameworkInstance.d.ts.map +1 -0
- package/build/cli/instances/testFrameworkInstance.js +97 -0
- package/build/cli/instances/testFrameworkInstance.js.map +1 -0
- package/build/cli/instances/trackedContext.d.ts +32 -0
- package/build/cli/instances/trackedContext.d.ts.map +1 -0
- package/build/cli/instances/trackedContext.js +48 -0
- package/build/cli/instances/trackedContext.js.map +1 -0
- package/build/cli/instances/trackedInstance.d.ts +40 -0
- package/build/cli/instances/trackedInstance.d.ts.map +1 -0
- package/build/cli/instances/trackedInstance.js +64 -0
- package/build/cli/instances/trackedInstance.js.map +1 -0
- package/build/cli/modules/accessibilityModule.d.ts +46 -0
- package/build/cli/modules/accessibilityModule.d.ts.map +1 -0
- package/build/cli/modules/accessibilityModule.js +447 -0
- package/build/cli/modules/accessibilityModule.js.map +1 -0
- package/build/cli/modules/automateModule.d.ts +26 -0
- package/build/cli/modules/automateModule.d.ts.map +1 -0
- package/build/cli/modules/automateModule.js +223 -0
- package/build/cli/modules/automateModule.js.map +1 -0
- package/build/cli/modules/baseModule.d.ts +33 -0
- package/build/cli/modules/baseModule.d.ts.map +1 -0
- package/build/cli/modules/baseModule.js +52 -0
- package/build/cli/modules/baseModule.js.map +1 -0
- package/build/cli/modules/observabilityModule.d.ts +22 -0
- package/build/cli/modules/observabilityModule.d.ts.map +1 -0
- package/build/cli/modules/observabilityModule.js +46 -0
- package/build/cli/modules/observabilityModule.js.map +1 -0
- package/build/cli/modules/percyModule.d.ts +19 -0
- package/build/cli/modules/percyModule.d.ts.map +1 -0
- package/build/cli/modules/percyModule.js +78 -0
- package/build/cli/modules/percyModule.js.map +1 -0
- package/build/cli/modules/testHubModule.d.ts +30 -0
- package/build/cli/modules/testHubModule.d.ts.map +1 -0
- package/build/cli/modules/testHubModule.js +233 -0
- package/build/cli/modules/testHubModule.js.map +1 -0
- package/build/cli/modules/webdriverIOModule.d.ts +29 -0
- package/build/cli/modules/webdriverIOModule.d.ts.map +1 -0
- package/build/cli/modules/webdriverIOModule.js +129 -0
- package/build/cli/modules/webdriverIOModule.js.map +1 -0
- package/build/cli/states/automationFrameworkState.d.ts +65 -0
- package/build/cli/states/automationFrameworkState.d.ts.map +1 -0
- package/build/cli/states/automationFrameworkState.js +62 -0
- package/build/cli/states/automationFrameworkState.js.map +1 -0
- package/build/cli/states/hookState.d.ts +45 -0
- package/build/cli/states/hookState.d.ts.map +1 -0
- package/build/cli/states/hookState.js +44 -0
- package/build/cli/states/hookState.js.map +1 -0
- package/build/cli/states/testFrameworkState.d.ts +125 -0
- package/build/cli/states/testFrameworkState.d.ts.map +1 -0
- package/build/cli/states/testFrameworkState.js +116 -0
- package/build/cli/states/testFrameworkState.js.map +1 -0
- package/build/config.d.ts +29 -0
- package/build/config.d.ts.map +1 -0
- package/build/config.js +52 -0
- package/build/config.js.map +1 -0
- package/build/constants.d.ts +96 -0
- package/build/constants.d.ts.map +1 -0
- package/build/constants.js +156 -0
- package/build/constants.js.map +1 -0
- package/build/crash-reporter.d.ts +18 -0
- package/build/crash-reporter.d.ts.map +1 -0
- package/build/crash-reporter.js +134 -0
- package/build/crash-reporter.js.map +1 -0
- package/build/cucumber-types.d.ts +194 -0
- package/build/cucumber-types.d.ts.map +1 -0
- package/build/cucumber-types.js +2 -0
- package/build/cucumber-types.js.map +1 -0
- package/build/data-store.d.ts +4 -0
- package/build/data-store.d.ts.map +1 -0
- package/build/data-store.js +42 -0
- package/build/data-store.js.map +1 -0
- package/build/exitHandler.d.ts +4 -0
- package/build/exitHandler.d.ts.map +1 -0
- package/build/exitHandler.js +105 -0
- package/build/exitHandler.js.map +1 -0
- package/build/fileStream.d.ts +9 -0
- package/build/fileStream.d.ts.map +1 -0
- package/build/fileStream.js +13 -0
- package/build/fileStream.js.map +1 -0
- package/build/grpc/generated/sdk-messages-accessibility.d.ts +76 -0
- package/build/grpc/generated/sdk-messages-accessibility.d.ts.map +1 -0
- package/build/grpc/generated/sdk-messages-accessibility.js +908 -0
- package/build/grpc/generated/sdk-messages-accessibility.js.map +1 -0
- package/build/grpc/generated/sdk-messages-ai.d.ts +52 -0
- package/build/grpc/generated/sdk-messages-ai.d.ts.map +1 -0
- package/build/grpc/generated/sdk-messages-ai.js +643 -0
- package/build/grpc/generated/sdk-messages-ai.js.map +1 -0
- package/build/grpc/generated/sdk-messages-observability.d.ts +44 -0
- package/build/grpc/generated/sdk-messages-observability.d.ts.map +1 -0
- package/build/grpc/generated/sdk-messages-observability.js +505 -0
- package/build/grpc/generated/sdk-messages-observability.js.map +1 -0
- package/build/grpc/generated/sdk-messages-percy.d.ts +16 -0
- package/build/grpc/generated/sdk-messages-percy.d.ts.map +1 -0
- package/build/grpc/generated/sdk-messages-percy.js +111 -0
- package/build/grpc/generated/sdk-messages-percy.js.map +1 -0
- package/build/grpc/generated/sdk-messages-testhub.d.ts +16 -0
- package/build/grpc/generated/sdk-messages-testhub.d.ts.map +1 -0
- package/build/grpc/generated/sdk-messages-testhub.js +132 -0
- package/build/grpc/generated/sdk-messages-testhub.js.map +1 -0
- package/build/grpc/generated/sdk-messages.d.ts +288 -0
- package/build/grpc/generated/sdk-messages.d.ts.map +1 -0
- package/build/grpc/generated/sdk-messages.js +3958 -0
- package/build/grpc/generated/sdk-messages.js.map +1 -0
- package/build/grpc/generated/sdk.d.ts +282 -0
- package/build/grpc/generated/sdk.d.ts.map +1 -0
- package/build/grpc/generated/sdk.js +197 -0
- package/build/grpc/generated/sdk.js.map +1 -0
- package/build/grpc/index.d.ts +43 -0
- package/build/grpc/index.d.ts.map +1 -0
- package/build/grpc/index.js +154 -0
- package/build/grpc/index.js.map +1 -0
- package/build/index.d.ts +20 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +13 -0
- package/build/index.js.map +1 -0
- package/build/insights-handler.d.ts +76 -0
- package/build/insights-handler.d.ts.map +1 -0
- package/build/insights-handler.js +855 -0
- package/build/insights-handler.js.map +1 -0
- package/build/instrumentation/funnelInstrumentation.d.ts +8 -0
- package/build/instrumentation/funnelInstrumentation.d.ts.map +1 -0
- package/build/instrumentation/funnelInstrumentation.js +235 -0
- package/build/instrumentation/funnelInstrumentation.js.map +1 -0
- package/build/instrumentation/performance/constants.d.ts +151 -0
- package/build/instrumentation/performance/constants.d.ts.map +1 -0
- package/build/instrumentation/performance/constants.js +158 -0
- package/build/instrumentation/performance/constants.js.map +1 -0
- package/build/instrumentation/performance/performance-tester.d.ts +55 -0
- package/build/instrumentation/performance/performance-tester.d.ts.map +1 -0
- package/build/instrumentation/performance/performance-tester.js +403 -0
- package/build/instrumentation/performance/performance-tester.js.map +1 -0
- package/build/launcher.d.ts +49 -0
- package/build/launcher.d.ts.map +1 -0
- package/build/launcher.js +1077 -0
- package/build/launcher.js.map +1 -0
- package/build/log4jsAppender.d.ts +2 -0
- package/build/log4jsAppender.d.ts.map +1 -0
- package/build/log4jsAppender.js +20 -0
- package/build/log4jsAppender.js.map +1 -0
- package/build/logPatcher.d.ts +13 -0
- package/build/logPatcher.d.ts.map +1 -0
- package/build/logPatcher.js +39 -0
- package/build/logPatcher.js.map +1 -0
- package/build/logReportingAPI.d.ts +12 -0
- package/build/logReportingAPI.d.ts.map +1 -0
- package/build/logReportingAPI.js +57 -0
- package/build/logReportingAPI.js.map +1 -0
- package/build/reporter.d.ts +40 -0
- package/build/reporter.d.ts.map +1 -0
- package/build/reporter.js +254 -0
- package/build/reporter.js.map +1 -0
- package/build/request-handler.d.ts +19 -0
- package/build/request-handler.d.ts.map +1 -0
- package/build/request-handler.js +78 -0
- package/build/request-handler.js.map +1 -0
- package/build/scripts/accessibility-scripts.d.ts +26 -0
- package/build/scripts/accessibility-scripts.d.ts.map +1 -0
- package/build/scripts/accessibility-scripts.js +91 -0
- package/build/scripts/accessibility-scripts.js.map +1 -0
- package/build/service.d.ts +68 -0
- package/build/service.d.ts.map +1 -0
- package/build/service.js +740 -0
- package/build/service.js.map +1 -0
- package/build/testHub/utils.d.ts +10 -0
- package/build/testHub/utils.d.ts.map +1 -0
- package/build/testHub/utils.js +67 -0
- package/build/testHub/utils.js.map +1 -0
- package/build/testOps/featureStats.d.ts +45 -0
- package/build/testOps/featureStats.d.ts.map +1 -0
- package/build/testOps/featureStats.js +117 -0
- package/build/testOps/featureStats.js.map +1 -0
- package/build/testOps/featureUsage.d.ts +22 -0
- package/build/testOps/featureUsage.d.ts.map +1 -0
- package/build/testOps/featureUsage.js +48 -0
- package/build/testOps/featureUsage.js.map +1 -0
- package/build/testOps/listener.d.ts +40 -0
- package/build/testOps/listener.d.ts.map +1 -0
- package/build/testOps/listener.js +269 -0
- package/build/testOps/listener.js.map +1 -0
- package/build/testOps/requestUtils.d.ts +4 -0
- package/build/testOps/requestUtils.d.ts.map +1 -0
- package/build/testOps/requestUtils.js +41 -0
- package/build/testOps/requestUtils.js.map +1 -0
- package/build/testOps/testOpsConfig.d.ts +11 -0
- package/build/testOps/testOpsConfig.d.ts.map +1 -0
- package/build/testOps/testOpsConfig.js +20 -0
- package/build/testOps/testOpsConfig.js.map +1 -0
- package/build/testOps/usageStats.d.ts +404 -0
- package/build/testOps/usageStats.d.ts.map +1 -0
- package/build/testOps/usageStats.js +115 -0
- package/build/testOps/usageStats.js.map +1 -0
- package/build/testorchestration/apply-orchestration.d.ts +7 -0
- package/build/testorchestration/apply-orchestration.d.ts.map +1 -0
- package/build/testorchestration/apply-orchestration.js +43 -0
- package/build/testorchestration/apply-orchestration.js.map +1 -0
- package/build/testorchestration/helpers.d.ts +20 -0
- package/build/testorchestration/helpers.d.ts.map +1 -0
- package/build/testorchestration/helpers.js +413 -0
- package/build/testorchestration/helpers.js.map +1 -0
- package/build/testorchestration/request-utils.d.ts +23 -0
- package/build/testorchestration/request-utils.d.ts.map +1 -0
- package/build/testorchestration/request-utils.js +82 -0
- package/build/testorchestration/request-utils.js.map +1 -0
- package/build/testorchestration/test-ordering-server.d.ts +33 -0
- package/build/testorchestration/test-ordering-server.d.ts.map +1 -0
- package/build/testorchestration/test-ordering-server.js +169 -0
- package/build/testorchestration/test-ordering-server.js.map +1 -0
- package/build/testorchestration/testorcherstrationhandler.d.ts +50 -0
- package/build/testorchestration/testorcherstrationhandler.d.ts.map +1 -0
- package/build/testorchestration/testorcherstrationhandler.js +134 -0
- package/build/testorchestration/testorcherstrationhandler.js.map +1 -0
- package/build/testorchestration/testorcherstrationutils.d.ts +86 -0
- package/build/testorchestration/testorcherstrationutils.d.ts.map +1 -0
- package/build/testorchestration/testorcherstrationutils.js +380 -0
- package/build/testorchestration/testorcherstrationutils.js.map +1 -0
- package/build/types.d.ts +413 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/util.d.ts +241 -0
- package/build/util.d.ts.map +1 -0
- package/build/util.js +1665 -0
- package/build/util.js.map +1 -0
- package/package.json +96 -0
- package/src/proto/browserstack/sdk/v1/sdk-messages-accessibility.proto +71 -0
- package/src/proto/browserstack/sdk/v1/sdk-messages-ai.proto +48 -0
- package/src/proto/browserstack/sdk/v1/sdk-messages-observability.proto +39 -0
- package/src/proto/browserstack/sdk/v1/sdk-messages-percy.proto +10 -0
- package/src/proto/browserstack/sdk/v1/sdk-messages-testhub.proto +10 -0
- package/src/proto/browserstack/sdk/v1/sdk-messages.proto +276 -0
- package/src/proto/browserstack/sdk/v1/sdk.proto +41 -0
package/build/service.js
ADDED
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
import got from 'got';
|
|
8
|
+
import { getBrowserDescription, getBrowserCapabilities, isBrowserstackCapability, getParentSuiteName, isBrowserstackSession, patchConsoleLogs, shouldAddServiceVersion, isTrue, normalizeTestReportingConfig, normalizeTestReportingEnvVariables } from './util.js';
|
|
9
|
+
import InsightsHandler from './insights-handler.js';
|
|
10
|
+
import TestReporter from './reporter.js';
|
|
11
|
+
import { DEFAULT_OPTIONS, NOT_ALLOWED_KEYS_IN_CAPS, PERF_MEASUREMENT_ENV } from './constants.js';
|
|
12
|
+
import CrashReporter from './crash-reporter.js';
|
|
13
|
+
import AccessibilityHandler from './accessibility-handler.js';
|
|
14
|
+
import { BStackLogger } from './bstackLogger.js';
|
|
15
|
+
import PercyHandler from './Percy/Percy-Handler.js';
|
|
16
|
+
import Listener from './testOps/listener.js';
|
|
17
|
+
import { saveWorkerData } from './data-store.js';
|
|
18
|
+
import UsageStats from './testOps/usageStats.js';
|
|
19
|
+
import { shouldProcessEventForTesthub } from './testHub/utils.js';
|
|
20
|
+
import AiHandler from './ai-handler.js';
|
|
21
|
+
import PerformanceTester from './instrumentation/performance/performance-tester.js';
|
|
22
|
+
import * as PERFORMANCE_SDK_EVENTS from './instrumentation/performance/constants.js';
|
|
23
|
+
import { EVENTS } from './instrumentation/performance/constants.js';
|
|
24
|
+
import { BrowserstackCLI } from './cli/index.js';
|
|
25
|
+
import { TestFrameworkState } from './cli/states/testFrameworkState.js';
|
|
26
|
+
import { HookState } from './cli/states/hookState.js';
|
|
27
|
+
import { AutomationFrameworkState } from './cli/states/automationFrameworkState.js';
|
|
28
|
+
import TestFramework from './cli/frameworks/testFramework.js';
|
|
29
|
+
import { TestFrameworkConstants } from './cli/frameworks/constants/testFrameworkConstants.js';
|
|
30
|
+
import { AutomationFrameworkConstants } from './cli/frameworks/constants/automationFrameworkConstants.js';
|
|
31
|
+
import AutomationFramework from './cli/frameworks/automationFramework.js';
|
|
32
|
+
import util from 'node:util';
|
|
33
|
+
import APIUtils from './cli/apiUtils.js';
|
|
34
|
+
import { CLIUtils } from './cli/cliUtils.js';
|
|
35
|
+
export default class BrowserstackService {
|
|
36
|
+
_caps;
|
|
37
|
+
_config;
|
|
38
|
+
_sessionBaseUrl = `${APIUtils.BROWSERSTACK_AUTOMATE_API_URL}/automate/sessions`;
|
|
39
|
+
_failReasons = [];
|
|
40
|
+
_hookFailReasons = [];
|
|
41
|
+
_pureTestFailReasons = [];
|
|
42
|
+
_scenariosThatRan = [];
|
|
43
|
+
_failureStatuses = ['failed', 'ambiguous', 'undefined', 'unknown'];
|
|
44
|
+
_browser;
|
|
45
|
+
_suiteTitle;
|
|
46
|
+
_suiteFile;
|
|
47
|
+
_fullTitle;
|
|
48
|
+
_options;
|
|
49
|
+
_specsRan = false;
|
|
50
|
+
_observability;
|
|
51
|
+
_currentTest;
|
|
52
|
+
_insightsHandler;
|
|
53
|
+
_accessibility;
|
|
54
|
+
_accessibilityHandler;
|
|
55
|
+
_percy;
|
|
56
|
+
_percyCaptureMode = undefined;
|
|
57
|
+
_percyHandler;
|
|
58
|
+
_turboScale;
|
|
59
|
+
constructor(options, _caps, _config) {
|
|
60
|
+
this._caps = _caps;
|
|
61
|
+
this._config = _config;
|
|
62
|
+
this._options = { ...DEFAULT_OPTIONS, ...options };
|
|
63
|
+
// added to maintain backward compatibility with webdriverIO v5
|
|
64
|
+
this._config || (this._config = this._options);
|
|
65
|
+
normalizeTestReportingConfig(this._options);
|
|
66
|
+
normalizeTestReportingEnvVariables();
|
|
67
|
+
this._observability = this._options.testObservability;
|
|
68
|
+
this._accessibility = this._options.accessibility;
|
|
69
|
+
this._percy = isTrue(process.env.BROWSERSTACK_PERCY);
|
|
70
|
+
this._percyCaptureMode = process.env.BROWSERSTACK_PERCY_CAPTURE_MODE;
|
|
71
|
+
this._turboScale = this._options.turboScale;
|
|
72
|
+
PerformanceTester.startMonitoring('performance-report-service.csv');
|
|
73
|
+
if (shouldProcessEventForTesthub('')) {
|
|
74
|
+
this._config.reporters?.push(TestReporter);
|
|
75
|
+
}
|
|
76
|
+
if (process.env.BROWSERSTACK_TURBOSCALE) {
|
|
77
|
+
this._turboScale = process.env.BROWSERSTACK_TURBOSCALE === 'true';
|
|
78
|
+
}
|
|
79
|
+
process.env.BROWSERSTACK_TURBOSCALE_INTERNAL = String(this._turboScale);
|
|
80
|
+
// Cucumber specific
|
|
81
|
+
const strict = Boolean(this._config.cucumberOpts && this._config.cucumberOpts.strict);
|
|
82
|
+
// See https://github.com/cucumber/cucumber-js/blob/master/src/runtime/index.ts#L136
|
|
83
|
+
if (strict) {
|
|
84
|
+
this._failureStatuses.push('pending');
|
|
85
|
+
}
|
|
86
|
+
if (process.env.WDIO_WORKER_ID === process.env.BEST_PLATFORM_CID) {
|
|
87
|
+
process.env.PERCY_SNAPSHOT = 'true';
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
_updateCaps(fn) {
|
|
91
|
+
const multiRemoteCap = this._caps;
|
|
92
|
+
if (multiRemoteCap.capabilities) {
|
|
93
|
+
return Object.entries(multiRemoteCap).forEach(([, caps]) => fn(caps.capabilities));
|
|
94
|
+
}
|
|
95
|
+
return fn(this._caps);
|
|
96
|
+
}
|
|
97
|
+
async beforeSession(config, capabilities) {
|
|
98
|
+
PerformanceTester.start(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.INIT);
|
|
99
|
+
// if no user and key is specified even though a browserstack service was
|
|
100
|
+
// provided set user and key with values so that the session request
|
|
101
|
+
// will fail
|
|
102
|
+
const testObservabilityOptions = this._options.testObservabilityOptions;
|
|
103
|
+
if (!config.user && !(testObservabilityOptions && testObservabilityOptions.user)) {
|
|
104
|
+
config.user = 'NotSetUser';
|
|
105
|
+
}
|
|
106
|
+
if (!config.key && !(testObservabilityOptions && testObservabilityOptions.key)) {
|
|
107
|
+
config.key = 'NotSetKey';
|
|
108
|
+
}
|
|
109
|
+
this._config.user = config.user;
|
|
110
|
+
this._config.key = config.key;
|
|
111
|
+
try {
|
|
112
|
+
// Detect if multi-remote and disable CLI for those sessions
|
|
113
|
+
if (CLIUtils.checkCLISupportedFrameworks(this._config.framework) && process.env.BROWSERSTACK_IS_MULTIREMOTE !== 'true') {
|
|
114
|
+
// Connect to Browserstack CLI from worker
|
|
115
|
+
await BrowserstackCLI.getInstance().bootstrap(this._options, this._config);
|
|
116
|
+
// Get the nearest hub and update it in config
|
|
117
|
+
PerformanceTester.start(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_FIND_NEAREST_HUB);
|
|
118
|
+
const hubUrl = BrowserstackCLI.getInstance().getConfig().hubUrl;
|
|
119
|
+
if (hubUrl) {
|
|
120
|
+
this._config.hostname = new URL(hubUrl).hostname;
|
|
121
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_FIND_NEAREST_HUB);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_FIND_NEAREST_HUB, false, 'Hub URL not found');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (BrowserstackCLI.getInstance().isRunning()) {
|
|
128
|
+
await BrowserstackCLI.getInstance().getAutomationFramework().trackEvent(AutomationFrameworkState.CREATE, HookState.PRE, { caps: capabilities });
|
|
129
|
+
const instance = AutomationFramework.getTrackedInstance();
|
|
130
|
+
const caps = AutomationFramework.getState(instance, AutomationFrameworkConstants.KEY_CAPABILITIES);
|
|
131
|
+
Object.assign(capabilities, caps);
|
|
132
|
+
// Strip CLI-only options that BrowserStack hub doesn't accept
|
|
133
|
+
const bstackOptions = capabilities['bstack:options'];
|
|
134
|
+
if (bstackOptions && typeof bstackOptions === 'object') {
|
|
135
|
+
NOT_ALLOWED_KEYS_IN_CAPS.forEach(key => delete bstackOptions[key]);
|
|
136
|
+
}
|
|
137
|
+
NOT_ALLOWED_KEYS_IN_CAPS.forEach(key => delete capabilities[`browserstack.${key}`]);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (err) {
|
|
141
|
+
BStackLogger.error(`Error while connecting to Browserstack CLI: ${err}`);
|
|
142
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.INIT, false, util.format(err));
|
|
143
|
+
throw err;
|
|
144
|
+
}
|
|
145
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.INIT);
|
|
146
|
+
// Start tracking device allocation - this measures the gap between beforeSession end and before start
|
|
147
|
+
// This captures the time WebDriverIO takes to create the remote session with BrowserStack
|
|
148
|
+
PerformanceTester.start(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_DEVICE_ALLOCATION);
|
|
149
|
+
BStackLogger.debug('Device allocation tracking started - waiting for WebDriverIO to create remote session');
|
|
150
|
+
}
|
|
151
|
+
async before(caps, specs, browser) {
|
|
152
|
+
// End device allocation tracking - remote session is now created and browser object is available
|
|
153
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_DEVICE_ALLOCATION, true, 'Device allocated and session created');
|
|
154
|
+
BStackLogger.debug('Device allocation tracking ended - remote session created successfully');
|
|
155
|
+
PerformanceTester.start(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.PRE_INITIALIZE);
|
|
156
|
+
// added to maintain backward compatibility with webdriverIO v5
|
|
157
|
+
this._browser = browser ? browser : globalThis.browser;
|
|
158
|
+
PerformanceTester.browser = this._browser;
|
|
159
|
+
// Healing Support:
|
|
160
|
+
if (!shouldAddServiceVersion(this._config, this._options.testObservability, caps)) {
|
|
161
|
+
try {
|
|
162
|
+
await AiHandler.selfHeal(this._options, caps, this._browser);
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
if (this._options.selfHeal === true) {
|
|
166
|
+
BStackLogger.warn(`Error while setting up self-healing: ${err}. Disabling healing for this session.`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Ensure capabilities are not null in case of multiremote
|
|
171
|
+
if (this._isAppAutomate()) {
|
|
172
|
+
this._sessionBaseUrl = `${APIUtils.BROWSERSTACK_AA_API_CLOUD_URL}/app-automate/sessions`;
|
|
173
|
+
}
|
|
174
|
+
if (this._turboScale) {
|
|
175
|
+
this._sessionBaseUrl = `${APIUtils.BROWSERSTACK_AUTOMATE_API_URL}/automate-turboscale/v1/sessions`;
|
|
176
|
+
}
|
|
177
|
+
this._scenariosThatRan = [];
|
|
178
|
+
PerformanceTester.scenarioThatRan = [...(this._scenariosThatRan ?? [])];
|
|
179
|
+
if (this._browser) {
|
|
180
|
+
try {
|
|
181
|
+
const sessionId = this._browser.sessionId;
|
|
182
|
+
try {
|
|
183
|
+
this._accessibilityHandler = new AccessibilityHandler(this._browser, this._caps, this._options, this._isAppAutomate(), this._config, this._config.framework, this._accessibility, this._turboScale, this._options.accessibilityOptions);
|
|
184
|
+
if (isBrowserstackSession(this._browser) && BrowserstackCLI.getInstance().isRunning()) {
|
|
185
|
+
BStackLogger.info(`CLI is running, tracking accessibility event for before: ${sessionId}`);
|
|
186
|
+
// BrowserstackCLI.getInstance().getTestFramework()!.trackEvent(AutomationFrameworkState.CREATE, HookState.POST, { sessionId })
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
await this._accessibilityHandler.before(sessionId);
|
|
190
|
+
}
|
|
191
|
+
Listener.setAccessibilityOptions(this._options.accessibilityOptions);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
BStackLogger.error(`[Accessibility Test Run] Error in service class before function: ${err}`);
|
|
195
|
+
}
|
|
196
|
+
if (shouldProcessEventForTesthub('')) {
|
|
197
|
+
patchConsoleLogs();
|
|
198
|
+
this._insightsHandler = new InsightsHandler(this._browser, this._config.framework, this._caps, this._options);
|
|
199
|
+
if (BrowserstackCLI.getInstance().isRunning()) {
|
|
200
|
+
await BrowserstackCLI.getInstance().getAutomationFramework().trackEvent(AutomationFrameworkState.CREATE, HookState.POST, { browser: this._browser, hubUrl: this._config.hostname });
|
|
201
|
+
this._insightsHandler.setGitConfigPath();
|
|
202
|
+
/**
|
|
203
|
+
* SDK-6277: register the command/result listeners in the CLI/binary flow too,
|
|
204
|
+
* so the user's saveScreenshot()/takeScreenshot() command result is captured and
|
|
205
|
+
* forwarded for screenshot-on-failure. Without this the screenshot is dropped in v8.
|
|
206
|
+
* (browserCommand routes the screenshot over gRPC in CLI mode — see insights-handler.)
|
|
207
|
+
*/
|
|
208
|
+
// 'client:beforeCommand' only records the command synchronously — no await needed.
|
|
209
|
+
this._browser.on('command', (command) => {
|
|
210
|
+
if (shouldProcessEventForTesthub('')) {
|
|
211
|
+
this._insightsHandler?.browserCommand('client:beforeCommand', Object.assign(command, { sessionId }), this._currentTest);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
this._browser.on('result', (result) => {
|
|
215
|
+
if (shouldProcessEventForTesthub('')) {
|
|
216
|
+
// 'client:afterCommand' is async in CLI mode (forwards the screenshot to the
|
|
217
|
+
// binary over gRPC). Handle the returned promise so a failed upload is logged
|
|
218
|
+
// rather than dropped. (o11yClassErrorHandler also routes method errors to
|
|
219
|
+
// processError; this .catch makes the call-site handling explicit.)
|
|
220
|
+
this._insightsHandler?.browserCommand('client:afterCommand', Object.assign(result, { sessionId }), this._currentTest)?.catch((err) => BStackLogger.debug(`Error forwarding afterCommand in CLI mode: ${err}`));
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.PRE_INITIALIZE);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
await this._insightsHandler.before();
|
|
227
|
+
}
|
|
228
|
+
if (!BrowserstackCLI.getInstance().isRunning()) {
|
|
229
|
+
/**
|
|
230
|
+
* register command event
|
|
231
|
+
*/
|
|
232
|
+
this._browser.on('command', async (command) => {
|
|
233
|
+
if (shouldProcessEventForTesthub('')) {
|
|
234
|
+
this._insightsHandler?.browserCommand('client:beforeCommand', Object.assign(command, { sessionId }), this._currentTest);
|
|
235
|
+
}
|
|
236
|
+
await this._percyHandler?.browserBeforeCommand(Object.assign(command, { sessionId }));
|
|
237
|
+
});
|
|
238
|
+
/**
|
|
239
|
+
* register result event
|
|
240
|
+
*/
|
|
241
|
+
this._browser.on('result', (result) => {
|
|
242
|
+
if (shouldProcessEventForTesthub('')) {
|
|
243
|
+
this._insightsHandler?.browserCommand('client:afterCommand', Object.assign(result, { sessionId }), this._currentTest);
|
|
244
|
+
}
|
|
245
|
+
this._percyHandler?.browserAfterCommand(Object.assign(result, { sessionId }));
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
catch (err) {
|
|
250
|
+
BStackLogger.error(`Error in service class before function: ${err}`);
|
|
251
|
+
if (shouldProcessEventForTesthub('')) {
|
|
252
|
+
CrashReporter.uploadCrashReport(`Error in service class before function: ${err}`, err && err.stack);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (!BrowserstackCLI.getInstance().isRunning()) {
|
|
256
|
+
if (this._percy) {
|
|
257
|
+
this._percyHandler = new PercyHandler(this._percyCaptureMode, this._browser, this._caps, this._isAppAutomate(), this._config.framework);
|
|
258
|
+
this._percyHandler.before();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.PRE_INITIALIZE);
|
|
263
|
+
PerformanceTester.start(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.POST_INITIALIZE);
|
|
264
|
+
const result = await this._printSessionURL();
|
|
265
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.POST_INITIALIZE);
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Set the default job name at the suite level to make sure we account
|
|
270
|
+
* for the cases where there is a long running `before` function for a
|
|
271
|
+
* suite or one that can fail.
|
|
272
|
+
* Don't do this for Jasmine because `suite.title` is `Jasmine__TopLevel__Suite`
|
|
273
|
+
* and `suite.fullTitle` is `undefined`, so no alternative to use for the job name.
|
|
274
|
+
*/
|
|
275
|
+
async beforeSuite(suite) {
|
|
276
|
+
this._suiteTitle = suite.title;
|
|
277
|
+
this._insightsHandler?.setSuiteFile(suite.file);
|
|
278
|
+
this._accessibilityHandler?.setSuiteFile(suite.file);
|
|
279
|
+
if (suite.title && suite.title !== 'Jasmine__TopLevel__Suite') {
|
|
280
|
+
if (!BrowserstackCLI.getInstance().isRunning() || this._config.framework !== 'mocha') {
|
|
281
|
+
await this._setSessionName(suite.title);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
async beforeHook(test, context) {
|
|
286
|
+
if (this._config.framework !== 'cucumber') {
|
|
287
|
+
this._currentTest = test; // not update currentTest when this is called for cucumber step
|
|
288
|
+
}
|
|
289
|
+
await this._insightsHandler?.beforeHook(test, context);
|
|
290
|
+
}
|
|
291
|
+
async afterHook(test, context, result) {
|
|
292
|
+
// Track hook failures separately
|
|
293
|
+
if (result && !result.passed) {
|
|
294
|
+
const hookError = (result.error && result.error.message) || 'Hook failed';
|
|
295
|
+
this._hookFailReasons.push(hookError);
|
|
296
|
+
// Still add to main failReasons for backward compatibility if ignoreHooksStatus is not enabled
|
|
297
|
+
if (!this._options.testObservabilityOptions?.ignoreHooksStatus) {
|
|
298
|
+
this._failReasons.push(hookError);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
await this._insightsHandler?.afterHook(test, result);
|
|
302
|
+
}
|
|
303
|
+
async beforeTest(test) {
|
|
304
|
+
this._currentTest = test;
|
|
305
|
+
let suiteTitle = this._suiteTitle;
|
|
306
|
+
if (test.fullName) {
|
|
307
|
+
// For Jasmine, `suite.title` is `Jasmine__TopLevel__Suite`.
|
|
308
|
+
// This tweak allows us to set the real suite name.
|
|
309
|
+
const testSuiteName = test.fullName.slice(0, test.fullName.indexOf(test.description || '') - 1);
|
|
310
|
+
if (this._suiteTitle === 'Jasmine__TopLevel__Suite') {
|
|
311
|
+
suiteTitle = testSuiteName;
|
|
312
|
+
}
|
|
313
|
+
else if (this._suiteTitle) {
|
|
314
|
+
suiteTitle = getParentSuiteName(this._suiteTitle, testSuiteName);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
await this._setAnnotation(`Test: ${test.fullName ?? test.title}`);
|
|
318
|
+
if (BrowserstackCLI.getInstance().isRunning()) {
|
|
319
|
+
await BrowserstackCLI.getInstance().getTestFramework().trackEvent(TestFrameworkState.INIT_TEST, HookState.PRE, { test });
|
|
320
|
+
const uuid = TestFramework.getState(TestFramework.getTrackedInstance(), TestFrameworkConstants.KEY_TEST_UUID);
|
|
321
|
+
this._insightsHandler?.setTestData(test, uuid);
|
|
322
|
+
await BrowserstackCLI.getInstance().getTestFramework().trackEvent(TestFrameworkState.TEST, HookState.PRE, { test, suiteTitle });
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
await this._setSessionName(suiteTitle, test);
|
|
326
|
+
await this._accessibilityHandler?.beforeTest(suiteTitle, test);
|
|
327
|
+
await this._insightsHandler?.beforeTest(test);
|
|
328
|
+
}
|
|
329
|
+
async afterTest(test, context, results) {
|
|
330
|
+
this._specsRan = true;
|
|
331
|
+
const { error, passed } = results;
|
|
332
|
+
if (!passed) {
|
|
333
|
+
const testError = (error && error.message) || 'Unknown Error';
|
|
334
|
+
this._failReasons.push(testError);
|
|
335
|
+
// Track this as a pure test failure (not hook-related)
|
|
336
|
+
this._pureTestFailReasons.push(testError);
|
|
337
|
+
}
|
|
338
|
+
if (BrowserstackCLI.getInstance().isRunning()) {
|
|
339
|
+
await BrowserstackCLI.getInstance().getTestFramework().trackEvent(TestFrameworkState.LOG_REPORT, HookState.POST, { test, result: results });
|
|
340
|
+
await BrowserstackCLI.getInstance().getTestFramework().trackEvent(TestFrameworkState.TEST, HookState.POST, { test, result: results, suiteTite: this._suiteTitle });
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
await this._accessibilityHandler?.afterTest(this._suiteTitle, test);
|
|
344
|
+
await this._insightsHandler?.afterTest(test, results);
|
|
345
|
+
await this._percyHandler?.afterTest();
|
|
346
|
+
}
|
|
347
|
+
async after(result) {
|
|
348
|
+
PerformanceTester.start(PERFORMANCE_SDK_EVENTS.HOOK_EVENTS.AFTER);
|
|
349
|
+
PerformanceTester.start(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.QUIT);
|
|
350
|
+
try {
|
|
351
|
+
if (BrowserstackCLI.getInstance().isRunning()) {
|
|
352
|
+
await BrowserstackCLI.getInstance().getAutomationFramework().trackEvent(AutomationFrameworkState.EXECUTE, HookState.POST, {});
|
|
353
|
+
}
|
|
354
|
+
const { preferScenarioName, setSessionName, setSessionStatus } = this._options;
|
|
355
|
+
// For Cucumber: Checks scenarios that ran (i.e. not skipped) on the session
|
|
356
|
+
// Only 1 Scenario ran and option enabled => Redefine session name to Scenario's name
|
|
357
|
+
if (preferScenarioName && this._scenariosThatRan.length === 1) {
|
|
358
|
+
this._fullTitle = this._scenariosThatRan.pop();
|
|
359
|
+
}
|
|
360
|
+
await PerformanceTester.measureWrapper(PERFORMANCE_SDK_EVENTS.AUTOMATE_EVENTS.SESSION_STATUS, async () => {
|
|
361
|
+
if (setSessionStatus && !BrowserstackCLI.getInstance().isRunning()) {
|
|
362
|
+
const ignoreHooksStatus = this._options.testObservabilityOptions?.ignoreHooksStatus === true;
|
|
363
|
+
let sessionStatus;
|
|
364
|
+
let failureReason;
|
|
365
|
+
if (result === 0 && this._specsRan) {
|
|
366
|
+
// Test runner reported success and tests ran
|
|
367
|
+
if (ignoreHooksStatus) {
|
|
368
|
+
// Only consider pure test failures, ignore hook failures
|
|
369
|
+
const hasPureTestFailures = this._pureTestFailReasons.length > 0;
|
|
370
|
+
sessionStatus = hasPureTestFailures ? 'failed' : 'passed';
|
|
371
|
+
failureReason = hasPureTestFailures ? this._pureTestFailReasons.join('\n') : undefined;
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
// Default behavior: consider all failures including hooks
|
|
375
|
+
const hasReasons = this._failReasons.length > 0;
|
|
376
|
+
sessionStatus = hasReasons ? 'failed' : 'passed';
|
|
377
|
+
failureReason = hasReasons ? this._failReasons.join('\n') : undefined;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
else if (ignoreHooksStatus && this._specsRan) {
|
|
381
|
+
// Test runner reported failure but ignoreHooksStatus is enabled
|
|
382
|
+
// Check if we only have hook failures and no pure test failures
|
|
383
|
+
const hasPureTestFailures = this._pureTestFailReasons.length > 0;
|
|
384
|
+
const hasOnlyHookFailures = this._failReasons.length === 0 && this._hookFailReasons.length > 0;
|
|
385
|
+
if (hasOnlyHookFailures && !hasPureTestFailures) {
|
|
386
|
+
// Only hook failures exist - mark as passed when ignoreHooksStatus is true
|
|
387
|
+
sessionStatus = 'passed';
|
|
388
|
+
failureReason = undefined;
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
// Pure test failures exist - mark as failed
|
|
392
|
+
sessionStatus = 'failed';
|
|
393
|
+
failureReason = hasPureTestFailures ? this._pureTestFailReasons.join('\n') : undefined;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
// Default behavior: mark as failed (test runner reported failure or no tests ran)
|
|
398
|
+
sessionStatus = 'failed';
|
|
399
|
+
if (ignoreHooksStatus && this._pureTestFailReasons.length > 0) {
|
|
400
|
+
failureReason = this._pureTestFailReasons.join('\n');
|
|
401
|
+
}
|
|
402
|
+
else if (this._failReasons.length > 0) {
|
|
403
|
+
failureReason = this._failReasons.join('\n');
|
|
404
|
+
}
|
|
405
|
+
else {
|
|
406
|
+
failureReason = undefined;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
BStackLogger.debug(`Setting session status to ${sessionStatus} for sessionId ${this._browser?.sessionId}, failure reason: ${failureReason}`);
|
|
410
|
+
await this._updateJob({
|
|
411
|
+
status: sessionStatus,
|
|
412
|
+
...(setSessionName ? { name: this._fullTitle } : {}),
|
|
413
|
+
...(failureReason ? { reason: failureReason } : {})
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
})();
|
|
417
|
+
// Track Listener cleanup
|
|
418
|
+
PerformanceTester.start(EVENTS.SDK_LISTENER_WORKER_END);
|
|
419
|
+
await Listener.getInstance().onWorkerEnd();
|
|
420
|
+
PerformanceTester.end(EVENTS.SDK_LISTENER_WORKER_END, true);
|
|
421
|
+
// Track Percy teardown (only if CLI is not running)
|
|
422
|
+
if (!BrowserstackCLI.getInstance().isRunning()) {
|
|
423
|
+
PerformanceTester.start(EVENTS.SDK_PERCY_TEARDOWN);
|
|
424
|
+
try {
|
|
425
|
+
await this._percyHandler?.teardown();
|
|
426
|
+
PerformanceTester.end(EVENTS.SDK_PERCY_TEARDOWN, true);
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
PerformanceTester.end(EVENTS.SDK_PERCY_TEARDOWN, false, util.format(error));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
// Track worker data save
|
|
433
|
+
PerformanceTester.start(EVENTS.SDK_WORKER_SAVE_DATA);
|
|
434
|
+
this.saveWorkerData();
|
|
435
|
+
PerformanceTester.end(EVENTS.SDK_WORKER_SAVE_DATA, true);
|
|
436
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.QUIT);
|
|
437
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.HOOK_EVENTS.AFTER);
|
|
438
|
+
// Give the performance observer time to process the last few events
|
|
439
|
+
// before disconnecting in stopAndGenerate()
|
|
440
|
+
// await new Promise(resolve => setTimeout(resolve, 100))
|
|
441
|
+
// Track performance report generation (this is the big operation!)
|
|
442
|
+
await PerformanceTester.stopAndGenerate('performance-service.html');
|
|
443
|
+
if (process.env[PERF_MEASUREMENT_ENV]) {
|
|
444
|
+
PerformanceTester.calculateTimes([
|
|
445
|
+
'onRunnerStart', 'onSuiteStart', 'onSuiteEnd',
|
|
446
|
+
'onTestStart', 'onTestEnd', 'onTestSkip', 'before',
|
|
447
|
+
'beforeHook', 'afterHook', 'beforeTest', 'afterTest',
|
|
448
|
+
'uploadPending', 'teardown', 'browserCommand'
|
|
449
|
+
]);
|
|
450
|
+
}
|
|
451
|
+
// Override process exit when we have only hook failures and ignoreHooksStatus is true
|
|
452
|
+
const ignoreHooksStatus = this._options.testObservabilityOptions?.ignoreHooksStatus === true;
|
|
453
|
+
const hasOnlyHookFailures = this._failReasons.length === 0 && this._hookFailReasons.length > 0;
|
|
454
|
+
const shouldOverrideResult = ignoreHooksStatus && this._specsRan && hasOnlyHookFailures;
|
|
455
|
+
if (shouldOverrideResult && result !== 0) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
BStackLogger.error(`Error in after hook: ${error}`);
|
|
461
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.DRIVER_EVENT.QUIT, false, util.format(error));
|
|
462
|
+
PerformanceTester.end(PERFORMANCE_SDK_EVENTS.HOOK_EVENTS.AFTER, false, util.format(error));
|
|
463
|
+
await PerformanceTester.stopAndGenerate('performance-service.html');
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* For CucumberJS
|
|
468
|
+
*/
|
|
469
|
+
async beforeFeature(uri, feature) {
|
|
470
|
+
this._suiteTitle = feature.name;
|
|
471
|
+
await this._setSessionName(feature.name);
|
|
472
|
+
await this._setAnnotation(`Feature: ${feature.name}`);
|
|
473
|
+
await this._insightsHandler?.beforeFeature(uri, feature);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Runs before a Cucumber Scenario.
|
|
477
|
+
* @param world world object containing information on pickle and test step
|
|
478
|
+
*/
|
|
479
|
+
async beforeScenario(world) {
|
|
480
|
+
this._currentTest = world;
|
|
481
|
+
await this._accessibilityHandler?.beforeScenario(world);
|
|
482
|
+
await this._insightsHandler?.beforeScenario(world);
|
|
483
|
+
const scenarioName = world.pickle.name || 'unknown scenario';
|
|
484
|
+
await this._setAnnotation(`Scenario: ${scenarioName}`);
|
|
485
|
+
}
|
|
486
|
+
async afterScenario(world) {
|
|
487
|
+
this._specsRan = true;
|
|
488
|
+
const status = world.result?.status.toLowerCase();
|
|
489
|
+
if (status !== 'skipped') {
|
|
490
|
+
this._scenariosThatRan.push(world.pickle.name || 'unknown pickle name');
|
|
491
|
+
}
|
|
492
|
+
if (status && this._failureStatuses.includes(status)) {
|
|
493
|
+
const exception = ((world.result && world.result.message) ||
|
|
494
|
+
(status === 'pending'
|
|
495
|
+
? `Some steps/hooks are pending for scenario "${world.pickle.name}"`
|
|
496
|
+
: 'Unknown Error'));
|
|
497
|
+
// For Cucumber with ignoreHooksStatus, check if failure is due to test steps or hooks
|
|
498
|
+
const ignoreHooksStatus = this._options.testObservabilityOptions?.ignoreHooksStatus === true;
|
|
499
|
+
if (ignoreHooksStatus && this._insightsHandler) {
|
|
500
|
+
// Check if any test steps failed (excluding hook failures)
|
|
501
|
+
const hasTestStepFailures = this._insightsHandler.hasTestStepFailures(world);
|
|
502
|
+
if (hasTestStepFailures) {
|
|
503
|
+
// Test steps failed - this is a pure test failure, add to both arrays
|
|
504
|
+
this._failReasons.push(exception);
|
|
505
|
+
this._pureTestFailReasons.push(exception);
|
|
506
|
+
}
|
|
507
|
+
// If no test steps failed, this is likely a hook-only failure - don't add to main failure arrays
|
|
508
|
+
}
|
|
509
|
+
else {
|
|
510
|
+
// Default behavior: treat all scenario failures as test failures
|
|
511
|
+
this._failReasons.push(exception);
|
|
512
|
+
this._pureTestFailReasons.push(exception);
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
await this._accessibilityHandler?.afterScenario(world);
|
|
516
|
+
await this._insightsHandler?.afterScenario(world);
|
|
517
|
+
if (!BrowserstackCLI.getInstance().isRunning()) {
|
|
518
|
+
await this._percyHandler?.afterScenario();
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
async beforeStep(step, scenario) {
|
|
522
|
+
await this._insightsHandler?.beforeStep(step, scenario);
|
|
523
|
+
await this._setAnnotation(`Step: ${step.keyword}${step.text}`);
|
|
524
|
+
}
|
|
525
|
+
async afterStep(step, scenario, result) {
|
|
526
|
+
await this._insightsHandler?.afterStep(step, scenario, result);
|
|
527
|
+
}
|
|
528
|
+
async onReload(oldSessionId, newSessionId) {
|
|
529
|
+
if (!this._browser) {
|
|
530
|
+
return Promise.resolve();
|
|
531
|
+
}
|
|
532
|
+
const { setSessionName, setSessionStatus } = this._options;
|
|
533
|
+
const ignoreHooksStatus = this._options.testObservabilityOptions?.ignoreHooksStatus === true;
|
|
534
|
+
let sessionStatus;
|
|
535
|
+
let failureReason;
|
|
536
|
+
if (ignoreHooksStatus) {
|
|
537
|
+
// Only consider pure test failures, ignore hook failures
|
|
538
|
+
const hasPureTestFailures = this._pureTestFailReasons.length > 0;
|
|
539
|
+
sessionStatus = hasPureTestFailures ? 'failed' : 'passed';
|
|
540
|
+
failureReason = hasPureTestFailures ? this._pureTestFailReasons.join('\n') : undefined;
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
// Default behavior: consider all failures including hooks
|
|
544
|
+
const hasReasons = this._failReasons.length > 0;
|
|
545
|
+
sessionStatus = hasReasons ? 'failed' : 'passed';
|
|
546
|
+
failureReason = hasReasons ? this._failReasons.join('\n') : undefined;
|
|
547
|
+
}
|
|
548
|
+
if (!this._browser.isMultiremote) {
|
|
549
|
+
BStackLogger.info(`Update (reloaded) job with sessionId ${oldSessionId}, ${sessionStatus}`);
|
|
550
|
+
}
|
|
551
|
+
else {
|
|
552
|
+
const browserName = this._browser.instances.filter((browserName) => this._browser && this._browser.getInstance(browserName).sessionId === newSessionId)[0];
|
|
553
|
+
BStackLogger.info(`Update (reloaded) multiremote job for browser "${browserName}" and sessionId ${oldSessionId}, ${sessionStatus}`);
|
|
554
|
+
}
|
|
555
|
+
if (setSessionStatus) {
|
|
556
|
+
await this._update(oldSessionId, {
|
|
557
|
+
status: sessionStatus,
|
|
558
|
+
...(setSessionName ? { name: this._fullTitle } : {}),
|
|
559
|
+
...(failureReason ? { reason: failureReason } : {})
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
BStackLogger.warn(`Session Reloaded: Old Session Id: ${oldSessionId}, New Session Id: ${newSessionId}`);
|
|
563
|
+
await this._insightsHandler?.sendCBTInfo();
|
|
564
|
+
this._scenariosThatRan = [];
|
|
565
|
+
delete this._fullTitle;
|
|
566
|
+
delete this._suiteFile;
|
|
567
|
+
this._failReasons = [];
|
|
568
|
+
this._hookFailReasons = [];
|
|
569
|
+
this._pureTestFailReasons = [];
|
|
570
|
+
await this._printSessionURL();
|
|
571
|
+
}
|
|
572
|
+
_isAppAutomate() {
|
|
573
|
+
const browserDesiredCapabilities = (this._browser?.capabilities ?? {});
|
|
574
|
+
const desiredCapabilities = (this._caps ?? {});
|
|
575
|
+
return !!browserDesiredCapabilities['appium:app'] || !!desiredCapabilities['appium:app'] || !!(desiredCapabilities['appium:options']?.app);
|
|
576
|
+
}
|
|
577
|
+
_updateJob(requestBody) {
|
|
578
|
+
return this._multiRemoteAction((sessionId, browserName) => {
|
|
579
|
+
BStackLogger.info(browserName
|
|
580
|
+
? `Update multiremote job for browser "${browserName}" and sessionId ${sessionId}`
|
|
581
|
+
: `Update job with sessionId ${sessionId}`);
|
|
582
|
+
return this._update(sessionId, requestBody);
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
_multiRemoteAction(action) {
|
|
586
|
+
if (!this._browser) {
|
|
587
|
+
return Promise.resolve();
|
|
588
|
+
}
|
|
589
|
+
if (!this._browser.isMultiremote) {
|
|
590
|
+
return action(this._browser.sessionId);
|
|
591
|
+
}
|
|
592
|
+
const multiremotebrowser = this._browser;
|
|
593
|
+
return Promise.all(multiremotebrowser.instances
|
|
594
|
+
.filter((browserName) => {
|
|
595
|
+
const cap = getBrowserCapabilities(multiremotebrowser, this._caps, browserName);
|
|
596
|
+
return isBrowserstackCapability(cap);
|
|
597
|
+
})
|
|
598
|
+
.map((browserName) => (action(multiremotebrowser.getInstance(browserName).sessionId, browserName))));
|
|
599
|
+
}
|
|
600
|
+
_update(sessionId, requestBody) {
|
|
601
|
+
if (!isBrowserstackSession(this._browser)) {
|
|
602
|
+
return Promise.resolve();
|
|
603
|
+
}
|
|
604
|
+
const sessionUrl = `${this._sessionBaseUrl}/${sessionId}.json`;
|
|
605
|
+
BStackLogger.debug(`Updating Browserstack session at ${sessionUrl} with request body: `, requestBody);
|
|
606
|
+
if (this._turboScale) {
|
|
607
|
+
return got.patch(sessionUrl, {
|
|
608
|
+
json: requestBody,
|
|
609
|
+
username: this._config.user,
|
|
610
|
+
password: this._config.key
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
return got.put(sessionUrl, {
|
|
614
|
+
json: requestBody,
|
|
615
|
+
username: this._config.user,
|
|
616
|
+
password: this._config.key
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
async _printSessionURL() {
|
|
620
|
+
if (!this._browser || !isBrowserstackSession(this._browser)) {
|
|
621
|
+
return Promise.resolve();
|
|
622
|
+
}
|
|
623
|
+
await this._multiRemoteAction(async (sessionId, browserName) => {
|
|
624
|
+
const sessionUrl = `${this._sessionBaseUrl}/${sessionId}.json`;
|
|
625
|
+
BStackLogger.debug(`Requesting Browserstack session URL at ${sessionUrl}`);
|
|
626
|
+
let browserUrl;
|
|
627
|
+
const reqOpts = {
|
|
628
|
+
username: this._config.user,
|
|
629
|
+
password: this._config.key,
|
|
630
|
+
responseType: 'json'
|
|
631
|
+
};
|
|
632
|
+
if (this._turboScale) {
|
|
633
|
+
const response = await got(sessionUrl, reqOpts);
|
|
634
|
+
browserUrl = response.body.url;
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
const response = await got(sessionUrl, reqOpts);
|
|
638
|
+
browserUrl = response.body.automation_session.browser_url;
|
|
639
|
+
}
|
|
640
|
+
if (!this._browser) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
const capabilities = getBrowserCapabilities(this._browser, this._caps, browserName);
|
|
644
|
+
const browserString = getBrowserDescription(capabilities);
|
|
645
|
+
BStackLogger.info(`${browserString} session: ${browserUrl}`);
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
async _setSessionName(suiteTitle, test) {
|
|
649
|
+
if (!this._options.setSessionName || !suiteTitle) {
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
652
|
+
let name = suiteTitle;
|
|
653
|
+
if (this._options.sessionNameFormat) {
|
|
654
|
+
name = this._options.sessionNameFormat(this._config, this._caps, suiteTitle, test?.title);
|
|
655
|
+
}
|
|
656
|
+
else if (test && !test.fullName && this._config.framework !== 'jasmine') {
|
|
657
|
+
// Mocha
|
|
658
|
+
const pre = this._options.sessionNamePrependTopLevelSuiteTitle ? `${suiteTitle} - ` : '';
|
|
659
|
+
const post = !this._options.sessionNameOmitTestTitle ? ` - ${test.title}` : '';
|
|
660
|
+
name = `${pre}${test.parent}${post}`;
|
|
661
|
+
}
|
|
662
|
+
else if (this._config.framework === 'jasmine') {
|
|
663
|
+
// Jasmine
|
|
664
|
+
name = this._caps?.['bstack:options']?.sessionName;
|
|
665
|
+
}
|
|
666
|
+
if (!BrowserstackCLI.getInstance().isRunning()) {
|
|
667
|
+
this._percyHandler?._setSessionName(name);
|
|
668
|
+
}
|
|
669
|
+
if (name !== this._fullTitle) {
|
|
670
|
+
this._fullTitle = name;
|
|
671
|
+
await this._updateJob({ name });
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
_setAnnotation(data) {
|
|
675
|
+
return this._executeCommand('annotate', { data, level: 'info' });
|
|
676
|
+
}
|
|
677
|
+
async _executeCommand(action, args) {
|
|
678
|
+
if (!this._browser || !isBrowserstackSession(this._browser)) {
|
|
679
|
+
return Promise.resolve();
|
|
680
|
+
}
|
|
681
|
+
const cmd = { action, ...(args ? { arguments: args } : {}) };
|
|
682
|
+
const script = `browserstack_executor: ${JSON.stringify(cmd)}`;
|
|
683
|
+
if (this._browser.isMultiremote) {
|
|
684
|
+
const multiRemoteBrowser = this._browser;
|
|
685
|
+
return Promise.all(Object.keys(this._caps).map(async (browserName) => {
|
|
686
|
+
const browser = multiRemoteBrowser.getInstance(browserName);
|
|
687
|
+
return (await browser.execute(script));
|
|
688
|
+
}));
|
|
689
|
+
}
|
|
690
|
+
return (await this._browser.execute(script));
|
|
691
|
+
}
|
|
692
|
+
saveWorkerData() {
|
|
693
|
+
saveWorkerData({
|
|
694
|
+
usageStats: UsageStats.getInstance().getDataToSave()
|
|
695
|
+
});
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
__decorate([
|
|
699
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'beforeSession' })
|
|
700
|
+
], BrowserstackService.prototype, "beforeSession", null);
|
|
701
|
+
__decorate([
|
|
702
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'before' })
|
|
703
|
+
], BrowserstackService.prototype, "before", null);
|
|
704
|
+
__decorate([
|
|
705
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'beforeSuite' })
|
|
706
|
+
], BrowserstackService.prototype, "beforeSuite", null);
|
|
707
|
+
__decorate([
|
|
708
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'beforeHook' })
|
|
709
|
+
], BrowserstackService.prototype, "beforeHook", null);
|
|
710
|
+
__decorate([
|
|
711
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'afterHook' })
|
|
712
|
+
], BrowserstackService.prototype, "afterHook", null);
|
|
713
|
+
__decorate([
|
|
714
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'beforeTest' })
|
|
715
|
+
], BrowserstackService.prototype, "beforeTest", null);
|
|
716
|
+
__decorate([
|
|
717
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'afterTest' })
|
|
718
|
+
], BrowserstackService.prototype, "afterTest", null);
|
|
719
|
+
__decorate([
|
|
720
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'beforeFeature' })
|
|
721
|
+
], BrowserstackService.prototype, "beforeFeature", null);
|
|
722
|
+
__decorate([
|
|
723
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'beforeScenario' })
|
|
724
|
+
], BrowserstackService.prototype, "beforeScenario", null);
|
|
725
|
+
__decorate([
|
|
726
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'afterScenario' })
|
|
727
|
+
], BrowserstackService.prototype, "afterScenario", null);
|
|
728
|
+
__decorate([
|
|
729
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'beforeStep' })
|
|
730
|
+
], BrowserstackService.prototype, "beforeStep", null);
|
|
731
|
+
__decorate([
|
|
732
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'afterStep' })
|
|
733
|
+
], BrowserstackService.prototype, "afterStep", null);
|
|
734
|
+
__decorate([
|
|
735
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_HOOK, { hookType: 'onReload' })
|
|
736
|
+
], BrowserstackService.prototype, "onReload", null);
|
|
737
|
+
__decorate([
|
|
738
|
+
PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.AUTOMATE_EVENTS.PRINT_BUILDLINK)
|
|
739
|
+
], BrowserstackService.prototype, "_printSessionURL", null);
|
|
740
|
+
//# sourceMappingURL=service.js.map
|