@xenon-device-management/xenon 2.14.0 → 2.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/README.md +44 -29
  2. package/lib/package.json +6 -5
  3. package/lib/public/assets/{AnnotationOverlay-B_VNtKBH.js → AnnotationOverlay-2W4JvEyZ.js} +1 -1
  4. package/lib/public/assets/{ApiKeyGate-BwuaMjkX.js → ApiKeyGate-GmckX_7r.js} +1 -1
  5. package/lib/public/assets/DeviceMosaicView-DqODzkyo.js +2 -0
  6. package/lib/public/assets/{EmptyState-BSjhk_IV.js → EmptyState-7hETKEWb.js} +1 -1
  7. package/lib/public/assets/{FieldGroup-Cb5QZsE1.js → FieldGroup-GOgssFEP.js} +1 -1
  8. package/lib/public/assets/{FilterMenu-BAaSlWbW.js → FilterMenu-Yvf6RGMd.js} +1 -1
  9. package/lib/public/assets/{Menu-JGGYrSQQ.js → Menu-KrPnfdsf.js} +1 -1
  10. package/lib/public/assets/{Modal-CFCp3n9d.js → Modal-C6WjZFeI.js} +1 -1
  11. package/lib/public/assets/{RecordingPage-CbJj_EBT.js → RecordingPage-CSRIhZEh.js} +1 -1
  12. package/lib/public/assets/{RecordingsPage-BJeYnygR.js → RecordingsPage-CB8ST3nE.js} +1 -1
  13. package/lib/public/assets/{SegmentedControl-Bg42Atjy.js → SegmentedControl-C9o90KId.js} +1 -1
  14. package/lib/public/assets/{SettingCard-Njz1pUIJ.js → SettingCard-CPm3w9l2.js} +1 -1
  15. package/lib/public/assets/{Table-BcTm5xV2.js → Table-DwIgERRt.js} +1 -1
  16. package/lib/public/assets/{activity-DOVdsfMQ.js → activity-DrLbHifn.js} +1 -1
  17. package/lib/public/assets/ai-settings-DU_Ls06P.js +16 -0
  18. package/lib/public/assets/{api-keys-BKnNSTw9.js → api-keys-CWYdEzJ_.js} +1 -1
  19. package/lib/public/assets/{apps-D0SC6uw6.js → apps-BqNVdln7.js} +1 -1
  20. package/lib/public/assets/{arrow-left-MMbTmgoE.js → arrow-left-BtB0_Jg_.js} +1 -1
  21. package/lib/public/assets/{arrow-right-BMfnGmxW.js → arrow-right-C8NhdtyN.js} +1 -1
  22. package/lib/public/assets/{arrow-up-right-DuAftMWB.js → arrow-up-right-gMg0ffvv.js} +1 -1
  23. package/lib/public/assets/{auth-shell-nZC2JNDO.js → auth-shell-CiYZ3N4M.js} +2 -2
  24. package/lib/public/assets/{builds-page-hDCgTwkS.js → builds-page-EaaqDmLN.js} +1 -1
  25. package/lib/public/assets/{button-Dda4irn_.js → button-DDau4Axv.js} +1 -1
  26. package/lib/public/assets/{calendar-BMr0Q62v.js → calendar-DQ83B1ed.js} +1 -1
  27. package/lib/public/assets/{check-CZ9iw9-e.js → check-K1ovTQam.js} +1 -1
  28. package/lib/public/assets/{chevron-right-BRYJJVC9.js → chevron-right-Biq00pMt.js} +1 -1
  29. package/lib/public/assets/{circle-check-CqAJjQ5O.js → circle-check-CBv88cH7.js} +1 -1
  30. package/lib/public/assets/{circle-x-DlDJtnVz.js → circle-x-Bmltailn.js} +1 -1
  31. package/lib/public/assets/{clock-CpuDKysU.js → clock-BPrld-3h.js} +1 -1
  32. package/lib/public/assets/{copy-CAQydNFe.js → copy-t0QB14JI.js} +1 -1
  33. package/lib/public/assets/derive-UnLRp4r3.js +1 -0
  34. package/lib/public/assets/device-explorer-BgGZ8AvV.js +282 -0
  35. package/lib/public/assets/{download-C9XG6lLv.js → download-BroHxNOh.js} +1 -1
  36. package/lib/public/assets/forgot-password-Bjmf49i6.js +1 -0
  37. package/lib/public/assets/{h264Stream-VQBphYhg.js → h264Stream-DeD__6wa.js} +6 -6
  38. package/lib/public/assets/{index-DJGc1M2f.js → index-72oF89LJ.js} +1 -1
  39. package/lib/public/assets/index-B0JeuZbg.js +15 -0
  40. package/lib/public/assets/index-B0gQkEg0.js +219 -0
  41. package/lib/public/assets/{input-Cm0tmSoc.js → input-DIc4MSC7.js} +1 -1
  42. package/lib/public/assets/{line-chart-D2DyJCml.js → line-chart-BcWm2vW8.js} +1 -1
  43. package/lib/public/assets/{list-checks-UWf1GY99.js → list-checks-B06IJJad.js} +1 -1
  44. package/lib/public/assets/{lock-CCJv9m_I.js → lock-pVLvnN30.js} +1 -1
  45. package/lib/public/assets/{login-f8vOVW-U.js → login-CN77vy--.js} +1 -1
  46. package/lib/public/assets/{maintenance-settings-C1PrkuAH.js → maintenance-settings-CSdzQSkY.js} +1 -1
  47. package/lib/public/assets/{monitor-B2jDgwm7.js → monitor-BtrN5IkI.js} +1 -1
  48. package/lib/public/assets/{mouse-pointer-2-DLI1RtzU.js → mouse-pointer-2-CNsQfo6w.js} +1 -1
  49. package/lib/public/assets/{network-ebT86qoi.js → network-BSfA8ZxG.js} +1 -1
  50. package/lib/public/assets/{overview-C0aykVrd.js → overview-BfTebCK5.js} +1 -1
  51. package/lib/public/assets/{page-header-BjzgWz-T.js → page-header-D8KUX6Qg.js} +1 -1
  52. package/lib/public/assets/{play-DLG9t8FU.js → play-Cvc8xzic.js} +1 -1
  53. package/lib/public/assets/{plus-nBFSMu_X.js → plus-C9Zj84UF.js} +1 -1
  54. package/lib/public/assets/{profile-page-CSl0UYlv.js → profile-page-Bi27STgs.js} +1 -1
  55. package/lib/public/assets/{reset-password-LeOuHqmB.js → reset-password-CPsOuwSu.js} +1 -1
  56. package/lib/public/assets/runbook-page-DEQ-DBOX.js +304 -0
  57. package/lib/public/assets/{select-88jHcl-Z.js → select-BWpnhWXj.js} +1 -1
  58. package/lib/public/assets/{selector-detail-redirect-BbXKJjZM.js → selector-detail-redirect-BefSCSpe.js} +1 -1
  59. package/lib/public/assets/{selector-health-page-B4rA1Xkv.js → selector-health-page-MSk96Zz7.js} +1 -1
  60. package/lib/public/assets/session-detail-page-DPyBNef_.js +4 -0
  61. package/lib/public/assets/{settings-Dep_3kbA.css → settings-K8UHWMdI.css} +1 -1
  62. package/lib/public/assets/{settings-GF6ZMSQ6.js → settings-V6x-cXwy.js} +1 -1
  63. package/lib/public/assets/{stat-tile-BV98e_Sv.js → stat-tile-Duex-GZD.js} +1 -1
  64. package/lib/public/assets/{tablet-BiAsnFDj.js → tablet-BBcoqBmL.js} +1 -1
  65. package/lib/public/assets/{teams-B4-jrr-1.js → teams-ts8p5rEh.js} +1 -1
  66. package/lib/public/assets/{trash-2-C5RSuX0e.js → trash-2-CZ2ew6AZ.js} +1 -1
  67. package/lib/public/assets/{upload-BG7_rMXK.js → upload-Dv99y_le.js} +1 -1
  68. package/lib/public/assets/{use-builds-data-J-riQYyC.js → use-builds-data-DrYC8JCF.js} +1 -1
  69. package/lib/public/assets/{use-password-reset-mode-C8-eH-UG.js → use-password-reset-mode-C7QkBNkP.js} +1 -1
  70. package/lib/public/assets/{users-CFba5TSy.js → users-D7tKMESH.js} +1 -1
  71. package/lib/public/assets/{users-Cp0beJm3.js → users-gJ5Y01yo.js} +1 -1
  72. package/lib/public/assets/{video-off-DDsEISjV.js → video-off-CqZVuZGs.js} +1 -1
  73. package/lib/public/assets/{webhook-settings-BGu32vSB.js → webhook-settings-DI5DSW99.js} +1 -1
  74. package/lib/public/assets/{zap-l4VaXzq8.js → zap-ClcHFxfo.js} +1 -1
  75. package/lib/public/index.html +1 -1
  76. package/lib/schema.json +16 -12
  77. package/lib/src/InternalHttpClient.js +27 -5
  78. package/lib/src/XenonCapabilityManager.js +10 -3
  79. package/lib/src/app/dashboardPluginLink.js +117 -0
  80. package/lib/src/app/index.js +13 -34
  81. package/lib/src/app/openapi/control.yaml +6 -4
  82. package/lib/src/app/openapi/grid.yaml +8 -2
  83. package/lib/src/app/openapi/identity.yaml +103 -31
  84. package/lib/src/app/openapi/platform.yaml +33 -15
  85. package/lib/src/app/openapi/sessions.yaml +12 -3
  86. package/lib/src/app/routers/apikeys.js +14 -2
  87. package/lib/src/app/routers/auth.js +31 -6
  88. package/lib/src/app/routers/control.js +13 -5
  89. package/lib/src/app/routers/dashboard.js +42 -27
  90. package/lib/src/app/routers/grid.js +5 -3
  91. package/lib/src/app/routers/profile.js +21 -0
  92. package/lib/src/app/routers/users.js +12 -6
  93. package/lib/src/app/swagger.js +2 -0
  94. package/lib/src/app/ws/h264StreamWs.js +10 -1
  95. package/lib/src/app/ws/nodeSocketRelay.js +77 -11
  96. package/lib/src/config.js +1 -0
  97. package/lib/src/dashboard/commandLogFields.js +42 -0
  98. package/lib/src/dashboard/commands.js +5 -5
  99. package/lib/src/dashboard/event-manager.js +64 -204
  100. package/lib/src/dashboard/services/failure-analysis-service.js +118 -105
  101. package/lib/src/dashboard/services/failureCategories.js +264 -0
  102. package/lib/src/dashboard/sessionCommandSummary.js +39 -0
  103. package/lib/src/data-service/device-store.js +14 -1
  104. package/lib/src/data-service/prisma-store.js +19 -7
  105. package/lib/src/data-service/web-config-service.js +7 -0
  106. package/lib/src/device-managers/AndroidDeviceManager.js +61 -23
  107. package/lib/src/device-managers/IOSDeviceManager.js +10 -0
  108. package/lib/src/device-managers/android/AndroidH264StreamService.js +69 -20
  109. package/lib/src/device-managers/android/H264Multiplexer.js +38 -4
  110. package/lib/src/device-utils.js +29 -3
  111. package/lib/src/gateway/defaultGateway.js +1 -1
  112. package/lib/src/gateway/forwardToNode.js +6 -11
  113. package/lib/src/gateway/healReport.js +87 -0
  114. package/lib/src/gateway/hubSessionToken.js +27 -4
  115. package/lib/src/gateway/nodeWebDriverUrl.js +3 -5
  116. package/lib/src/gateway/sessionGateway.js +12 -2
  117. package/lib/src/generated/client/edge.js +4 -3
  118. package/lib/src/generated/client/index-browser.js +1 -0
  119. package/lib/src/generated/client/index.d.ts +32 -0
  120. package/lib/src/generated/client/index.js +4 -3
  121. package/lib/src/generated/client/package.json +1 -1
  122. package/lib/src/generated/client/schema.prisma +3 -0
  123. package/lib/src/generated/client/wasm.js +1 -0
  124. package/lib/src/helpers/outboundProxy.js +219 -0
  125. package/lib/src/index.js +21 -0
  126. package/lib/src/interceptors/CommandInterceptor.js +256 -196
  127. package/lib/src/interceptors/xenonScripts.js +28 -7
  128. package/lib/src/logger.js +3 -1
  129. package/lib/src/middleware/authMiddleware.js +15 -3
  130. package/lib/src/middleware/commandCaller.js +3 -3
  131. package/lib/src/middleware/verifyCredential.js +58 -1
  132. package/lib/src/plugin.js +16 -6
  133. package/lib/src/scripts/database-check.js +3 -3
  134. package/lib/src/scripts/initialize-database.js +35 -33
  135. package/lib/src/scripts/run-migrations.js +1079 -27
  136. package/lib/src/services/AICommandService.js +9 -2
  137. package/lib/src/services/AIService.js +192 -42
  138. package/lib/src/services/ApiKeyService.js +16 -5
  139. package/lib/src/services/EmailService.js +29 -0
  140. package/lib/src/services/InterceptorService.js +6 -2
  141. package/lib/src/services/OrphanSweeper.js +8 -0
  142. package/lib/src/services/SelectorStateService.js +11 -7
  143. package/lib/src/services/SelectorVerificationJob.js +21 -6
  144. package/lib/src/services/ServerManager.js +11 -1
  145. package/lib/src/services/SessionLifecycleService.js +118 -44
  146. package/lib/src/services/SocketClient.js +11 -0
  147. package/lib/src/services/SocketServer.js +380 -45
  148. package/lib/src/services/TeamService.js +5 -1
  149. package/lib/src/services/TracingService.js +171 -60
  150. package/lib/src/services/UserService.js +14 -3
  151. package/lib/src/services/UserSessionService.js +16 -4
  152. package/lib/src/services/bug-report/video-slice.js +4 -1
  153. package/lib/src/services/device-access/leaseOverride.js +7 -5
  154. package/lib/src/services/healing/FuzzyXmlHealingProvider.js +51 -0
  155. package/lib/src/services/healing/HealEtalonService.js +3 -2
  156. package/lib/src/services/healing/HealingOrchestrator.js +57 -72
  157. package/lib/src/services/healing/OcrHealingProvider.js +41 -64
  158. package/lib/src/services/healing/ResilioTreeHealingProvider.js +41 -50
  159. package/lib/src/services/healing/VisualAiHealingProvider.js +1 -1
  160. package/lib/src/services/healing/fingerprintIdentity.js +126 -0
  161. package/lib/src/services/healing/resilioPath.js +274 -0
  162. package/lib/src/services/identity/identityChanges.js +41 -0
  163. package/lib/src/services/logcat/SessionDeviceLogs.js +358 -0
  164. package/lib/src/services/logcat/deviceLogBook.js +164 -0
  165. package/lib/src/services/logcat/iosDriverLog.js +178 -0
  166. package/lib/src/services/ocr/ocrData.js +118 -0
  167. package/lib/src/services/ocr/vendor/LICENSE-tessdata.txt +202 -0
  168. package/lib/src/services/ocr/vendor/eng.traineddata.gz +0 -0
  169. package/lib/src/services/omni-vision/OmniVisionService.js +83 -56
  170. package/lib/src/services/passwordResetLink.js +50 -9
  171. package/lib/src/services/passwordResetMode.js +3 -1
  172. package/lib/src/services/publicUrl.js +45 -0
  173. package/lib/src/services/selector-health/SelectorVisibilityResolver.js +121 -0
  174. package/lib/src/services/session/sessionIdentity.js +5 -3
  175. package/lib/src/services/session/shutdownReason.js +26 -0
  176. package/lib/src/services/settings/aiEngineSettings.js +219 -0
  177. package/lib/src/services/token/JwtKeyService.js +8 -2
  178. package/lib/src/services/token/mcpTokenTtl.js +27 -0
  179. package/lib/src/services/token/mintedLifetime.js +51 -0
  180. package/lib/src/sessions/LocalSession.js +29 -1
  181. package/lib/src/sessions/RemoteSession.js +12 -2
  182. package/lib/src/sessions/SessionManager.js +4 -1
  183. package/lib/src/sessions/sessionMemory.js +25 -0
  184. package/lib/test/helpers/driver-log.js +38 -0
  185. package/lib/test/helpers/fake-ai-provider.js +188 -0
  186. package/lib/test/helpers/fake-http-proxy.js +106 -0
  187. package/lib/test/helpers/loki-stores.js +33 -0
  188. package/lib/test/helpers/otlp-probe.js +107 -0
  189. package/lib/test/helpers/page-sources.js +164 -0
  190. package/lib/test/helpers/scratch-database.js +57 -13
  191. package/lib/test/integration/androidDevices.spec.js +1 -1
  192. package/lib/test/integration/audit-ingest.spec.js +10 -10
  193. package/lib/test/integration/auth-flow.spec.js +9 -14
  194. package/lib/test/integration/auth-rate-limit.spec.js +6 -3
  195. package/lib/test/integration/bug-report-route.spec.js +22 -2
  196. package/lib/test/integration/forgot-password-rate-limit.spec.js +6 -3
  197. package/lib/test/integration/forgot-password.spec.js +17 -18
  198. package/lib/test/integration/ios/01iOSSimulator.spec.js +1 -1
  199. package/lib/test/integration/ios/02iOSDevices.spec.js +1 -1
  200. package/lib/test/integration/legacy-deprecation-headers.spec.js +5 -2
  201. package/lib/test/integration/migration.spec.js +6 -1
  202. package/lib/test/integration/node-pair-auth-rest.spec.js +5 -9
  203. package/lib/test/integration/ports-router.spec.js +4 -4
  204. package/lib/test/integration/prisma-device-store-add-devices.spec.js +2 -3
  205. package/lib/test/integration/prisma-device-store-team-filter.spec.js +2 -5
  206. package/lib/test/integration/profile-tokens.spec.js +10 -16
  207. package/lib/test/integration/recordings-router.spec.js +16 -16
  208. package/lib/test/integration/reset-revokes-sessions.spec.js +4 -9
  209. package/lib/test/integration/role-matrix.spec.js +4 -9
  210. package/lib/test/integration/sdk-leases-router.spec.js +9 -9
  211. package/lib/test/integration/sdk-version.spec.js +2 -2
  212. package/lib/test/integration/selector-health-endpoint.spec.js +5 -5
  213. package/lib/test/integration/selector-state-flow.spec.js +5 -5
  214. package/lib/test/integration/team-membership-migration.spec.js +73 -12
  215. package/lib/test/integration/team-visibility-apps.spec.js +15 -22
  216. package/lib/test/integration/team-visibility-control.spec.js +96 -69
  217. package/lib/test/integration/team-visibility-dashboard.spec.js +7 -23
  218. package/lib/test/integration/team-visibility-grid.spec.js +16 -15
  219. package/lib/test/integration/team-visibility-leases.spec.js +8 -11
  220. package/lib/test/integration/team-visibility-recordings.spec.js +6 -19
  221. package/lib/test/integration/team-visibility-sessions.spec.js +6 -19
  222. package/lib/test/integration/users-flow.spec.js +7 -12
  223. package/lib/test/integration/users-lockout.spec.js +5 -3
  224. package/lib/test/unit/EmailService.test.js +66 -0
  225. package/lib/test/unit/ResilioTreeHealingProvider.test.js +46 -19
  226. package/lib/test/unit/XenonCapabilityManager.spec.js +23 -0
  227. package/lib/test/unit/ai-call-time-limit.spec.js +130 -0
  228. package/lib/test/unit/ai-engine-settings.spec.js +338 -0
  229. package/lib/test/unit/ai-provider-switch.spec.js +130 -0
  230. package/lib/test/unit/ai-rate-limit.spec.js +126 -0
  231. package/lib/test/unit/android-adb-retry.spec.js +112 -0
  232. package/lib/test/unit/android-h264-viewers.spec.js +272 -0
  233. package/lib/test/unit/android-preview-hold.spec.js +3 -2
  234. package/lib/test/unit/apikeys-router.spec.js +84 -0
  235. package/lib/test/unit/authMiddleware.bearer.spec.js +9 -0
  236. package/lib/test/unit/authMiddleware.credentialLifetime.spec.js +149 -0
  237. package/lib/test/unit/bug-report/video-slice.spec.js +19 -0
  238. package/lib/test/unit/command-log-network-capture.spec.js +191 -0
  239. package/lib/test/unit/dashboard-events-device-logs.spec.js +255 -0
  240. package/lib/test/unit/dashboard-events-team-scope.spec.js +6 -3
  241. package/lib/test/unit/dashboard-plugin-link.spec.js +213 -0
  242. package/lib/test/unit/database-check.spec.js +1 -0
  243. package/lib/test/unit/device-log-book.spec.js +180 -0
  244. package/lib/test/unit/device-utils.spec.js +13 -4
  245. package/lib/test/unit/enable-dashboard-option.spec.js +57 -0
  246. package/lib/test/unit/event-log-session-data.spec.js +251 -0
  247. package/lib/test/unit/failure-analysis.spec.js +196 -0
  248. package/lib/test/unit/failure-categories.spec.js +130 -0
  249. package/lib/test/unit/failure-categorization.spec.js +393 -0
  250. package/lib/test/unit/fingerprint-identity.spec.js +433 -0
  251. package/lib/test/unit/h264-multiplexer.spec.js +46 -0
  252. package/lib/test/unit/heal-report.spec.js +71 -0
  253. package/lib/test/unit/healing-instrumentation.spec.js +6 -1
  254. package/lib/test/unit/healing-tier-gate.spec.js +74 -29
  255. package/lib/test/unit/healing-tiers-every-session.spec.js +219 -0
  256. package/lib/test/unit/hub-node-gateway-appium-server.spec.js +243 -1
  257. package/lib/test/unit/interceptor-visual-elements.spec.js +192 -0
  258. package/lib/test/unit/ios-driver-log.spec.js +168 -0
  259. package/lib/test/unit/ios-simulator-session-readiness.spec.js +187 -0
  260. package/lib/test/unit/ios-simulator-session-teardown.spec.js +158 -0
  261. package/lib/test/unit/lease/lease-override.spec.js +23 -6
  262. package/lib/test/unit/lease/session-lease-proof.spec.js +16 -4
  263. package/lib/test/unit/logcat-ws.spec.js +78 -37
  264. package/lib/test/unit/logger-json-levels.spec.js +44 -0
  265. package/lib/test/unit/mcp-token-ttl.spec.js +38 -0
  266. package/lib/test/unit/observability-example-loki.spec.js +25 -0
  267. package/lib/test/unit/ocr-healing-tier-real-ocr.spec.js +113 -0
  268. package/lib/test/unit/ocr-healing-tier.spec.js +122 -0
  269. package/lib/test/unit/ocr-offline.spec.js +213 -0
  270. package/lib/test/unit/omni-virtual-element-store.spec.js +301 -0
  271. package/lib/test/unit/omni-vision-ios-points.spec.js +0 -2
  272. package/lib/test/unit/omni-vision.spec.js +5 -7
  273. package/lib/test/unit/openapi-for-site.spec.js +80 -0
  274. package/lib/test/unit/outbound-proxy.spec.js +320 -0
  275. package/lib/test/unit/package-engines.spec.js +27 -0
  276. package/lib/test/unit/passwordResetMode.test.js +10 -2
  277. package/lib/test/unit/port-exhaustion.spec.js +2 -1
  278. package/lib/test/unit/profile-router.test.js +122 -1
  279. package/lib/test/unit/proxy-option.spec.js +358 -0
  280. package/lib/test/unit/queue-requester.spec.js +1 -1
  281. package/lib/test/unit/reset-link-address.spec.js +266 -0
  282. package/lib/test/unit/reset-link.test.js +10 -4
  283. package/lib/test/unit/resilio-path-healing.spec.js +270 -0
  284. package/lib/test/unit/run-migrations-database.spec.js +807 -0
  285. package/lib/test/unit/run-migrations-fail-fast.test.js +41 -27
  286. package/lib/test/unit/run-migrations.test.js +864 -0
  287. package/lib/test/unit/selector-health-actions.spec.js +3 -1
  288. package/lib/test/unit/selector-learning-every-session.spec.js +179 -0
  289. package/lib/test/unit/selector-state-service.spec.js +72 -23
  290. package/lib/test/unit/selector-verification-clean-build.spec.js +130 -0
  291. package/lib/test/unit/selector-verification-job.spec.js +28 -19
  292. package/lib/test/unit/selector-verification-missed-regression.spec.js +2 -2
  293. package/lib/test/unit/selector-visibility-resolver.spec.js +209 -0
  294. package/lib/test/unit/self-healing-boot.spec.js +10 -5
  295. package/lib/test/unit/self-healing-setting.spec.js +7 -6
  296. package/lib/test/unit/session-app-resolution.spec.js +5 -1
  297. package/lib/test/unit/session-attribution-wiring.spec.js +41 -13
  298. package/lib/test/unit/session-create-credential-owner.spec.js +242 -0
  299. package/lib/test/unit/session-credential-strip.spec.js +1 -1
  300. package/lib/test/unit/session-device-logs-ios.spec.js +180 -0
  301. package/lib/test/unit/session-device-logs.spec.js +356 -0
  302. package/lib/test/unit/session-end-failure-analysis.spec.js +133 -0
  303. package/lib/test/unit/session-log-indexes.spec.js +64 -3
  304. package/lib/test/unit/socket-team-scope.integration.spec.js +749 -0
  305. package/lib/test/unit/socketServer.scopedEmit.spec.js +237 -5
  306. package/lib/test/unit/socketServer.sessionCookie.spec.js +2 -1
  307. package/lib/test/unit/stream-start-pipelines.spec.js +72 -18
  308. package/lib/test/unit/token-mint-limits.spec.js +165 -0
  309. package/lib/test/unit/tracing-export-routing.spec.js +137 -0
  310. package/lib/test/unit/tracing-session-spans.spec.js +337 -0
  311. package/lib/test/unit/unexpected-shutdown-reason.spec.js +163 -0
  312. package/lib/test/unit/verify-credential.spec.js +33 -0
  313. package/lib/test/unit/vision-assertion.spec.js +3 -2
  314. package/lib/test/unit/web-config-service.spec.js +19 -0
  315. package/lib/tsconfig.tsbuildinfo +1 -1
  316. package/package.json +6 -5
  317. package/prisma/migrations/20261004174413_locator_etalon_path/migration.sql +2 -0
  318. package/prisma/schema.prisma +3 -0
  319. package/schema.json +16 -12
  320. package/scripts/dev/readme-screenshots.js +276 -23
  321. package/scripts/dev/screenshot-fixtures/logcat.js +157 -0
  322. package/scripts/dev/screenshot-fixtures/phone-screen.html +469 -0
  323. package/scripts/dev/screenshot-fixtures/session.js +242 -0
  324. package/scripts/export-openapi.js +32 -0
  325. package/scripts/lib/openapi-for-site.js +79 -0
  326. package/lib/public/assets/BugReportButton-BAt0_pzq.js +0 -15
  327. package/lib/public/assets/DeviceMosaicView-BYaQHhTf.js +0 -2
  328. package/lib/public/assets/ai-settings-CsuJroRf.js +0 -21
  329. package/lib/public/assets/device-explorer-CdO6_nrn.js +0 -282
  330. package/lib/public/assets/forgot-password-CGnkRgE9.js +0 -1
  331. package/lib/public/assets/index-BbKEXUFH.js +0 -219
  332. package/lib/public/assets/runbook-page-CMMeQml9.js +0 -124
  333. package/lib/public/assets/session-detail-page-COL_RZxw.js +0 -4
  334. package/lib/src/scripts/prepare-prisma.js +0 -89
  335. /package/lib/public/assets/{BugReportButton-CPBjfiv4.css → index-CPBjfiv4.css} +0 -0
@@ -0,0 +1,393 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ require("reflect-metadata");
13
+ const chai_1 = require("chai");
14
+ const failureCategories_1 = require("../../src/dashboard/services/failureCategories");
15
+ const failure_analysis_service_1 = require("../../src/dashboard/services/failure-analysis-service");
16
+ const prisma_1 = require("../../src/prisma");
17
+ const scratch_database_1 = require("../helpers/scratch-database");
18
+ /**
19
+ * A failed session's category, from what Appium and the drivers really say.
20
+ *
21
+ * Each text below is quoted from the installed drivers (UiAutomator2 6.4.1
22
+ * with its server 9.3.1, XCUITest 10.9.0 with WebDriverAgent 10.2.3,
23
+ * base-driver 10.x), from Appium or from Xenon, with where it comes from, or
24
+ * is marked as a test's own words. Through 2.15 the rules matched none of App
25
+ * crash's texts, read a hub's recorded stack traces (netty's IdleStateHandler
26
+ * is in every UiAutomator2 one) and let a failure the test had recovered from
27
+ * decide the category.
28
+ */
29
+ /** A row this server records for its own sessions: the error's message only (CommandInterceptor). */
30
+ const local = (message) => (0, failureCategories_1.commandErrorOf)(JSON.stringify({ value: { error: message } }));
31
+ /** A row a hub records for a node's session: the node's whole WebDriver answer. */
32
+ const hub = (error, message, stacktrace = '') => (0, failureCategories_1.commandErrorOf)(JSON.stringify({ value: { error, message, stacktrace } }));
33
+ /**
34
+ * The stack trace the UiAutomator2 server sends with an error it answers
35
+ * (base-driver passes it on). Abbreviated; the netty frame is verbatim from a
36
+ * captured 9.3.1 answer: its pipeline starts with KeepAliveStateHandler, an
37
+ * IdleStateHandler. Nothing but that frame says "timeout".
38
+ */
39
+ const UIA2_STACK = [
40
+ 'io.appium.uiautomator2.common.exceptions.InvalidElementStateException: Cannot set the element',
41
+ '\tat io.appium.uiautomator2.handler.SendKeysToElement.safeHandle(SendKeysToElement.java:89)',
42
+ '\tat io.appium.uiautomator2.http.ServerHandler.channelRead(ServerHandler.java:68)',
43
+ '\tat io.netty.handler.timeout.IdleStateHandler.channelRead(IdleStateHandler.java:293)',
44
+ ].join('\n');
45
+ const NEW_COMMAND_TIMEOUT = "New Command Timeout of 60 seconds expired. Try customizing the timeout using the 'newCommandTimeout' desired capability";
46
+ const NOT_LOCATED = 'An element could not be located on the page using the given search parameters.';
47
+ describe('failure categories, from what Appium really says', () => {
48
+ describe('only what the failure says is read', () => {
49
+ it("a UiAutomator2 error on a hub isn't a timeout because netty's IdleStateHandler is in its stack", () => {
50
+ const row = hub('invalid element state', 'Cannot set the element', UIA2_STACK);
51
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('invalid element state', [row])).to.equal('UNKNOWN');
52
+ });
53
+ it('keeps only the code and the message of a recorded answer', () => {
54
+ (0, chai_1.expect)((0, failureCategories_1.commandErrorOf)(JSON.stringify({ value: { error: 'e', message: 'm', stacktrace: 's' } }))).to.deep.equal({ error: 'e', message: 'm' });
55
+ (0, chai_1.expect)((0, failureCategories_1.commandErrorOf)(null)).to.deep.equal({});
56
+ (0, chai_1.expect)((0, failureCategories_1.commandErrorOf)('not json')).to.deep.equal({});
57
+ (0, chai_1.expect)((0, failureCategories_1.commandErrorOf)(JSON.stringify({ value: 'x' }))).to.deep.equal({});
58
+ });
59
+ it("a selector quoted in Xenon's own message doesn't decide the category", () => {
60
+ // OmniVision's miss and autowait quote the test's selector
61
+ // (CommandInterceptor), which can hold any rule's phrase.
62
+ for (const selector of [
63
+ 'Allow notifications to be enabled',
64
+ 'Session timed out due to inactivity',
65
+ 'Hub shutdown',
66
+ ]) {
67
+ const miss = `Xenon found nothing on the screen matching -custom:ai-text "${selector}".`;
68
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)(miss, [local(miss)]), selector).to.equal('ELEMENT_NOT_FOUND');
69
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('no such element', [hub('no such element', miss)]), `hub: ${selector}`).to.equal('ELEMENT_NOT_FOUND');
70
+ }
71
+ });
72
+ it("a locator quoted in a driver's message doesn't either", () => {
73
+ // "emfile" is a substring of "systemFileList".
74
+ const stale = "The element 'By.id: com.acme:id/systemFileList' does not exist in DOM anymore";
75
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)(stale, [local(stale)])).to.equal('STALE_ELEMENT');
76
+ });
77
+ });
78
+ describe('the failure that ended the session decides alone', () => {
79
+ // Each older failure's rule comes before the ending one's, so reading
80
+ // them all as one text would give the older one's category.
81
+ it('an earlier failure the test recovered from does not decide', () => {
82
+ const recoveredStale = hub('stale element reference', "The element 'By.id: com.acme:id/list' does not exist in DOM anymore");
83
+ const finalMiss = hub('no such element', NOT_LOCATED);
84
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('no such element', [finalMiss, recoveredStale])).to.equal('ELEMENT_NOT_FOUND');
85
+ const recoveredLost = local('Could not proxy command to the remote server. Original error: socket hang up');
86
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)(NEW_COMMAND_TIMEOUT, [local(NOT_LOCATED), recoveredLost])).to.equal('TIMEOUT');
87
+ });
88
+ it('a reason no rule matches is unknown, whatever failed before it', () => {
89
+ // A test's own reason, and an ending error nothing matches.
90
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('expected Total to be 12', [local(NOT_LOCATED)])).to.equal('UNKNOWN');
91
+ const notInteractable = hub('element not interactable', 'Element is not interactable');
92
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('element not interactable', [notInteractable, local(NOT_LOCATED)])).to.equal('UNKNOWN');
93
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('Shutdown timeout (Cleanup hung for > 60s)', [])).to.equal('UNKNOWN');
94
+ });
95
+ it("for a node's session, the reason is the bare code, so its command's message is read with it", () => {
96
+ // UiAutomator2 answers an expired cache entry as "no such element"
97
+ // (ElementsCache.get): it is a stale element.
98
+ const row = hub('no such element', "The element identified by '00000000-0000-0000-0000-000000000bad' is not present in the cache or has expired. Try to find it again");
99
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('no such element', [row])).to.equal('STALE_ELEMENT');
100
+ });
101
+ it('a reason Xenon writes whole is matched whole', () => {
102
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('Hub shutdown', [])).to.equal('HUB_RESTART');
103
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('Assertion failed after Hub shutdown', [])).to.equal('UNKNOWN');
104
+ });
105
+ it('with no reason at all, the newest failed command decides, then older ones', () => {
106
+ const olderStale = hub('stale element reference', "The element 'By.id: com.acme:id/list' does not exist in DOM anymore");
107
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('', [hub('no such element', NOT_LOCATED), olderStale])).to.equal('ELEMENT_NOT_FOUND');
108
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('', [local('boom'), olderStale])).to.equal('STALE_ELEMENT');
109
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)('', [])).to.equal('UNKNOWN');
110
+ });
111
+ });
112
+ const cases = [
113
+ // SESSION_LOST: the phone's helper or the session went away.
114
+ [
115
+ 'base-driver proxy, helper dropped the connection (jsonwp-proxy/proxy.js)',
116
+ 'Could not proxy command to the remote server. Original error: socket hang up',
117
+ [],
118
+ 'SESSION_LOST',
119
+ ],
120
+ [
121
+ 'base-driver proxy, helper gone',
122
+ 'Could not proxy command to the remote server. Original error: connect ECONNREFUSED 127.0.0.1:8200',
123
+ [],
124
+ 'SESSION_LOST',
125
+ ],
126
+ [
127
+ "base-driver proxy, helper hung: a read time-out, which isn't the test's timeout",
128
+ 'Could not proxy command to the remote server. Original error: timeout of 240000ms exceeded',
129
+ [],
130
+ 'SESSION_LOST',
131
+ ],
132
+ [
133
+ 'UiAutomator2 instrumentation exited: the helper, though it says "probably crashed" (uiautomator2.js)',
134
+ "'POST /element' cannot be proxied to UiAutomator2 server because the instrumentation process is not running (probably crashed). Check the server log and/or the logcat output for more details",
135
+ [],
136
+ 'SESSION_LOST',
137
+ ],
138
+ [
139
+ 'WebDriverAgent restarted without the session, for a node (FBRoute.m)',
140
+ 'invalid session id',
141
+ [hub('invalid session id', 'Session does not exist')],
142
+ 'SESSION_LOST',
143
+ ],
144
+ [
145
+ 'base-driver, a command after the session ended (protocol.js)',
146
+ 'A session is either terminated or not started',
147
+ [],
148
+ 'SESSION_LOST',
149
+ ],
150
+ [
151
+ "Appium, a command racing the session's end (appium.js)",
152
+ "The session with id '6f1c2a8e' does not exist",
153
+ [],
154
+ 'SESSION_LOST',
155
+ ],
156
+ [
157
+ 'Appium ended the session without a cause (base-driver driver.js)',
158
+ 'The driver was unexpectedly shut down!',
159
+ [],
160
+ 'SESSION_LOST',
161
+ ],
162
+ [
163
+ "Xenon's onUnexpectedShutdown, no cause given (shutdownReason.ts)",
164
+ 'Driver shut down unexpectedly',
165
+ [],
166
+ 'SESSION_LOST',
167
+ ],
168
+ [
169
+ 'UiAutomator2, a web view (android-driver context/exports.js)',
170
+ 'Chromedriver quit unexpectedly during session',
171
+ [],
172
+ 'SESSION_LOST',
173
+ ],
174
+ [
175
+ "XCUITest's commandTimeouts, which then ends the session (proxy-helper.js)",
176
+ 'timeout',
177
+ [hub('timeout', "Appium did not get any response from 'click' command in 60000 ms")],
178
+ 'SESSION_LOST',
179
+ ],
180
+ ['OrphanSweeper', 'Session heartbeat timeout', [], 'SESSION_LOST'],
181
+ [
182
+ 'SessionHeartbeatService, node no longer has it',
183
+ 'Session terminal failure (SESSION_NOT_FOUND: The node no longer has this session)',
184
+ [],
185
+ 'SESSION_LOST',
186
+ ],
187
+ [
188
+ "SessionHeartbeatService, node unreachable: not a timeout of the test's",
189
+ 'Session terminal failure (TIMEOUT: Node session status failed: ETIMEDOUT)',
190
+ [],
191
+ 'SESSION_LOST',
192
+ ],
193
+ [
194
+ "the hub, for a node it can't reach (forwardToNode.ts)",
195
+ 'unknown error',
196
+ [hub('unknown error', 'Xenon could not reach the node running this session.')],
197
+ 'SESSION_LOST',
198
+ ],
199
+ // APP_CRASH
200
+ [
201
+ 'WebDriverAgent, app under test gone, for a node (FBSession.m)',
202
+ 'invalid element state',
203
+ [
204
+ hub('invalid element state', "The application under test with bundle id 'com.acme.shop' is not running, possibly crashed"),
205
+ ],
206
+ 'APP_CRASH',
207
+ ],
208
+ ["a test's own words", 'The application has crashed', [], 'APP_CRASH'],
209
+ // SYSTEM_OVERLOAD
210
+ [
211
+ 'UiAutomator2 server out of memory',
212
+ 'java.lang.OutOfMemoryError: Failed to allocate a 268435472 byte allocation',
213
+ [],
214
+ 'SYSTEM_OVERLOAD',
215
+ ],
216
+ [
217
+ "the server out of files, though it reached the proxy: not the helper's loss",
218
+ 'Could not proxy command to the remote server. Original error: connect EMFILE 127.0.0.1:8200 - Local (undefined:undefined)',
219
+ [],
220
+ 'SYSTEM_OVERLOAD',
221
+ ],
222
+ ['Node, opening a file', 'EMFILE: too many open files, open /tmp/x.png', [], 'SYSTEM_OVERLOAD'],
223
+ // STALE_ELEMENT
224
+ [
225
+ 'WebDriverAgent snapshot (XCUIElement+FBUtilities.m)',
226
+ 'The previously found element "Button, label: \'Pay\'" is not present in the current view anymore. Make sure the application UI has the expected state',
227
+ [],
228
+ 'STALE_ELEMENT',
229
+ ],
230
+ [
231
+ 'WebDriverAgent cache (FBElementCache.m)',
232
+ 'The element identified by "5D2F" is either not present or it has expired from the internal cache. Try to find it again',
233
+ [],
234
+ 'STALE_ELEMENT',
235
+ ],
236
+ [
237
+ 'UiAutomator2 server (ElementsCache.java)',
238
+ "The element 'By.id: com.acme:id/pay' is not linked to the same object in DOM anymore",
239
+ [],
240
+ 'STALE_ELEMENT',
241
+ ],
242
+ [
243
+ 'UiAutomator2 server, several (ElementsCache.restore)',
244
+ "Cached elements 'By.id: com.acme:id/row' do not exist in DOM anymore",
245
+ [],
246
+ 'STALE_ELEMENT',
247
+ ],
248
+ [
249
+ "base-driver's default, which XENON_COMMAND_FAILURE used to catch",
250
+ 'An element command failed because the referenced element is no longer attached to the DOM.',
251
+ [],
252
+ 'STALE_ELEMENT',
253
+ ],
254
+ [
255
+ 'Safari on an iPhone (remote-debugger atoms)',
256
+ 'Element does not exist in cache',
257
+ [],
258
+ 'STALE_ELEMENT',
259
+ ],
260
+ [
261
+ 'for a node, by code',
262
+ 'stale element reference',
263
+ [hub('stale element reference', '')],
264
+ 'STALE_ELEMENT',
265
+ ],
266
+ // TIMEOUT
267
+ ['Appium, idle past newCommandTimeout', NEW_COMMAND_TIMEOUT, [], 'TIMEOUT'],
268
+ ["Xenon's idle sweep", 'Session timed out due to inactivity', [], 'TIMEOUT'],
269
+ [
270
+ "base-driver's timeout default",
271
+ 'An operation did not complete before its timeout expired.',
272
+ [],
273
+ 'TIMEOUT',
274
+ ],
275
+ [
276
+ 'Safari on an iPhone (remote-debugger)',
277
+ 'Timed out waiting for asynchronous script result after 5000 ms',
278
+ [],
279
+ 'TIMEOUT',
280
+ ],
281
+ [
282
+ 'XCUITest (xctest.js)',
283
+ "Timed out after '120000ms' waiting for XCTest to complete",
284
+ [],
285
+ 'TIMEOUT',
286
+ ],
287
+ [
288
+ 'WebDriverAgent (FBXCTestDaemonsProxy.m), on this server: no code to go by',
289
+ "The application 'com.acme.shop' cannot be launched within 60 seconds timeout",
290
+ [],
291
+ 'TIMEOUT',
292
+ ],
293
+ [
294
+ 'WebDriverAgent (XCUIDevice+FBHelpers.m), on this server',
295
+ 'Timed out while waiting until the screen gets locked',
296
+ [],
297
+ 'TIMEOUT',
298
+ ],
299
+ [
300
+ 'UiAutomator2, a screen that never goes idle: not a crash (AXWindowHelpers)',
301
+ 'Timed out after 10000ms waiting for the root AccessibilityNodeInfo in the active window. Make sure the active window is not constantly hogging the main UI thread (e.g. the application is being idle long enough), so the accessibility manager could do its work',
302
+ [],
303
+ 'TIMEOUT',
304
+ ],
305
+ ['for a node, by code', 'script timeout', [hub('script timeout', 'x')], 'TIMEOUT'],
306
+ [
307
+ "Xenon's autowait, an element that never became enabled",
308
+ 'Autowait timed out after 10000 ms waiting for element 00000000-0001 to be enabled',
309
+ [],
310
+ 'TIMEOUT',
311
+ ],
312
+ // ELEMENT_NOT_FOUND
313
+ ["base-driver's default, both platforms' finds", NOT_LOCATED, [], 'ELEMENT_NOT_FOUND'],
314
+ [
315
+ 'WebDriverAgent identifier lookup (FBElementCommands.m)',
316
+ "'Pay' identifier didn't match any elements",
317
+ [],
318
+ 'ELEMENT_NOT_FOUND',
319
+ ],
320
+ [
321
+ "Xenon's autowait gave up looking: not a timeout",
322
+ 'Autowait timed out after 10000 ms waiting for element',
323
+ [],
324
+ 'ELEMENT_NOT_FOUND',
325
+ ],
326
+ [
327
+ 'Xenon, an element id it holds no more',
328
+ 'Xenon has no element omni_42.',
329
+ [],
330
+ 'ELEMENT_NOT_FOUND',
331
+ ],
332
+ ['for a node, by code', 'no such element', [hub('no such element', 'x')], 'ELEMENT_NOT_FOUND'],
333
+ // PERMISSION_BLOCKED
334
+ [
335
+ 'a web page on an iPhone (base-driver)',
336
+ 'unexpected alert open',
337
+ [hub('unexpected alert open', 'A modal dialog was open, blocking this operation')],
338
+ 'PERMISSION_BLOCKED',
339
+ ],
340
+ // HUB_RESTART
341
+ ["ShutdownCoordinator's drain", 'Hub shutdown', [], 'HUB_RESTART'],
342
+ ];
343
+ for (const [source, reason, commands, category] of cases) {
344
+ it(`${category}: ${source}`, () => {
345
+ (0, chai_1.expect)((0, failureCategories_1.categorizeFailure)(reason, commands)).to.equal(category);
346
+ });
347
+ }
348
+ });
349
+ describe('categorizeSessionFailure', function () {
350
+ this.timeout(30000);
351
+ const scratch = (0, scratch_database_1.useScratchDatabase)();
352
+ beforeEach(() => __awaiter(this, void 0, void 0, function* () {
353
+ yield scratch.db.sessionLog.deleteMany({});
354
+ yield scratch.db.session.deleteMany({});
355
+ }));
356
+ it("reads a node session's newest failed command with its reason, and nothing older", () => __awaiter(this, void 0, void 0, function* () {
357
+ // The reason is the newest command's bare code. Its message makes it a
358
+ // stale element; read alone, the code would be Element not found; read
359
+ // oldest first, the older lost helper would decide.
360
+ yield scratch.db.session.create({
361
+ data: {
362
+ id: 's-stale',
363
+ status: 'failed',
364
+ failure_reason: 'no such element',
365
+ desired_capabilities: '{}',
366
+ session_capabilities: '{}',
367
+ node_id: 'n-1',
368
+ has_live_video: false,
369
+ device_udid: 'R5CT',
370
+ device_platform: 'android',
371
+ device_version: '10',
372
+ },
373
+ });
374
+ const row = (n, error, message) => ({
375
+ session_id: 's-stale',
376
+ url: '/element',
377
+ method: 'POST',
378
+ title: 'Find Element',
379
+ is_error: true,
380
+ createdAt: new Date(Date.UTC(2026, 9, 5, 9, 0, n)),
381
+ response: JSON.stringify({ value: { error, message, stacktrace: UIA2_STACK } }),
382
+ });
383
+ yield scratch.db.sessionLog.createMany({
384
+ data: [
385
+ row(1, 'unknown error', 'Could not proxy command to the remote server. Original error: socket hang up'),
386
+ row(2, 'no such element', "The element identified by '00000000-0000-0000-0000-000000000bad' is not present in the cache or has expired. Try to find it again"),
387
+ ],
388
+ });
389
+ yield (0, failure_analysis_service_1.categorizeSessionFailure)('s-stale');
390
+ const session = yield prisma_1.prisma.session.findUnique({ where: { id: 's-stale' } });
391
+ (0, chai_1.expect)(session === null || session === void 0 ? void 0 : session.failure_category).to.equal('STALE_ELEMENT');
392
+ }));
393
+ });