@wdio/browserstack-service 9.0.0-alpha.9 → 9.0.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/README.md +9 -6
- package/build/Percy/Percy-Handler.d.ts +1 -1
- package/build/Percy/Percy-Handler.d.ts.map +1 -1
- package/build/Percy/PercyHelper.d.ts +1 -1
- package/build/Percy/PercyHelper.d.ts.map +1 -1
- package/build/accessibility-handler.d.ts +2 -2
- package/build/accessibility-handler.d.ts.map +1 -1
- package/build/ai-handler.d.ts +23 -0
- package/build/ai-handler.d.ts.map +1 -0
- package/build/constants.d.ts +10 -1
- package/build/constants.d.ts.map +1 -1
- package/build/crash-reporter.d.ts +2 -2
- package/build/crash-reporter.d.ts.map +1 -1
- package/build/fileStream.d.ts +0 -2
- package/build/fileStream.d.ts.map +1 -1
- package/build/index.js +5822 -12
- package/build/insights-handler.d.ts +5 -1
- package/build/insights-handler.d.ts.map +1 -1
- package/build/instrumentation/funnelInstrumentation.d.ts +2 -0
- package/build/instrumentation/funnelInstrumentation.d.ts.map +1 -1
- package/build/launcher.d.ts +6 -6
- package/build/launcher.d.ts.map +1 -1
- package/build/performance-tester.d.ts +0 -1
- package/build/performance-tester.d.ts.map +1 -1
- package/build/reporter.d.ts.map +1 -1
- package/build/service.d.ts +4 -4
- package/build/service.d.ts.map +1 -1
- package/build/testOps/listener.d.ts +1 -0
- package/build/testOps/listener.d.ts.map +1 -1
- package/build/types.d.ts +9 -3
- package/build/types.d.ts.map +1 -1
- package/build/util.d.ts +43 -30
- package/build/util.d.ts.map +1 -1
- package/package.json +13 -10
- package/build/Percy/Percy-Handler.js +0 -156
- package/build/Percy/Percy.js +0 -123
- package/build/Percy/PercyBinary.js +0 -142
- package/build/Percy/PercyCaptureMap.js +0 -35
- package/build/Percy/PercyHelper.js +0 -67
- package/build/Percy/PercyLogger.js +0 -67
- package/build/Percy/PercySDK.js +0 -39
- package/build/accessibility-handler.js +0 -287
- package/build/bstackLogger.js +0 -70
- package/build/cleanup.js +0 -89
- package/build/config.js +0 -39
- package/build/constants.js +0 -73
- package/build/crash-reporter.js +0 -138
- package/build/cucumber-types.js +0 -1
- package/build/data-store.js +0 -41
- package/build/exitHandler.js +0 -29
- package/build/fetchWrapper.js +0 -14
- package/build/fileStream.js +0 -12
- package/build/insights-handler.js +0 -719
- package/build/instrumentation/funnelInstrumentation.js +0 -120
- package/build/launcher.js +0 -741
- package/build/log4jsAppender.js +0 -19
- package/build/logPatcher.js +0 -38
- package/build/logReportingAPI.js +0 -56
- package/build/performance-tester.js +0 -94
- package/build/reporter.js +0 -251
- package/build/request-handler.js +0 -74
- package/build/scripts/accessibility-scripts.js +0 -61
- package/build/service.js +0 -428
- package/build/testOps/featureStats.js +0 -116
- package/build/testOps/featureUsage.js +0 -47
- package/build/testOps/listener.js +0 -222
- package/build/testOps/requestUtils.js +0 -39
- package/build/testOps/testOpsConfig.js +0 -19
- package/build/testOps/usageStats.js +0 -114
- package/build/types.js +0 -1
- package/build/util.js +0 -1132
- /package/{LICENSE-MIT → LICENSE} +0 -0
|
@@ -1,287 +0,0 @@
|
|
|
1
|
-
import util from 'node:util';
|
|
2
|
-
import { getA11yResultsSummary, getA11yResults, performA11yScan, getUniqueIdentifier, getUniqueIdentifierForCucumber, isAccessibilityAutomationSession, isBrowserstackSession, o11yClassErrorHandler, shouldScanTestForAccessibility, validateCapsWithA11y, isTrue } from './util.js';
|
|
3
|
-
import accessibilityScripts from './scripts/accessibility-scripts.js';
|
|
4
|
-
import { BStackLogger } from './bstackLogger.js';
|
|
5
|
-
class _AccessibilityHandler {
|
|
6
|
-
_browser;
|
|
7
|
-
_capabilities;
|
|
8
|
-
_framework;
|
|
9
|
-
_accessibilityAutomation;
|
|
10
|
-
_accessibilityOpts;
|
|
11
|
-
_platformA11yMeta;
|
|
12
|
-
_caps;
|
|
13
|
-
_suiteFile;
|
|
14
|
-
_accessibility;
|
|
15
|
-
_accessibilityOptions;
|
|
16
|
-
_testMetadata = {};
|
|
17
|
-
static _a11yScanSessionMap = {};
|
|
18
|
-
_sessionId = null;
|
|
19
|
-
constructor(_browser, _capabilities, isAppAutomate, _framework, _accessibilityAutomation, _accessibilityOpts) {
|
|
20
|
-
this._browser = _browser;
|
|
21
|
-
this._capabilities = _capabilities;
|
|
22
|
-
this._framework = _framework;
|
|
23
|
-
this._accessibilityAutomation = _accessibilityAutomation;
|
|
24
|
-
this._accessibilityOpts = _accessibilityOpts;
|
|
25
|
-
const caps = this._browser.capabilities;
|
|
26
|
-
this._platformA11yMeta = {
|
|
27
|
-
browser_name: caps.browserName,
|
|
28
|
-
browser_version: caps?.browserVersion || caps?.version || 'latest',
|
|
29
|
-
os_name: this._getCapabilityValue(_capabilities, 'os', 'os'),
|
|
30
|
-
os_version: this._getCapabilityValue(_capabilities, 'osVersion', 'os_version')
|
|
31
|
-
};
|
|
32
|
-
this._caps = _capabilities;
|
|
33
|
-
this._accessibility = isTrue(_accessibilityAutomation);
|
|
34
|
-
this._accessibilityOptions = _accessibilityOpts;
|
|
35
|
-
}
|
|
36
|
-
setSuiteFile(filename) {
|
|
37
|
-
this._suiteFile = filename;
|
|
38
|
-
}
|
|
39
|
-
_getCapabilityValue(caps, capType, legacyCapType) {
|
|
40
|
-
if (caps) {
|
|
41
|
-
if (capType === 'accessibility') {
|
|
42
|
-
if (caps['bstack:options'] && (isTrue(caps['bstack:options']?.accessibility))) {
|
|
43
|
-
return caps['bstack:options']?.accessibility;
|
|
44
|
-
}
|
|
45
|
-
else if (isTrue(caps['browserstack.accessibility'])) {
|
|
46
|
-
return caps['browserstack.accessibility'];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
else if (capType === 'deviceName') {
|
|
50
|
-
if (caps['bstack:options'] && caps['bstack:options']?.deviceName) {
|
|
51
|
-
return caps['bstack:options']?.deviceName;
|
|
52
|
-
}
|
|
53
|
-
else if (caps['bstack:options'] && caps['bstack:options']?.device) {
|
|
54
|
-
return caps['bstack:options']?.device;
|
|
55
|
-
}
|
|
56
|
-
else if (caps['appium:deviceName']) {
|
|
57
|
-
return caps['appium:deviceName'];
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
else if (capType === 'goog:chromeOptions' && caps['goog:chromeOptions']) {
|
|
61
|
-
return caps['goog:chromeOptions'];
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
const bstackOptions = caps['bstack:options'];
|
|
65
|
-
if (bstackOptions && bstackOptions?.[capType]) {
|
|
66
|
-
return bstackOptions?.[capType];
|
|
67
|
-
}
|
|
68
|
-
else if (caps[legacyCapType]) {
|
|
69
|
-
return caps[legacyCapType];
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
async before(sessionId) {
|
|
75
|
-
this._sessionId = sessionId;
|
|
76
|
-
this._accessibility = isTrue(this._getCapabilityValue(this._caps, 'accessibility', 'browserstack.accessibility'));
|
|
77
|
-
if (isBrowserstackSession(this._browser) && isAccessibilityAutomationSession(this._accessibility)) {
|
|
78
|
-
const deviceName = this._getCapabilityValue(this._caps, 'deviceName', 'device');
|
|
79
|
-
const chromeOptions = this._getCapabilityValue(this._caps, 'goog:chromeOptions', '');
|
|
80
|
-
this._accessibility = validateCapsWithA11y(deviceName, this._platformA11yMeta, chromeOptions);
|
|
81
|
-
}
|
|
82
|
-
this._browser.getAccessibilityResultsSummary = async () => {
|
|
83
|
-
return await getA11yResultsSummary(this._browser, isBrowserstackSession(this._browser), this._accessibility);
|
|
84
|
-
};
|
|
85
|
-
this._browser.getAccessibilityResults = async () => {
|
|
86
|
-
return await getA11yResults(this._browser, isBrowserstackSession(this._browser), this._accessibility);
|
|
87
|
-
};
|
|
88
|
-
this._browser.performScan = async () => {
|
|
89
|
-
return await performA11yScan(this._browser, isBrowserstackSession(this._browser), this._accessibility);
|
|
90
|
-
};
|
|
91
|
-
if (!this._accessibility) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
if (!('overwriteCommand' in this._browser && Array.isArray(accessibilityScripts.commandsToWrap))) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
accessibilityScripts.commandsToWrap
|
|
98
|
-
.filter((command) => command.name && command.class)
|
|
99
|
-
.forEach((command) => {
|
|
100
|
-
const browser = this._browser;
|
|
101
|
-
browser.overwriteCommand(command.name, this.commandWrapper.bind(this, command), command.class === 'Element');
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
async beforeTest(suiteTitle, test) {
|
|
105
|
-
try {
|
|
106
|
-
if (this._framework !== 'mocha' ||
|
|
107
|
-
!this.shouldRunTestHooks(this._browser, this._accessibility)) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const shouldScanTest = shouldScanTestForAccessibility(suiteTitle, test.title, this._accessibilityOptions);
|
|
111
|
-
const testIdentifier = this.getIdentifier(test);
|
|
112
|
-
const isPageOpened = await this.checkIfPageOpened(this._browser, testIdentifier, shouldScanTest);
|
|
113
|
-
if (this._sessionId) {
|
|
114
|
-
/* For case with multiple tests under one browser, before hook of 2nd test should change this map value */
|
|
115
|
-
AccessibilityHandler._a11yScanSessionMap[this._sessionId] = shouldScanTest;
|
|
116
|
-
}
|
|
117
|
-
if (!isPageOpened) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
this._testMetadata[testIdentifier].accessibilityScanStarted = shouldScanTest;
|
|
121
|
-
if (shouldScanTest) {
|
|
122
|
-
BStackLogger.info('Automate test case execution has started.');
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
BStackLogger.error(`Exception in starting accessibility automation scan for this test case ${error}`);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
async afterTest(suiteTitle, test) {
|
|
130
|
-
BStackLogger.debug('Accessibility after test hook. Before sending test stop event');
|
|
131
|
-
if (this._framework !== 'mocha' ||
|
|
132
|
-
!this.shouldRunTestHooks(this._browser, this._accessibility)) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
try {
|
|
136
|
-
const testIdentifier = this.getIdentifier(test);
|
|
137
|
-
const accessibilityScanStarted = this._testMetadata[testIdentifier]?.accessibilityScanStarted;
|
|
138
|
-
const shouldScanTestForAccessibility = this._testMetadata[testIdentifier]?.scanTestForAccessibility;
|
|
139
|
-
if (!accessibilityScanStarted) {
|
|
140
|
-
return;
|
|
141
|
-
}
|
|
142
|
-
if (shouldScanTestForAccessibility) {
|
|
143
|
-
BStackLogger.info('Automate test case execution has ended. Processing for accessibility testing is underway. ');
|
|
144
|
-
}
|
|
145
|
-
const dataForExtension = {
|
|
146
|
-
saveResults: shouldScanTestForAccessibility,
|
|
147
|
-
testDetails: {
|
|
148
|
-
'name': test.title,
|
|
149
|
-
'testRunId': process.env.BS_A11Y_TEST_RUN_ID,
|
|
150
|
-
'filePath': this._suiteFile,
|
|
151
|
-
'scopeList': [suiteTitle, test.title]
|
|
152
|
-
},
|
|
153
|
-
platform: this._platformA11yMeta
|
|
154
|
-
};
|
|
155
|
-
await this.sendTestStopEvent(this._browser, dataForExtension);
|
|
156
|
-
if (shouldScanTestForAccessibility) {
|
|
157
|
-
BStackLogger.info('Accessibility testing for this test case has ended.');
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
catch (error) {
|
|
161
|
-
BStackLogger.error(`Accessibility results could not be processed for the test case ${test.title}. Error : ${error}`);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Cucumber Only
|
|
166
|
-
*/
|
|
167
|
-
async beforeScenario(world) {
|
|
168
|
-
const pickleData = world.pickle;
|
|
169
|
-
const gherkinDocument = world.gherkinDocument;
|
|
170
|
-
const featureData = gherkinDocument.feature;
|
|
171
|
-
const uniqueId = getUniqueIdentifierForCucumber(world);
|
|
172
|
-
if (!this.shouldRunTestHooks(this._browser, this._accessibility)) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
try {
|
|
176
|
-
const shouldScanScenario = shouldScanTestForAccessibility(featureData?.name, pickleData.name, this._accessibilityOptions, world, true);
|
|
177
|
-
const isPageOpened = await this.checkIfPageOpened(this._browser, uniqueId, shouldScanScenario);
|
|
178
|
-
if (this._sessionId) {
|
|
179
|
-
/* For case with multiple tests under one browser, before hook of 2nd test should change this map value */
|
|
180
|
-
AccessibilityHandler._a11yScanSessionMap[this._sessionId] = shouldScanScenario;
|
|
181
|
-
}
|
|
182
|
-
if (!isPageOpened) {
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
|
-
this._testMetadata[uniqueId].accessibilityScanStarted = shouldScanScenario;
|
|
186
|
-
if (shouldScanScenario) {
|
|
187
|
-
BStackLogger.info('Automate test case execution has started.');
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
catch (error) {
|
|
191
|
-
BStackLogger.error(`Exception in starting accessibility automation scan for this test case ${error}`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
async afterScenario(world) {
|
|
195
|
-
BStackLogger.debug('Accessibility after scenario hook. Before sending test stop event');
|
|
196
|
-
if (!this.shouldRunTestHooks(this._browser, this._accessibility)) {
|
|
197
|
-
return;
|
|
198
|
-
}
|
|
199
|
-
const pickleData = world.pickle;
|
|
200
|
-
try {
|
|
201
|
-
const gherkinDocument = world.gherkinDocument;
|
|
202
|
-
const featureData = gherkinDocument.feature;
|
|
203
|
-
const uniqueId = getUniqueIdentifierForCucumber(world);
|
|
204
|
-
const accessibilityScanStarted = this._testMetadata[uniqueId]?.accessibilityScanStarted;
|
|
205
|
-
const shouldScanTestForAccessibility = this._testMetadata[uniqueId]?.scanTestForAccessibility;
|
|
206
|
-
if (!accessibilityScanStarted) {
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
if (shouldScanTestForAccessibility) {
|
|
210
|
-
BStackLogger.info('Automate test case execution has ended. Processing for accessibility testing is underway. ');
|
|
211
|
-
}
|
|
212
|
-
const dataForExtension = {
|
|
213
|
-
saveResults: shouldScanTestForAccessibility,
|
|
214
|
-
testDetails: {
|
|
215
|
-
'name': pickleData.name,
|
|
216
|
-
'testRunId': process.env.BS_A11Y_TEST_RUN_ID,
|
|
217
|
-
'filePath': gherkinDocument.uri,
|
|
218
|
-
'scopeList': [featureData?.name, pickleData.name]
|
|
219
|
-
},
|
|
220
|
-
platform: this._platformA11yMeta
|
|
221
|
-
};
|
|
222
|
-
await this.sendTestStopEvent(this._browser, dataForExtension);
|
|
223
|
-
if (shouldScanTestForAccessibility) {
|
|
224
|
-
BStackLogger.info('Accessibility testing for this test case has ended.');
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
catch (error) {
|
|
228
|
-
BStackLogger.error(`Accessibility results could not be processed for the test case ${pickleData.name}. Error : ${error}`);
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
/*
|
|
232
|
-
* private methods
|
|
233
|
-
*/
|
|
234
|
-
async commandWrapper(command, origFunction, ...args) {
|
|
235
|
-
if (this._sessionId && AccessibilityHandler._a11yScanSessionMap[this._sessionId] &&
|
|
236
|
-
(!command.name.includes('execute') ||
|
|
237
|
-
!AccessibilityHandler.shouldPatchExecuteScript(args.length ? args[0] : null))) {
|
|
238
|
-
BStackLogger.debug(`Performing scan for ${command.class} ${command.name}`);
|
|
239
|
-
await performA11yScan(this._browser, true, true, command.name);
|
|
240
|
-
}
|
|
241
|
-
return origFunction(...args);
|
|
242
|
-
}
|
|
243
|
-
async sendTestStopEvent(browser, dataForExtension) {
|
|
244
|
-
BStackLogger.debug('Performing scan before saving results');
|
|
245
|
-
await performA11yScan(browser, true, true);
|
|
246
|
-
const results = await browser.executeAsync(accessibilityScripts.saveTestResults, dataForExtension);
|
|
247
|
-
BStackLogger.debug(util.format(results));
|
|
248
|
-
}
|
|
249
|
-
getIdentifier(test) {
|
|
250
|
-
if ('pickle' in test) {
|
|
251
|
-
return getUniqueIdentifierForCucumber(test);
|
|
252
|
-
}
|
|
253
|
-
return getUniqueIdentifier(test, this._framework);
|
|
254
|
-
}
|
|
255
|
-
shouldRunTestHooks(browser, isAccessibility) {
|
|
256
|
-
if (!browser) {
|
|
257
|
-
return false;
|
|
258
|
-
}
|
|
259
|
-
return isBrowserstackSession(browser) && isAccessibilityAutomationSession(isAccessibility);
|
|
260
|
-
}
|
|
261
|
-
async checkIfPageOpened(browser, testIdentifier, shouldScanTest) {
|
|
262
|
-
let pageOpen = false;
|
|
263
|
-
this._testMetadata[testIdentifier] = {
|
|
264
|
-
scanTestForAccessibility: shouldScanTest,
|
|
265
|
-
accessibilityScanStarted: true
|
|
266
|
-
};
|
|
267
|
-
try {
|
|
268
|
-
const currentURL = await browser.getUrl();
|
|
269
|
-
const url = new URL(currentURL);
|
|
270
|
-
pageOpen = url?.protocol === 'http:' || url?.protocol === 'https:';
|
|
271
|
-
}
|
|
272
|
-
catch (e) {
|
|
273
|
-
pageOpen = false;
|
|
274
|
-
}
|
|
275
|
-
return pageOpen;
|
|
276
|
-
}
|
|
277
|
-
static shouldPatchExecuteScript(script) {
|
|
278
|
-
if (!script || typeof script !== 'string') {
|
|
279
|
-
return true;
|
|
280
|
-
}
|
|
281
|
-
return (script.toLowerCase().indexOf('browserstack_executor') !== -1 ||
|
|
282
|
-
script.toLowerCase().indexOf('browserstack_accessibility_automation_script') !== -1);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
// https://github.com/microsoft/TypeScript/issues/6543
|
|
286
|
-
const AccessibilityHandler = o11yClassErrorHandler(_AccessibilityHandler);
|
|
287
|
-
export default AccessibilityHandler;
|
package/build/bstackLogger.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import logger from '@wdio/logger';
|
|
5
|
-
import { LOGS_FILE } from './constants.js';
|
|
6
|
-
import { COLORS } from './util.js';
|
|
7
|
-
const log = logger('@wdio/browserstack-service');
|
|
8
|
-
export class BStackLogger {
|
|
9
|
-
static logFilePath = path.join(process.cwd(), LOGS_FILE);
|
|
10
|
-
static logFolderPath = path.join(process.cwd(), 'logs');
|
|
11
|
-
static logFileStream;
|
|
12
|
-
static logToFile(logMessage, logLevel) {
|
|
13
|
-
try {
|
|
14
|
-
if (!this.logFileStream) {
|
|
15
|
-
this.ensureLogsFolder();
|
|
16
|
-
this.logFileStream = fs.createWriteStream(this.logFilePath, { flags: 'a' });
|
|
17
|
-
}
|
|
18
|
-
if (this.logFileStream && this.logFileStream.writable) {
|
|
19
|
-
this.logFileStream.write(this.formatLog(logMessage, logLevel));
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
catch (error) {
|
|
23
|
-
log.debug(`Failed to log to file. Error ${error}`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
static formatLog(logMessage, level) {
|
|
27
|
-
return `${chalk.gray(new Date().toISOString())} ${chalk[COLORS[level]](level.toUpperCase())} ${chalk.whiteBright('@wdio/browserstack-service')} ${logMessage}\n`;
|
|
28
|
-
}
|
|
29
|
-
static info(message) {
|
|
30
|
-
this.logToFile(message, 'info');
|
|
31
|
-
log.info(message);
|
|
32
|
-
}
|
|
33
|
-
static error(message) {
|
|
34
|
-
this.logToFile(message, 'error');
|
|
35
|
-
log.error(message);
|
|
36
|
-
}
|
|
37
|
-
static debug(message, param) {
|
|
38
|
-
this.logToFile(message, 'debug');
|
|
39
|
-
if (param) {
|
|
40
|
-
log.debug(message, param);
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
log.debug(message);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
static warn(message) {
|
|
47
|
-
this.logToFile(message, 'warn');
|
|
48
|
-
log.warn(message);
|
|
49
|
-
}
|
|
50
|
-
static trace(message) {
|
|
51
|
-
this.logToFile(message, 'trace');
|
|
52
|
-
log.trace(message);
|
|
53
|
-
}
|
|
54
|
-
static clearLogger() {
|
|
55
|
-
if (this.logFileStream) {
|
|
56
|
-
this.logFileStream.end();
|
|
57
|
-
}
|
|
58
|
-
this.logFileStream = null;
|
|
59
|
-
}
|
|
60
|
-
static clearLogFile() {
|
|
61
|
-
if (fs.existsSync(this.logFilePath)) {
|
|
62
|
-
fs.truncateSync(this.logFilePath);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
static ensureLogsFolder() {
|
|
66
|
-
if (!fs.existsSync(this.logFolderPath)) {
|
|
67
|
-
fs.mkdirSync(this.logFolderPath);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
package/build/cleanup.js
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { getErrorString, stopBuildUpstream } from './util.js';
|
|
2
|
-
import { BStackLogger } from './bstackLogger.js';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import { fireFunnelRequest } from './instrumentation/funnelInstrumentation.js';
|
|
5
|
-
import { TESTOPS_BUILD_ID_ENV, TESTOPS_JWT_ENV } from './constants.js';
|
|
6
|
-
export default class BStackCleanup {
|
|
7
|
-
static async startCleanup() {
|
|
8
|
-
try {
|
|
9
|
-
// Get funnel data object from saved file
|
|
10
|
-
const funnelDataCleanup = process.argv.includes('--funnelData');
|
|
11
|
-
let funnelData = null;
|
|
12
|
-
if (funnelDataCleanup) {
|
|
13
|
-
const index = process.argv.indexOf('--funnelData');
|
|
14
|
-
const filePath = process.argv[index + 1];
|
|
15
|
-
funnelData = this.getFunnelDataFromFile(filePath);
|
|
16
|
-
}
|
|
17
|
-
if (process.argv.includes('--observability')) {
|
|
18
|
-
await this.executeObservabilityCleanup(funnelData);
|
|
19
|
-
}
|
|
20
|
-
if (funnelDataCleanup && funnelData) {
|
|
21
|
-
await this.sendFunnelData(funnelData);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
catch (err) {
|
|
25
|
-
const error = err;
|
|
26
|
-
BStackLogger.error(error);
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
static async executeObservabilityCleanup(funnelData) {
|
|
30
|
-
if (!process.env[TESTOPS_JWT_ENV]) {
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
BStackLogger.debug('Executing observability cleanup');
|
|
34
|
-
try {
|
|
35
|
-
const result = await stopBuildUpstream();
|
|
36
|
-
if (process.env[TESTOPS_BUILD_ID_ENV]) {
|
|
37
|
-
BStackLogger.info(`\nVisit https://observability.browserstack.com/builds/${process.env[TESTOPS_BUILD_ID_ENV]} to view build report, insights, and many more debugging information all at one place!\n`);
|
|
38
|
-
}
|
|
39
|
-
const status = (result && result.status) || 'failed';
|
|
40
|
-
const message = (result && result.message);
|
|
41
|
-
this.updateO11yStopData(funnelData, status, status === 'failed' ? message : undefined);
|
|
42
|
-
}
|
|
43
|
-
catch (e) {
|
|
44
|
-
BStackLogger.error('Error in stopping Observability build: ' + e);
|
|
45
|
-
this.updateO11yStopData(funnelData, 'failed', e);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
static updateO11yStopData(funnelData, status, error = undefined) {
|
|
49
|
-
const toData = funnelData?.event_properties?.productUsage?.testObservability;
|
|
50
|
-
// Return if no O11y data in funnel data
|
|
51
|
-
if (!toData) {
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
let existingStopData = toData.events.buildEvents.finished;
|
|
55
|
-
existingStopData = existingStopData || {};
|
|
56
|
-
existingStopData = {
|
|
57
|
-
...existingStopData,
|
|
58
|
-
status,
|
|
59
|
-
error: getErrorString(error),
|
|
60
|
-
stoppedFrom: 'exitHook'
|
|
61
|
-
};
|
|
62
|
-
toData.events.buildEvents.finished = existingStopData;
|
|
63
|
-
}
|
|
64
|
-
static async sendFunnelData(funnelData) {
|
|
65
|
-
try {
|
|
66
|
-
await fireFunnelRequest(funnelData);
|
|
67
|
-
BStackLogger.debug('Funnel data sent successfully from cleanup');
|
|
68
|
-
}
|
|
69
|
-
catch (e) {
|
|
70
|
-
BStackLogger.error('Error in sending funnel data: ' + e);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
static getFunnelDataFromFile(filePath) {
|
|
74
|
-
if (!filePath) {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
78
|
-
const data = JSON.parse(content);
|
|
79
|
-
this.removeFunnelDataFile(filePath);
|
|
80
|
-
return data;
|
|
81
|
-
}
|
|
82
|
-
static removeFunnelDataFile(filePath) {
|
|
83
|
-
if (!filePath) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
fs.rmSync(filePath, { force: true });
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
await BStackCleanup.startCleanup();
|
package/build/config.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import TestOpsConfig from './testOps/testOpsConfig.js';
|
|
2
|
-
import { isUndefined } from './util.js';
|
|
3
|
-
class BrowserStackConfig {
|
|
4
|
-
static getInstance(options, config) {
|
|
5
|
-
if (!this._instance && options && config) {
|
|
6
|
-
this._instance = new BrowserStackConfig(options, config);
|
|
7
|
-
}
|
|
8
|
-
return this._instance;
|
|
9
|
-
}
|
|
10
|
-
userName;
|
|
11
|
-
accessKey;
|
|
12
|
-
framework;
|
|
13
|
-
buildName;
|
|
14
|
-
buildIdentifier;
|
|
15
|
-
testObservability;
|
|
16
|
-
percy;
|
|
17
|
-
accessibility;
|
|
18
|
-
app;
|
|
19
|
-
static _instance;
|
|
20
|
-
appAutomate;
|
|
21
|
-
automate;
|
|
22
|
-
funnelDataSent = false;
|
|
23
|
-
constructor(options, config) {
|
|
24
|
-
this.framework = config.framework;
|
|
25
|
-
this.userName = config.user;
|
|
26
|
-
this.accessKey = config.key;
|
|
27
|
-
this.testObservability = new TestOpsConfig(options.testObservability !== false, !isUndefined(options.testObservability));
|
|
28
|
-
this.percy = options.percy || false;
|
|
29
|
-
this.accessibility = options.accessibility || false;
|
|
30
|
-
this.app = options.app;
|
|
31
|
-
this.appAutomate = !isUndefined(options.app);
|
|
32
|
-
this.automate = !this.appAutomate;
|
|
33
|
-
this.buildIdentifier = options.buildIdentifier;
|
|
34
|
-
}
|
|
35
|
-
sentFunnelData() {
|
|
36
|
-
this.funnelDataSent = true;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
export default BrowserStackConfig;
|
package/build/constants.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { createRequire } from 'node:module';
|
|
2
|
-
const require = createRequire(import.meta.url);
|
|
3
|
-
const { version: bstackServiceVersion } = require('../package.json');
|
|
4
|
-
export const BROWSER_DESCRIPTION = [
|
|
5
|
-
'device',
|
|
6
|
-
'os',
|
|
7
|
-
'osVersion',
|
|
8
|
-
'os_version',
|
|
9
|
-
'browserName',
|
|
10
|
-
'browser',
|
|
11
|
-
'browserVersion',
|
|
12
|
-
'browser_version'
|
|
13
|
-
];
|
|
14
|
-
export const VALID_APP_EXTENSION = [
|
|
15
|
-
'.apk',
|
|
16
|
-
'.aab',
|
|
17
|
-
'.ipa'
|
|
18
|
-
];
|
|
19
|
-
export const DEFAULT_OPTIONS = {
|
|
20
|
-
setSessionName: true,
|
|
21
|
-
setSessionStatus: true,
|
|
22
|
-
testObservability: true,
|
|
23
|
-
accessibility: false
|
|
24
|
-
};
|
|
25
|
-
export const consoleHolder = Object.assign({}, console);
|
|
26
|
-
export const DATA_ENDPOINT = 'https://collector-observability.browserstack.com';
|
|
27
|
-
export const DATA_EVENT_ENDPOINT = 'api/v1/event';
|
|
28
|
-
export const DATA_BATCH_ENDPOINT = 'api/v1/batch';
|
|
29
|
-
export const DATA_SCREENSHOT_ENDPOINT = 'api/v1/screenshots';
|
|
30
|
-
export const DATA_BATCH_SIZE = 1000;
|
|
31
|
-
export const DATA_BATCH_INTERVAL = 2000;
|
|
32
|
-
export const BATCH_EVENT_TYPES = ['LogCreated', 'TestRunStarted', 'TestRunFinished', 'HookRunFinished', 'HookRunStarted', 'ScreenshotCreated'];
|
|
33
|
-
export const DEFAULT_WAIT_TIMEOUT_FOR_PENDING_UPLOADS = 5000; // 5s
|
|
34
|
-
export const DEFAULT_WAIT_INTERVAL_FOR_PENDING_UPLOADS = 100; // 100ms
|
|
35
|
-
export const BSTACK_SERVICE_VERSION = bstackServiceVersion;
|
|
36
|
-
export const ACCESSIBILITY_API_URL = 'https://accessibility.browserstack.com/api';
|
|
37
|
-
export const NOT_ALLOWED_KEYS_IN_CAPS = ['includeTagsInTestingScope', 'excludeTagsInTestingScope'];
|
|
38
|
-
export const LOGS_FILE = 'logs/bstack-wdio-service.log';
|
|
39
|
-
export const UPLOAD_LOGS_ADDRESS = 'https://upload-observability.browserstack.com';
|
|
40
|
-
export const UPLOAD_LOGS_ENDPOINT = 'client-logs/upload';
|
|
41
|
-
export const PERCY_LOGS_FILE = 'logs/percy.log';
|
|
42
|
-
export const PERCY_DOM_CHANGING_COMMANDS_ENDPOINTS = [
|
|
43
|
-
'/session/:sessionId/url',
|
|
44
|
-
'/session/:sessionId/forward',
|
|
45
|
-
'/session/:sessionId/back',
|
|
46
|
-
'/session/:sessionId/refresh',
|
|
47
|
-
'/session/:sessionId/screenshot',
|
|
48
|
-
'/session/:sessionId/actions',
|
|
49
|
-
'/session/:sessionId/appium/device/shake'
|
|
50
|
-
];
|
|
51
|
-
export const CAPTURE_MODES = ['click', 'auto', 'screenshot', 'manual', 'testcase'];
|
|
52
|
-
export const LOG_KIND_USAGE_MAP = {
|
|
53
|
-
'TEST_LOG': 'log',
|
|
54
|
-
'TEST_SCREENSHOT': 'screenshot',
|
|
55
|
-
'TEST_STEP': 'step',
|
|
56
|
-
'HTTP': 'http'
|
|
57
|
-
};
|
|
58
|
-
export const FUNNEL_INSTRUMENTATION_URL = 'https://api.browserstack.com/sdk/v1/event';
|
|
59
|
-
// Env variables - Define all the env variable constants over here
|
|
60
|
-
// To store the JWT token returned the session launch
|
|
61
|
-
export const TESTOPS_JWT_ENV = 'BS_TESTOPS_JWT';
|
|
62
|
-
// To store the setting of whether to send screenshots or not
|
|
63
|
-
export const TESTOPS_SCREENSHOT_ENV = 'BS_TESTOPS_ALLOW_SCREENSHOTS';
|
|
64
|
-
// To store build hashed id
|
|
65
|
-
export const TESTOPS_BUILD_ID_ENV = 'BS_TESTOPS_BUILD_HASHED_ID';
|
|
66
|
-
// Whether to collect performance instrumentation or not
|
|
67
|
-
export const PERF_MEASUREMENT_ENV = 'BROWSERSTACK_O11Y_PERF_MEASUREMENT';
|
|
68
|
-
// Whether the current run is rerun or not
|
|
69
|
-
export const RERUN_TESTS_ENV = 'BROWSERSTACK_RERUN_TESTS';
|
|
70
|
-
// The tests that needs to be rerun
|
|
71
|
-
export const RERUN_ENV = 'BROWSERSTACK_RERUN';
|
|
72
|
-
// To store whether the build launch has completed or not
|
|
73
|
-
export const TESTOPS_BUILD_COMPLETED_ENV = 'BS_TESTOPS_BUILD_COMPLETED';
|