@wdio/browserstack-service 0.0.0-canary-20260722132416

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (331) hide show
  1. package/LICENSE-MIT +20 -0
  2. package/README.md +392 -0
  3. package/browserstack-service.d.ts +43 -0
  4. package/build/Percy/Percy-Handler.d.ts +32 -0
  5. package/build/Percy/Percy-Handler.d.ts.map +1 -0
  6. package/build/Percy/Percy-Handler.js +165 -0
  7. package/build/Percy/Percy-Handler.js.map +1 -0
  8. package/build/Percy/Percy.d.ts +19 -0
  9. package/build/Percy/Percy.d.ts.map +1 -0
  10. package/build/Percy/Percy.js +164 -0
  11. package/build/Percy/Percy.js.map +1 -0
  12. package/build/Percy/PercyBinary.d.ts +10 -0
  13. package/build/Percy/PercyBinary.d.ts.map +1 -0
  14. package/build/Percy/PercyBinary.js +161 -0
  15. package/build/Percy/PercyBinary.js.map +1 -0
  16. package/build/Percy/PercyCaptureMap.d.ts +9 -0
  17. package/build/Percy/PercyCaptureMap.d.ts.map +1 -0
  18. package/build/Percy/PercyCaptureMap.js +36 -0
  19. package/build/Percy/PercyCaptureMap.js.map +1 -0
  20. package/build/Percy/PercyHelper.d.ts +8 -0
  21. package/build/Percy/PercyHelper.d.ts.map +1 -0
  22. package/build/Percy/PercyHelper.js +68 -0
  23. package/build/Percy/PercyHelper.js.map +1 -0
  24. package/build/Percy/PercyLogger.d.ts +15 -0
  25. package/build/Percy/PercyLogger.d.ts.map +1 -0
  26. package/build/Percy/PercyLogger.js +68 -0
  27. package/build/Percy/PercyLogger.js.map +1 -0
  28. package/build/Percy/PercySDK.d.ts +4 -0
  29. package/build/Percy/PercySDK.d.ts.map +1 -0
  30. package/build/Percy/PercySDK.js +86 -0
  31. package/build/Percy/PercySDK.js.map +1 -0
  32. package/build/accessibility-handler.d.ts +52 -0
  33. package/build/accessibility-handler.d.ts.map +1 -0
  34. package/build/accessibility-handler.js +361 -0
  35. package/build/accessibility-handler.js.map +1 -0
  36. package/build/ai-handler.d.ts +23 -0
  37. package/build/ai-handler.d.ts.map +1 -0
  38. package/build/ai-handler.js +184 -0
  39. package/build/ai-handler.js.map +1 -0
  40. package/build/bstackLogger.d.ts +16 -0
  41. package/build/bstackLogger.d.ts.map +1 -0
  42. package/build/bstackLogger.js +71 -0
  43. package/build/bstackLogger.js.map +1 -0
  44. package/build/cleanup.d.ts +9 -0
  45. package/build/cleanup.d.ts.map +1 -0
  46. package/build/cleanup.js +101 -0
  47. package/build/cleanup.js.map +1 -0
  48. package/build/cli/apiUtils.d.ts +14 -0
  49. package/build/cli/apiUtils.d.ts.map +1 -0
  50. package/build/cli/apiUtils.js +25 -0
  51. package/build/cli/apiUtils.js.map +1 -0
  52. package/build/cli/cliLogger.d.ts +17 -0
  53. package/build/cli/cliLogger.d.ts.map +1 -0
  54. package/build/cli/cliLogger.js +82 -0
  55. package/build/cli/cliLogger.js.map +1 -0
  56. package/build/cli/cliUtils.d.ts +64 -0
  57. package/build/cli/cliUtils.d.ts.map +1 -0
  58. package/build/cli/cliUtils.js +461 -0
  59. package/build/cli/cliUtils.js.map +1 -0
  60. package/build/cli/eventDispatcher.d.ts +28 -0
  61. package/build/cli/eventDispatcher.d.ts.map +1 -0
  62. package/build/cli/eventDispatcher.js +49 -0
  63. package/build/cli/eventDispatcher.js.map +1 -0
  64. package/build/cli/frameworks/automationFramework.d.ts +89 -0
  65. package/build/cli/frameworks/automationFramework.d.ts.map +1 -0
  66. package/build/cli/frameworks/automationFramework.js +132 -0
  67. package/build/cli/frameworks/automationFramework.js.map +1 -0
  68. package/build/cli/frameworks/constants/automationFrameworkConstants.d.ts +18 -0
  69. package/build/cli/frameworks/constants/automationFrameworkConstants.d.ts.map +1 -0
  70. package/build/cli/frameworks/constants/automationFrameworkConstants.js +18 -0
  71. package/build/cli/frameworks/constants/automationFrameworkConstants.js.map +1 -0
  72. package/build/cli/frameworks/constants/testFrameworkConstants.d.ts +43 -0
  73. package/build/cli/frameworks/constants/testFrameworkConstants.d.ts.map +1 -0
  74. package/build/cli/frameworks/constants/testFrameworkConstants.js +43 -0
  75. package/build/cli/frameworks/constants/testFrameworkConstants.js.map +1 -0
  76. package/build/cli/frameworks/testFramework.d.ts +89 -0
  77. package/build/cli/frameworks/testFramework.d.ts.map +1 -0
  78. package/build/cli/frameworks/testFramework.js +126 -0
  79. package/build/cli/frameworks/testFramework.js.map +1 -0
  80. package/build/cli/frameworks/wdioAutomationFramework.d.ts +28 -0
  81. package/build/cli/frameworks/wdioAutomationFramework.d.ts.map +1 -0
  82. package/build/cli/frameworks/wdioAutomationFramework.js +67 -0
  83. package/build/cli/frameworks/wdioAutomationFramework.js.map +1 -0
  84. package/build/cli/frameworks/wdioMochaTestFramework.d.ts +82 -0
  85. package/build/cli/frameworks/wdioMochaTestFramework.d.ts.map +1 -0
  86. package/build/cli/frameworks/wdioMochaTestFramework.js +322 -0
  87. package/build/cli/frameworks/wdioMochaTestFramework.js.map +1 -0
  88. package/build/cli/grpcClient.d.ts +86 -0
  89. package/build/cli/grpcClient.d.ts.map +1 -0
  90. package/build/cli/grpcClient.js +532 -0
  91. package/build/cli/grpcClient.js.map +1 -0
  92. package/build/cli/index.d.ts +150 -0
  93. package/build/cli/index.d.ts.map +1 -0
  94. package/build/cli/index.js +464 -0
  95. package/build/cli/index.js.map +1 -0
  96. package/build/cli/instances/automationFrameworkInstance.d.ts +33 -0
  97. package/build/cli/instances/automationFrameworkInstance.d.ts.map +1 -0
  98. package/build/cli/instances/automationFrameworkInstance.js +45 -0
  99. package/build/cli/instances/automationFrameworkInstance.js.map +1 -0
  100. package/build/cli/instances/testFrameworkInstance.d.ts +62 -0
  101. package/build/cli/instances/testFrameworkInstance.d.ts.map +1 -0
  102. package/build/cli/instances/testFrameworkInstance.js +97 -0
  103. package/build/cli/instances/testFrameworkInstance.js.map +1 -0
  104. package/build/cli/instances/trackedContext.d.ts +32 -0
  105. package/build/cli/instances/trackedContext.d.ts.map +1 -0
  106. package/build/cli/instances/trackedContext.js +48 -0
  107. package/build/cli/instances/trackedContext.js.map +1 -0
  108. package/build/cli/instances/trackedInstance.d.ts +40 -0
  109. package/build/cli/instances/trackedInstance.d.ts.map +1 -0
  110. package/build/cli/instances/trackedInstance.js +64 -0
  111. package/build/cli/instances/trackedInstance.js.map +1 -0
  112. package/build/cli/modules/accessibilityModule.d.ts +46 -0
  113. package/build/cli/modules/accessibilityModule.d.ts.map +1 -0
  114. package/build/cli/modules/accessibilityModule.js +447 -0
  115. package/build/cli/modules/accessibilityModule.js.map +1 -0
  116. package/build/cli/modules/automateModule.d.ts +26 -0
  117. package/build/cli/modules/automateModule.d.ts.map +1 -0
  118. package/build/cli/modules/automateModule.js +223 -0
  119. package/build/cli/modules/automateModule.js.map +1 -0
  120. package/build/cli/modules/baseModule.d.ts +33 -0
  121. package/build/cli/modules/baseModule.d.ts.map +1 -0
  122. package/build/cli/modules/baseModule.js +52 -0
  123. package/build/cli/modules/baseModule.js.map +1 -0
  124. package/build/cli/modules/observabilityModule.d.ts +22 -0
  125. package/build/cli/modules/observabilityModule.d.ts.map +1 -0
  126. package/build/cli/modules/observabilityModule.js +46 -0
  127. package/build/cli/modules/observabilityModule.js.map +1 -0
  128. package/build/cli/modules/percyModule.d.ts +19 -0
  129. package/build/cli/modules/percyModule.d.ts.map +1 -0
  130. package/build/cli/modules/percyModule.js +78 -0
  131. package/build/cli/modules/percyModule.js.map +1 -0
  132. package/build/cli/modules/testHubModule.d.ts +30 -0
  133. package/build/cli/modules/testHubModule.d.ts.map +1 -0
  134. package/build/cli/modules/testHubModule.js +233 -0
  135. package/build/cli/modules/testHubModule.js.map +1 -0
  136. package/build/cli/modules/webdriverIOModule.d.ts +29 -0
  137. package/build/cli/modules/webdriverIOModule.d.ts.map +1 -0
  138. package/build/cli/modules/webdriverIOModule.js +129 -0
  139. package/build/cli/modules/webdriverIOModule.js.map +1 -0
  140. package/build/cli/states/automationFrameworkState.d.ts +65 -0
  141. package/build/cli/states/automationFrameworkState.d.ts.map +1 -0
  142. package/build/cli/states/automationFrameworkState.js +62 -0
  143. package/build/cli/states/automationFrameworkState.js.map +1 -0
  144. package/build/cli/states/hookState.d.ts +45 -0
  145. package/build/cli/states/hookState.d.ts.map +1 -0
  146. package/build/cli/states/hookState.js +44 -0
  147. package/build/cli/states/hookState.js.map +1 -0
  148. package/build/cli/states/testFrameworkState.d.ts +125 -0
  149. package/build/cli/states/testFrameworkState.d.ts.map +1 -0
  150. package/build/cli/states/testFrameworkState.js +116 -0
  151. package/build/cli/states/testFrameworkState.js.map +1 -0
  152. package/build/config.d.ts +29 -0
  153. package/build/config.d.ts.map +1 -0
  154. package/build/config.js +52 -0
  155. package/build/config.js.map +1 -0
  156. package/build/constants.d.ts +96 -0
  157. package/build/constants.d.ts.map +1 -0
  158. package/build/constants.js +156 -0
  159. package/build/constants.js.map +1 -0
  160. package/build/crash-reporter.d.ts +18 -0
  161. package/build/crash-reporter.d.ts.map +1 -0
  162. package/build/crash-reporter.js +134 -0
  163. package/build/crash-reporter.js.map +1 -0
  164. package/build/cucumber-types.d.ts +194 -0
  165. package/build/cucumber-types.d.ts.map +1 -0
  166. package/build/cucumber-types.js +2 -0
  167. package/build/cucumber-types.js.map +1 -0
  168. package/build/data-store.d.ts +4 -0
  169. package/build/data-store.d.ts.map +1 -0
  170. package/build/data-store.js +42 -0
  171. package/build/data-store.js.map +1 -0
  172. package/build/exitHandler.d.ts +4 -0
  173. package/build/exitHandler.d.ts.map +1 -0
  174. package/build/exitHandler.js +105 -0
  175. package/build/exitHandler.js.map +1 -0
  176. package/build/fileStream.d.ts +9 -0
  177. package/build/fileStream.d.ts.map +1 -0
  178. package/build/fileStream.js +13 -0
  179. package/build/fileStream.js.map +1 -0
  180. package/build/grpc/generated/sdk-messages-accessibility.d.ts +76 -0
  181. package/build/grpc/generated/sdk-messages-accessibility.d.ts.map +1 -0
  182. package/build/grpc/generated/sdk-messages-accessibility.js +908 -0
  183. package/build/grpc/generated/sdk-messages-accessibility.js.map +1 -0
  184. package/build/grpc/generated/sdk-messages-ai.d.ts +52 -0
  185. package/build/grpc/generated/sdk-messages-ai.d.ts.map +1 -0
  186. package/build/grpc/generated/sdk-messages-ai.js +643 -0
  187. package/build/grpc/generated/sdk-messages-ai.js.map +1 -0
  188. package/build/grpc/generated/sdk-messages-observability.d.ts +44 -0
  189. package/build/grpc/generated/sdk-messages-observability.d.ts.map +1 -0
  190. package/build/grpc/generated/sdk-messages-observability.js +505 -0
  191. package/build/grpc/generated/sdk-messages-observability.js.map +1 -0
  192. package/build/grpc/generated/sdk-messages-percy.d.ts +16 -0
  193. package/build/grpc/generated/sdk-messages-percy.d.ts.map +1 -0
  194. package/build/grpc/generated/sdk-messages-percy.js +111 -0
  195. package/build/grpc/generated/sdk-messages-percy.js.map +1 -0
  196. package/build/grpc/generated/sdk-messages-testhub.d.ts +16 -0
  197. package/build/grpc/generated/sdk-messages-testhub.d.ts.map +1 -0
  198. package/build/grpc/generated/sdk-messages-testhub.js +132 -0
  199. package/build/grpc/generated/sdk-messages-testhub.js.map +1 -0
  200. package/build/grpc/generated/sdk-messages.d.ts +288 -0
  201. package/build/grpc/generated/sdk-messages.d.ts.map +1 -0
  202. package/build/grpc/generated/sdk-messages.js +3958 -0
  203. package/build/grpc/generated/sdk-messages.js.map +1 -0
  204. package/build/grpc/generated/sdk.d.ts +282 -0
  205. package/build/grpc/generated/sdk.d.ts.map +1 -0
  206. package/build/grpc/generated/sdk.js +197 -0
  207. package/build/grpc/generated/sdk.js.map +1 -0
  208. package/build/grpc/index.d.ts +43 -0
  209. package/build/grpc/index.d.ts.map +1 -0
  210. package/build/grpc/index.js +154 -0
  211. package/build/grpc/index.js.map +1 -0
  212. package/build/index.d.ts +20 -0
  213. package/build/index.d.ts.map +1 -0
  214. package/build/index.js +13 -0
  215. package/build/index.js.map +1 -0
  216. package/build/insights-handler.d.ts +76 -0
  217. package/build/insights-handler.d.ts.map +1 -0
  218. package/build/insights-handler.js +855 -0
  219. package/build/insights-handler.js.map +1 -0
  220. package/build/instrumentation/funnelInstrumentation.d.ts +8 -0
  221. package/build/instrumentation/funnelInstrumentation.d.ts.map +1 -0
  222. package/build/instrumentation/funnelInstrumentation.js +235 -0
  223. package/build/instrumentation/funnelInstrumentation.js.map +1 -0
  224. package/build/instrumentation/performance/constants.d.ts +151 -0
  225. package/build/instrumentation/performance/constants.d.ts.map +1 -0
  226. package/build/instrumentation/performance/constants.js +158 -0
  227. package/build/instrumentation/performance/constants.js.map +1 -0
  228. package/build/instrumentation/performance/performance-tester.d.ts +55 -0
  229. package/build/instrumentation/performance/performance-tester.d.ts.map +1 -0
  230. package/build/instrumentation/performance/performance-tester.js +403 -0
  231. package/build/instrumentation/performance/performance-tester.js.map +1 -0
  232. package/build/launcher.d.ts +49 -0
  233. package/build/launcher.d.ts.map +1 -0
  234. package/build/launcher.js +1077 -0
  235. package/build/launcher.js.map +1 -0
  236. package/build/log4jsAppender.d.ts +2 -0
  237. package/build/log4jsAppender.d.ts.map +1 -0
  238. package/build/log4jsAppender.js +20 -0
  239. package/build/log4jsAppender.js.map +1 -0
  240. package/build/logPatcher.d.ts +13 -0
  241. package/build/logPatcher.d.ts.map +1 -0
  242. package/build/logPatcher.js +39 -0
  243. package/build/logPatcher.js.map +1 -0
  244. package/build/logReportingAPI.d.ts +12 -0
  245. package/build/logReportingAPI.d.ts.map +1 -0
  246. package/build/logReportingAPI.js +57 -0
  247. package/build/logReportingAPI.js.map +1 -0
  248. package/build/reporter.d.ts +40 -0
  249. package/build/reporter.d.ts.map +1 -0
  250. package/build/reporter.js +254 -0
  251. package/build/reporter.js.map +1 -0
  252. package/build/request-handler.d.ts +19 -0
  253. package/build/request-handler.d.ts.map +1 -0
  254. package/build/request-handler.js +78 -0
  255. package/build/request-handler.js.map +1 -0
  256. package/build/scripts/accessibility-scripts.d.ts +26 -0
  257. package/build/scripts/accessibility-scripts.d.ts.map +1 -0
  258. package/build/scripts/accessibility-scripts.js +91 -0
  259. package/build/scripts/accessibility-scripts.js.map +1 -0
  260. package/build/service.d.ts +68 -0
  261. package/build/service.d.ts.map +1 -0
  262. package/build/service.js +740 -0
  263. package/build/service.js.map +1 -0
  264. package/build/testHub/utils.d.ts +10 -0
  265. package/build/testHub/utils.d.ts.map +1 -0
  266. package/build/testHub/utils.js +67 -0
  267. package/build/testHub/utils.js.map +1 -0
  268. package/build/testOps/featureStats.d.ts +45 -0
  269. package/build/testOps/featureStats.d.ts.map +1 -0
  270. package/build/testOps/featureStats.js +117 -0
  271. package/build/testOps/featureStats.js.map +1 -0
  272. package/build/testOps/featureUsage.d.ts +22 -0
  273. package/build/testOps/featureUsage.d.ts.map +1 -0
  274. package/build/testOps/featureUsage.js +48 -0
  275. package/build/testOps/featureUsage.js.map +1 -0
  276. package/build/testOps/listener.d.ts +40 -0
  277. package/build/testOps/listener.d.ts.map +1 -0
  278. package/build/testOps/listener.js +269 -0
  279. package/build/testOps/listener.js.map +1 -0
  280. package/build/testOps/requestUtils.d.ts +4 -0
  281. package/build/testOps/requestUtils.d.ts.map +1 -0
  282. package/build/testOps/requestUtils.js +41 -0
  283. package/build/testOps/requestUtils.js.map +1 -0
  284. package/build/testOps/testOpsConfig.d.ts +11 -0
  285. package/build/testOps/testOpsConfig.d.ts.map +1 -0
  286. package/build/testOps/testOpsConfig.js +20 -0
  287. package/build/testOps/testOpsConfig.js.map +1 -0
  288. package/build/testOps/usageStats.d.ts +404 -0
  289. package/build/testOps/usageStats.d.ts.map +1 -0
  290. package/build/testOps/usageStats.js +115 -0
  291. package/build/testOps/usageStats.js.map +1 -0
  292. package/build/testorchestration/apply-orchestration.d.ts +7 -0
  293. package/build/testorchestration/apply-orchestration.d.ts.map +1 -0
  294. package/build/testorchestration/apply-orchestration.js +43 -0
  295. package/build/testorchestration/apply-orchestration.js.map +1 -0
  296. package/build/testorchestration/helpers.d.ts +20 -0
  297. package/build/testorchestration/helpers.d.ts.map +1 -0
  298. package/build/testorchestration/helpers.js +413 -0
  299. package/build/testorchestration/helpers.js.map +1 -0
  300. package/build/testorchestration/request-utils.d.ts +23 -0
  301. package/build/testorchestration/request-utils.d.ts.map +1 -0
  302. package/build/testorchestration/request-utils.js +82 -0
  303. package/build/testorchestration/request-utils.js.map +1 -0
  304. package/build/testorchestration/test-ordering-server.d.ts +33 -0
  305. package/build/testorchestration/test-ordering-server.d.ts.map +1 -0
  306. package/build/testorchestration/test-ordering-server.js +169 -0
  307. package/build/testorchestration/test-ordering-server.js.map +1 -0
  308. package/build/testorchestration/testorcherstrationhandler.d.ts +50 -0
  309. package/build/testorchestration/testorcherstrationhandler.d.ts.map +1 -0
  310. package/build/testorchestration/testorcherstrationhandler.js +134 -0
  311. package/build/testorchestration/testorcherstrationhandler.js.map +1 -0
  312. package/build/testorchestration/testorcherstrationutils.d.ts +86 -0
  313. package/build/testorchestration/testorcherstrationutils.d.ts.map +1 -0
  314. package/build/testorchestration/testorcherstrationutils.js +380 -0
  315. package/build/testorchestration/testorcherstrationutils.js.map +1 -0
  316. package/build/types.d.ts +413 -0
  317. package/build/types.d.ts.map +1 -0
  318. package/build/types.js +2 -0
  319. package/build/types.js.map +1 -0
  320. package/build/util.d.ts +241 -0
  321. package/build/util.d.ts.map +1 -0
  322. package/build/util.js +1665 -0
  323. package/build/util.js.map +1 -0
  324. package/package.json +96 -0
  325. package/src/proto/browserstack/sdk/v1/sdk-messages-accessibility.proto +71 -0
  326. package/src/proto/browserstack/sdk/v1/sdk-messages-ai.proto +48 -0
  327. package/src/proto/browserstack/sdk/v1/sdk-messages-observability.proto +39 -0
  328. package/src/proto/browserstack/sdk/v1/sdk-messages-percy.proto +10 -0
  329. package/src/proto/browserstack/sdk/v1/sdk-messages-testhub.proto +10 -0
  330. package/src/proto/browserstack/sdk/v1/sdk-messages.proto +276 -0
  331. package/src/proto/browserstack/sdk/v1/sdk.proto +41 -0
@@ -0,0 +1,1077 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import got from 'got';
8
+ import { FormData } from 'formdata-node';
9
+ import { v4 as uuidv4 } from 'uuid';
10
+ import fs from 'node:fs';
11
+ import path from 'node:path';
12
+ import { promisify, format } from 'node:util';
13
+ import { performance, PerformanceObserver } from 'node:perf_hooks';
14
+ import os from 'node:os';
15
+ import { SevereServiceError } from 'webdriverio';
16
+ import * as BrowserstackLocalLauncher from 'browserstack-local';
17
+ import { startPercy, stopPercy, getBestPlatformForPercySnapshot } from './Percy/PercyHelper.js';
18
+ import { BSTACK_SERVICE_VERSION, NOT_ALLOWED_KEYS_IN_CAPS, PERF_MEASUREMENT_ENV, RERUN_ENV, RERUN_TESTS_ENV, BROWSERSTACK_TESTHUB_UUID, VALID_APP_EXTENSION, BROWSERSTACK_PERCY, BROWSERSTACK_OBSERVABILITY, WDIO_NAMING_PREFIX } from './constants.js';
19
+ import { launchTestSession, shouldAddServiceVersion, stopBuildUpstream, getCiInfo, isBStackSession, isUndefined, isAccessibilityAutomationSession, isTrue, getBrowserStackUser, getBrowserStackKey, uploadLogs, ObjectsAreEqual, isValidCapsForHealing, getBooleanValueFromString, validateCapsWithNonBstackA11y, mergeChromeOptions, normalizeTestReportingConfig, normalizeTestReportingEnvVariables, isValidEnabledValue, isMultiRemoteCaps } from './util.js';
20
+ import { getProductMap } from './testHub/utils.js';
21
+ import CrashReporter from './crash-reporter.js';
22
+ import { BStackLogger } from './bstackLogger.js';
23
+ import { PercyLogger } from './Percy/PercyLogger.js';
24
+ import { FileStream } from './fileStream.js';
25
+ import { sendStart, sendFinish } from './instrumentation/funnelInstrumentation.js';
26
+ import BrowserStackConfig from './config.js';
27
+ import { setupExitHandlers } from './exitHandler.js';
28
+ import AiHandler from './ai-handler.js';
29
+ import TestOpsConfig from './testOps/testOpsConfig.js';
30
+ import PerformanceTester from './instrumentation/performance/performance-tester.js';
31
+ import * as PERFORMANCE_SDK_EVENTS from './instrumentation/performance/constants.js';
32
+ import { BrowserstackCLI } from './cli/index.js';
33
+ import { CLIUtils } from './cli/cliUtils.js';
34
+ import accessibilityScripts from './scripts/accessibility-scripts.js';
35
+ import util from 'node:util';
36
+ import APIUtils from './cli/apiUtils.js';
37
+ export default class BrowserstackLauncherService {
38
+ _options;
39
+ _config;
40
+ browserstackLocal;
41
+ _buildName;
42
+ _projectName;
43
+ _buildTag;
44
+ _buildIdentifier;
45
+ _accessibilityAutomation = null;
46
+ _percy;
47
+ _percyBestPlatformCaps;
48
+ browserStackConfig;
49
+ constructor(_options, capabilities, _config) {
50
+ this._options = _options;
51
+ this._config = _config;
52
+ BStackLogger.clearLogFile();
53
+ PercyLogger.clearLogFile();
54
+ setupExitHandlers();
55
+ // added to maintain backward compatibility with webdriverIO v5
56
+ this._config || (this._config = _options);
57
+ //normalizing testReporting config and env variables
58
+ normalizeTestReportingConfig(this._options);
59
+ normalizeTestReportingEnvVariables();
60
+ this.browserStackConfig = BrowserStackConfig.getInstance(_options, _config);
61
+ if (Array.isArray(capabilities)) {
62
+ capabilities
63
+ .flatMap((c) => {
64
+ if (Object.values(c).length > 0 && Object.values(c).every(c => typeof c === 'object' && c.capabilities)) {
65
+ return Object.values(c).map((o) => o.capabilities);
66
+ }
67
+ return c;
68
+ })
69
+ .forEach((capability) => {
70
+ if (!capability['bstack:options']) {
71
+ // Skipping adding of service version if session is not of browserstack
72
+ if (isBStackSession(this._config)) {
73
+ const extensionCaps = Object.keys(capability).filter((cap) => cap.includes(':'));
74
+ if (extensionCaps.length) {
75
+ capability['bstack:options'] = { wdioService: BSTACK_SERVICE_VERSION };
76
+ if (!isUndefined(capability['browserstack.accessibility'])) {
77
+ this._accessibilityAutomation ||= isTrue(capability['browserstack.accessibility']);
78
+ }
79
+ else if (isTrue(this._options.accessibility)) {
80
+ capability['bstack:options'].accessibility = true;
81
+ }
82
+ }
83
+ else if (shouldAddServiceVersion(this._config, this._options.testObservability)) {
84
+ capability['browserstack.wdioService'] = BSTACK_SERVICE_VERSION;
85
+ }
86
+ }
87
+ // Need this details for sending data to Observability
88
+ this._buildIdentifier = capability['browserstack.buildIdentifier']?.toString();
89
+ this._buildName = capability.build?.toString();
90
+ }
91
+ else {
92
+ capability['bstack:options'].wdioService = BSTACK_SERVICE_VERSION;
93
+ this._buildName = capability['bstack:options'].buildName;
94
+ this._projectName = capability['bstack:options'].projectName;
95
+ this._buildTag = capability['bstack:options'].buildTag;
96
+ this._buildIdentifier = capability['bstack:options'].buildIdentifier;
97
+ if (!isUndefined(capability['bstack:options'].accessibility)) {
98
+ this._accessibilityAutomation ||= isTrue(capability['bstack:options'].accessibility);
99
+ }
100
+ else if (isTrue(this._options.accessibility)) {
101
+ capability['bstack:options'].accessibility = (isTrue(this._options.accessibility));
102
+ }
103
+ }
104
+ });
105
+ }
106
+ else if (typeof capabilities === 'object') {
107
+ Object.entries(capabilities).forEach(([, caps]) => {
108
+ if (!caps.capabilities['bstack:options']) {
109
+ if (isBStackSession(this._config)) {
110
+ const extensionCaps = Object.keys(caps.capabilities).filter((cap) => cap.includes(':'));
111
+ if (extensionCaps.length) {
112
+ caps.capabilities['bstack:options'] = { wdioService: BSTACK_SERVICE_VERSION };
113
+ if (!isUndefined(caps.capabilities['browserstack.accessibility'])) {
114
+ this._accessibilityAutomation ||= isTrue(caps.capabilities['browserstack.accessibility']);
115
+ }
116
+ else if (isTrue(this._options.accessibility)) {
117
+ caps.capabilities['bstack:options'] = { wdioService: BSTACK_SERVICE_VERSION, accessibility: (isTrue(this._options.accessibility)) };
118
+ }
119
+ }
120
+ else if (shouldAddServiceVersion(this._config, this._options.testObservability)) {
121
+ caps.capabilities['browserstack.wdioService'] = BSTACK_SERVICE_VERSION;
122
+ }
123
+ }
124
+ this._buildIdentifier = caps.capabilities['browserstack.buildIdentifier'];
125
+ }
126
+ else {
127
+ const bstackOptions = caps.capabilities['bstack:options'];
128
+ bstackOptions.wdioService = BSTACK_SERVICE_VERSION;
129
+ this._buildName = bstackOptions.buildName;
130
+ this._projectName = bstackOptions.projectName;
131
+ this._buildTag = bstackOptions.buildTag;
132
+ this._buildIdentifier = bstackOptions.buildIdentifier;
133
+ if (!isUndefined(bstackOptions.accessibility)) {
134
+ this._accessibilityAutomation ||= isTrue(bstackOptions.accessibility);
135
+ }
136
+ else if (isTrue(this._options.accessibility)) {
137
+ bstackOptions.accessibility = isTrue(this._options.accessibility);
138
+ }
139
+ }
140
+ });
141
+ }
142
+ this.browserStackConfig.buildIdentifier = this._buildIdentifier;
143
+ this.browserStackConfig.buildName = this._buildName;
144
+ PerformanceTester.startMonitoring('performance-report-launcher.csv');
145
+ if (!isUndefined(this._options.accessibility)) {
146
+ this._accessibilityAutomation ||= isTrue(this._options.accessibility);
147
+ }
148
+ this._options.accessibility = this._accessibilityAutomation;
149
+ // by default observability will be true unless specified as false
150
+ this._options.testObservability = this._options.testObservability !== false;
151
+ if (this._options.testObservability
152
+ &&
153
+ // update files to run if it's a rerun
154
+ process.env[RERUN_ENV] && process.env[RERUN_TESTS_ENV]) {
155
+ this._config.specs = process.env[RERUN_TESTS_ENV].split(',');
156
+ }
157
+ try {
158
+ CrashReporter.setConfigDetails(this._config, capabilities, this._options);
159
+ }
160
+ catch (error) {
161
+ BStackLogger.error(`[Crash_Report_Upload] Config processing failed due to ${error}`);
162
+ }
163
+ }
164
+ async onWorkerStart(cid, caps) {
165
+ try {
166
+ if (this._options.percy && this._percyBestPlatformCaps) {
167
+ const isThisBestPercyPlatform = ObjectsAreEqual(caps, this._percyBestPlatformCaps);
168
+ if (isThisBestPercyPlatform) {
169
+ process.env.BEST_PLATFORM_CID = cid;
170
+ }
171
+ }
172
+ }
173
+ catch (err) {
174
+ PercyLogger.error(`Error while setting best platform for Percy snapshot at worker start ${err}`);
175
+ }
176
+ }
177
+ async onPrepare(config, capabilities) {
178
+ PerformanceTester.start(PERFORMANCE_SDK_EVENTS.FRAMEWORK_EVENTS.INIT);
179
+ // // Send Funnel start request
180
+ await sendStart(this.browserStackConfig);
181
+ // Convert glob patterns in specs to resolved relative paths
182
+ if (config.specs && Array.isArray(config.specs) && isValidEnabledValue(this._options.testOrchestrationOptions?.runSmartSelection?.enabled)) {
183
+ try {
184
+ // Import glob for expanding file patterns
185
+ const glob = (await import('glob')).sync;
186
+ const path = await import('node:path');
187
+ // Use ConfigParser.getFilePaths equivalent logic to expand specs
188
+ const expandedSpecs = [];
189
+ for (const specPattern of config.specs) {
190
+ if (typeof specPattern === 'string') {
191
+ if (specPattern.startsWith('file://')) {
192
+ expandedSpecs.push(specPattern);
193
+ continue;
194
+ }
195
+ // Expand glob pattern to relative paths
196
+ const pattern = specPattern.replace(/\\/g, '/');
197
+ // Use config.rootDir which is set to the config file's directory
198
+ const rootDir = config.rootDir || process.cwd();
199
+ // Get current working directory for final relative path calculation
200
+ const cwd = process.cwd();
201
+ const filenames = glob(pattern, {
202
+ cwd: rootDir,
203
+ matchBase: true
204
+ }) || [];
205
+ // Convert paths to be relative to the current working directory (where command is run)
206
+ filenames
207
+ .forEach((filename) => {
208
+ let absolutePath = filename;
209
+ // If filename is not absolute, resolve it relative to rootDir (config file's directory)
210
+ if (!path.isAbsolute(filename)) {
211
+ absolutePath = path.resolve(rootDir, filename);
212
+ }
213
+ // Make path relative to current working directory
214
+ let relativePath = path.relative(cwd, absolutePath);
215
+ // Normalize path separators for consistency (Windows compatibility)
216
+ relativePath = relativePath.replace(/\\/g, '/');
217
+ expandedSpecs.push(relativePath);
218
+ });
219
+ }
220
+ }
221
+ if (expandedSpecs.length > 0) {
222
+ BStackLogger.info(`Expanded specs from glob patterns to ${expandedSpecs.length} files`);
223
+ config.specs = expandedSpecs;
224
+ }
225
+ }
226
+ catch (error) {
227
+ BStackLogger.error(`Failed to expand spec patterns: ${error}`);
228
+ }
229
+ }
230
+ try {
231
+ // Detect if multi-remote and disable CLI for those sessions
232
+ const isMultiremote = isMultiRemoteCaps(capabilities);
233
+ process.env.BROWSERSTACK_IS_MULTIREMOTE = String(isMultiremote);
234
+ if (CLIUtils.checkCLISupportedFrameworks(config.framework) && !isMultiremote) {
235
+ PerformanceTester.start(PERFORMANCE_SDK_EVENTS.FRAMEWORK_EVENTS.START);
236
+ CLIUtils.setFrameworkDetail(WDIO_NAMING_PREFIX + config.framework, 'WebdriverIO'); // TODO: make this constant
237
+ const binconfig = CLIUtils.getBinConfig(config, capabilities, this._options, this._buildTag);
238
+ await BrowserstackCLI.getInstance().bootstrap(this._options, config, binconfig);
239
+ BStackLogger.debug(`Is CLI running ${BrowserstackCLI.getInstance().isRunning()}`);
240
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.FRAMEWORK_EVENTS.START);
241
+ }
242
+ }
243
+ catch (err) {
244
+ BStackLogger.error(`Error while starting CLI ${err}`);
245
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.FRAMEWORK_EVENTS.START, false, util.format(err));
246
+ }
247
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.FRAMEWORK_EVENTS.INIT);
248
+ // Setting up healing for those sessions where we don't add the service version capability as it indicates that the session is not being run on BrowserStack
249
+ if (!shouldAddServiceVersion(this._config, this._options.testObservability, capabilities)) {
250
+ try {
251
+ if (capabilities.browserName) {
252
+ capabilities = await AiHandler.setup(this._config, this.browserStackConfig, this._options, capabilities, false);
253
+ }
254
+ else if (Array.isArray(capabilities)) {
255
+ for (let i = 0; i < capabilities.length; i++) {
256
+ if (capabilities[i].browserName) {
257
+ capabilities[i] = await AiHandler.setup(this._config, this.browserStackConfig, this._options, capabilities[i], false);
258
+ }
259
+ }
260
+ }
261
+ else if (isValidCapsForHealing(capabilities)) {
262
+ // setting up healing in case capabilities.xyz.capabilities.browserName where xyz can be anything:
263
+ capabilities = await AiHandler.setup(this._config, this.browserStackConfig, this._options, capabilities, true);
264
+ }
265
+ }
266
+ catch (err) {
267
+ if (this._options.selfHeal === true) {
268
+ BStackLogger.warn(`Error while setting up Browserstack healing Extension ${err}. Disabling healing for this session.`);
269
+ }
270
+ }
271
+ }
272
+ /**
273
+ * Upload app to BrowserStack if valid file path to app is given.
274
+ * Update app value of capability directly if app_url, custom_id, shareable_id is given
275
+ */
276
+ if (!BrowserstackCLI.getInstance().isRunning()) {
277
+ if (!this._options.app) {
278
+ BStackLogger.debug('app is not defined in browserstack-service config, skipping ...');
279
+ }
280
+ else {
281
+ let app = {};
282
+ const appConfig = this._options.app;
283
+ try {
284
+ app = await this._validateApp(appConfig);
285
+ }
286
+ catch (error) {
287
+ throw new SevereServiceError(error);
288
+ }
289
+ if (VALID_APP_EXTENSION.includes(path.extname(app.app))) {
290
+ if (fs.existsSync(app.app)) {
291
+ const data = await this._uploadApp(app);
292
+ BStackLogger.info(`app upload completed: ${JSON.stringify(data)}`);
293
+ app.app = data.app_url;
294
+ }
295
+ else if (app.customId) {
296
+ app.app = app.customId;
297
+ }
298
+ else {
299
+ throw new SevereServiceError(`[Invalid app path] app path ${app.app} is not correct, Provide correct path to app under test`);
300
+ }
301
+ }
302
+ BStackLogger.info(`Using app: ${app.app}`);
303
+ this._updateCaps(capabilities, 'app', app.app);
304
+ }
305
+ }
306
+ /**
307
+ * buildIdentifier in service options will take precedence over specified in capabilities
308
+ */
309
+ if (this._options.buildIdentifier) {
310
+ this._buildIdentifier = this._options.buildIdentifier;
311
+ this._updateCaps(capabilities, 'buildIdentifier', this._buildIdentifier);
312
+ }
313
+ /**
314
+ * evaluate buildIdentifier in case unique execution identifiers are present
315
+ * e.g., ${BUILD_NUMBER} and ${DATE_TIME}
316
+ */
317
+ this._handleBuildIdentifier(capabilities);
318
+ // remove accessibilityOptions from the capabilities if present
319
+ this._updateObjectTypeCaps(capabilities, 'accessibilityOptions');
320
+ const shouldSetupPercy = this._options.percy || (isUndefined(this._options.percy) && this._options.app);
321
+ let buildStartResponse = null;
322
+ if (!BrowserstackCLI.getInstance().isRunning() && (this._options.testObservability || this._accessibilityAutomation || shouldSetupPercy)) {
323
+ BStackLogger.debug('Sending launch start event');
324
+ buildStartResponse = await launchTestSession(this._options, this._config, {
325
+ projectName: this._projectName,
326
+ buildName: this._buildName,
327
+ buildTag: this._buildTag,
328
+ bstackServiceVersion: BSTACK_SERVICE_VERSION,
329
+ buildIdentifier: this._buildIdentifier
330
+ }, this.browserStackConfig, this._accessibilityAutomation);
331
+ //added checks for Accessibility running on non-bstack infra
332
+ if (isAccessibilityAutomationSession(this._accessibilityAutomation) && (process.env.BROWSERSTACK_TURBOSCALE || !shouldAddServiceVersion(this._config, this._options.testObservability))) {
333
+ const overrideOptions = accessibilityScripts.ChromeExtension;
334
+ this._updateObjectTypeCaps(capabilities, 'goog:chromeOptions', overrideOptions);
335
+ }
336
+ if (buildStartResponse?.accessibility) {
337
+ if (this._accessibilityAutomation === null) {
338
+ this.browserStackConfig.accessibility = buildStartResponse.accessibility.success;
339
+ this._accessibilityAutomation = buildStartResponse.accessibility.success;
340
+ this._options.accessibility = buildStartResponse.accessibility.success;
341
+ if (buildStartResponse.accessibility.success === true) {
342
+ this._updateCaps(capabilities, 'accessibility', 'true');
343
+ }
344
+ }
345
+ }
346
+ this.browserStackConfig.accessibility = this._accessibilityAutomation;
347
+ if (this._accessibilityAutomation && this._options.accessibilityOptions) {
348
+ const filteredOpts = Object.keys(this._options.accessibilityOptions)
349
+ .filter(key => !NOT_ALLOWED_KEYS_IN_CAPS.includes(key))
350
+ .reduce((opts, key) => {
351
+ return {
352
+ ...opts,
353
+ [key]: this._options.accessibilityOptions?.[key]
354
+ };
355
+ }, {});
356
+ this._updateObjectTypeCaps(capabilities, 'accessibilityOptions', filteredOpts);
357
+ }
358
+ else if (isAccessibilityAutomationSession(this._accessibilityAutomation)) {
359
+ this._updateObjectTypeCaps(capabilities, 'accessibilityOptions', {});
360
+ }
361
+ this._removeCliOnlyCapabilityOptions(capabilities);
362
+ if (shouldSetupPercy) {
363
+ try {
364
+ const bestPlatformPercyCaps = getBestPlatformForPercySnapshot(capabilities);
365
+ this._percyBestPlatformCaps = bestPlatformPercyCaps;
366
+ process.env[BROWSERSTACK_PERCY] = 'false';
367
+ await this.setupPercy(this._options, this._config, {
368
+ projectName: this._projectName
369
+ });
370
+ this._updateBrowserStackPercyConfig();
371
+ }
372
+ catch (err) {
373
+ PercyLogger.error(`Error while setting up Percy ${err}`);
374
+ }
375
+ }
376
+ }
377
+ // send testhub build uuid and product map instrumentation
378
+ this._updateCaps(capabilities, 'testhubBuildUuid');
379
+ this._updateCaps(capabilities, 'buildProductMap');
380
+ if (isValidEnabledValue(this._options.testOrchestrationOptions?.runSmartSelection?.enabled)) {
381
+ // Helper function to convert specs from cwd-relative to rootDir-relative
382
+ const convertToRootDirRelative = (specs) => {
383
+ const rootDir = config.rootDir || process.cwd();
384
+ const cwd = process.cwd();
385
+ return specs.map((spec) => {
386
+ if (typeof spec !== 'string') {
387
+ return spec;
388
+ }
389
+ // Convert from cwd-relative to absolute
390
+ const absolutePath = path.isAbsolute(spec) ? spec : path.resolve(cwd, spec);
391
+ // Then make it relative to rootDir (config file's directory)
392
+ const relativePath = path.relative(rootDir, absolutePath);
393
+ // Normalize path separators
394
+ return relativePath.replace(/\\/g, '/');
395
+ });
396
+ };
397
+ // Apply test orchestration if enabled
398
+ try {
399
+ // Import dynamically to avoid circular dependencies
400
+ const { applyOrchestrationIfEnabled } = await import('./testorchestration/apply-orchestration.js');
401
+ if (config.specs && config.specs.length > 0 && this._options.testObservability && isValidEnabledValue(this._options.testOrchestrationOptions?.runSmartSelection?.enabled)) {
402
+ BStackLogger.info('Applying test orchestration');
403
+ // Ensure we're passing string[] to applyOrchestrationIfEnabled
404
+ const specs = config.specs.filter(spec => typeof spec === 'string');
405
+ console.log(`Specs before orchestration: ${specs}`);
406
+ const orderedSpecs = await applyOrchestrationIfEnabled(specs, this._options);
407
+ console.log(`Specs after orchestration before conversion: ${orderedSpecs}`);
408
+ // Use ordered specs if available, otherwise use original specs
409
+ const specsToConvert = orderedSpecs && orderedSpecs.length > 0 ? orderedSpecs : specs;
410
+ config.specs = convertToRootDirRelative(specsToConvert);
411
+ console.log(`Specs after orchestration: ${config.specs}`);
412
+ BStackLogger.info('Test specs updated with orchestrated order');
413
+ }
414
+ }
415
+ catch (error) {
416
+ BStackLogger.error(`Error applying test orchestration: ${error}`);
417
+ // On error, we still need to convert specs from cwd-relative to rootDir-relative
418
+ if (config.specs && config.specs.length > 0) {
419
+ const specs = config.specs.filter(spec => typeof spec === 'string');
420
+ config.specs = convertToRootDirRelative(specs);
421
+ BStackLogger.debug(`Specs converted back to rootDir-relative after error: ${config.specs}`);
422
+ }
423
+ }
424
+ }
425
+ // local binary will be handled by CLI
426
+ if (BrowserstackCLI.getInstance().isRunning()) {
427
+ return;
428
+ }
429
+ if (!this._options.browserstackLocal) {
430
+ return BStackLogger.info('browserstackLocal is not enabled - skipping...');
431
+ }
432
+ const opts = {
433
+ key: this._config.key,
434
+ ...this._options.opts
435
+ };
436
+ this.browserstackLocal = new BrowserstackLocalLauncher.Local();
437
+ this._updateCaps(capabilities, 'local');
438
+ if (opts.localIdentifier) {
439
+ this._updateCaps(capabilities, 'localIdentifier', opts.localIdentifier);
440
+ }
441
+ /**
442
+ * measure BrowserStack tunnel boot time
443
+ */
444
+ const obs = new PerformanceObserver((list) => {
445
+ const entry = list.getEntries()[0];
446
+ BStackLogger.info(`Browserstack Local successfully started after ${entry.duration}ms`);
447
+ });
448
+ obs.observe({ entryTypes: ['measure'] });
449
+ let timer;
450
+ performance.mark('tbTunnelStart');
451
+ PerformanceTester.start(PERFORMANCE_SDK_EVENTS.AUTOMATE_EVENTS.LOCAL_START);
452
+ return Promise.race([
453
+ promisify(this.browserstackLocal.start.bind(this.browserstackLocal))(opts),
454
+ new Promise((resolve, reject) => {
455
+ /* istanbul ignore next */
456
+ timer = setTimeout(function () {
457
+ reject('Browserstack Local failed to start within 60 seconds!');
458
+ }, 60000);
459
+ })
460
+ ]).then(function (result) {
461
+ clearTimeout(timer);
462
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.AUTOMATE_EVENTS.LOCAL_START);
463
+ performance.mark('tbTunnelEnd');
464
+ performance.measure('bootTime', 'tbTunnelStart', 'tbTunnelEnd');
465
+ return Promise.resolve(result);
466
+ }, function (err) {
467
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.AUTOMATE_EVENTS.LOCAL_START, false, err);
468
+ clearTimeout(timer);
469
+ return Promise.reject(err);
470
+ });
471
+ }
472
+ async onComplete() {
473
+ PerformanceTester.start(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_CLEANUP);
474
+ PerformanceTester.start(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_ON_STOP);
475
+ PerformanceTester.start(PERFORMANCE_SDK_EVENTS.FRAMEWORK_EVENTS.STOP);
476
+ try {
477
+ BStackLogger.debug('Inside OnComplete hook..');
478
+ BStackLogger.debug('Sending stop launch event');
479
+ const isCLIEnabled = BrowserstackCLI.getInstance().isRunning();
480
+ try {
481
+ await (isCLIEnabled ? BrowserstackCLI.getInstance().stop() : stopBuildUpstream());
482
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.FRAMEWORK_EVENTS.STOP);
483
+ }
484
+ catch (err) {
485
+ BStackLogger.error(`Error while stopping CLI ${err}`);
486
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.FRAMEWORK_EVENTS.STOP, false, util.format(err));
487
+ }
488
+ if (process.env[BROWSERSTACK_OBSERVABILITY] && process.env[BROWSERSTACK_TESTHUB_UUID]) {
489
+ console.log(`\nVisit https://automation.browserstack.com/builds/${process.env[BROWSERSTACK_TESTHUB_UUID]} to view build report, insights, and many more debugging information all at one place!\n`);
490
+ }
491
+ this.browserStackConfig.testObservability.buildStopped = true;
492
+ if (process.env[PERF_MEASUREMENT_ENV]) {
493
+ PerformanceTester.calculateTimes(['launchTestSession', 'stopBuildUpstream']);
494
+ if (!process.env.START_TIME) {
495
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_CLEANUP);
496
+ await PerformanceTester.stopAndGenerate('performance-launcher.html');
497
+ return;
498
+ }
499
+ const duration = (new Date()).getTime() - (new Date(process.env.START_TIME)).getTime();
500
+ BStackLogger.info(`Total duration is ${duration / 1000} s`);
501
+ }
502
+ BStackLogger.info(`BrowserStack service run ended for id: ${this.browserStackConfig?.sdkRunID} testhub id: ${TestOpsConfig.getInstance()?.buildHashedId}`);
503
+ await sendFinish(this.browserStackConfig, isCLIEnabled);
504
+ try {
505
+ PerformanceTester.start(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_SEND_LOGS);
506
+ await this._uploadServiceLogs();
507
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_SEND_LOGS);
508
+ }
509
+ catch (error) {
510
+ BStackLogger.debug(`Failed to upload BrowserStack WDIO Service logs ${error}`);
511
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_SEND_LOGS, false, util.format(error));
512
+ }
513
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_ON_STOP);
514
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_CLEANUP);
515
+ await PerformanceTester.stopAndGenerate('performance-launcher.html');
516
+ }
517
+ catch (error) {
518
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_ON_STOP, false, util.format(error));
519
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_CLEANUP, false, util.format(error));
520
+ await PerformanceTester.stopAndGenerate('performance-launcher.html');
521
+ BStackLogger.error(`Error in onComplete hook: ${error}`);
522
+ }
523
+ BStackLogger.clearLogger();
524
+ if (this._options.percy) {
525
+ await this.stopPercy();
526
+ PercyLogger.clearLogger();
527
+ }
528
+ // local binary will be handled by CLI
529
+ if (BrowserstackCLI.getInstance().isRunning()) {
530
+ return;
531
+ }
532
+ if (!this.browserstackLocal || !this.browserstackLocal.isRunning()) {
533
+ return;
534
+ }
535
+ if (this._options.forcedStop) {
536
+ return process.kill(this.browserstackLocal.pid);
537
+ }
538
+ let timer;
539
+ PerformanceTester.start(PERFORMANCE_SDK_EVENTS.AUTOMATE_EVENTS.LOCAL_STOP);
540
+ return Promise.race([
541
+ new Promise((resolve, reject) => {
542
+ this.browserstackLocal?.stop((err) => {
543
+ if (err) {
544
+ return reject(err);
545
+ }
546
+ resolve();
547
+ });
548
+ }),
549
+ new Promise((resolve, reject) => {
550
+ /* istanbul ignore next */
551
+ timer = setTimeout(() => reject(new Error('Browserstack Local failed to stop within 60 seconds!')), 60000);
552
+ })
553
+ ]).then(function (result) {
554
+ clearTimeout(timer);
555
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.AUTOMATE_EVENTS.LOCAL_STOP);
556
+ return Promise.resolve(result);
557
+ }, function (err) {
558
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.AUTOMATE_EVENTS.LOCAL_STOP, false, err);
559
+ clearTimeout(timer);
560
+ return Promise.reject(err);
561
+ });
562
+ }
563
+ async setupPercy(options, config, bsConfig) {
564
+ if (this._percy?.isRunning()) {
565
+ process.env[BROWSERSTACK_PERCY] = 'true';
566
+ return;
567
+ }
568
+ try {
569
+ this._percy = await startPercy(options, config, bsConfig);
570
+ if (!this._percy) {
571
+ throw new Error('Could not start percy, check percy logs for info.');
572
+ }
573
+ PercyLogger.info('Percy started successfully');
574
+ process.env[BROWSERSTACK_PERCY] = 'true';
575
+ let signal = 0;
576
+ const handler = async () => {
577
+ signal++;
578
+ signal === 1 && await this.stopPercy();
579
+ };
580
+ process.on('beforeExit', handler);
581
+ process.on('SIGINT', handler);
582
+ process.on('SIGTERM', handler);
583
+ }
584
+ catch (err) {
585
+ PercyLogger.debug(`Error in percy setup ${err}`);
586
+ process.env[BROWSERSTACK_PERCY] = 'false';
587
+ }
588
+ }
589
+ async stopPercy() {
590
+ if (!this._percy || !this._percy.isRunning()) {
591
+ return;
592
+ }
593
+ try {
594
+ await stopPercy(this._percy);
595
+ PercyLogger.info('Percy stopped');
596
+ }
597
+ catch (err) {
598
+ PercyLogger.error('Error occured while stopping percy : ' + err);
599
+ }
600
+ }
601
+ async _uploadApp(app) {
602
+ BStackLogger.info(`uploading app ${app.app} ${app.customId ? `and custom_id: ${app.customId}` : ''} to browserstack`);
603
+ const form = new FormData();
604
+ if (app.app) {
605
+ const fileName = path.basename(app.app);
606
+ form.append('file', new FileStream(fs.createReadStream(app.app)), fileName);
607
+ }
608
+ if (app.customId) {
609
+ form.append('custom_id', app.customId);
610
+ }
611
+ const res = await got.post(`${APIUtils.BROWSERSTACK_AA_API_CLOUD_URL}/app-automate/upload`, {
612
+ body: form,
613
+ username: this._config.user,
614
+ password: this._config.key
615
+ }).json().catch((err) => {
616
+ throw new SevereServiceError(`app upload failed ${err.message}`);
617
+ });
618
+ return res;
619
+ }
620
+ /**
621
+ * @param {String | AppConfig} appConfig <string>: should be "app file path" or "app_url" or "custom_id" or "shareable_id".
622
+ * <object>: only "path" and "custom_id" should coexist as multiple properties.
623
+ */
624
+ async _validateApp(appConfig) {
625
+ const app = {};
626
+ if (typeof appConfig === 'string') {
627
+ app.app = appConfig;
628
+ }
629
+ else if (typeof appConfig === 'object' && Object.keys(appConfig).length) {
630
+ if (Object.keys(appConfig).length > 2 || (Object.keys(appConfig).length === 2 && (!appConfig.path || !appConfig.custom_id))) {
631
+ throw new SevereServiceError(`keys ${Object.keys(appConfig)} can't co-exist as app values, use any one property from
632
+ {id<string>, path<string>, custom_id<string>, shareable_id<string>}, only "path" and "custom_id" can co-exist.`);
633
+ }
634
+ app.app = appConfig.id || appConfig.path || appConfig.custom_id || appConfig.shareable_id;
635
+ app.customId = appConfig.custom_id;
636
+ }
637
+ else {
638
+ throw new SevereServiceError('[Invalid format] app should be string or an object');
639
+ }
640
+ if (!app.app) {
641
+ throw new SevereServiceError(`[Invalid app property] supported properties are {id<string>, path<string>, custom_id<string>, shareable_id<string>}.
642
+ For more details please visit https://www.browserstack.com/docs/app-automate/appium/set-up-tests/specify-app ')`);
643
+ }
644
+ return app;
645
+ }
646
+ async _uploadServiceLogs() {
647
+ const clientBuildUuid = this._getClientBuildUuid();
648
+ await PerformanceTester.measureWrapper(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_UPLOAD_LOGS, async () => {
649
+ const response = await uploadLogs(getBrowserStackUser(this._config), getBrowserStackKey(this._config), clientBuildUuid);
650
+ BStackLogger.logToFile(`Response - ${format(response)}`, 'debug');
651
+ })();
652
+ }
653
+ _removeCliOnlyCapabilityOptions(capabilities) {
654
+ if (!capabilities || typeof capabilities !== 'object') {
655
+ return;
656
+ }
657
+ const strip = (capability) => {
658
+ const capabilityRecord = capability;
659
+ const bstackOptions = capabilityRecord['bstack:options'];
660
+ if (bstackOptions && typeof bstackOptions === 'object') {
661
+ NOT_ALLOWED_KEYS_IN_CAPS.forEach(key => delete bstackOptions[key]);
662
+ }
663
+ NOT_ALLOWED_KEYS_IN_CAPS.forEach(key => delete capabilityRecord[`browserstack.${key}`]);
664
+ const alwaysMatch = capability.alwaysMatch;
665
+ if (alwaysMatch && typeof alwaysMatch === 'object') {
666
+ strip(alwaysMatch);
667
+ }
668
+ };
669
+ if (Array.isArray(capabilities)) {
670
+ capabilities
671
+ .flatMap((c) => {
672
+ if (Object.values(c).length > 0 && Object.values(c).every(c => typeof c === 'object' && c.capabilities)) {
673
+ return Object.values(c).map((o) => o.capabilities);
674
+ }
675
+ return c;
676
+ })
677
+ .forEach(strip);
678
+ }
679
+ else {
680
+ Object.entries(capabilities).forEach(([, caps]) => {
681
+ strip(caps.capabilities);
682
+ });
683
+ }
684
+ }
685
+ _updateObjectTypeCaps(capabilities, capType, value) {
686
+ try {
687
+ if (Array.isArray(capabilities)) {
688
+ capabilities
689
+ .flatMap((c) => {
690
+ if (Object.values(c).length > 0 && Object.values(c).every(c => typeof c === 'object' && c.capabilities)) {
691
+ return Object.values(c).map((o) => o.capabilities);
692
+ }
693
+ return c;
694
+ })
695
+ .forEach((capability) => {
696
+ if (validateCapsWithNonBstackA11y(capability.browserName, capability.browserVersion)) {
697
+ if (capType === 'goog:chromeOptions' && value) {
698
+ const chromeOptions = capability['goog:chromeOptions'];
699
+ if (chromeOptions) {
700
+ const finalChromeOptions = mergeChromeOptions(chromeOptions, value);
701
+ capability['goog:chromeOptions'] = finalChromeOptions;
702
+ }
703
+ else {
704
+ capability['goog:chromeOptions'] = value;
705
+ }
706
+ return;
707
+ }
708
+ }
709
+ if (!capability['bstack:options']) {
710
+ const extensionCaps = Object.keys(capability).filter((cap) => cap.includes(':'));
711
+ if (extensionCaps.length) {
712
+ if (capType === 'accessibilityOptions' && value) {
713
+ capability['bstack:options'] = { accessibilityOptions: value };
714
+ }
715
+ }
716
+ else if (capType === 'accessibilityOptions') {
717
+ if (value) {
718
+ const accessibilityOpts = { ...value };
719
+ if (capability?.accessibility) {
720
+ accessibilityOpts.authToken = process.env.BSTACK_A11Y_JWT;
721
+ accessibilityOpts.scannerVersion = process.env.BSTACK_A11Y_SCANNER_VERSION;
722
+ }
723
+ capability['browserstack.accessibilityOptions'] = accessibilityOpts;
724
+ }
725
+ else {
726
+ delete capability['browserstack.accessibilityOptions'];
727
+ }
728
+ }
729
+ }
730
+ else if (capType === 'accessibilityOptions') {
731
+ if (value) {
732
+ const accessibilityOpts = { ...value };
733
+ if (capability['bstack:options'].accessibility) {
734
+ accessibilityOpts.authToken = process.env.BSTACK_A11Y_JWT;
735
+ accessibilityOpts.scannerVersion = process.env.BSTACK_A11Y_SCANNER_VERSION;
736
+ }
737
+ capability['bstack:options'].accessibilityOptions = accessibilityOpts;
738
+ }
739
+ else {
740
+ delete capability['bstack:options'].accessibilityOptions;
741
+ }
742
+ }
743
+ });
744
+ }
745
+ else if (typeof capabilities === 'object') {
746
+ Object.entries(capabilities).forEach(([, caps]) => {
747
+ if (validateCapsWithNonBstackA11y(caps.capabilities.browserName, caps.capabilities.browserVersion)) {
748
+ if (capType === 'goog:chromeOptions' && value) {
749
+ const chromeOptions = caps.capabilities['goog:chromeOptions'];
750
+ if (chromeOptions) {
751
+ const finalChromeOptions = mergeChromeOptions(chromeOptions, value);
752
+ caps.capabilities['goog:chromeOptions'] = finalChromeOptions;
753
+ }
754
+ else {
755
+ caps.capabilities['goog:chromeOptions'] = value;
756
+ }
757
+ return;
758
+ }
759
+ }
760
+ if (!caps.capabilities['bstack:options']) {
761
+ const extensionCaps = Object.keys(caps.capabilities).filter((cap) => cap.includes(':'));
762
+ if (extensionCaps.length) {
763
+ if (capType === 'accessibilityOptions' && value) {
764
+ caps.capabilities['bstack:options'] = { accessibilityOptions: value };
765
+ }
766
+ }
767
+ else if (capType === 'accessibilityOptions') {
768
+ if (value) {
769
+ const accessibilityOpts = { ...value };
770
+ if (caps.capabilities['browserstack.accessibility']) {
771
+ accessibilityOpts.authToken = process.env.BSTACK_A11Y_JWT;
772
+ accessibilityOpts.scannerVersion = process.env.BSTACK_A11Y_SCANNER_VERSION;
773
+ }
774
+ caps.capabilities['browserstack.accessibilityOptions'] = accessibilityOpts;
775
+ }
776
+ else {
777
+ delete caps.capabilities['browserstack.accessibilityOptions'];
778
+ }
779
+ }
780
+ }
781
+ else if (capType === 'accessibilityOptions') {
782
+ if (value) {
783
+ const accessibilityOpts = { ...value };
784
+ if (caps.capabilities['bstack:options'].accessibility) {
785
+ accessibilityOpts.authToken = process.env.BSTACK_A11Y_JWT;
786
+ accessibilityOpts.scannerVersion = process.env.BSTACK_A11Y_SCANNER_VERSION;
787
+ }
788
+ caps.capabilities['bstack:options'].accessibilityOptions = accessibilityOpts;
789
+ }
790
+ else {
791
+ delete caps.capabilities['bstack:options'].accessibilityOptions;
792
+ }
793
+ }
794
+ });
795
+ }
796
+ }
797
+ catch (error) {
798
+ BStackLogger.debug(`Exception while retrieving capability value. Error - ${error}`);
799
+ }
800
+ }
801
+ _updateCaps(capabilities, capType, value) {
802
+ if (Array.isArray(capabilities)) {
803
+ capabilities
804
+ .flatMap((c) => {
805
+ if (Object.values(c).length > 0 && Object.values(c).every(c => typeof c === 'object' && c.capabilities)) {
806
+ return Object.values(c).map((o) => o.capabilities);
807
+ }
808
+ return c;
809
+ })
810
+ .forEach((capability) => {
811
+ if (!capability['bstack:options']) {
812
+ const extensionCaps = Object.keys(capability).filter((cap) => cap.includes(':'));
813
+ if (extensionCaps.length) {
814
+ if (capType === 'local') {
815
+ capability['bstack:options'] = { local: true };
816
+ }
817
+ else if (capType === 'app') {
818
+ capability['appium:app'] = value;
819
+ }
820
+ else if (capType === 'buildIdentifier' && value) {
821
+ capability['bstack:options'] = { buildIdentifier: value };
822
+ }
823
+ else if (capType === 'testhubBuildUuid' && TestOpsConfig.getInstance().buildHashedId) {
824
+ capability['bstack:options'] = { testhubBuildUuid: TestOpsConfig.getInstance().buildHashedId };
825
+ }
826
+ else if (capType === 'buildProductMap' && getProductMap(this.browserStackConfig)) {
827
+ capability['bstack:options'] = { buildProductMap: getProductMap(this.browserStackConfig) };
828
+ }
829
+ else if (capType === 'accessibility') {
830
+ capability['bstack:options'] = { accessibility: getBooleanValueFromString(value) };
831
+ }
832
+ }
833
+ else if (capType === 'local') {
834
+ capability['browserstack.local'] = true;
835
+ }
836
+ else if (capType === 'app') {
837
+ capability.app = value;
838
+ }
839
+ else if (capType === 'buildIdentifier') {
840
+ if (value) {
841
+ capability['browserstack.buildIdentifier'] = value;
842
+ }
843
+ else {
844
+ delete capability['browserstack.buildIdentifier'];
845
+ }
846
+ }
847
+ else if (capType === 'localIdentifier') {
848
+ capability['browserstack.localIdentifier'] = value;
849
+ }
850
+ else if (capType === 'testhubBuildUuid') {
851
+ capability['browserstack.testhubBuildUuid'] = TestOpsConfig.getInstance().buildHashedId;
852
+ }
853
+ else if (capType === 'buildProductMap') {
854
+ capability['browserstack.buildProductMap'] = getProductMap(this.browserStackConfig);
855
+ }
856
+ else if (capType === 'accessibility') {
857
+ capability['browserstack.accessibility'] = getBooleanValueFromString(value);
858
+ }
859
+ }
860
+ else if (capType === 'local') {
861
+ capability['bstack:options'].local = true;
862
+ }
863
+ else if (capType === 'app') {
864
+ capability['appium:app'] = value;
865
+ }
866
+ else if (capType === 'buildIdentifier') {
867
+ if (value) {
868
+ capability['bstack:options'].buildIdentifier = value;
869
+ }
870
+ else {
871
+ delete capability['bstack:options'].buildIdentifier;
872
+ }
873
+ }
874
+ else if (capType === 'localIdentifier') {
875
+ capability['bstack:options'].localIdentifier = value;
876
+ }
877
+ else if (capType === 'testhubBuildUuid') {
878
+ capability['bstack:options'].testhubBuildUuid = TestOpsConfig.getInstance().buildHashedId;
879
+ }
880
+ else if (capType === 'buildProductMap') {
881
+ capability['bstack:options'].buildProductMap = getProductMap(this.browserStackConfig);
882
+ }
883
+ else if (capType === 'accessibility') {
884
+ capability['bstack:options'].accessibility = getBooleanValueFromString(value);
885
+ }
886
+ });
887
+ }
888
+ else if (typeof capabilities === 'object') {
889
+ Object.entries(capabilities).forEach(([, caps]) => {
890
+ if (!caps.capabilities['bstack:options']) {
891
+ const extensionCaps = Object.keys(caps.capabilities).filter((cap) => cap.includes(':'));
892
+ if (extensionCaps.length) {
893
+ if (capType === 'local') {
894
+ caps.capabilities['bstack:options'] = { local: true };
895
+ }
896
+ else if (capType === 'app') {
897
+ caps.capabilities['appium:app'] = value;
898
+ }
899
+ else if (capType === 'buildIdentifier' && value) {
900
+ caps.capabilities['bstack:options'] = { buildIdentifier: value };
901
+ }
902
+ else if (capType === 'testhubBuildUuid') {
903
+ caps.capabilities['bstack:options'] = { testhubBuildUuid: TestOpsConfig.getInstance().buildHashedId };
904
+ }
905
+ else if (capType === 'buildProductMap') {
906
+ caps.capabilities['bstack:options'] = { buildProductMap: getProductMap(this.browserStackConfig) };
907
+ }
908
+ else if (capType === 'accessibility') {
909
+ caps.capabilities['bstack:options'] = { accessibility: getBooleanValueFromString(value) };
910
+ }
911
+ }
912
+ else if (capType === 'local') {
913
+ caps.capabilities['browserstack.local'] = true;
914
+ }
915
+ else if (capType === 'app') {
916
+ caps.capabilities['appium:app'] = value;
917
+ }
918
+ else if (capType === 'buildIdentifier') {
919
+ if (value) {
920
+ caps.capabilities['browserstack.buildIdentifier'] = value;
921
+ }
922
+ else {
923
+ delete caps.capabilities['browserstack.buildIdentifier'];
924
+ }
925
+ }
926
+ else if (capType === 'localIdentifier') {
927
+ caps.capabilities['browserstack.localIdentifier'] = value;
928
+ }
929
+ else if (capType === 'testhubBuildUuid') {
930
+ caps.capabilities['browserstack.testhubBuildUuid'] = TestOpsConfig.getInstance().buildHashedId;
931
+ }
932
+ else if (capType === 'buildProductMap') {
933
+ caps.capabilities['browserstack.buildProductMap'] = getProductMap(this.browserStackConfig);
934
+ }
935
+ else if (capType === 'accessibility') {
936
+ caps.capabilities['browserstack.accessibility'] = getBooleanValueFromString(value);
937
+ }
938
+ }
939
+ else if (capType === 'local') {
940
+ caps.capabilities['bstack:options'].local = true;
941
+ }
942
+ else if (capType === 'app') {
943
+ caps.capabilities['appium:app'] = value;
944
+ }
945
+ else if (capType === 'buildIdentifier') {
946
+ if (value) {
947
+ caps.capabilities['bstack:options'].buildIdentifier = value;
948
+ }
949
+ else {
950
+ delete caps.capabilities['bstack:options'].buildIdentifier;
951
+ }
952
+ }
953
+ else if (capType === 'localIdentifier') {
954
+ caps.capabilities['bstack:options'].localIdentifier = value;
955
+ }
956
+ else if (capType === 'testhubBuildUuid') {
957
+ caps.capabilities['bstack:options'].testhubBuildUuid = TestOpsConfig.getInstance().buildHashedId;
958
+ }
959
+ else if (capType === 'buildProductMap') {
960
+ caps.capabilities['bstack:options'].buildProductMap = getProductMap(this.browserStackConfig);
961
+ }
962
+ else if (capType === 'accessibility') {
963
+ caps.capabilities['bstack:options'].accessibility = getBooleanValueFromString(value);
964
+ }
965
+ });
966
+ }
967
+ else {
968
+ throw new SevereServiceError('Capabilities should be an object or Array!');
969
+ }
970
+ }
971
+ _updateBrowserStackPercyConfig() {
972
+ const { percyAutoEnabled = false, percyCaptureMode, buildId, percy } = this._percy || {};
973
+ // Setting to browserStackConfig for populating data in funnel instrumentaion
974
+ this.browserStackConfig.percyCaptureMode = percyCaptureMode;
975
+ this.browserStackConfig.percyBuildId = buildId;
976
+ this.browserStackConfig.isPercyAutoEnabled = percyAutoEnabled;
977
+ // To handle stop percy build
978
+ this._options.percy = percy;
979
+ // To pass data to workers
980
+ process.env.BROWSERSTACK_PERCY = String(percy);
981
+ process.env.BROWSERSTACK_PERCY_CAPTURE_MODE = percyCaptureMode;
982
+ }
983
+ _handleBuildIdentifier(capabilities) {
984
+ if (!this._buildIdentifier) {
985
+ return;
986
+ }
987
+ if ((!this._buildName || process.env.BROWSERSTACK_BUILD_NAME) && this._buildIdentifier) {
988
+ this._updateCaps(capabilities, 'buildIdentifier');
989
+ BStackLogger.warn('Skipping buildIdentifier as buildName is not passed.');
990
+ return;
991
+ }
992
+ if (this._buildIdentifier && this._buildIdentifier.includes('${DATE_TIME}')) {
993
+ const formattedDate = new Intl.DateTimeFormat('en-GB', {
994
+ month: 'short',
995
+ day: '2-digit',
996
+ hour: '2-digit',
997
+ minute: '2-digit',
998
+ hour12: false
999
+ })
1000
+ .format(new Date())
1001
+ .replace(/ |, /g, '-');
1002
+ this._buildIdentifier = this._buildIdentifier.replace('${DATE_TIME}', formattedDate);
1003
+ this._updateCaps(capabilities, 'buildIdentifier', this._buildIdentifier);
1004
+ }
1005
+ if (!this._buildIdentifier.includes('${BUILD_NUMBER}')) {
1006
+ return;
1007
+ }
1008
+ const ciInfo = getCiInfo();
1009
+ if (ciInfo !== null && ciInfo.build_number) {
1010
+ this._buildIdentifier = this._buildIdentifier.replace('${BUILD_NUMBER}', 'CI ' + ciInfo.build_number);
1011
+ this._updateCaps(capabilities, 'buildIdentifier', this._buildIdentifier);
1012
+ }
1013
+ else {
1014
+ const localBuildNumber = this._getLocalBuildNumber();
1015
+ if (localBuildNumber) {
1016
+ this._buildIdentifier = this._buildIdentifier.replace('${BUILD_NUMBER}', localBuildNumber);
1017
+ this._updateCaps(capabilities, 'buildIdentifier', this._buildIdentifier);
1018
+ }
1019
+ }
1020
+ }
1021
+ /**
1022
+ * @return {string} if buildName doesn't exist in json file, it will return 1
1023
+ * else returns corresponding value in json file (e.g. { "wdio-build": { "identifier" : 2 } } => 2 in this case)
1024
+ */
1025
+ _getLocalBuildNumber() {
1026
+ const browserstackFolderPath = path.join(os.homedir(), '.browserstack');
1027
+ try {
1028
+ if (!fs.existsSync(browserstackFolderPath)) {
1029
+ fs.mkdirSync(browserstackFolderPath);
1030
+ }
1031
+ const filePath = path.join(browserstackFolderPath, '.build-name-cache.json');
1032
+ if (!fs.existsSync(filePath)) {
1033
+ fs.appendFileSync(filePath, JSON.stringify({}));
1034
+ }
1035
+ const buildCacheFileData = fs.readFileSync(filePath);
1036
+ const parsedBuildCacheFileData = JSON.parse(buildCacheFileData.toString());
1037
+ if (this._buildName && this._buildName in parsedBuildCacheFileData) {
1038
+ const prevIdentifier = parseInt((parsedBuildCacheFileData[this._buildName].identifier));
1039
+ const newIdentifier = prevIdentifier + 1;
1040
+ this._updateLocalBuildCache(filePath, this._buildName, newIdentifier);
1041
+ return newIdentifier.toString();
1042
+ }
1043
+ const newIdentifier = 1;
1044
+ this._updateLocalBuildCache(filePath, this._buildName, 1);
1045
+ return newIdentifier.toString();
1046
+ }
1047
+ catch (error) {
1048
+ return null;
1049
+ }
1050
+ }
1051
+ _updateLocalBuildCache(filePath, buildName, buildIdentifier) {
1052
+ if (!buildName || !filePath) {
1053
+ return;
1054
+ }
1055
+ const jsonContent = JSON.parse(fs.readFileSync(filePath).toString());
1056
+ jsonContent[buildName] = { 'identifier': buildIdentifier };
1057
+ fs.writeFileSync(filePath, JSON.stringify(jsonContent));
1058
+ }
1059
+ _getClientBuildUuid() {
1060
+ if (process.env[BROWSERSTACK_TESTHUB_UUID]) {
1061
+ return process.env[BROWSERSTACK_TESTHUB_UUID];
1062
+ }
1063
+ const uuid = uuidv4();
1064
+ BStackLogger.logToFile(`If facing any issues, please contact BrowserStack support with the Build Run Id - ${uuid}`, 'info');
1065
+ return uuid;
1066
+ }
1067
+ }
1068
+ __decorate([
1069
+ PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_SETUP)
1070
+ ], BrowserstackLauncherService.prototype, "onWorkerStart", null);
1071
+ __decorate([
1072
+ PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.EVENTS.SDK_PRE_TEST)
1073
+ ], BrowserstackLauncherService.prototype, "onPrepare", null);
1074
+ __decorate([
1075
+ PerformanceTester.Measure(PERFORMANCE_SDK_EVENTS.APP_AUTOMATE_EVENTS.APP_UPLOAD)
1076
+ ], BrowserstackLauncherService.prototype, "_uploadApp", null);
1077
+ //# sourceMappingURL=launcher.js.map