browserstack-node-sdk 1.65.6 → 1.66.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/ats/src/cloudproviders/AWS.js +1 -1
- package/ats/src/cloudproviders/Azure.js +1 -1
- package/ats/src/cloudproviders/CloudProvider.js +1 -1
- package/ats/src/cloudproviders/GoogleCloudProvider.js +1 -1
- package/ats/src/cloudproviders/utils.js +1 -1
- package/ats/src/commands/configure.js +1 -1
- package/ats/src/commands/connect.js +1 -1
- package/ats/src/commands/connect_commands/grid.js +1 -1
- package/ats/src/commands/create.js +1 -1
- package/ats/src/commands/create_commands/grid.js +1 -1
- package/ats/src/commands/delete.js +1 -1
- package/ats/src/commands/delete_commands/grid.js +1 -1
- package/ats/src/commands/init.js +1 -1
- package/ats/src/commands/list.js +1 -1
- package/ats/src/commands/list_commands/grid_profiles.js +1 -1
- package/ats/src/commands/list_commands/grids.js +1 -1
- package/ats/src/commands/precheck.js +1 -1
- package/ats/src/commands/precheck_commands/setup_precheck.js +1 -1
- package/ats/src/commands/start.js +1 -1
- package/ats/src/commands/start_commands/grid.js +1 -1
- package/ats/src/commands/stop.js +1 -1
- package/ats/src/commands/stop_commands/grid.js +1 -1
- package/ats/src/config/constants.js +1 -1
- package/ats/src/config/logger.js +1 -1
- package/ats/src/index.js +1 -1
- package/ats/src/utils/AWSUtils.js +1 -1
- package/ats/src/utils/createAutoscalerStack.js +1 -1
- package/ats/src/utils/createClusterStack.js +1 -1
- package/ats/src/utils/createHubHaStack.js +1 -1
- package/ats/src/utils/createk8sClient.js +1 -1
- package/ats/src/utils/deleteEFSMountTargets.js +1 -1
- package/ats/src/utils/deleteStack.js +1 -1
- package/ats/src/utils/describeStack.js +1 -1
- package/ats/src/utils/gridComponents.js +1 -1
- package/ats/src/utils/hstUtils.js +1 -1
- package/ats/src/utils/instrumentationUtils.js +1 -1
- package/ats/src/utils/userInput.js +1 -1
- package/ats/src/utils/utilityMethods.js +1 -1
- package/ats/src/utils/webSocketUtils.js +1 -1
- package/generated/sdk-messages_pb.d.ts +118 -0
- package/generated/sdk-messages_pb.js +908 -0
- package/generated/sdk_grpc_pb.d.ts +10 -0
- package/generated/sdk_grpc_pb.js +74 -0
- package/package.json +1 -1
- package/src/bin/codeceptjs/BrowserStackPlugin.js +1 -1
- package/src/bin/codeceptjs/command.js +1 -1
- package/src/bin/codeceptjs/helper.js +1 -1
- package/src/bin/codeceptjs/o11yHelper.js +1 -1
- package/src/bin/cucumber-js/command.js +1 -1
- package/src/bin/cucumber-js/formatter/custom_formatter.js +1 -1
- package/src/bin/cucumber-js/formatter/custom_formatter_old_versions.js +1 -1
- package/src/bin/cucumber-js/test-observability.js +1 -1
- package/src/bin/cucumber-js/test.setup.js +1 -1
- package/src/bin/cucumber-js/test_old.setup.js +1 -1
- package/src/bin/cucumber-js/timeout.setup.js +1 -1
- package/src/bin/detox/a11ySetup.js +1 -0
- package/src/bin/detox/command.js +2 -0
- package/src/bin/detox/modifyDetoxConf.js +1 -0
- package/src/bin/detox/traEmitter.js +1 -0
- package/src/bin/integrations/command.js +1 -1
- package/src/bin/integrations/constants.js +1 -1
- package/src/bin/jasmine/bstack-reporter/customReporter.js +1 -1
- package/src/bin/jasmine/command.js +1 -1
- package/src/bin/jasmine/test.setup.js +1 -1
- package/src/bin/jasmine/testRunMap.js +1 -1
- package/src/bin/jest/command.js +1 -1
- package/src/bin/jest/customImplements/customCircus.js +1 -1
- package/src/bin/jest/customImplements/customEnvironment.js +1 -1
- package/src/bin/jest/customImplements/customJestAdapterInit.js +1 -1
- package/src/bin/jest/customImplements/customSequencer.js +1 -1
- package/src/bin/jest/customImplements/customTestRunnerTemplate.js +1 -1
- package/src/bin/jest/jest.setup.js +1 -1
- package/src/bin/jest/test-observability/data-hooks.js +1 -1
- package/src/bin/jest/test-observability/helper.js +1 -1
- package/src/bin/jest/test-observability/obsReporter.js +1 -1
- package/src/bin/jest/test-observability/test-details.js +1 -1
- package/src/bin/jest/test.setup.js +1 -1
- package/src/bin/jest/test_before.setup.js +1 -1
- package/src/bin/launcher/launcher.js +1 -1
- package/src/bin/load/index.js +1 -1
- package/src/bin/load/loadErrors.js +1 -1
- package/src/bin/load/loadUtils.js +1 -1
- package/src/bin/mocha/accessibility-automation/helper.js +1 -1
- package/src/bin/mocha/bstack-reporter/customReporter.js +1 -1
- package/src/bin/mocha/command.js +1 -1
- package/src/bin/mocha/test-observability/data-hooks.js +1 -1
- package/src/bin/mocha/test-observability/mochaUtils.js +1 -1
- package/src/bin/mocha/test.setup.js +1 -1
- package/src/bin/newman/command.js +1 -1
- package/src/bin/newman/helper.js +1 -1
- package/src/bin/newman/reporter/index.js +1 -1
- package/src/bin/newman/scriptRunner.js +1 -1
- package/src/bin/nightwatch/command.js +1 -1
- package/src/bin/node-generic/client.js +1 -1
- package/src/bin/playwright/accessibility-helper.js +1 -1
- package/src/bin/playwright/command.js +1 -1
- package/src/bin/playwright/globalSetup.js +1 -1
- package/src/bin/playwright/mock-process.js +1 -1
- package/src/bin/playwright/modifyPlaywrightConf.js +1 -1
- package/src/bin/playwright/patches.js +1 -1
- package/src/bin/playwright/reporter/pwUtils.js +1 -1
- package/src/bin/playwright/reporter/reporter.js +1 -1
- package/src/bin/playwright/reporter/test-details.js +1 -1
- package/src/bin/playwright/reporterArgs.js +1 -1
- package/src/bin/playwright/sessionDetails.js +1 -1
- package/src/bin/puppeteer/patches.js +1 -1
- package/src/bin/puppeteer/sessionDetails.js +1 -1
- package/src/bin/puppeteer/test.setup.js +1 -1
- package/src/bin/runner.js +1 -1
- package/src/bin/setup.js +1 -1
- package/src/bin/test-runner/index.js +1 -1
- package/src/bin/test-runner/runTest.js +1 -1
- package/src/bin/test-runner/testWorker.js +1 -1
- package/src/bin/testcafe/a11y-runner.js +1 -1
- package/src/bin/testcafe/command.js +1 -1
- package/src/bin/testcafe/reporter/index.js +1 -1
- package/src/bin/testcafe/reporter/tcUtils.js +1 -1
- package/src/bin/testcafe/session-registry.js +1 -1
- package/src/bin/testcafe/stdout-interceptor.js +1 -1
- package/src/bin/testcafe/testcafe-action-patcher.js +1 -1
- package/src/bin/testcafe/w3c-driver.js +1 -1
- package/src/bin/utils/apiUtilityMethods.js +1 -1
- package/src/bin/utils/clientTroubleshootingService.js +1 -1
- package/src/bin/utils/constants.js +1 -1
- package/src/bin/utils/log4jsAppender.js +1 -1
- package/src/bin/utils/logPatcher.js +1 -1
- package/src/bin/utils/logReportingAPI.js +1 -1
- package/src/bin/utils/playwright-patcher.js +1 -1
- package/src/bin/utils/puppeteer-patcher.js +1 -1
- package/src/bin/utils/sharedPlaywrightSessionDetails.js +1 -1
- package/src/bin/utils/sharedUtils.js +1 -1
- package/src/bin/v2/TestInfo.js +1 -1
- package/src/bin/v2/browserstackCLI.js +1 -1
- package/src/bin/v2/cliUtils.js +1 -1
- package/src/bin/v2/customTagManager.js +1 -1
- package/src/bin/v2/eventDispatcher.js +1 -1
- package/src/bin/v2/framework/automationFramework.js +1 -1
- package/src/bin/v2/framework/constants/automationFrameworkConstants.js +1 -1
- package/src/bin/v2/framework/constants/testFrameworkConstants.js +1 -1
- package/src/bin/v2/framework/cucumberTestFramework.js +1 -1
- package/src/bin/v2/framework/jestTestFramework.js +1 -1
- package/src/bin/v2/framework/newmanTestFramework.js +1 -1
- package/src/bin/v2/framework/nodeGenericTestFramework.js +1 -1
- package/src/bin/v2/framework/playwrightAutomationFramework.js +1 -1
- package/src/bin/v2/framework/playwrightTestFramework.js +1 -1
- package/src/bin/v2/framework/puppeteerAutomationFramework.js +1 -1
- package/src/bin/v2/framework/testFramework.js +1 -1
- package/src/bin/v2/framework/testcafeAutomationFramework.js +1 -1
- package/src/bin/v2/framework/testcafeTestFramework.js +1 -1
- package/src/bin/v2/grpcClient.js +1 -1
- package/src/bin/v2/instances/automationFrameworkInstance.js +1 -1
- package/src/bin/v2/instances/testFrameworkInstance.js +1 -1
- package/src/bin/v2/instances/trackedContext.js +1 -1
- package/src/bin/v2/instances/trackedInstance.js +1 -1
- package/src/bin/v2/modules/accessibilityModule.js +1 -1
- package/src/bin/v2/modules/baseModule.js +1 -1
- package/src/bin/v2/modules/loadTestModule.js +1 -1
- package/src/bin/v2/modules/observabilityModule.js +1 -1
- package/src/bin/v2/modules/playwrightDriverModule.js +1 -1
- package/src/bin/v2/modules/puppeteerDriverModule.js +1 -1
- package/src/bin/v2/modules/testcafeDriverModule.js +1 -1
- package/src/bin/v2/modules/testhubModule.js +1 -1
- package/src/bin/v2/sdkErrorHandler.js +1 -1
- package/src/bin/v2/state/automationFrameworkState.js +1 -1
- package/src/bin/v2/state/hookState.js +1 -1
- package/src/bin/v2/state/testFrameworkState.js +1 -1
- package/src/bin/v2/uploadAttachmentsManager.js +1 -1
- package/src/bin/v2/utils/cucumber-utils.js +1 -1
- package/src/bin/v2/utils/jest-utils.js +1 -1
- package/src/bin/vanilla-js/command.js +1 -1
- package/src/browserStackSdk.js +1 -1
- package/src/detox-driver/a11y.js +574 -0
- package/src/detox-driver/allocationDriver.js +26 -0
- package/src/detox-driver/cloudPlatformAction.js +111 -0
- package/src/detox-driver/environment.js +253 -0
- package/src/detox-driver/expect.js +44 -0
- package/src/detox-driver/index.js +12 -0
- package/src/detox-driver/login-timeout-preload.js +25 -0
- package/src/detox-driver/runtimeDriver.js +351 -0
- package/src/detox-driver/session-reporter.js +41 -0
- package/src/detox-driver/tra-capture.js +81 -0
- package/src/helpers/BrowserStackSetup.js +1 -1
- package/src/helpers/accessibility-automation/accessibilityRetryHelper.js +1 -1
- package/src/helpers/accessibility-automation/constants.js +1 -1
- package/src/helpers/accessibility-automation/helper.js +1 -1
- package/src/helpers/accessibility-automation/scripts.js +1 -1
- package/src/helpers/ats/constants.js +1 -1
- package/src/helpers/ats/helper.js +1 -1
- package/src/helpers/caCertHelper.js +1 -1
- package/src/helpers/capsMapping.js +1 -1
- package/src/helpers/central-scanner/constants.js +1 -1
- package/src/helpers/central-scanner/helpers.js +1 -1
- package/src/helpers/classLogger.js +1 -1
- package/src/helpers/driverInitErrorReporter.js +1 -1
- package/src/helpers/globals.js +1 -1
- package/src/helpers/helper.js +1 -1
- package/src/helpers/logger.js +1 -1
- package/src/helpers/metadata.js +1 -1
- package/src/helpers/patchHelper.js +1 -1
- package/src/helpers/patchHelpers.js +1 -1
- package/src/helpers/percy/Percy.js +1 -1
- package/src/helpers/percy/PercyBinary.js +1 -1
- package/src/helpers/percy/PercyCaptureMap.js +1 -1
- package/src/helpers/percy/PercyCaptureModes.js +1 -1
- package/src/helpers/percy/PercySDK.js +1 -1
- package/src/helpers/percy/index.js +1 -1
- package/src/helpers/percy/utils.js +1 -1
- package/src/helpers/performance/constants.js +1 -1
- package/src/helpers/performance/performance-tester.js +1 -1
- package/src/helpers/playwright-helper.js +1 -1
- package/src/helpers/processHelper.js +1 -1
- package/src/helpers/puppeteer-helper.js +1 -1
- package/src/helpers/request-spy.js +1 -1
- package/src/helpers/setKeepAlive.js +1 -1
- package/src/helpers/stackFrameUtils.js +1 -1
- package/src/helpers/test-observability/constants.js +1 -1
- package/src/helpers/test-observability/error-handler.js +1 -1
- package/src/helpers/test-observability/requestQueueHandler.js +1 -1
- package/src/helpers/test-observability/sessionHandler.js +1 -1
- package/src/helpers/test-observability/utils.js +1 -1
- package/src/helpers/test-orchestration/constants.js +1 -1
- package/src/helpers/test-orchestration/failfastManager.js +1 -1
- package/src/helpers/test-orchestration/fileHelper.js +1 -1
- package/src/helpers/test-orchestration/orchestrationUtils.js +1 -1
- package/src/helpers/test-orchestration/requestUtils.js +1 -1
- package/src/helpers/test-orchestration/testOrchestrationHandler.js +1 -1
- package/src/helpers/test-orchestration/testOrderingServerHandler.js +1 -1
- package/src/helpers/testhub/constants.js +1 -1
- package/src/helpers/testhub/testhubHandler.js +1 -1
- package/src/helpers/testhub/utils.js +1 -1
- package/src/helpers/utilities.js +1 -1
- package/src/helpers/w3cMapping.js +1 -1
- package/src/hubAllocationMethods.js +1 -1
- package/src/index.js +1 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// The BrowserStack cloud instrumentation bridge is driven by a `CloudPlatform`
|
|
4
|
+
// action. We ship vanilla, unpatched Detox, so this is built on detox's exported
|
|
5
|
+
// `Action` base and sent through the public `client.sendAction(action)` — no core
|
|
6
|
+
// patch. Params are FLAT ({method, ...extra}, e.g. {method:'launchApp', launchArgs:{}}),
|
|
7
|
+
// matching the bridge's expected shape.
|
|
8
|
+
//
|
|
9
|
+
// detox is the CUSTOMER's dependency, not the SDK's — resolve it from process.cwd()
|
|
10
|
+
// (the SDK package has no detox installed), the same way the driver module is loaded.
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const detoxRoot = path.dirname(require.resolve('detox/package.json', {paths: [process.cwd()]}));
|
|
15
|
+
const {Action} = require(path.join(detoxRoot, 'src/client/actions/actions'));
|
|
16
|
+
|
|
17
|
+
// Current jest spec-file path, read from the worker's jest state. Detox opens one
|
|
18
|
+
// cloud session per spec file, so this ties the session id to its spec — letting the
|
|
19
|
+
// SDK correlate each session with the spec's verdict (from the jest reporter) and mark
|
|
20
|
+
// it individually. Empty string when unavailable (falls back to the run-level verdict).
|
|
21
|
+
function currentTestPath() {
|
|
22
|
+
try {
|
|
23
|
+
// Preferred: the spec path stashed by the spec-capture setupFilesAfterEnv module
|
|
24
|
+
// (jest's expect.getState().testPath is populated there but not in this driver's
|
|
25
|
+
// load context).
|
|
26
|
+
if (global.__BSTACK_DETOX_SPEC__) {
|
|
27
|
+
return global.__BSTACK_DETOX_SPEC__;
|
|
28
|
+
}
|
|
29
|
+
const state = global.expect && global.expect.getState && global.expect.getState();
|
|
30
|
+
|
|
31
|
+
return (state && state.testPath) || '';
|
|
32
|
+
} catch (err) {
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Last id seen on the socket, kept in memory for callers that need to address the LIVE
|
|
38
|
+
// session (the a11y scan relay) rather than reconcile it after the run.
|
|
39
|
+
let latestCloudSessionId = '';
|
|
40
|
+
|
|
41
|
+
// The BrowserStack session id ("cloudSessionId") rides every CloudPlatform response.
|
|
42
|
+
// Persist `<id>\t<specPath>` to the file the SDK's detox command points us at, so the
|
|
43
|
+
// main process can mark each session (status + name) on the dashboard after the run.
|
|
44
|
+
// Best-effort: this must never break the test run.
|
|
45
|
+
function recordCloudSessionId(response) {
|
|
46
|
+
try {
|
|
47
|
+
const id = response && response.cloudSessionId;
|
|
48
|
+
if (!id) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
latestCloudSessionId = id;
|
|
52
|
+
const file = process.env.BROWSERSTACK_DETOX_SESSION_IDS_FILE;
|
|
53
|
+
if (file) {
|
|
54
|
+
fs.appendFileSync(file, id + '\t' + currentTestPath() + '\n');
|
|
55
|
+
}
|
|
56
|
+
} catch (err) { /* best-effort session capture */ }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const getCloudSessionId = () => latestCloudSessionId;
|
|
60
|
+
|
|
61
|
+
class CloudPlatformAction extends Action {
|
|
62
|
+
constructor(params) {
|
|
63
|
+
super('CloudPlatform', params);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
get isAtomic() {
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get timeout() {
|
|
71
|
+
return 90000;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async handle(response) {
|
|
75
|
+
this.expectResponseOfType(response, 'CloudPlatform');
|
|
76
|
+
|
|
77
|
+
return response;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Carrier for the App Accessibility scan relay. Two deliberate departures from
|
|
82
|
+
// CloudPlatformAction: a caller-supplied timeout (a scan must be written off long before
|
|
83
|
+
// CloudPlatformAction's 90s), and NO response-type assertion — the whole point is to see
|
|
84
|
+
// whatever the Hub answers with, including a shape nobody has documented, instead of
|
|
85
|
+
// throwing an internal error that hides it.
|
|
86
|
+
class ScanRelayAction extends CloudPlatformAction {
|
|
87
|
+
constructor(params, timeoutMs, type) {
|
|
88
|
+
super(params);
|
|
89
|
+
if (type) {
|
|
90
|
+
this.type = type;
|
|
91
|
+
}
|
|
92
|
+
this._timeoutMs = timeoutMs;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
get timeout() {
|
|
96
|
+
return this._timeoutMs;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async handle(response) {
|
|
100
|
+
return response;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function executeCloudPlatform(client, method, extra = {}) {
|
|
105
|
+
const response = await client.sendAction(new CloudPlatformAction(Object.assign({method}, extra)));
|
|
106
|
+
recordCloudSessionId(response);
|
|
107
|
+
|
|
108
|
+
return response;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
module.exports = {CloudPlatformAction, ScanRelayAction, executeCloudPlatform, getCloudSessionId};
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Custom jest testEnvironment for BrowserStack Detox runs. Extends detox's
|
|
4
|
+
// DetoxCircusEnvironment (so detox's own init / listeners / artifacts still run) and
|
|
5
|
+
// stashes the current spec path on the Node-realm `global` that the CloudPlatform
|
|
6
|
+
// driver reads. The driver loads in this same Node realm during detox.init — where
|
|
7
|
+
// jest's sandbox `expect.getState().testPath` is NOT visible — so the environment
|
|
8
|
+
// (also Node realm, and holding the spec path via `context.testPath`) is the correct
|
|
9
|
+
// place to bridge it. Best-effort: never break detox's environment setup.
|
|
10
|
+
//
|
|
11
|
+
// TRA tap: this environment also observes jest-circus lifecycle events (tests +
|
|
12
|
+
// hooks) and appends JSONL records to the per-slot capture file. Detox's own
|
|
13
|
+
// constructor binds `this.handleTestEvent` (index.js:56) — with a subclass the bind
|
|
14
|
+
// resolves to the override below, which delegates to detox's via super. Capture is
|
|
15
|
+
// active only when the SDK main process exported the events file (TRA enabled).
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
const traCapture = require('./tra-capture');
|
|
19
|
+
|
|
20
|
+
const detoxRoot = path.dirname(require.resolve('detox/package.json', {paths: [process.cwd()]}));
|
|
21
|
+
const detoxEnvModule = require(path.join(detoxRoot, 'runners/jest/testEnvironment'));
|
|
22
|
+
const DetoxCircusEnvironment = detoxEnvModule && detoxEnvModule.default ? detoxEnvModule.default : detoxEnvModule;
|
|
23
|
+
|
|
24
|
+
class BrowserStackDetoxEnvironment extends DetoxCircusEnvironment {
|
|
25
|
+
constructor(config, context) {
|
|
26
|
+
super(config, context);
|
|
27
|
+
try {
|
|
28
|
+
const testPath = (context && context.testPath) || this.testPath;
|
|
29
|
+
if (testPath) {
|
|
30
|
+
global.__BSTACK_DETOX_SPEC__ = testPath;
|
|
31
|
+
}
|
|
32
|
+
this._bstackSpecPath = testPath || null;
|
|
33
|
+
this._bstackTests = new Map();
|
|
34
|
+
this._bstackHooks = new Map();
|
|
35
|
+
// jest re-runs a test in place under `retryTimes`, so the same identity
|
|
36
|
+
// fires test_start/test_done more than once. Remember the first attempt's
|
|
37
|
+
// uuid per identity so later attempts report as retries of it instead of
|
|
38
|
+
// landing as independent dashboard rows.
|
|
39
|
+
this._bstackFirstUuidByKey = new Map();
|
|
40
|
+
} catch (err) { /* best-effort spec capture */ }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async handleTestEvent(event, state) {
|
|
44
|
+
try {
|
|
45
|
+
if (traCapture.enabled() && event && event.name) {
|
|
46
|
+
this._bstackCaptureEvent(event, state, Date.now());
|
|
47
|
+
// A failure screenshot needs detox's async device API, so it cannot ride
|
|
48
|
+
// the synchronous capture switch. Awaited here, before detox's own
|
|
49
|
+
// handler tears the test down and the device becomes unusable.
|
|
50
|
+
if (event.name === 'test_done' && event.test && event.test.errors && event.test.errors.length) {
|
|
51
|
+
await this._bstackCaptureScreenshot(event.test);
|
|
52
|
+
}
|
|
53
|
+
// Cleared only after the failure capture above, so the driver's tee can still
|
|
54
|
+
// attribute that screenshot to this test.
|
|
55
|
+
if (event.name === 'test_done') {
|
|
56
|
+
global.__BSTACK_DETOX_TEST_UUID__ = null;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch (err) { /* best-effort: capture must never affect the run */ }
|
|
60
|
+
if (super.handleTestEvent) {
|
|
61
|
+
return super.handleTestEvent(event, state);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Console output from the spec is only observable inside this worker, so tee it
|
|
66
|
+
// into the capture stream tagged with whichever test is running. Wrapping is
|
|
67
|
+
// guarded and always delegates to the original writer — a customer's console
|
|
68
|
+
// output must never be swallowed or duplicated.
|
|
69
|
+
_bstackPatchConsole() {
|
|
70
|
+
try {
|
|
71
|
+
if (this._bstackConsolePatched || !this.global || !this.global.console) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
this._bstackConsolePatched = true;
|
|
75
|
+
const levels = {log: 'INFO', info: 'INFO', warn: 'WARN', error: 'ERROR', debug: 'DEBUG'};
|
|
76
|
+
Object.keys(levels).forEach((method) => {
|
|
77
|
+
const original = this.global.console[method];
|
|
78
|
+
if (typeof original !== 'function') {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
this.global.console[method] = (...args) => {
|
|
82
|
+
try {
|
|
83
|
+
const meta = this._bstackCurrentTestMeta;
|
|
84
|
+
if (meta && meta.uuid) {
|
|
85
|
+
traCapture.append({
|
|
86
|
+
kind: 'log',
|
|
87
|
+
testUuid: meta.uuid,
|
|
88
|
+
specPath: this._bstackSpecPath,
|
|
89
|
+
level: levels[method],
|
|
90
|
+
message: args.map((a) => (typeof a === 'string' ? a : traCapture.safeStringify(a))).join(' '),
|
|
91
|
+
timestamp: new Date().toISOString()
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
} catch (err) { /* never let capture break console */ }
|
|
95
|
+
|
|
96
|
+
return original.apply(this.global.console, args);
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
} catch (err) { /* best-effort */ }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Screenshot on failure. `device.takeScreenshot` returns the path of the png the
|
|
103
|
+
// cloud driver wrote from the device's in-app Espresso capture; the image is read
|
|
104
|
+
// here and carried INLINE as base64, the shape the binary ultimately stores and
|
|
105
|
+
// the one puppeteer/playwright already ship in production. Carrying bytes rather
|
|
106
|
+
// than a path removes the binary's cross-process `readFileSync(filePath)` from the
|
|
107
|
+
// path — that read fails with only a `warn` and an emptied field, i.e. it loses
|
|
108
|
+
// screenshots silently. `filePath` still rides along as a fallback.
|
|
109
|
+
//
|
|
110
|
+
// Bounded by a timeout because a wedged device would otherwise hold the whole run
|
|
111
|
+
// hostage, and fully swallowed — a missing screenshot must never turn a suite red.
|
|
112
|
+
async _bstackCaptureScreenshot(test) {
|
|
113
|
+
try {
|
|
114
|
+
const device = this.global && this.global.device;
|
|
115
|
+
if (!device || typeof device.takeScreenshot !== 'function') {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const meta = this._bstackTests.get(test) || {};
|
|
119
|
+
if (!meta.uuid) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const name = `failure-${meta.uuid}`;
|
|
123
|
+
const filePath = await Promise.race([
|
|
124
|
+
device.takeScreenshot(name),
|
|
125
|
+
new Promise((resolve) => setTimeout(() => resolve(null), 15000))
|
|
126
|
+
]);
|
|
127
|
+
if (!filePath || typeof filePath !== 'string') {
|
|
128
|
+
this._bstackDebug(`screenshot: no path returned for ${name}`);
|
|
129
|
+
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
// No record is written here on purpose: the driver tees every takeScreenshot into
|
|
133
|
+
// the TRA stream (bytes already in hand), so appending again would double-report.
|
|
134
|
+
this._bstackDebug(`screenshot: captured ${name} -> ${filePath}`);
|
|
135
|
+
} catch (err) {
|
|
136
|
+
this._bstackDebug(`screenshot: capture failed: ${err && err.message}`);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// The capture path was silently unobservable — a screenshot that never reached the
|
|
141
|
+
// dashboard left no trace of where it was dropped. Opt-in so it costs nothing.
|
|
142
|
+
_bstackDebug(message) {
|
|
143
|
+
try {
|
|
144
|
+
if (process.env.BROWSERSTACK_DETOX_SCREENSHOT_DEBUG === 'true') {
|
|
145
|
+
console.log(`[bstack-detox] ${message}`);
|
|
146
|
+
}
|
|
147
|
+
} catch (err) { /* best-effort */ }
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
_bstackCaptureEvent(event, state, now) {
|
|
151
|
+
switch (event.name) {
|
|
152
|
+
case 'setup': {
|
|
153
|
+
this._bstackPatchConsole();
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
case 'test_start': {
|
|
157
|
+
if (event.test) {
|
|
158
|
+
const key = [this._bstackSpecPath, traCapture.scopesOf(event.test).join('>'), String(event.test.name || '')].join('|');
|
|
159
|
+
const firstUuid = this._bstackFirstUuidByKey.get(key);
|
|
160
|
+
const uuid = traCapture.mintUuid();
|
|
161
|
+
if (!firstUuid) {
|
|
162
|
+
this._bstackFirstUuidByKey.set(key, uuid);
|
|
163
|
+
}
|
|
164
|
+
this._bstackTests.set(event.test, {uuid, startedAt: now, retryOf: firstUuid || null});
|
|
165
|
+
this._bstackCurrentTestMeta = this._bstackTests.get(event.test);
|
|
166
|
+
// Published for the cloud driver (same Node realm, the way __BSTACK_DETOX_SPEC__
|
|
167
|
+
// already is): it tees EVERY device.takeScreenshot() — including the customer's
|
|
168
|
+
// own calls from a test body — and needs the owning test to attribute it to.
|
|
169
|
+
global.__BSTACK_DETOX_TEST_UUID__ = uuid;
|
|
170
|
+
}
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
case 'test_done': {
|
|
174
|
+
const test = event.test;
|
|
175
|
+
if (!test) {
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
const meta = this._bstackTests.get(test) || {};
|
|
179
|
+
const failed = Boolean(test.errors && test.errors.length);
|
|
180
|
+
traCapture.append({
|
|
181
|
+
kind: 'test',
|
|
182
|
+
uuid: meta.uuid || traCapture.mintUuid(),
|
|
183
|
+
specPath: this._bstackSpecPath,
|
|
184
|
+
name: String(test.name || ''),
|
|
185
|
+
scopes: traCapture.scopesOf(test),
|
|
186
|
+
status: failed ? 'failed' : 'passed',
|
|
187
|
+
startedAt: meta.startedAt || null,
|
|
188
|
+
endedAt: now,
|
|
189
|
+
durationMs: meta.startedAt ? now - meta.startedAt : null,
|
|
190
|
+
failure: traCapture.serializeErrors(test.errors),
|
|
191
|
+
...(meta.retryOf ? {retryOf: meta.retryOf} : {})
|
|
192
|
+
});
|
|
193
|
+
this._bstackCurrentTestMeta = null;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
case 'test_skip':
|
|
197
|
+
case 'test_todo': {
|
|
198
|
+
const test = event.test;
|
|
199
|
+
if (!test) {
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
traCapture.append({
|
|
203
|
+
kind: 'test',
|
|
204
|
+
uuid: traCapture.mintUuid(),
|
|
205
|
+
specPath: this._bstackSpecPath,
|
|
206
|
+
name: String(test.name || ''),
|
|
207
|
+
scopes: traCapture.scopesOf(test),
|
|
208
|
+
status: 'skipped',
|
|
209
|
+
startedAt: now,
|
|
210
|
+
endedAt: now,
|
|
211
|
+
durationMs: 0
|
|
212
|
+
});
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
case 'hook_start': {
|
|
216
|
+
if (event.hook) {
|
|
217
|
+
this._bstackHooks.set(event.hook, {uuid: traCapture.mintUuid(), startedAt: now});
|
|
218
|
+
}
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
case 'hook_success':
|
|
222
|
+
case 'hook_failure': {
|
|
223
|
+
const hook = event.hook;
|
|
224
|
+
if (!hook) {
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
const meta = this._bstackHooks.get(hook) || {};
|
|
228
|
+
const currentTest = state && state.currentlyRunningTest;
|
|
229
|
+
const testMeta = currentTest ? this._bstackTests.get(currentTest) : null;
|
|
230
|
+
traCapture.append({
|
|
231
|
+
kind: 'hook',
|
|
232
|
+
uuid: meta.uuid || traCapture.mintUuid(),
|
|
233
|
+
specPath: this._bstackSpecPath,
|
|
234
|
+
hookType: String(hook.type || ''),
|
|
235
|
+
testUuid: (testMeta && testMeta.uuid) || null,
|
|
236
|
+
testName: currentTest ? String(currentTest.name || '') : null,
|
|
237
|
+
scopes: traCapture.scopesOf(hook),
|
|
238
|
+
status: event.name === 'hook_failure' ? 'failed' : 'passed',
|
|
239
|
+
startedAt: meta.startedAt || null,
|
|
240
|
+
endedAt: now,
|
|
241
|
+
durationMs: meta.startedAt ? now - meta.startedAt : null,
|
|
242
|
+
failure: event.error ? traCapture.serializeErrors([event.error]) : undefined
|
|
243
|
+
});
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
default:
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
module.exports = BrowserStackDetoxEnvironment;
|
|
253
|
+
module.exports.default = BrowserStackDetoxEnvironment;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Reuse vanilla detox's Android matchers (element/expect/waitFor/web). The built-in
|
|
4
|
+
// Android matchers factory remaps its deps before construction
|
|
5
|
+
// (`{invocationManager, device, emitter}`), but the external matchers factory
|
|
6
|
+
// (matchers/factories/index.js: External.createMatchers) passes
|
|
7
|
+
// `{invocationManager, runtimeDevice, eventEmitter}` straight through unmapped — so
|
|
8
|
+
// this adapter does the same remap AndroidExpect expects.
|
|
9
|
+
//
|
|
10
|
+
// detox is resolved from process.cwd() (the customer's install), not the SDK package.
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
const detoxRoot = path.dirname(require.resolve('detox/package.json', {paths: [process.cwd()]}));
|
|
14
|
+
const AndroidExpect = require(path.join(detoxRoot, 'src/android/AndroidExpect'));
|
|
15
|
+
|
|
16
|
+
const a11y = require('./a11y');
|
|
17
|
+
|
|
18
|
+
class CloudExpect extends AndroidExpect {
|
|
19
|
+
constructor({invocationManager, runtimeDevice, eventEmitter}) {
|
|
20
|
+
super({invocationManager, device: runtimeDevice, emitter: eventEmitter});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// App Accessibility wraps mapped element and web-view actions so a scan can fire on
|
|
24
|
+
// their completion. This is the SDK-owned seam for it: AndroidExpect binds
|
|
25
|
+
// element/web/waitFor/expect in its constructor, so a subclass override is what those
|
|
26
|
+
// bindings resolve to — no Detox internal is patched, and `waitFor`/`expect` are left
|
|
27
|
+
// alone because no matcher or expectation is a scannable command.
|
|
28
|
+
//
|
|
29
|
+
// The wrapper is installed ONLY when a11y is armed. With it disabled these overrides
|
|
30
|
+
// return exactly what AndroidExpect returns, so the non-a11y path is unchanged.
|
|
31
|
+
element(matcher) {
|
|
32
|
+
const element = super.element(matcher);
|
|
33
|
+
|
|
34
|
+
return a11y.enabled() ? a11y.wrap(element, 'NativeElement') : element;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
web(matcher) {
|
|
38
|
+
const webView = super.web(matcher);
|
|
39
|
+
|
|
40
|
+
return a11y.enabled() ? a11y.wrap(webView, 'WebViewElement') : webView;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = CloudExpect;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Vanilla Detox external device module contract (detox/src/environmentFactory.js:
|
|
4
|
+
// _getExternalModuleFactories). device.type must be a require-able path to this
|
|
5
|
+
// directory; these three exports are validated by
|
|
6
|
+
// devices/allocation/factories/external.js, devices/runtime/factories/external.js and
|
|
7
|
+
// matchers/factories/index.js's External classes.
|
|
8
|
+
module.exports = {
|
|
9
|
+
DeviceAllocationDriverClass: require('./allocationDriver'),
|
|
10
|
+
RuntimeDriverClass: require('./runtimeDriver'),
|
|
11
|
+
ExpectClass: require('./expect')
|
|
12
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Login-timeout fix, loaded via NODE_OPTIONS=--require BEFORE detox loads (the SDK's
|
|
4
|
+
// `detox` command injects it, mirroring the nightwatch spawn preload). Vanilla detox
|
|
5
|
+
// times out the Login WS action at 1000ms; BrowserStack cloud device allocation takes
|
|
6
|
+
// longer, so the timeout must be raised before detox is required — an in-driver patch
|
|
7
|
+
// loads too late (Login fires before the device driver module is required).
|
|
8
|
+
//
|
|
9
|
+
// Guarded: a failure here must never break the customer's run (the default 1000ms
|
|
10
|
+
// window would apply and Detox would surface its own timeout, not an SDK crash).
|
|
11
|
+
try {
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const detoxRoot = path.dirname(require.resolve('detox/package.json', {paths: [process.cwd()]}));
|
|
14
|
+
const actions = require(path.join(detoxRoot, 'src/client/actions/actions'));
|
|
15
|
+
const ms = Number(process.env.BSTACK_DETOX_LOGIN_TIMEOUT_MS || 240000);
|
|
16
|
+
Object.defineProperty(actions.Login.prototype, 'timeout', {
|
|
17
|
+
configurable: true,
|
|
18
|
+
get() {
|
|
19
|
+
return ms;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
} catch (err) {
|
|
23
|
+
// eslint-disable-next-line no-console
|
|
24
|
+
console.error('[browserstack-detox] login-timeout preload failed: ' + (err && err.message));
|
|
25
|
+
}
|