@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,855 @@
1
+ import path from 'node:path';
2
+ import { v4 as uuidv4 } from 'uuid';
3
+ import TestReporter from './reporter.js';
4
+ import { frameworkSupportsHook, getCloudProvider, getFailureObject, getGitMetaData, getHookType, getPlatformVersion, getScenarioExamples, getUniqueIdentifier, getUniqueIdentifierForCucumber, isBrowserstackSession, isScreenshotCommand, isUndefined, o11yClassErrorHandler, removeAnsiColors, getObservabilityProduct } from './util.js';
5
+ import { BStackLogger } from './bstackLogger.js';
6
+ import Listener from './testOps/listener.js';
7
+ import { TESTOPS_SCREENSHOT_ENV } from './constants.js';
8
+ import { BrowserstackCLI } from './cli/index.js';
9
+ import { TestFrameworkState } from './cli/states/testFrameworkState.js';
10
+ import { HookState } from './cli/states/hookState.js';
11
+ import PerformanceTester from './instrumentation/performance/performance-tester.js';
12
+ import * as PERFORMANCE_SDK_EVENTS from './instrumentation/performance/constants.js';
13
+ class _InsightsHandler {
14
+ _browser;
15
+ _framework;
16
+ _tests = {};
17
+ _hooks = {};
18
+ _platformMeta;
19
+ _commands = {};
20
+ _gitConfigPath;
21
+ _suiteFile;
22
+ static currentTest = {};
23
+ _currentHook = {};
24
+ _cucumberData = {
25
+ stepsStarted: false,
26
+ scenariosStarted: false,
27
+ steps: []
28
+ };
29
+ _userCaps = {};
30
+ _options;
31
+ listener = Listener.getInstance();
32
+ currentTestId;
33
+ cbtQueue = [];
34
+ constructor(_browser, _framework, _userCaps, _options) {
35
+ this._browser = _browser;
36
+ this._framework = _framework;
37
+ const caps = this._browser.capabilities;
38
+ const sessionId = this._browser.sessionId;
39
+ this._userCaps = _userCaps;
40
+ this._options = _options;
41
+ this._platformMeta = {
42
+ browserName: caps?.browserName,
43
+ browserVersion: caps?.browserVersion,
44
+ platformName: caps?.platformName,
45
+ caps: caps,
46
+ sessionId,
47
+ product: getObservabilityProduct(_options, this._isAppAutomate())
48
+ };
49
+ this.registerListeners();
50
+ }
51
+ _isAppAutomate() {
52
+ const browserDesiredCapabilities = (this._browser?.capabilities ?? {});
53
+ const desiredCapabilities = (this._userCaps ?? {});
54
+ return !!browserDesiredCapabilities['appium:app'] || !!desiredCapabilities['appium:app'] || !!(desiredCapabilities['appium:options']?.app);
55
+ }
56
+ registerListeners() {
57
+ if (!(this._framework === 'mocha' || this._framework === 'cucumber')) {
58
+ return;
59
+ }
60
+ process.removeAllListeners(`bs:addLog:${process.pid}`);
61
+ process.on(`bs:addLog:${process.pid}`, this.appendTestItemLog.bind(this));
62
+ }
63
+ setSuiteFile(filename) {
64
+ this._suiteFile = filename;
65
+ }
66
+ async setGitConfigPath() {
67
+ const gitMeta = await getGitMetaData();
68
+ if (gitMeta) {
69
+ this._gitConfigPath = gitMeta.root;
70
+ }
71
+ }
72
+ async before() {
73
+ PerformanceTester.start(PERFORMANCE_SDK_EVENTS.CONFIG_EVENTS.OBSERVABILITY);
74
+ if (isBrowserstackSession(this._browser)) {
75
+ await this._browser.execute(`browserstack_executor: ${JSON.stringify({
76
+ action: 'annotate',
77
+ arguments: {
78
+ data: `ObservabilitySync:${Date.now()}`,
79
+ level: 'debug'
80
+ }
81
+ })}`);
82
+ }
83
+ const gitMeta = await getGitMetaData();
84
+ if (gitMeta) {
85
+ this._gitConfigPath = gitMeta.root;
86
+ }
87
+ PerformanceTester.end(PERFORMANCE_SDK_EVENTS.CONFIG_EVENTS.OBSERVABILITY);
88
+ }
89
+ getCucumberHookType(test) {
90
+ let hookType = null;
91
+ if (!test) {
92
+ hookType = this._cucumberData.scenariosStarted ? 'AFTER_ALL' : 'BEFORE_ALL';
93
+ }
94
+ else if (!this._cucumberData.stepsStarted) {
95
+ hookType = 'BEFORE_EACH';
96
+ }
97
+ else if (this._cucumberData.steps?.length > 0) {
98
+ // beforeStep or afterStep
99
+ }
100
+ else {
101
+ hookType = 'AFTER_EACH';
102
+ }
103
+ return hookType;
104
+ }
105
+ getCucumberHookName(hookType) {
106
+ switch (hookType) {
107
+ case 'BEFORE_EACH':
108
+ case 'AFTER_EACH':
109
+ return `${hookType} for ${this._cucumberData.scenario?.name}`;
110
+ case 'BEFORE_ALL':
111
+ case 'AFTER_ALL':
112
+ return `${hookType} for ${this._cucumberData.feature?.name}`;
113
+ }
114
+ return '';
115
+ }
116
+ getCucumberHookUniqueId(hookType, hook) {
117
+ switch (hookType) {
118
+ case 'BEFORE_EACH':
119
+ case 'AFTER_EACH':
120
+ return hook.hookId;
121
+ case 'BEFORE_ALL':
122
+ case 'AFTER_ALL':
123
+ // Can only work for single beforeAll or afterAll
124
+ return `${hookType} for ${this.getCucumberFeatureUniqueId()}`;
125
+ }
126
+ return null;
127
+ }
128
+ getCucumberFeatureUniqueId() {
129
+ const { uri, feature } = this._cucumberData;
130
+ return `${uri}:${feature?.name}`;
131
+ }
132
+ setCurrentHook(hookDetails) {
133
+ if (hookDetails.finished) {
134
+ if (this._currentHook.uuid === hookDetails.uuid) {
135
+ this._currentHook.finished = true;
136
+ }
137
+ return;
138
+ }
139
+ this._currentHook = {
140
+ uuid: hookDetails.uuid,
141
+ finished: false
142
+ };
143
+ }
144
+ async sendScenarioObjectSkipped(scenario, feature, uri) {
145
+ const testMetaData = {
146
+ uuid: uuidv4(),
147
+ startedAt: (new Date()).toISOString(),
148
+ finishedAt: (new Date()).toISOString(),
149
+ scenario: {
150
+ name: scenario.name
151
+ },
152
+ feature: {
153
+ path: uri,
154
+ name: feature.name,
155
+ description: feature.description
156
+ },
157
+ steps: scenario.steps.map((step) => {
158
+ return {
159
+ id: step.id,
160
+ text: step.text,
161
+ keyword: step.keyword,
162
+ result: 'skipped',
163
+ };
164
+ }),
165
+ };
166
+ this.listener.testFinished(this.getTestRunDataForCucumber(null, 'TestRunSkipped', testMetaData));
167
+ }
168
+ async processCucumberHook(test, params, result) {
169
+ const hookType = this.getCucumberHookType(test);
170
+ if (!hookType) {
171
+ return;
172
+ }
173
+ const { event, hookUUID } = params;
174
+ const hookId = this.getCucumberHookUniqueId(hookType, test);
175
+ if (!hookId) {
176
+ return;
177
+ }
178
+ if (event === 'before') {
179
+ this.setCurrentHook({ uuid: hookUUID });
180
+ const hookMetaData = {
181
+ uuid: hookUUID,
182
+ startedAt: (new Date()).toISOString(),
183
+ testRunId: InsightsHandler.currentTest.uuid,
184
+ hookType: hookType
185
+ };
186
+ this._tests[hookId] = hookMetaData;
187
+ this.listener.hookStarted(this.getHookRunDataForCucumber(hookMetaData, 'HookRunStarted'));
188
+ }
189
+ else {
190
+ this._tests[hookId].finishedAt = (new Date()).toISOString();
191
+ this.setCurrentHook({ uuid: this._tests[hookId].uuid, finished: true });
192
+ this.listener.hookFinished(this.getHookRunDataForCucumber(this._tests[hookId], 'HookRunFinished', result));
193
+ if (hookType === 'BEFORE_ALL' && result && !result.passed) {
194
+ const { feature, uri } = this._cucumberData;
195
+ if (!feature) {
196
+ return;
197
+ }
198
+ feature.children.map(async (childObj) => {
199
+ if (childObj.rule) {
200
+ childObj.rule.children.map(async (scenarioObj) => {
201
+ if (scenarioObj.scenario) {
202
+ await this.sendScenarioObjectSkipped(scenarioObj.scenario, feature, uri);
203
+ }
204
+ });
205
+ }
206
+ else if (childObj.scenario) {
207
+ await this.sendScenarioObjectSkipped(childObj.scenario, feature, uri);
208
+ }
209
+ });
210
+ }
211
+ }
212
+ }
213
+ async beforeHook(test, context) {
214
+ if (!frameworkSupportsHook('before', this._framework)) {
215
+ return;
216
+ }
217
+ const hookUUID = uuidv4();
218
+ if (this._framework === 'cucumber') {
219
+ test = test;
220
+ await this.processCucumberHook(test, { event: 'before', hookUUID });
221
+ return;
222
+ }
223
+ test = test;
224
+ const fullTitle = getUniqueIdentifier(test, this._framework);
225
+ this._tests[fullTitle] = {
226
+ uuid: hookUUID,
227
+ startedAt: (new Date()).toISOString()
228
+ };
229
+ this.setCurrentHook({ uuid: hookUUID });
230
+ this.attachHookData(context, hookUUID);
231
+ this.listener.hookStarted(this.getRunData(test, 'HookRunStarted'));
232
+ }
233
+ async afterHook(test, result) {
234
+ if (!frameworkSupportsHook('after', this._framework)) {
235
+ return;
236
+ }
237
+ if (this._framework === 'cucumber') {
238
+ await this.processCucumberHook(test, { event: 'after' }, result);
239
+ return;
240
+ }
241
+ test = test;
242
+ const fullTitle = getUniqueIdentifier(test, this._framework);
243
+ if (this._tests[fullTitle]) {
244
+ this._tests[fullTitle].finishedAt = (new Date()).toISOString();
245
+ }
246
+ else {
247
+ this._tests[fullTitle] = {
248
+ finishedAt: (new Date()).toISOString()
249
+ };
250
+ }
251
+ this.setCurrentHook({ uuid: this._tests[fullTitle].uuid, finished: true });
252
+ this.listener.hookFinished(this.getRunData(test, 'HookRunFinished', result));
253
+ const hookType = getHookType(test.title);
254
+ /*
255
+ If any of the `beforeAll`, `beforeEach`, `afterEach` then the tests after the hook won't run in mocha (https://github.com/mochajs/mocha/issues/4392)
256
+ So if any of this hook fails, then we are sending the next tests in the suite as skipped.
257
+ This won't be needed for `afterAll`, as even if `afterAll` fails all the tests that we need are already run by then, so we don't need to send the stats for them separately
258
+ */
259
+ if (!result.passed && (hookType === 'BEFORE_EACH' || hookType === 'BEFORE_ALL' || hookType === 'AFTER_EACH')) {
260
+ const sendTestSkip = async (skippedTest) => {
261
+ // We only need to send the tests that whose state is not determined yet. The state of tests which is determined will already be sent.
262
+ if (skippedTest.state === undefined) {
263
+ const fullTitle = `${skippedTest.parent.title} - ${skippedTest.title}`;
264
+ this._tests[fullTitle] = {
265
+ uuid: uuidv4(),
266
+ startedAt: (new Date()).toISOString(),
267
+ finishedAt: (new Date()).toISOString()
268
+ };
269
+ this.listener.testFinished(this.getRunData(skippedTest, 'TestRunSkipped'));
270
+ }
271
+ };
272
+ /*
273
+ Recursively send the tests as skipped for all suites below the hook. This is to handle nested describe blocks
274
+ */
275
+ const sendSuiteSkipped = async (suite) => {
276
+ for (const skippedTest of suite.tests) {
277
+ await sendTestSkip(skippedTest);
278
+ }
279
+ for (const skippedSuite of suite.suites) {
280
+ await sendSuiteSkipped(skippedSuite);
281
+ }
282
+ };
283
+ await sendSuiteSkipped(test.ctx.test.parent);
284
+ }
285
+ }
286
+ getHookRunDataForCucumber(hookData, eventType, result) {
287
+ const { uri, feature } = this._cucumberData;
288
+ const testData = {
289
+ uuid: hookData.uuid,
290
+ type: 'hook',
291
+ name: this.getCucumberHookName(hookData.hookType),
292
+ body: {
293
+ lang: 'webdriverio',
294
+ code: null
295
+ },
296
+ started_at: hookData.startedAt,
297
+ finished_at: hookData.finishedAt,
298
+ hook_type: hookData.hookType,
299
+ test_run_id: hookData.testRunId,
300
+ scope: feature?.name,
301
+ scopes: [feature?.name || ''],
302
+ file_name: uri ? path.relative(process.cwd(), uri) : undefined,
303
+ location: uri ? path.relative(process.cwd(), uri) : undefined,
304
+ vc_filepath: (this._gitConfigPath && uri) ? path.relative(this._gitConfigPath, uri) : undefined,
305
+ result: 'pending',
306
+ framework: this._framework
307
+ };
308
+ if (eventType === 'HookRunFinished' && result) {
309
+ testData.result = result.passed ? 'passed' : 'failed';
310
+ testData.retries = result.retries;
311
+ testData.duration_in_ms = result.duration;
312
+ if (!result.passed) {
313
+ Object.assign(testData, getFailureObject(result.error));
314
+ }
315
+ }
316
+ if (eventType === 'HookRunStarted') {
317
+ testData.integrations = {};
318
+ if (this._browser && this._platformMeta) {
319
+ const provider = getCloudProvider(this._browser);
320
+ testData.integrations[provider] = this.getIntegrationsObject();
321
+ }
322
+ }
323
+ return testData;
324
+ }
325
+ setTestData(test, uuid) {
326
+ InsightsHandler.currentTest = {
327
+ test, uuid
328
+ };
329
+ if (this._framework !== 'mocha') {
330
+ return;
331
+ }
332
+ const fullTitle = getUniqueIdentifier(test, this._framework);
333
+ this._tests[fullTitle] = {
334
+ uuid,
335
+ startedAt: (new Date()).toISOString()
336
+ };
337
+ }
338
+ async beforeTest(test) {
339
+ const uuid = uuidv4();
340
+ InsightsHandler.currentTest = {
341
+ test, uuid
342
+ };
343
+ if (this._framework !== 'mocha') {
344
+ return;
345
+ }
346
+ const fullTitle = getUniqueIdentifier(test, this._framework);
347
+ this._tests[fullTitle] = {
348
+ uuid,
349
+ startedAt: (new Date()).toISOString()
350
+ };
351
+ this.listener.testStarted(this.getRunData(test, 'TestRunStarted'));
352
+ }
353
+ async afterTest(test, result) {
354
+ if (this._framework !== 'mocha') {
355
+ return;
356
+ }
357
+ const fullTitle = getUniqueIdentifier(test, this._framework);
358
+ this._tests[fullTitle] = {
359
+ ...(this._tests[fullTitle] || {}),
360
+ finishedAt: (new Date()).toISOString()
361
+ };
362
+ BStackLogger.debug('calling testFinished');
363
+ this.flushCBTDataQueue();
364
+ this.listener.testFinished(this.getRunData(test, 'TestRunFinished', result));
365
+ }
366
+ /**
367
+ * Cucumber Only
368
+ */
369
+ async beforeFeature(uri, feature) {
370
+ this._cucumberData.scenariosStarted = false;
371
+ this._cucumberData.feature = feature;
372
+ this._cucumberData.uri = uri;
373
+ }
374
+ async beforeScenario(world) {
375
+ const uuid = uuidv4();
376
+ InsightsHandler.currentTest = {
377
+ uuid
378
+ };
379
+ this._cucumberData.scenario = world.pickle;
380
+ this._cucumberData.scenariosStarted = true;
381
+ this._cucumberData.stepsStarted = false;
382
+ const pickleData = world.pickle;
383
+ const gherkinDocument = world.gherkinDocument;
384
+ const featureData = gherkinDocument.feature;
385
+ const uniqueId = getUniqueIdentifierForCucumber(world);
386
+ const testMetaData = {
387
+ uuid: uuid,
388
+ startedAt: (new Date()).toISOString()
389
+ };
390
+ if (pickleData) {
391
+ testMetaData.scenario = {
392
+ name: pickleData.name,
393
+ };
394
+ }
395
+ if (gherkinDocument && featureData) {
396
+ testMetaData.feature = {
397
+ path: gherkinDocument.uri,
398
+ name: featureData.name,
399
+ description: featureData.description,
400
+ };
401
+ }
402
+ this._tests[uniqueId] = testMetaData;
403
+ this.listener.testStarted(this.getTestRunDataForCucumber(world, 'TestRunStarted'));
404
+ }
405
+ async afterScenario(world) {
406
+ this._cucumberData.scenario = undefined;
407
+ this.flushCBTDataQueue();
408
+ this.listener.testFinished(this.getTestRunDataForCucumber(world, 'TestRunFinished'));
409
+ }
410
+ async beforeStep(step, scenario) {
411
+ this._cucumberData.stepsStarted = true;
412
+ this._cucumberData.steps.push(step);
413
+ const uniqueId = getUniqueIdentifierForCucumber({ pickle: scenario });
414
+ const testMetaData = this._tests[uniqueId] || { steps: [] };
415
+ if (testMetaData && !testMetaData.steps) {
416
+ testMetaData.steps = [];
417
+ }
418
+ testMetaData.steps?.push({
419
+ id: step.id,
420
+ text: step.text,
421
+ keyword: step.keyword,
422
+ started_at: (new Date()).toISOString()
423
+ });
424
+ this._tests[uniqueId] = testMetaData;
425
+ }
426
+ async afterStep(step, scenario, result) {
427
+ this._cucumberData.steps.pop();
428
+ const uniqueId = getUniqueIdentifierForCucumber({ pickle: scenario });
429
+ const testMetaData = this._tests[uniqueId] || { steps: [] };
430
+ if (!testMetaData.steps) {
431
+ testMetaData.steps = [{
432
+ id: step.id,
433
+ text: step.text,
434
+ keyword: step.keyword,
435
+ finished_at: (new Date()).toISOString(),
436
+ result: result.passed ? 'PASSED' : 'FAILED',
437
+ duration: result.duration,
438
+ failure: result.error ? removeAnsiColors(result.error) : result.error
439
+ }];
440
+ }
441
+ const stepDetails = testMetaData.steps?.find(item => item.id === step.id);
442
+ if (stepDetails) {
443
+ stepDetails.finished_at = (new Date()).toISOString();
444
+ stepDetails.result = result.passed ? 'PASSED' : 'FAILED';
445
+ stepDetails.duration = result.duration;
446
+ stepDetails.failure = result.error ? removeAnsiColors(result.error) : result.error;
447
+ }
448
+ this._tests[uniqueId] = testMetaData;
449
+ }
450
+ /**
451
+ * misc methods
452
+ */
453
+ appendTestItemLog = async (stdLog) => {
454
+ try {
455
+ if (BrowserstackCLI.getInstance().isRunning()) {
456
+ await BrowserstackCLI.getInstance().getTestFramework().trackEvent(TestFrameworkState.LOG, HookState.POST, { logEntry: stdLog });
457
+ return;
458
+ }
459
+ if (this._currentHook.uuid && !this._currentHook.finished && (this._framework === 'mocha' || this._framework === 'cucumber')) {
460
+ stdLog.hook_run_uuid = this._currentHook.uuid;
461
+ }
462
+ else if (InsightsHandler.currentTest.uuid && (this._framework === 'mocha' || this._framework === 'cucumber')) {
463
+ stdLog.test_run_uuid = InsightsHandler.currentTest.uuid;
464
+ }
465
+ if (stdLog.hook_run_uuid || stdLog.test_run_uuid) {
466
+ this.listener.logCreated([stdLog]);
467
+ }
468
+ }
469
+ catch (error) {
470
+ BStackLogger.debug(`Exception in uploading log data to Test Reporting and Analytics with error : ${error}`);
471
+ }
472
+ };
473
+ async browserCommand(commandType, args, test) {
474
+ const dataKey = `${args.sessionId}_${args.method}_${args.endpoint}`;
475
+ if (commandType === 'client:beforeCommand') {
476
+ this._commands[dataKey] = args;
477
+ return;
478
+ }
479
+ if (!test) {
480
+ return;
481
+ }
482
+ const identifier = this.getIdentifier(test);
483
+ const testMeta = this._tests[identifier] || TestReporter.getTests()[identifier];
484
+ if (!testMeta) {
485
+ return;
486
+ }
487
+ // log screenshot
488
+ const body = 'body' in args ? args.body : undefined;
489
+ const result = 'result' in args ? args.result : undefined;
490
+ if (Boolean(process.env[TESTOPS_SCREENSHOT_ENV]) && isScreenshotCommand(args) && result?.value) {
491
+ /**
492
+ * SDK-6277: in the CLI/binary (v8) flow, forward the screenshot to the binary over gRPC
493
+ * as a TEST_SCREENSHOT log. The binary uploads it via its own authorized testhub session.
494
+ * The direct-HTTP listener.onScreenshot() path 401s in CLI mode (the worker's binary-issued
495
+ * JWT is not valid for direct collector POSTs).
496
+ */
497
+ await (BrowserstackCLI.getInstance().isRunning()
498
+ ? BrowserstackCLI.getInstance().getTestFramework().trackEvent(TestFrameworkState.LOG, HookState.POST, {
499
+ logEntry: {
500
+ kind: 'TEST_SCREENSHOT',
501
+ message: result.value,
502
+ level: 'INFO',
503
+ timestamp: new Date().toISOString()
504
+ }
505
+ })
506
+ : this.listener.onScreenshot([{
507
+ test_run_uuid: testMeta.uuid,
508
+ timestamp: new Date().toISOString(),
509
+ message: result.value,
510
+ kind: 'TEST_SCREENSHOT'
511
+ }]));
512
+ }
513
+ if (BrowserstackCLI.getInstance().isRunning()) {
514
+ /**
515
+ * SDK-6277: in CLI mode the binary captures command/network logs itself; the service only
516
+ * forwards the screenshot above. The direct-HTTP per-command logCreated() path 401s here.
517
+ */
518
+ return;
519
+ }
520
+ const requestData = this._commands[dataKey];
521
+ if (!requestData) {
522
+ return;
523
+ }
524
+ // log http request
525
+ this.listener.logCreated([{
526
+ test_run_uuid: testMeta.uuid,
527
+ timestamp: new Date().toISOString(),
528
+ kind: 'HTTP',
529
+ http_response: {
530
+ path: requestData.endpoint,
531
+ method: requestData.method,
532
+ body,
533
+ response: result
534
+ }
535
+ }]);
536
+ }
537
+ /*
538
+ * private methods
539
+ */
540
+ /**
541
+ * Check if any test steps failed (excluding hook failures)
542
+ * This is used when ignoreHooksStatus is true to determine test status based only on test steps
543
+ */
544
+ hasTestStepFailures(world) {
545
+ if (!world?.pickle) {
546
+ return false;
547
+ }
548
+ const uniqueId = getUniqueIdentifierForCucumber(world);
549
+ const testMetaData = this._tests[uniqueId];
550
+ if (!testMetaData?.steps) {
551
+ return false;
552
+ }
553
+ // Check if any step failed
554
+ return testMetaData.steps.some(step => step.result === 'FAILED');
555
+ }
556
+ attachHookData(context, hookId) {
557
+ if (context.currentTest && context.currentTest.parent) {
558
+ const parentTest = `${context.currentTest.parent.title} - ${context.currentTest.title}`;
559
+ if (!this._hooks[parentTest]) {
560
+ this._hooks[parentTest] = [];
561
+ }
562
+ this._hooks[parentTest].push(hookId);
563
+ return;
564
+ }
565
+ else if (context.test) {
566
+ this.setHooksFromSuite(context.test.parent, hookId);
567
+ }
568
+ }
569
+ setHooksFromSuite(parent, hookId) {
570
+ if (!parent) {
571
+ return false;
572
+ }
573
+ if (parent.tests && parent.tests.length > 0) {
574
+ const uniqueIdentifier = getUniqueIdentifier(parent.tests[0], this._framework);
575
+ if (!this._hooks[uniqueIdentifier]) {
576
+ this._hooks[uniqueIdentifier] = [];
577
+ }
578
+ this._hooks[uniqueIdentifier].push(hookId);
579
+ return true;
580
+ }
581
+ for (const suite of parent.suites) {
582
+ const result = this.setHooksFromSuite(suite, hookId);
583
+ if (result) {
584
+ return true;
585
+ }
586
+ }
587
+ return false;
588
+ }
589
+ /*
590
+ * Get hierarchy info
591
+ */
592
+ getHierarchy(test) {
593
+ const value = [];
594
+ if (test.ctx && test.ctx.test) {
595
+ // If we already have the parent object, utilize it else get from context
596
+ let parent = typeof test.parent === 'object' ? test.parent : test.ctx.test.parent;
597
+ while (parent && parent.title !== '') {
598
+ value.push(parent.title);
599
+ parent = parent.parent;
600
+ }
601
+ }
602
+ else if (test.description && test.fullName) {
603
+ // for Jasmine
604
+ value.push(test.description);
605
+ // Strip the trailing " <description>" suffix without building a regex from
606
+ // a variable (avoids ReDoS and regex-metacharacter mismatches in descriptions).
607
+ const descSuffix = ` ${test.description}`;
608
+ value.push(test.fullName.endsWith(descSuffix) ? test.fullName.slice(0, -descSuffix.length) : test.fullName);
609
+ }
610
+ return value.reverse();
611
+ }
612
+ getRunData(test, eventType, results) {
613
+ const fullTitle = getUniqueIdentifier(test, this._framework);
614
+ const testMetaData = this._tests[fullTitle];
615
+ const filename = test.file || this._suiteFile;
616
+ this.currentTestId = testMetaData.uuid;
617
+ if (eventType === 'TestRunStarted') {
618
+ InsightsHandler.currentTest.name = test.title || test.description;
619
+ }
620
+ const testData = {
621
+ uuid: testMetaData.uuid,
622
+ type: test.type || 'test',
623
+ name: test.title || test.description,
624
+ body: {
625
+ lang: 'webdriverio',
626
+ code: test.body
627
+ },
628
+ scope: fullTitle,
629
+ scopes: this.getHierarchy(test),
630
+ identifier: fullTitle,
631
+ file_name: filename ? path.relative(process.cwd(), filename) : undefined,
632
+ location: filename ? path.relative(process.cwd(), filename) : undefined,
633
+ vc_filepath: (this._gitConfigPath && filename) ? path.relative(this._gitConfigPath, filename) : undefined,
634
+ started_at: testMetaData.startedAt,
635
+ finished_at: testMetaData.finishedAt,
636
+ result: 'pending',
637
+ framework: this._framework
638
+ };
639
+ if ((eventType === 'TestRunFinished' || eventType === 'HookRunFinished') && results) {
640
+ const { error, passed } = results;
641
+ if (!passed) {
642
+ testData.result = (error && error.message && error.message.includes('sync skip; aborting execution')) ? 'ignore' : 'failed';
643
+ if (error && testData.result !== 'skipped') {
644
+ testData.failure = [{ backtrace: [removeAnsiColors(error.message), removeAnsiColors(error.stack || '')] }]; // add all errors here
645
+ testData.failure_reason = removeAnsiColors(error.message);
646
+ testData.failure_type = isUndefined(error.message) ? null : error.message.toString().match(/AssertionError/) ? 'AssertionError' : 'UnhandledError'; //verify if this is working
647
+ }
648
+ }
649
+ else {
650
+ testData.result = 'passed';
651
+ }
652
+ testData.retries = results.retries;
653
+ testData.duration_in_ms = results.duration;
654
+ if (this._hooks[fullTitle]) {
655
+ testData.hooks = this._hooks[fullTitle];
656
+ }
657
+ }
658
+ if (eventType === 'TestRunStarted' || eventType === 'TestRunSkipped' || eventType === 'HookRunStarted') {
659
+ testData.integrations = {};
660
+ if (this._browser && this._platformMeta) {
661
+ const provider = getCloudProvider(this._browser);
662
+ testData.integrations[provider] = this.getIntegrationsObject();
663
+ }
664
+ }
665
+ if (eventType === 'TestRunSkipped') {
666
+ if (this._hooks[fullTitle]) {
667
+ testData.hooks = this._hooks[fullTitle];
668
+ }
669
+ testData.result = 'skipped';
670
+ eventType = 'TestRunFinished';
671
+ }
672
+ /* istanbul ignore if */
673
+ if (eventType.match(/HookRun/)) {
674
+ testData.hook_type = testData.name?.toLowerCase() ? getHookType(testData.name.toLowerCase()) : 'undefined';
675
+ testData.test_run_id = this.getTestRunId(test.ctx);
676
+ }
677
+ return testData;
678
+ }
679
+ getTestRunId(context) {
680
+ if (!context) {
681
+ return;
682
+ }
683
+ if (context.currentTest) {
684
+ const uniqueIdentifier = getUniqueIdentifier(context.currentTest, this._framework);
685
+ return this._tests[uniqueIdentifier] && this._tests[uniqueIdentifier].uuid;
686
+ }
687
+ if (!context.test) {
688
+ return;
689
+ }
690
+ return this.getTestRunIdFromSuite(context.test.parent);
691
+ }
692
+ getTestRunIdFromSuite(parent) {
693
+ if (!parent) {
694
+ return;
695
+ }
696
+ for (const test of parent.tests) {
697
+ const uniqueIdentifier = getUniqueIdentifier(test, this._framework);
698
+ if (this._tests[uniqueIdentifier]) {
699
+ return this._tests[uniqueIdentifier].uuid;
700
+ }
701
+ }
702
+ for (const suite of parent.suites) {
703
+ const testRunId = this.getTestRunIdFromSuite(suite);
704
+ if (testRunId) {
705
+ return testRunId;
706
+ }
707
+ }
708
+ return;
709
+ }
710
+ getTestRunDataForCucumber(worldObj, eventType, testMetaData = null) {
711
+ const world = worldObj;
712
+ const dataHub = testMetaData ? testMetaData : (this._tests[getUniqueIdentifierForCucumber(world)] || {});
713
+ const { feature, scenario, steps, uuid, startedAt, finishedAt } = dataHub;
714
+ const examples = !testMetaData ? getScenarioExamples(world) : undefined;
715
+ let fullNameWithExamples;
716
+ if (!testMetaData) {
717
+ fullNameWithExamples = examples
718
+ ? world.pickle.name + ' (' + examples.join(', ') + ')'
719
+ : world.pickle.name;
720
+ }
721
+ else {
722
+ fullNameWithExamples = scenario?.name || '';
723
+ }
724
+ this.currentTestId = uuid;
725
+ if (eventType === 'TestRunStarted') {
726
+ InsightsHandler.currentTest.name = fullNameWithExamples;
727
+ }
728
+ const testData = {
729
+ uuid: uuid,
730
+ started_at: startedAt,
731
+ finished_at: finishedAt,
732
+ type: 'test',
733
+ body: {
734
+ lang: 'webdriverio',
735
+ code: null
736
+ },
737
+ name: fullNameWithExamples,
738
+ scope: fullNameWithExamples,
739
+ scopes: [feature?.name || ''],
740
+ identifier: scenario?.name,
741
+ file_name: feature && feature.path ? path.relative(process.cwd(), feature.path) : undefined,
742
+ location: feature && feature.path ? path.relative(process.cwd(), feature.path) : undefined,
743
+ vc_filepath: (this._gitConfigPath && feature?.path) ? path.relative(this._gitConfigPath, feature?.path) : undefined,
744
+ framework: this._framework,
745
+ result: 'pending',
746
+ meta: {
747
+ feature: feature,
748
+ scenario: scenario,
749
+ steps: steps,
750
+ examples: examples
751
+ }
752
+ };
753
+ if (eventType === 'TestRunStarted' || eventType === 'TestRunSkipped') {
754
+ testData.integrations = {};
755
+ if (this._browser && this._platformMeta) {
756
+ const provider = getCloudProvider(this._browser);
757
+ testData.integrations[provider] = this.getIntegrationsObject();
758
+ }
759
+ }
760
+ /* istanbul ignore if */
761
+ if (world?.result) {
762
+ let result = world.result.status.toLowerCase();
763
+ if (result !== 'passed' && result !== 'failed') {
764
+ result = 'skipped'; // mark UNKNOWN/UNDEFINED/AMBIGUOUS/PENDING as skipped
765
+ }
766
+ // Handle ignoreHooksStatus: when enabled and scenario failed, check if it's due to hook failures only
767
+ const ignoreHooksStatus = this._options?.testObservabilityOptions?.ignoreHooksStatus === true;
768
+ if (ignoreHooksStatus && result === 'failed' && world) {
769
+ // Check if any test steps failed (excluding hook failures)
770
+ const hasTestStepFailures = this.hasTestStepFailures(world);
771
+ if (!hasTestStepFailures) {
772
+ // Only hooks failed, override result to passed for Test Observability
773
+ result = 'passed';
774
+ }
775
+ }
776
+ testData.finished_at = (new Date()).toISOString();
777
+ testData.result = result;
778
+ testData.duration_in_ms = world.result.duration.seconds * 1000 + world.result.duration.nanos / 1000000; // send duration in ms
779
+ if (result === 'failed') {
780
+ testData.failure = [
781
+ {
782
+ 'backtrace': [world.result.message ? removeAnsiColors(world.result.message) : 'unknown']
783
+ }
784
+ ];
785
+ testData.failure_reason = world.result.message ? removeAnsiColors(world.result.message) : world.result.message;
786
+ if (world.result.message) {
787
+ testData.failure_type = world.result.message.match(/AssertionError/)
788
+ ? 'AssertionError'
789
+ : 'UnhandledError';
790
+ }
791
+ }
792
+ }
793
+ if (world?.pickle) {
794
+ testData.tags = world.pickle.tags.map(({ name }) => (name));
795
+ }
796
+ if (eventType === 'TestRunSkipped') {
797
+ testData.result = 'skipped';
798
+ eventType = 'TestRunFinished';
799
+ }
800
+ return testData;
801
+ }
802
+ async flushCBTDataQueue() {
803
+ BStackLogger.debug(`Flushing CBT Data Queue ${this.currentTestId}`);
804
+ if (isUndefined(this.currentTestId)) {
805
+ return;
806
+ }
807
+ this.cbtQueue.forEach(cbtData => {
808
+ cbtData.uuid = this.currentTestId;
809
+ this.listener.cbtSessionCreated(cbtData);
810
+ });
811
+ this.currentTestId = undefined; // set undefined for next test
812
+ }
813
+ async sendCBTInfo() {
814
+ const integrationsData = {};
815
+ if (this._browser && this._platformMeta) {
816
+ const provider = getCloudProvider(this._browser);
817
+ integrationsData[provider] = this.getIntegrationsObject();
818
+ }
819
+ const cbtData = {
820
+ uuid: '',
821
+ integrations: integrationsData
822
+ };
823
+ BStackLogger.debug(`Sending CBT Data ${this.currentTestId} ${JSON.stringify(cbtData)}`);
824
+ if (this.currentTestId !== undefined) {
825
+ cbtData.uuid = this.currentTestId;
826
+ this.listener.cbtSessionCreated(cbtData);
827
+ }
828
+ else {
829
+ this.cbtQueue.push(cbtData);
830
+ }
831
+ }
832
+ getIntegrationsObject() {
833
+ const caps = this._browser?.capabilities;
834
+ const sessionId = this._browser?.sessionId;
835
+ return {
836
+ capabilities: caps,
837
+ session_id: sessionId,
838
+ browser: caps?.browserName,
839
+ browser_version: caps?.browserVersion,
840
+ platform: caps?.platformName,
841
+ product: this._platformMeta?.product,
842
+ platform_version: getPlatformVersion(caps, this._userCaps)
843
+ };
844
+ }
845
+ getIdentifier(test) {
846
+ if ('pickle' in test) {
847
+ return getUniqueIdentifierForCucumber(test);
848
+ }
849
+ return getUniqueIdentifier(test, this._framework);
850
+ }
851
+ }
852
+ // https://github.com/microsoft/TypeScript/issues/6543
853
+ const InsightsHandler = o11yClassErrorHandler(_InsightsHandler);
854
+ export default InsightsHandler;
855
+ //# sourceMappingURL=insights-handler.js.map