@wdio/browserstack-service 8.45.0 → 8.47.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.
Files changed (118) hide show
  1. package/README.md +44 -11
  2. package/build/Percy/Percy-Handler.d.ts.map +1 -1
  3. package/build/Percy/Percy-Handler.js +4 -1
  4. package/build/Percy/Percy.d.ts.map +1 -1
  5. package/build/Percy/Percy.js +2 -1
  6. package/build/accessibility-handler.d.ts +9 -3
  7. package/build/accessibility-handler.d.ts.map +1 -1
  8. package/build/accessibility-handler.js +52 -11
  9. package/build/cleanup.js +3 -3
  10. package/build/cli/apiUtils.d.ts +14 -0
  11. package/build/cli/apiUtils.d.ts.map +1 -0
  12. package/build/cli/apiUtils.js +24 -0
  13. package/build/cli/cliLogger.d.ts +16 -0
  14. package/build/cli/cliLogger.d.ts.map +1 -0
  15. package/build/cli/cliLogger.js +70 -0
  16. package/build/cli/cliUtils.d.ts +46 -0
  17. package/build/cli/cliUtils.d.ts.map +1 -0
  18. package/build/cli/cliUtils.js +430 -0
  19. package/build/cli/eventDispatcher.d.ts +28 -0
  20. package/build/cli/eventDispatcher.d.ts.map +1 -0
  21. package/build/cli/eventDispatcher.js +48 -0
  22. package/build/cli/frameworks/automationFramework.d.ts +89 -0
  23. package/build/cli/frameworks/automationFramework.d.ts.map +1 -0
  24. package/build/cli/frameworks/automationFramework.js +131 -0
  25. package/build/cli/frameworks/constants/automationFrameworkConstants.d.ts +18 -0
  26. package/build/cli/frameworks/constants/automationFrameworkConstants.d.ts.map +1 -0
  27. package/build/cli/frameworks/constants/automationFrameworkConstants.js +17 -0
  28. package/build/cli/frameworks/constants/testFrameworkConstants.d.ts +43 -0
  29. package/build/cli/frameworks/constants/testFrameworkConstants.d.ts.map +1 -0
  30. package/build/cli/frameworks/constants/testFrameworkConstants.js +42 -0
  31. package/build/cli/frameworks/testFramework.d.ts +89 -0
  32. package/build/cli/frameworks/testFramework.d.ts.map +1 -0
  33. package/build/cli/frameworks/testFramework.js +125 -0
  34. package/build/cli/frameworks/wdioAutomationFramework.d.ts +28 -0
  35. package/build/cli/frameworks/wdioAutomationFramework.d.ts.map +1 -0
  36. package/build/cli/frameworks/wdioAutomationFramework.js +66 -0
  37. package/build/cli/frameworks/wdioMochaTestFramework.d.ts +82 -0
  38. package/build/cli/frameworks/wdioMochaTestFramework.d.ts.map +1 -0
  39. package/build/cli/frameworks/wdioMochaTestFramework.js +319 -0
  40. package/build/cli/grpcClient.d.ts +70 -0
  41. package/build/cli/grpcClient.d.ts.map +1 -0
  42. package/build/cli/grpcClient.js +419 -0
  43. package/build/cli/index.d.ts +148 -0
  44. package/build/cli/index.d.ts.map +1 -0
  45. package/build/cli/index.js +415 -0
  46. package/build/cli/instances/automationFrameworkInstance.d.ts +33 -0
  47. package/build/cli/instances/automationFrameworkInstance.d.ts.map +1 -0
  48. package/build/cli/instances/automationFrameworkInstance.js +44 -0
  49. package/build/cli/instances/testFrameworkInstance.d.ts +62 -0
  50. package/build/cli/instances/testFrameworkInstance.d.ts.map +1 -0
  51. package/build/cli/instances/testFrameworkInstance.js +96 -0
  52. package/build/cli/instances/trackedContext.d.ts +32 -0
  53. package/build/cli/instances/trackedContext.d.ts.map +1 -0
  54. package/build/cli/instances/trackedContext.js +47 -0
  55. package/build/cli/instances/trackedInstance.d.ts +40 -0
  56. package/build/cli/instances/trackedInstance.d.ts.map +1 -0
  57. package/build/cli/instances/trackedInstance.js +63 -0
  58. package/build/cli/modules/accessibilityModule.d.ts +33 -0
  59. package/build/cli/modules/accessibilityModule.d.ts.map +1 -0
  60. package/build/cli/modules/accessibilityModule.js +347 -0
  61. package/build/cli/modules/automateModule.d.ts +26 -0
  62. package/build/cli/modules/automateModule.d.ts.map +1 -0
  63. package/build/cli/modules/automateModule.js +222 -0
  64. package/build/cli/modules/baseModule.d.ts +33 -0
  65. package/build/cli/modules/baseModule.d.ts.map +1 -0
  66. package/build/cli/modules/baseModule.js +51 -0
  67. package/build/cli/modules/observabilityModule.d.ts +22 -0
  68. package/build/cli/modules/observabilityModule.d.ts.map +1 -0
  69. package/build/cli/modules/observabilityModule.js +45 -0
  70. package/build/cli/modules/percyModule.d.ts +19 -0
  71. package/build/cli/modules/percyModule.d.ts.map +1 -0
  72. package/build/cli/modules/percyModule.js +77 -0
  73. package/build/cli/modules/testHubModule.d.ts +30 -0
  74. package/build/cli/modules/testHubModule.d.ts.map +1 -0
  75. package/build/cli/modules/testHubModule.js +232 -0
  76. package/build/cli/modules/webdriverIOModule.d.ts +29 -0
  77. package/build/cli/modules/webdriverIOModule.d.ts.map +1 -0
  78. package/build/cli/modules/webdriverIOModule.js +128 -0
  79. package/build/cli/states/automationFrameworkState.d.ts +65 -0
  80. package/build/cli/states/automationFrameworkState.d.ts.map +1 -0
  81. package/build/cli/states/automationFrameworkState.js +61 -0
  82. package/build/cli/states/hookState.d.ts +45 -0
  83. package/build/cli/states/hookState.d.ts.map +1 -0
  84. package/build/cli/states/hookState.js +43 -0
  85. package/build/cli/states/testFrameworkState.d.ts +125 -0
  86. package/build/cli/states/testFrameworkState.d.ts.map +1 -0
  87. package/build/cli/states/testFrameworkState.js +115 -0
  88. package/build/constants.d.ts +13 -0
  89. package/build/constants.d.ts.map +1 -1
  90. package/build/constants.js +21 -1
  91. package/build/crash-reporter.d.ts.map +1 -1
  92. package/build/crash-reporter.js +4 -3
  93. package/build/exitHandler.d.ts.map +1 -1
  94. package/build/exitHandler.js +47 -5
  95. package/build/insights-handler.d.ts +8 -0
  96. package/build/insights-handler.d.ts.map +1 -1
  97. package/build/insights-handler.js +57 -1
  98. package/build/instrumentation/funnelInstrumentation.d.ts.map +1 -1
  99. package/build/instrumentation/funnelInstrumentation.js +5 -4
  100. package/build/instrumentation/performance/constants.d.ts +8 -0
  101. package/build/instrumentation/performance/constants.d.ts.map +1 -1
  102. package/build/instrumentation/performance/constants.js +9 -1
  103. package/build/instrumentation/performance/performance-tester.d.ts.map +1 -1
  104. package/build/instrumentation/performance/performance-tester.js +5 -3
  105. package/build/launcher.d.ts.map +1 -1
  106. package/build/launcher.js +94 -51
  107. package/build/request-handler.js +1 -1
  108. package/build/service.d.ts +3 -1
  109. package/build/service.d.ts.map +1 -1
  110. package/build/service.js +244 -69
  111. package/build/testOps/requestUtils.js +3 -2
  112. package/build/types.d.ts +36 -3
  113. package/build/types.d.ts.map +1 -1
  114. package/build/util.d.ts +13 -1
  115. package/build/util.d.ts.map +1 -1
  116. package/build/util.js +173 -27
  117. package/package.json +7 -4
  118. package/tsconfig.prod.tsbuildinfo +1 -1
@@ -0,0 +1,430 @@
1
+ import fs from 'node:fs';
2
+ import fsp from 'node:fs/promises';
3
+ import { platform, arch, homedir } from 'node:os';
4
+ import path from 'node:path';
5
+ import util, { promisify } from 'node:util';
6
+ import { exec } from 'node:child_process';
7
+ import yauzl from 'yauzl';
8
+ import got from 'got';
9
+ import { threadId } from 'node:worker_threads';
10
+ import { createRequire } from 'node:module';
11
+ const require = createRequire(import.meta.url);
12
+ const { version: bstackServiceVersion } = require('../../package.json');
13
+ import { isNullOrEmpty, nestedKeyValue, createDir, isWritable, setReadWriteAccess, isTrue, nodeRequest, getBrowserStackUser, getBrowserStackKey, isFalse, isTurboScale, shouldAddServiceVersion, } from '../util.js';
14
+ import PerformanceTester from '../instrumentation/performance/performance-tester.js';
15
+ import { EVENTS as PerformanceEvents } from '../instrumentation/performance/constants.js';
16
+ import { BStackLogger as logger } from './cliLogger.js';
17
+ import { UPDATED_CLI_ENDPOINT } from '../constants.js';
18
+ import { TestFrameworkConstants } from './frameworks/constants/testFrameworkConstants.js';
19
+ import APIUtils from './apiUtils.js';
20
+ export class CLIUtils {
21
+ static automationFrameworkDetail = {};
22
+ static testFrameworkDetail = {};
23
+ static CLISupportedFrameworks = ['mocha'];
24
+ static isDevelopmentEnv() {
25
+ return process.env.BROWSERSTACK_CLI_ENV === 'development';
26
+ }
27
+ static getCLIParamsForDevEnv() {
28
+ return {
29
+ id: process.env.BROWSERSTACK_CLI_ENV || '',
30
+ listen: `unix:/tmp/sdk-platform-${process.env.BROWSERSTACK_CLI_ENV}.sock`
31
+ };
32
+ }
33
+ /**
34
+ * Build config object for binary session request
35
+ * @returns {string}
36
+ * @throws {Error}
37
+ */
38
+ static getBinConfig(config, capabilities, options, buildTag) {
39
+ const modifiedOpts = { ...options };
40
+ if (modifiedOpts.opts) {
41
+ modifiedOpts.browserStackLocalOptions = modifiedOpts.opts;
42
+ delete modifiedOpts.opts;
43
+ }
44
+ modifiedOpts.testContextOptions = {
45
+ skipSessionName: isFalse(modifiedOpts.setSessionName),
46
+ skipSessionStatus: isFalse(modifiedOpts.setSessionStatus),
47
+ sessionNameOmitTestTitle: modifiedOpts.sessionNameOmitTestTitle || false,
48
+ sessionNamePrependTopLevelSuiteTitle: modifiedOpts.sessionNamePrependTopLevelSuiteTitle || false,
49
+ sessionNameFormat: modifiedOpts.sessionNameFormat || ''
50
+ };
51
+ const commonBstackOptions = (() => {
52
+ const caps = config.capabilities;
53
+ if (caps &&
54
+ !Array.isArray(caps) &&
55
+ typeof caps === 'object' &&
56
+ 'bstack:options' in caps) {
57
+ // Cast after guard to satisfy TypeScript
58
+ return caps['bstack:options'] || {};
59
+ }
60
+ return {};
61
+ })();
62
+ const isNonBstackA11y = isTurboScale(options) || !shouldAddServiceVersion(config, options.testObservability);
63
+ const observabilityOptions = options.testObservabilityOptions || {};
64
+ const binconfig = {
65
+ userName: observabilityOptions.user || config.user,
66
+ accessKey: observabilityOptions.key || config.key,
67
+ platforms: [],
68
+ isNonBstackA11yWDIO: isNonBstackA11y,
69
+ ...modifiedOpts,
70
+ ...commonBstackOptions,
71
+ };
72
+ binconfig.buildName = observabilityOptions.buildName || binconfig.buildName;
73
+ binconfig.projectName = observabilityOptions.projectName || binconfig.projectName;
74
+ binconfig.buildTag = this.getObservabilityBuildTags(observabilityOptions, buildTag) || [];
75
+ let caps = capabilities;
76
+ if (capabilities && !Array.isArray(capabilities)) {
77
+ caps = [capabilities];
78
+ }
79
+ if (Array.isArray(caps)) {
80
+ for (const cap of caps) {
81
+ const platform = {};
82
+ const capability = cap;
83
+ Object.keys(capability)
84
+ .filter((key) => (key !== 'bstack:options'))
85
+ .forEach((key) => {
86
+ if (binconfig[key] === undefined) {
87
+ platform[key] = capability[key];
88
+ }
89
+ });
90
+ if (capability['bstack:options']) {
91
+ Object.keys(capability['bstack:options'])
92
+ .forEach((key) => {
93
+ if (binconfig[key] === undefined) {
94
+ platform[key] = capability['bstack:options'][key];
95
+ }
96
+ });
97
+ }
98
+ binconfig.platforms.push(platform);
99
+ }
100
+ }
101
+ return JSON.stringify(binconfig);
102
+ }
103
+ static getSdkVersion() {
104
+ return bstackServiceVersion;
105
+ }
106
+ static getSdkLanguage() {
107
+ return 'ECMAScript';
108
+ }
109
+ static async setupCliPath(config) {
110
+ logger.debug('Configuring Cli path.');
111
+ const developmentBinaryPath = process.env.SDK_CLI_BIN_PATH || null;
112
+ if (!isNullOrEmpty(developmentBinaryPath)) {
113
+ logger.debug(`Development Cli Path: ${developmentBinaryPath}`);
114
+ return developmentBinaryPath;
115
+ }
116
+ try {
117
+ const cliDir = this.getCliDir();
118
+ if (isNullOrEmpty(cliDir)) {
119
+ throw new Error('No writable directory available for the CLI');
120
+ }
121
+ const existingCliPath = this.getExistingCliPath(cliDir);
122
+ const finalBinaryPath = await this.checkAndUpdateCli(existingCliPath, cliDir, config);
123
+ logger.debug(`Resolved binary path: ${finalBinaryPath}`);
124
+ return finalBinaryPath;
125
+ }
126
+ catch (err) {
127
+ logger.debug(`Error in setting up cli path directory, Exception: ${util.format(err)}`);
128
+ }
129
+ return null;
130
+ }
131
+ static async checkAndUpdateCli(existingCliPath, cliDir, config) {
132
+ PerformanceTester.start(PerformanceEvents.SDK_CLI_CHECK_UPDATE);
133
+ logger.info(`Current CLI Path Found: ${existingCliPath}`);
134
+ const queryParams = {
135
+ sdk_version: CLIUtils.getSdkVersion(),
136
+ os: platform(),
137
+ os_arch: arch(),
138
+ cli_version: '0',
139
+ sdk_language: this.getSdkLanguage(),
140
+ };
141
+ if (!isNullOrEmpty(existingCliPath)) {
142
+ queryParams.cli_version = await this.runShellCommand(`${existingCliPath} version`);
143
+ }
144
+ const response = await this.requestToUpdateCLI(queryParams, config);
145
+ if (nestedKeyValue(response, ['updated_cli_version'])) {
146
+ logger.debug(`Need to update binary, current binary version: ${queryParams.cli_version}`);
147
+ const browserStackBinaryUrl = process.env.BROWSERSTACK_BINARY_URL || null;
148
+ if (!isNullOrEmpty(browserStackBinaryUrl)) {
149
+ logger.debug(`Using BROWSERSTACK_BINARY_URL: ${browserStackBinaryUrl}`);
150
+ response.url = browserStackBinaryUrl;
151
+ }
152
+ const finalBinaryPath = await this.downloadLatestBinary(nestedKeyValue(response, ['url']), cliDir);
153
+ PerformanceTester.end(PerformanceEvents.SDK_CLI_CHECK_UPDATE);
154
+ return finalBinaryPath;
155
+ }
156
+ PerformanceTester.end(PerformanceEvents.SDK_CLI_CHECK_UPDATE);
157
+ return existingCliPath;
158
+ }
159
+ static getCliDir() {
160
+ const writableDir = this.getWritableDir();
161
+ try {
162
+ if (isNullOrEmpty(writableDir)) {
163
+ throw new Error('No writable directory available for the CLI');
164
+ }
165
+ const cliDirPath = path.join(writableDir, 'cli');
166
+ if (!fs.existsSync(cliDirPath)) {
167
+ createDir(cliDirPath);
168
+ }
169
+ return cliDirPath;
170
+ }
171
+ catch (err) {
172
+ logger.error(`Error in getting writable directory, writableDir=${util.format(err)}`);
173
+ return '';
174
+ }
175
+ }
176
+ static getWritableDir() {
177
+ const writableDirOptions = [
178
+ process.env.BROWSERSTACK_FILES_DIR,
179
+ path.join(homedir(), '.browserstack'),
180
+ path.join('tmp', '.browserstack'),
181
+ ];
182
+ for (const path of writableDirOptions) {
183
+ if (isNullOrEmpty(path)) {
184
+ continue;
185
+ }
186
+ try {
187
+ if (fs.existsSync(path)) {
188
+ logger.debug(`File ${path} already exist`);
189
+ if (!isWritable(path)) {
190
+ logger.debug(`Giving write permission to ${path}`);
191
+ const success = setReadWriteAccess(path);
192
+ if (!isTrue(success)) {
193
+ logger.warn(`Unable to provide write permission to ${path}`);
194
+ }
195
+ }
196
+ }
197
+ else {
198
+ logger.debug(`File does not exist: ${path}`);
199
+ createDir(path);
200
+ logger.debug(`Giving write permission to ${path}`);
201
+ const success = setReadWriteAccess(path);
202
+ if (!isTrue(success)) {
203
+ logger.warn(`Unable to provide write permission to ${path}`);
204
+ }
205
+ }
206
+ return path;
207
+ }
208
+ catch (err) {
209
+ logger.error(`Unable to get writable directory, exception ${util.format(err)}`);
210
+ }
211
+ }
212
+ return null;
213
+ }
214
+ static getExistingCliPath(cliDir) {
215
+ try {
216
+ // Check if the path exists and is a directory
217
+ if (!fs.existsSync(cliDir) || !fs.statSync(cliDir).isDirectory()) {
218
+ return '';
219
+ }
220
+ // List all files in the directory that start with "binary-"
221
+ const allBinaries = fs
222
+ .readdirSync(cliDir)
223
+ .map((file) => path.join(cliDir, file))
224
+ .filter((filePath) => fs.statSync(filePath).isFile() && path.basename(filePath).startsWith('binary-'));
225
+ if (allBinaries.length > 0) {
226
+ // Get the latest binary by comparing the last modified time
227
+ const latestBinary = allBinaries
228
+ .map((filePath) => ({
229
+ filePath,
230
+ mtime: fs.statSync(filePath).mtime,
231
+ }))
232
+ .reduce((latest, current) => {
233
+ if (!latest || !latest.mtime) {
234
+ return current;
235
+ }
236
+ if (current.mtime > latest.mtime) {
237
+ return current;
238
+ }
239
+ return latest;
240
+ }, null);
241
+ return latestBinary ? latestBinary.filePath : '';
242
+ }
243
+ return ''; // No binary present
244
+ }
245
+ catch (err) {
246
+ logger.error(`Error while reading CLI path: ${util.format(err)}`);
247
+ return '';
248
+ }
249
+ }
250
+ static requestToUpdateCLI = async (queryParams, config) => {
251
+ const params = new URLSearchParams(queryParams);
252
+ const requestInit = {
253
+ headers: {
254
+ Authorization: `Basic ${Buffer.from(`${getBrowserStackUser(config)}:${getBrowserStackKey(config)}`).toString('base64')}`,
255
+ },
256
+ };
257
+ const response = await nodeRequest('GET', `${UPDATED_CLI_ENDPOINT}?${params.toString()}`, requestInit, APIUtils.BROWSERSTACK_AUTOMATE_API_URL);
258
+ logger.debug(`response ${JSON.stringify(response)}`);
259
+ return response;
260
+ };
261
+ static runShellCommand(cmdCommand, workingDir = '') {
262
+ return new Promise((resolve) => {
263
+ const process = exec(cmdCommand, { cwd: workingDir, timeout: 5000 }, (error, stdout, stderr) => {
264
+ if (error) {
265
+ resolve(stderr.trim() || 'SHELL_EXECUTE_ERROR');
266
+ }
267
+ else {
268
+ resolve(stdout.trim());
269
+ }
270
+ });
271
+ // Ensure the process is killed if it exceeds the timeout
272
+ process.on('error', () => {
273
+ resolve('SHELL_EXECUTE_ERROR');
274
+ });
275
+ });
276
+ }
277
+ static downloadLatestBinary = async (binDownloadUrl, cliDir) => {
278
+ PerformanceTester.start(PerformanceEvents.SDK_CLI_DOWNLOAD);
279
+ logger.debug(`Downloading SDK binary from: ${binDownloadUrl}`);
280
+ try {
281
+ const zipFilePath = path.join(cliDir, 'downloaded_file.zip');
282
+ const downloadedFileStream = fs.createWriteStream(zipFilePath);
283
+ return new Promise((resolve, reject) => {
284
+ const binaryName = null;
285
+ const stream = got.stream(binDownloadUrl, {
286
+ followRedirect: true
287
+ });
288
+ stream.pipe(downloadedFileStream);
289
+ downloadedFileStream.on('error', function (err) {
290
+ logger.error('Got Error while downloading percy binary file' + err);
291
+ PerformanceTester.end(PerformanceEvents.SDK_CLI_DOWNLOAD, false, util.format(err));
292
+ reject(err);
293
+ });
294
+ stream.on('error', (err) => {
295
+ logger.error(`Got Error in cli binary downloading request ${util.format(err)}`);
296
+ PerformanceTester.end(PerformanceEvents.SDK_CLI_DOWNLOAD, false, util.format(err));
297
+ reject(err);
298
+ });
299
+ CLIUtils.downloadFileStream(downloadedFileStream, binaryName, zipFilePath, cliDir, resolve, reject);
300
+ PerformanceTester.end(PerformanceEvents.SDK_CLI_DOWNLOAD);
301
+ });
302
+ }
303
+ catch (err) {
304
+ PerformanceTester.end(PerformanceEvents.SDK_CLI_DOWNLOAD, false, util.format(err));
305
+ logger.debug(`Failed to download binary, Exception: ${util.format(err)}`);
306
+ return null;
307
+ }
308
+ };
309
+ static downloadFileStream(downloadedFileStream, binaryName, zipFilePath, cliDir, resolve, reject) {
310
+ downloadedFileStream.on('close', async function () {
311
+ const yauzlOpenPromise = promisify(yauzl.open);
312
+ try {
313
+ const zipfile = await yauzlOpenPromise(zipFilePath, { lazyEntries: true });
314
+ zipfile.readEntry();
315
+ zipfile.on('entry', async (entry) => {
316
+ if (!binaryName) {
317
+ binaryName = entry.fileName;
318
+ }
319
+ if (/\/$/.test(entry.fileName)) {
320
+ // Directory file names end with '/'.
321
+ zipfile.readEntry();
322
+ }
323
+ else {
324
+ // file entry
325
+ const writeStream = fs.createWriteStream(path.join(cliDir, entry.fileName));
326
+ const openReadStreamPromise = promisify(zipfile.openReadStream).bind(zipfile);
327
+ try {
328
+ const readStream = await openReadStreamPromise(entry);
329
+ readStream.on('end', function () {
330
+ writeStream.close();
331
+ zipfile.readEntry();
332
+ });
333
+ readStream.pipe(writeStream);
334
+ }
335
+ catch (zipErr) {
336
+ reject(zipErr);
337
+ }
338
+ if (entry.fileName === binaryName) {
339
+ zipfile.close();
340
+ }
341
+ }
342
+ });
343
+ zipfile.on('error', (zipErr) => {
344
+ reject(zipErr);
345
+ });
346
+ zipfile.once('end', () => {
347
+ fsp.unlink(zipFilePath)
348
+ .catch(() => {
349
+ logger.warn(`Failed to delete zip file: ${zipFilePath}`);
350
+ });
351
+ fsp.chmod(`${cliDir}/${binaryName}`, '0755')
352
+ .then(() => {
353
+ resolve(`${cliDir}/${binaryName}`);
354
+ }).catch((err) => {
355
+ reject(err);
356
+ });
357
+ zipfile.close();
358
+ });
359
+ }
360
+ catch (err) {
361
+ reject(err);
362
+ }
363
+ });
364
+ }
365
+ static getTestFrameworkDetail() {
366
+ if (process.env.BROWSERSTACK_TEST_FRAMEWORK_DETAIL) {
367
+ return JSON.parse(process.env.BROWSERSTACK_TEST_FRAMEWORK_DETAIL);
368
+ }
369
+ return this.testFrameworkDetail;
370
+ }
371
+ static getAutomationFrameworkDetail() {
372
+ if (process.env.BROWSERSTACK_AUTOMATION_FRAMEWORK_DETAIL) {
373
+ return JSON.parse(process.env.BROWSERSTACK_AUTOMATION_FRAMEWORK_DETAIL);
374
+ }
375
+ return this.automationFrameworkDetail;
376
+ }
377
+ static setFrameworkDetail(testFramework, automationFramework) {
378
+ if (!testFramework || !automationFramework) {
379
+ logger.debug(`Test or Automation framework not provided testFramework=${testFramework}, automationFramework=${automationFramework}`);
380
+ }
381
+ this.testFrameworkDetail = {
382
+ name: testFramework,
383
+ version: { [testFramework]: CLIUtils.getSdkVersion() },
384
+ };
385
+ this.automationFrameworkDetail = {
386
+ name: automationFramework,
387
+ version: { [automationFramework]: CLIUtils.getSdkVersion() },
388
+ };
389
+ process.env.BROWSERSTACK_AUTOMATION_FRAMEWORK_DETAIL = JSON.stringify(this.automationFrameworkDetail);
390
+ process.env.BROWSERSTACK_TEST_FRAMEWORK_DETAIL = JSON.stringify(this.testFrameworkDetail);
391
+ }
392
+ /**
393
+ * Get the current instance name using thread id and processId
394
+ * @returns {string}
395
+ */
396
+ static getCurrentInstanceName() {
397
+ return `${process.pid}:${threadId}`;
398
+ }
399
+ /**
400
+ *
401
+ * @param {TestFrameworkState | AutomationFrameworkState} frameworkState
402
+ * @param {HookState} hookState
403
+ * @returns {string}
404
+ */
405
+ static getHookRegistryKey(frameworkState, hookState) {
406
+ return `${frameworkState}:${hookState}`;
407
+ }
408
+ static matchHookRegex(hookState) {
409
+ const pattern = new RegExp(TestFrameworkConstants.HOOK_REGEX);
410
+ return pattern.test(hookState);
411
+ }
412
+ static getObservabilityBuildTags(observabilityOptions, bstackBuildTag) {
413
+ if (process.env.TEST_OBSERVABILITY_BUILD_TAG) {
414
+ return process.env.TEST_OBSERVABILITY_BUILD_TAG.split(',');
415
+ }
416
+ if (observabilityOptions.buildTag) {
417
+ return observabilityOptions.buildTag;
418
+ }
419
+ if (bstackBuildTag) {
420
+ return [bstackBuildTag];
421
+ }
422
+ return [];
423
+ }
424
+ static checkCLISupportedFrameworks(framework) {
425
+ if (framework === undefined) {
426
+ return false;
427
+ }
428
+ return this.CLISupportedFrameworks.includes(framework);
429
+ }
430
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * EventDispatcher - Singleton class for event handling
3
+ */
4
+ declare class EventDispatcher {
5
+ #private;
6
+ observers: Record<string, Function[]>;
7
+ constructor();
8
+ /**
9
+ * Get the EventDispatcher singleton instance
10
+ * @returns {EventDispatcher} The singleton instance
11
+ */
12
+ static getInstance(): EventDispatcher;
13
+ /**
14
+ * Add event observer
15
+ * @param {string} event - Event name
16
+ * @param {Function} callback - Callback function
17
+ */
18
+ registerObserver(hookRegistryKey: string, callback: Function): void;
19
+ /**
20
+ * Notify registered observers on an event
21
+ * @param {string} event - Event name
22
+ * @param {*} data - Event data
23
+ */
24
+ notifyObserver(event: string, args: unknown): Promise<void>;
25
+ }
26
+ export declare const eventDispatcher: EventDispatcher;
27
+ export {};
28
+ //# sourceMappingURL=eventDispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventDispatcher.d.ts","sourceRoot":"","sources":["../../src/cli/eventDispatcher.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAM,eAAe;;IAGjB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;;IAMrC;;;KAGC;IACD,MAAM,CAAC,WAAW;IAOlB;;;;KAIC;IACD,gBAAgB,CAAC,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;IAQ5D;;;;KAIC;IACK,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAQpD;AAGD,eAAO,MAAM,eAAe,iBAAgC,CAAA"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * EventDispatcher - Singleton class for event handling
3
+ */
4
+ class EventDispatcher {
5
+ static #instance = null;
6
+ observers;
7
+ constructor() {
8
+ this.observers = {};
9
+ }
10
+ /**
11
+ * Get the EventDispatcher singleton instance
12
+ * @returns {EventDispatcher} The singleton instance
13
+ */
14
+ static getInstance() {
15
+ if (!EventDispatcher.#instance) {
16
+ EventDispatcher.#instance = new EventDispatcher();
17
+ }
18
+ return EventDispatcher.#instance;
19
+ }
20
+ /**
21
+ * Add event observer
22
+ * @param {string} event - Event name
23
+ * @param {Function} callback - Callback function
24
+ */
25
+ registerObserver(hookRegistryKey, callback) {
26
+ if (!this.observers[hookRegistryKey]) {
27
+ this.observers[hookRegistryKey] = [];
28
+ }
29
+ this.observers[hookRegistryKey].push(callback);
30
+ }
31
+ /**
32
+ * Notify registered observers on an event
33
+ * @param {string} event - Event name
34
+ * @param {*} data - Event data
35
+ */
36
+ async notifyObserver(event, args) {
37
+ if (this.observers[event]) {
38
+ for (const callback of this.observers[event]) {
39
+ await callback(args);
40
+ }
41
+ return;
42
+ }
43
+ }
44
+ }
45
+ // Create the singleton instance
46
+ export const eventDispatcher = EventDispatcher.getInstance();
47
+ // Object.freeze to prevent modification of the instance
48
+ Object.freeze(eventDispatcher);
@@ -0,0 +1,89 @@
1
+ import type AutomationFrameworkInstance from '../instances/automationFrameworkInstance.js';
2
+ import type TrackedContext from '../instances/trackedContext.js';
3
+ /**
4
+ * AutomationFramework - Automation Framework abstract class
5
+ */
6
+ export default class AutomationFramework {
7
+ #private;
8
+ static instances: Map<any, any>;
9
+ static KEY_AUTOMATION_SESSIONS: string;
10
+ static KEY_NON_BROWSERSTACK_AUTOMATION_SESSIONS: string;
11
+ /**
12
+ * Constructor for the AutomationFramework
13
+ * @param {string} automationFrameworkName - Name of the automation framework
14
+ * @param {string} automationFrameworkVersion - Version of the automation framework
15
+ */
16
+ constructor(automationFrameworkName: string, automationFrameworkVersion: string);
17
+ /**
18
+ * Get the automation framework name
19
+ * @returns {string} The name of the automation framework
20
+ */
21
+ getAutomationFrameworkName(): string;
22
+ /**
23
+ * Get the automation framework version
24
+ * @returns {string} The version of the automation framework
25
+ */
26
+ getAutomationFrameworkVersion(): string;
27
+ /**
28
+ * Track an event
29
+ * @param {Object}
30
+ * @param {Object}
31
+ * @param {Object}
32
+ * @returns {void}
33
+ */
34
+ trackEvent(automationFrameworkState: State, hookState: State, args?: unknown): Promise<void>;
35
+ /**
36
+ *
37
+ * @param {*} instance
38
+ * @param {*} automationFrameworkState
39
+ * @param {*} hookState
40
+ * @param {*} args
41
+ */
42
+ runHooks(instance: AutomationFrameworkInstance, automationFrameworkState: State, hookState: State, args?: unknown): Promise<void>;
43
+ /**
44
+ * Register an observer
45
+ * @returns {void}
46
+ */
47
+ static registerObserver(automationFrameworkState: State, hookState: State, callback: Function): void;
48
+ /**
49
+ * Set the tracked instance
50
+ * @param {TrackedInstance} context - The context
51
+ * @param {TrackedInstance} instance - The instance
52
+ * @returns {void}
53
+ */
54
+ static setTrackedInstance(context: TrackedContext, instance: AutomationFrameworkInstance): void;
55
+ /**
56
+ * Get the tracked instance
57
+ * @returns {TrackedInstance} The tracked instance
58
+ */
59
+ static getTrackedInstance(): any;
60
+ /**
61
+ * Set the state
62
+ * @param {TrackedInstance} instance - The instance
63
+ * @param {string} key - The key
64
+ * @param {*} value - The value
65
+ * @returns
66
+ */
67
+ static setState(instance: AutomationFrameworkInstance, key: string, value: unknown): void;
68
+ /**
69
+ * Get the state
70
+ * @param {TrackedInstance} instance - The instance
71
+ * @param {string} key - The key
72
+ * @returns {*} The state
73
+ */
74
+ static getState(instance: AutomationFrameworkInstance, key: string): any;
75
+ static isAutomationSession(instance: AutomationFrameworkInstance): boolean;
76
+ /**
77
+ * Set the driver for the automation framework instance
78
+ * @param {AutomationFrameworkInstance} instance - The automation framework instance
79
+ * @param {*} driver - The driver object
80
+ */
81
+ static setDriver(instance: AutomationFrameworkInstance, driver: unknown): void;
82
+ /**
83
+ * Get the driver from the automation framework instance
84
+ * @param {AutomationFrameworkInstance} instance - The automation framework instance
85
+ * @returns {*} The driver object or null
86
+ */
87
+ static getDriver(instance: AutomationFrameworkInstance): unknown;
88
+ }
89
+ //# sourceMappingURL=automationFramework.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"automationFramework.d.ts","sourceRoot":"","sources":["../../../src/cli/frameworks/automationFramework.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,2BAA2B,MAAM,6CAA6C,CAAA;AAC1F,OAAO,KAAK,cAAc,MAAM,gCAAgC,CAAA;AAGhE;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;;IAIpC,MAAM,CAAC,SAAS,gBAAY;IAC5B,MAAM,CAAC,uBAAuB,SAAwB;IACtD,MAAM,CAAC,wCAAwC,SAAyC;IAExF;;;;IAIA;gBACY,uBAAuB,EAAE,MAAM,EAAE,0BAA0B,EAAE,MAAM;IAK/E;;;KAGC;IACD,0BAA0B;IAI1B;;;KAGC;IACD,6BAA6B;IAI7B;;;;;;KAMC;IACK,UAAU,CAAC,wBAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,GAAE,OAAY;IAItF;;;;;;KAMC;IACK,QAAQ,CAAC,QAAQ,EAAE,2BAA2B,EAAE,wBAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,GAAE,OAAY;IAO3H;;;KAGC;IACD,MAAM,CAAC,gBAAgB,CAAC,wBAAwB,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,QAAQ;IAI7F;;;;;KAKC;IACD,MAAM,CAAC,kBAAkB,CAAC,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,2BAA2B;IAKxF;;;KAGC;IACD,MAAM,CAAC,kBAAkB;IAMzB;;;;;;KAMC;IACD,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,2BAA2B,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO;IAIlF;;;;;KAKC;IACD,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,2BAA2B,EAAE,GAAG,EAAE,MAAM;IAIlE,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,2BAA2B,GAAG,OAAO;IAI1E;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,2BAA2B,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAQ9E;;;;OAIG;IACH,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,2BAA2B,GAAG,OAAO;CAKnE"}