@hsuite/native-connect-angular 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (276) hide show
  1. package/README.md +48 -0
  2. package/USAGE_EXAMPLES.md +476 -0
  3. package/assets/wallets/extension.svg +7 -0
  4. package/assets/wallets/hashpack.svg +6 -0
  5. package/assets/wallets/hsuite.svg +11 -0
  6. package/assets/wallets/kabila.svg +11 -0
  7. package/assets/wallets/walletconnect.svg +13 -0
  8. package/coverage/base.css +224 -0
  9. package/coverage/block-navigation.js +87 -0
  10. package/coverage/coverage-summary.json +50 -0
  11. package/coverage/favicon.png +0 -0
  12. package/coverage/index.html +476 -0
  13. package/coverage/lcov-report/base.css +224 -0
  14. package/coverage/lcov-report/block-navigation.js +87 -0
  15. package/coverage/lcov-report/favicon.png +0 -0
  16. package/coverage/lcov-report/index.html +476 -0
  17. package/coverage/lcov-report/lib/components/account-selector/account-actions/account-actions.component.ts.html +868 -0
  18. package/coverage/lcov-report/lib/components/account-selector/account-actions/index.html +116 -0
  19. package/coverage/lcov-report/lib/components/account-selector/account-filter/account-filter.component.ts.html +1288 -0
  20. package/coverage/lcov-report/lib/components/account-selector/account-filter/index.html +116 -0
  21. package/coverage/lcov-report/lib/components/account-selector/account-formatting.service.ts.html +685 -0
  22. package/coverage/lcov-report/lib/components/account-selector/account-grouping.service.ts.html +766 -0
  23. package/coverage/lcov-report/lib/components/account-selector/account-list/account-list.component.ts.html +1495 -0
  24. package/coverage/lcov-report/lib/components/account-selector/account-list/index.html +116 -0
  25. package/coverage/lcov-report/lib/components/account-selector/account-selector.component.ts.html +1495 -0
  26. package/coverage/lcov-report/lib/components/account-selector/account-selector.service.ts.html +1588 -0
  27. package/coverage/lcov-report/lib/components/account-selector/index.html +161 -0
  28. package/coverage/lcov-report/lib/components/wallet-account-display/index.html +116 -0
  29. package/coverage/lcov-report/lib/components/wallet-account-display/wallet-account-display.component.ts.html +505 -0
  30. package/coverage/lcov-report/lib/components/wallet-connect-button/index.html +116 -0
  31. package/coverage/lcov-report/lib/components/wallet-connect-button/wallet-connect-button.component.ts.html +805 -0
  32. package/coverage/lcov-report/lib/components/wallet-connect-prompt/index.html +116 -0
  33. package/coverage/lcov-report/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts.html +409 -0
  34. package/coverage/lcov-report/lib/components/wallet-connected-guard/index.html +116 -0
  35. package/coverage/lcov-report/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts.html +304 -0
  36. package/coverage/lcov-report/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts.html +436 -0
  37. package/coverage/lcov-report/lib/components/wallet-connection-modal/connection-method-step/index.html +116 -0
  38. package/coverage/lcov-report/lib/components/wallet-connection-modal/index.html +116 -0
  39. package/coverage/lcov-report/lib/components/wallet-connection-modal/qr-pairing-step/index.html +116 -0
  40. package/coverage/lcov-report/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts.html +2287 -0
  41. package/coverage/lcov-report/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts.html +2275 -0
  42. package/coverage/lcov-report/lib/components/wallet-session-display/index.html +116 -0
  43. package/coverage/lcov-report/lib/components/wallet-session-display/wallet-session-display.component.ts.html +676 -0
  44. package/coverage/lcov-report/lib/components/wallet-transaction-status/index.html +116 -0
  45. package/coverage/lcov-report/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts.html +703 -0
  46. package/coverage/lcov-report/lib/directives/index.html +146 -0
  47. package/coverage/lcov-report/lib/directives/wallet-connected.directive.ts.html +670 -0
  48. package/coverage/lcov-report/lib/directives/wallet-context.directive.ts.html +547 -0
  49. package/coverage/lcov-report/lib/directives/wallet-events.directive.ts.html +781 -0
  50. package/coverage/lcov-report/lib/hsuite-wallet.module.ts.html +715 -0
  51. package/coverage/lcov-report/lib/index.html +116 -0
  52. package/coverage/lcov-report/lib/models/connection-config.model.ts.html +280 -0
  53. package/coverage/lcov-report/lib/models/index.html +131 -0
  54. package/coverage/lcov-report/lib/models/provider-types.ts.html +577 -0
  55. package/coverage/lcov-report/lib/providers/base-wallet-provider.ts.html +1138 -0
  56. package/coverage/lcov-report/lib/providers/hsuite-native/channel-client.service.ts.html +2671 -0
  57. package/coverage/lcov-report/lib/providers/hsuite-native/index.html +116 -0
  58. package/coverage/lcov-report/lib/providers/hsuite-native-provider.ts.html +2347 -0
  59. package/coverage/lcov-report/lib/providers/index.html +146 -0
  60. package/coverage/lcov-report/lib/providers/p2p-native/index.html +131 -0
  61. package/coverage/lcov-report/lib/providers/p2p-native/p2p-native.provider.ts.html +2254 -0
  62. package/coverage/lcov-report/lib/providers/p2p-native/p2p-session-manager.ts.html +2170 -0
  63. package/coverage/lcov-report/lib/providers/wallet-error-handler.ts.html +1132 -0
  64. package/coverage/lcov-report/lib/providers/walletconnect/core/index.html +176 -0
  65. package/coverage/lcov-report/lib/providers/walletconnect/core/session-health.ts.html +673 -0
  66. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-client-manager.ts.html +1177 -0
  67. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-provider.ts.html +2563 -0
  68. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-session-store.ts.html +904 -0
  69. package/coverage/lcov-report/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts.html +982 -0
  70. package/coverage/lcov-report/lib/providers/walletconnect/signers/hedera-signer.ts.html +1915 -0
  71. package/coverage/lcov-report/lib/providers/walletconnect/signers/index.html +146 -0
  72. package/coverage/lcov-report/lib/providers/walletconnect/signers/signer-factory.ts.html +445 -0
  73. package/coverage/lcov-report/lib/providers/walletconnect/signers/xrpl-signer.ts.html +1519 -0
  74. package/coverage/lcov-report/lib/services/index.html +191 -0
  75. package/coverage/lcov-report/lib/services/logger.service.ts.html +463 -0
  76. package/coverage/lcov-report/lib/services/transaction-builders/base-transaction-builder.service.ts.html +1840 -0
  77. package/coverage/lcov-report/lib/services/transaction-builders/hedera-amount-utils.ts.html +337 -0
  78. package/coverage/lcov-report/lib/services/transaction-builders/hedera-transaction-builder.service.ts.html +3940 -0
  79. package/coverage/lcov-report/lib/services/transaction-builders/index.html +161 -0
  80. package/coverage/lcov-report/lib/services/transaction-builders/xrpl-transaction-builder.service.ts.html +2581 -0
  81. package/coverage/lcov-report/lib/services/transaction.service.ts.html +1123 -0
  82. package/coverage/lcov-report/lib/services/unified-wallet.service.ts.html +2641 -0
  83. package/coverage/lcov-report/lib/services/wallet-context.service.ts.html +637 -0
  84. package/coverage/lcov-report/lib/services/wallet-event-bus.service.ts.html +643 -0
  85. package/coverage/lcov-report/lib/services/wallet-providers.service.ts.html +496 -0
  86. package/coverage/lcov-report/lib/transports/chrome-extension-transport.ts.html +823 -0
  87. package/coverage/lcov-report/lib/transports/index.html +116 -0
  88. package/coverage/lcov-report/lib/utils/index.html +116 -0
  89. package/coverage/lcov-report/lib/utils/ledger-icons.util.ts.html +319 -0
  90. package/coverage/lcov-report/prettify.css +1 -0
  91. package/coverage/lcov-report/prettify.js +2 -0
  92. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  93. package/coverage/lcov-report/sorter.js +210 -0
  94. package/coverage/lcov.info +19252 -0
  95. package/coverage/lib/components/account-selector/account-actions/account-actions.component.ts.html +868 -0
  96. package/coverage/lib/components/account-selector/account-actions/index.html +116 -0
  97. package/coverage/lib/components/account-selector/account-filter/account-filter.component.ts.html +1288 -0
  98. package/coverage/lib/components/account-selector/account-filter/index.html +116 -0
  99. package/coverage/lib/components/account-selector/account-formatting.service.ts.html +685 -0
  100. package/coverage/lib/components/account-selector/account-grouping.service.ts.html +766 -0
  101. package/coverage/lib/components/account-selector/account-list/account-list.component.ts.html +1495 -0
  102. package/coverage/lib/components/account-selector/account-list/index.html +116 -0
  103. package/coverage/lib/components/account-selector/account-selector.component.ts.html +1495 -0
  104. package/coverage/lib/components/account-selector/account-selector.service.ts.html +1588 -0
  105. package/coverage/lib/components/account-selector/index.html +161 -0
  106. package/coverage/lib/components/wallet-account-display/index.html +116 -0
  107. package/coverage/lib/components/wallet-account-display/wallet-account-display.component.ts.html +505 -0
  108. package/coverage/lib/components/wallet-connect-button/index.html +116 -0
  109. package/coverage/lib/components/wallet-connect-button/wallet-connect-button.component.ts.html +805 -0
  110. package/coverage/lib/components/wallet-connect-prompt/index.html +116 -0
  111. package/coverage/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts.html +409 -0
  112. package/coverage/lib/components/wallet-connected-guard/index.html +116 -0
  113. package/coverage/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts.html +304 -0
  114. package/coverage/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts.html +436 -0
  115. package/coverage/lib/components/wallet-connection-modal/connection-method-step/index.html +116 -0
  116. package/coverage/lib/components/wallet-connection-modal/index.html +116 -0
  117. package/coverage/lib/components/wallet-connection-modal/qr-pairing-step/index.html +116 -0
  118. package/coverage/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts.html +2287 -0
  119. package/coverage/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts.html +2275 -0
  120. package/coverage/lib/components/wallet-session-display/index.html +116 -0
  121. package/coverage/lib/components/wallet-session-display/wallet-session-display.component.ts.html +676 -0
  122. package/coverage/lib/components/wallet-transaction-status/index.html +116 -0
  123. package/coverage/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts.html +703 -0
  124. package/coverage/lib/directives/index.html +146 -0
  125. package/coverage/lib/directives/wallet-connected.directive.ts.html +670 -0
  126. package/coverage/lib/directives/wallet-context.directive.ts.html +547 -0
  127. package/coverage/lib/directives/wallet-events.directive.ts.html +781 -0
  128. package/coverage/lib/hsuite-wallet.module.ts.html +715 -0
  129. package/coverage/lib/index.html +116 -0
  130. package/coverage/lib/models/connection-config.model.ts.html +280 -0
  131. package/coverage/lib/models/index.html +131 -0
  132. package/coverage/lib/models/provider-types.ts.html +577 -0
  133. package/coverage/lib/providers/base-wallet-provider.ts.html +1138 -0
  134. package/coverage/lib/providers/hsuite-native/channel-client.service.ts.html +2671 -0
  135. package/coverage/lib/providers/hsuite-native/index.html +116 -0
  136. package/coverage/lib/providers/hsuite-native-provider.ts.html +2347 -0
  137. package/coverage/lib/providers/index.html +146 -0
  138. package/coverage/lib/providers/p2p-native/index.html +131 -0
  139. package/coverage/lib/providers/p2p-native/p2p-native.provider.ts.html +2254 -0
  140. package/coverage/lib/providers/p2p-native/p2p-session-manager.ts.html +2170 -0
  141. package/coverage/lib/providers/wallet-error-handler.ts.html +1132 -0
  142. package/coverage/lib/providers/walletconnect/core/index.html +176 -0
  143. package/coverage/lib/providers/walletconnect/core/session-health.ts.html +673 -0
  144. package/coverage/lib/providers/walletconnect/core/walletconnect-client-manager.ts.html +1177 -0
  145. package/coverage/lib/providers/walletconnect/core/walletconnect-provider.ts.html +2563 -0
  146. package/coverage/lib/providers/walletconnect/core/walletconnect-session-store.ts.html +904 -0
  147. package/coverage/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts.html +982 -0
  148. package/coverage/lib/providers/walletconnect/signers/hedera-signer.ts.html +1915 -0
  149. package/coverage/lib/providers/walletconnect/signers/index.html +146 -0
  150. package/coverage/lib/providers/walletconnect/signers/signer-factory.ts.html +445 -0
  151. package/coverage/lib/providers/walletconnect/signers/xrpl-signer.ts.html +1519 -0
  152. package/coverage/lib/services/index.html +191 -0
  153. package/coverage/lib/services/logger.service.ts.html +463 -0
  154. package/coverage/lib/services/transaction-builders/base-transaction-builder.service.ts.html +1840 -0
  155. package/coverage/lib/services/transaction-builders/hedera-amount-utils.ts.html +337 -0
  156. package/coverage/lib/services/transaction-builders/hedera-transaction-builder.service.ts.html +3940 -0
  157. package/coverage/lib/services/transaction-builders/index.html +161 -0
  158. package/coverage/lib/services/transaction-builders/xrpl-transaction-builder.service.ts.html +2581 -0
  159. package/coverage/lib/services/transaction.service.ts.html +1123 -0
  160. package/coverage/lib/services/unified-wallet.service.ts.html +2641 -0
  161. package/coverage/lib/services/wallet-context.service.ts.html +637 -0
  162. package/coverage/lib/services/wallet-event-bus.service.ts.html +643 -0
  163. package/coverage/lib/services/wallet-providers.service.ts.html +496 -0
  164. package/coverage/lib/transports/chrome-extension-transport.ts.html +823 -0
  165. package/coverage/lib/transports/index.html +116 -0
  166. package/coverage/lib/utils/index.html +116 -0
  167. package/coverage/lib/utils/ledger-icons.util.ts.html +319 -0
  168. package/coverage/prettify.css +1 -0
  169. package/coverage/prettify.js +2 -0
  170. package/coverage/sort-arrow-sprite.png +0 -0
  171. package/coverage/sorter.js +210 -0
  172. package/dist/README.md +48 -0
  173. package/dist/fesm2022/hsuite-native-connect-angular.mjs +14592 -0
  174. package/dist/fesm2022/hsuite-native-connect-angular.mjs.map +1 -0
  175. package/dist/index.d.ts +6949 -0
  176. package/examples/minimal-connect.ts +178 -0
  177. package/examples/multi-protocol.ts +495 -0
  178. package/examples/transaction-signing.ts +361 -0
  179. package/jest.config.json +45 -0
  180. package/karma.conf.js +42 -0
  181. package/ng-package.json +20 -0
  182. package/package.json +60 -0
  183. package/src/index.ts +203 -0
  184. package/src/lib/components/account-selector/account-actions/account-actions.component.ts +261 -0
  185. package/src/lib/components/account-selector/account-filter/account-filter.component.ts +401 -0
  186. package/src/lib/components/account-selector/account-formatting.service.ts +200 -0
  187. package/src/lib/components/account-selector/account-grouping.service.ts +227 -0
  188. package/src/lib/components/account-selector/account-list/account-list.component.ts +470 -0
  189. package/src/lib/components/account-selector/account-selector.component.html +135 -0
  190. package/src/lib/components/account-selector/account-selector.component.scss +2039 -0
  191. package/src/lib/components/account-selector/account-selector.component.ts +470 -0
  192. package/src/lib/components/account-selector/account-selector.service.ts +501 -0
  193. package/src/lib/components/wallet-account-display/wallet-account-display.component.html +34 -0
  194. package/src/lib/components/wallet-account-display/wallet-account-display.component.scss +99 -0
  195. package/src/lib/components/wallet-account-display/wallet-account-display.component.ts +140 -0
  196. package/src/lib/components/wallet-connect-button/wallet-connect-button.component.html +14 -0
  197. package/src/lib/components/wallet-connect-button/wallet-connect-button.component.scss +272 -0
  198. package/src/lib/components/wallet-connect-button/wallet-connect-button.component.ts +240 -0
  199. package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.html +24 -0
  200. package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.scss +50 -0
  201. package/src/lib/components/wallet-connect-prompt/wallet-connect-prompt.component.ts +108 -0
  202. package/src/lib/components/wallet-connected-guard/wallet-connected-guard.component.html +24 -0
  203. package/src/lib/components/wallet-connected-guard/wallet-connected-guard.component.ts +73 -0
  204. package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.html +56 -0
  205. package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.scss +218 -0
  206. package/src/lib/components/wallet-connection-modal/connection-method-step/connection-method-step.component.ts +117 -0
  207. package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.html +94 -0
  208. package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.scss +272 -0
  209. package/src/lib/components/wallet-connection-modal/qr-pairing-step/qr-pairing-step.component.ts +734 -0
  210. package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.html +197 -0
  211. package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.scss +678 -0
  212. package/src/lib/components/wallet-connection-modal/wallet-connection-modal.component.ts +730 -0
  213. package/src/lib/components/wallet-session-display/wallet-session-display.component.html +110 -0
  214. package/src/lib/components/wallet-session-display/wallet-session-display.component.scss +179 -0
  215. package/src/lib/components/wallet-session-display/wallet-session-display.component.ts +197 -0
  216. package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.html +65 -0
  217. package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.scss +254 -0
  218. package/src/lib/components/wallet-transaction-status/wallet-transaction-status.component.ts +206 -0
  219. package/src/lib/directives/wallet-connected.directive.ts +195 -0
  220. package/src/lib/directives/wallet-context.directive.ts +154 -0
  221. package/src/lib/directives/wallet-events.directive.ts +232 -0
  222. package/src/lib/hsuite-wallet.module.ts +210 -0
  223. package/src/lib/models/connection-config.model.ts +65 -0
  224. package/src/lib/models/provider-types.ts +164 -0
  225. package/src/lib/models/unified-account.model.ts +76 -0
  226. package/src/lib/models/wallet-context.model.ts +121 -0
  227. package/src/lib/models/wallet-events.model.ts +158 -0
  228. package/src/lib/providers/base-wallet-provider.ts +351 -0
  229. package/src/lib/providers/hsuite-native/channel-client.service.spec.ts +73 -0
  230. package/src/lib/providers/hsuite-native/channel-client.service.ts +862 -0
  231. package/src/lib/providers/hsuite-native/index.ts +8 -0
  232. package/src/lib/providers/hsuite-native-provider.ts +754 -0
  233. package/src/lib/providers/mobile-native/mobile-native.provider.spec.ts +19 -0
  234. package/src/lib/providers/p2p-native/index.ts +30 -0
  235. package/src/lib/providers/p2p-native/p2p-native.provider.spec.ts +523 -0
  236. package/src/lib/providers/p2p-native/p2p-native.provider.ts +723 -0
  237. package/src/lib/providers/p2p-native/p2p-session-manager.ts +695 -0
  238. package/src/lib/providers/wallet-error-handler.ts +349 -0
  239. package/src/lib/providers/walletconnect/core/base-signer.interface.ts +122 -0
  240. package/src/lib/providers/walletconnect/core/session-health.ts +196 -0
  241. package/src/lib/providers/walletconnect/core/walletconnect-client-manager.ts +364 -0
  242. package/src/lib/providers/walletconnect/core/walletconnect-provider.integration.spec.ts +348 -0
  243. package/src/lib/providers/walletconnect/core/walletconnect-provider.ts +826 -0
  244. package/src/lib/providers/walletconnect/core/walletconnect-session-store.ts +273 -0
  245. package/src/lib/providers/walletconnect/core/walletconnect-signing-orchestrator.ts +299 -0
  246. package/src/lib/providers/walletconnect/core/walletconnect-types.ts +48 -0
  247. package/src/lib/providers/walletconnect/index.ts +33 -0
  248. package/src/lib/providers/walletconnect/signers/hedera-signer.spec.ts +367 -0
  249. package/src/lib/providers/walletconnect/signers/hedera-signer.ts +610 -0
  250. package/src/lib/providers/walletconnect/signers/signer-factory.spec.ts +62 -0
  251. package/src/lib/providers/walletconnect/signers/signer-factory.ts +120 -0
  252. package/src/lib/providers/walletconnect/signers/xrpl-signer.spec.ts +296 -0
  253. package/src/lib/providers/walletconnect/signers/xrpl-signer.ts +478 -0
  254. package/src/lib/services/logger.service.ts +126 -0
  255. package/src/lib/services/transaction-builders/base-transaction-builder.service.ts +585 -0
  256. package/src/lib/services/transaction-builders/hedera-amount-utils.ts +84 -0
  257. package/src/lib/services/transaction-builders/hedera-transaction-builder.service.spec.ts +741 -0
  258. package/src/lib/services/transaction-builders/hedera-transaction-builder.service.ts +1285 -0
  259. package/src/lib/services/transaction-builders/index.ts +54 -0
  260. package/src/lib/services/transaction-builders/xrpl-transaction-builder.service.spec.ts +937 -0
  261. package/src/lib/services/transaction-builders/xrpl-transaction-builder.service.ts +832 -0
  262. package/src/lib/services/transaction.service.ts +346 -0
  263. package/src/lib/services/unified-wallet.service.spec.ts +1382 -0
  264. package/src/lib/services/unified-wallet.service.ts +852 -0
  265. package/src/lib/services/wallet-context.service.ts +184 -0
  266. package/src/lib/services/wallet-event-bus.service.ts +186 -0
  267. package/src/lib/services/wallet-providers.service.ts +137 -0
  268. package/src/lib/transports/chrome-extension-transport.ts +246 -0
  269. package/src/lib/utils/index.ts +14 -0
  270. package/src/lib/utils/ledger-icons.util.ts +78 -0
  271. package/test/test-setup.ts +21 -0
  272. package/test-setup.ts +63 -0
  273. package/tsconfig.build.json +11 -0
  274. package/tsconfig.json +29 -0
  275. package/tsconfig.spec.json +15 -0
  276. package/vitest.config.ts +48 -0
@@ -0,0 +1,1382 @@
1
+ /**
2
+ * @fileoverview Unit tests for UnifiedWalletService
3
+ *
4
+ * Tests cover:
5
+ * - Provider registration/unregistration
6
+ * - Connection management (connect, disconnect, disconnectAll)
7
+ * - Account management (active account, switching, filtering)
8
+ * - Transaction operations (sign, submit, signAndExecute, signMessage)
9
+ * - Session management (active sessions, session disconnect)
10
+ * - Provider queries (by ID, by type, summaries)
11
+ * - Event bus integration
12
+ * - Reconnection handling
13
+ * - Transport state tracking
14
+ * - Error handling
15
+ *
16
+ * @module services/unified-wallet
17
+ * @since 2.0.0
18
+ */
19
+
20
+ import { describe, it, expect, beforeEach, vi, type MockedFunction } from 'vitest';
21
+ import { signal, WritableSignal } from '@angular/core';
22
+ import type { UnifiedAccount } from '../models/unified-account.model';
23
+ import type { ConnectionStatus, ProviderMetadata, ProviderType } from '../models/provider-types';
24
+ import type { ConnectionConfig } from '../models/connection-config.model';
25
+ import type {
26
+ SignTransactionOptions,
27
+ SubmitTransactionOptions,
28
+ SignMessageOptions,
29
+ SignResult,
30
+ SubmitResult,
31
+ SignMessageResult,
32
+ } from '../providers/base-wallet-provider';
33
+
34
+ // Mock the logger
35
+ vi.mock('@hsuite/native-connect-sdk', () => ({
36
+ getLogger: vi.fn(() => ({
37
+ debug: vi.fn(),
38
+ info: vi.fn(),
39
+ warn: vi.fn(),
40
+ error: vi.fn(),
41
+ scoped: vi.fn(() => ({
42
+ debug: vi.fn(),
43
+ info: vi.fn(),
44
+ warn: vi.fn(),
45
+ error: vi.fn(),
46
+ })),
47
+ })),
48
+ }));
49
+
50
+ /**
51
+ * Mock implementation of BaseWalletProvider for testing
52
+ */
53
+ class MockWalletProvider {
54
+ readonly id: string;
55
+ readonly metadata: ProviderMetadata;
56
+ private _status: WritableSignal<ConnectionStatus>;
57
+ private _accounts: WritableSignal<UnifiedAccount[]>;
58
+ private _error: WritableSignal<string | null>;
59
+
60
+ // Mock functions
61
+ connect = vi.fn<[ConnectionConfig], Promise<void>>();
62
+ disconnect = vi.fn<[], Promise<void>>();
63
+ signTransaction = vi.fn<[SignTransactionOptions], Promise<SignResult>>();
64
+ submitTransaction = vi.fn<[SubmitTransactionOptions], Promise<SubmitResult>>();
65
+ signMessage = vi.fn<[SignMessageOptions], Promise<SignMessageResult>>();
66
+ isAvailable = vi.fn<[], Promise<boolean>>();
67
+
68
+ // Optional methods that some providers have
69
+ attemptReconnect?: MockedFunction<() => Promise<boolean>>;
70
+ getActiveSessions?: MockedFunction<() => unknown[]>;
71
+ disconnectSession?: MockedFunction<(sessionKey: string) => Promise<void>>;
72
+ signAndExecuteTransaction?: MockedFunction<(options: unknown) => Promise<unknown>>;
73
+ transportState?: () => string;
74
+
75
+ constructor(
76
+ id: string,
77
+ type: ProviderType,
78
+ name: string,
79
+ options?: {
80
+ supportsReconnect?: boolean;
81
+ supportsSessions?: boolean;
82
+ supportsSignAndExecute?: boolean;
83
+ transportState?: () => string;
84
+ },
85
+ ) {
86
+ this.id = id;
87
+ this.metadata = {
88
+ name,
89
+ type,
90
+ description: `Mock ${name} provider`,
91
+ };
92
+ this._status = signal<ConnectionStatus>('disconnected');
93
+ this._accounts = signal<UnifiedAccount[]>([]);
94
+ this._error = signal<string | null>(null);
95
+
96
+ if (options?.supportsReconnect) {
97
+ this.attemptReconnect = vi.fn<[], Promise<boolean>>().mockResolvedValue(true);
98
+ }
99
+ if (options?.supportsSessions) {
100
+ this.getActiveSessions = vi.fn<[], unknown[]>().mockReturnValue([]);
101
+ this.disconnectSession = vi.fn<[string], Promise<void>>().mockResolvedValue(undefined);
102
+ }
103
+ if (options?.supportsSignAndExecute) {
104
+ this.signAndExecuteTransaction = vi.fn<[unknown], Promise<unknown>>().mockResolvedValue({
105
+ transactionId: 'tx-123',
106
+ });
107
+ }
108
+ if (options?.transportState) {
109
+ this.transportState = options.transportState;
110
+ }
111
+
112
+ // Default implementations
113
+ this.connect.mockResolvedValue(undefined);
114
+ this.disconnect.mockImplementation(async () => {
115
+ this._status.set('disconnected');
116
+ this._accounts.set([]);
117
+ });
118
+ this.signTransaction.mockResolvedValue({ signedPayload: 'signed-payload' });
119
+ this.submitTransaction.mockResolvedValue({ transactionId: 'tx-123' });
120
+ this.signMessage.mockResolvedValue({ signature: 'sig-123' });
121
+ this.isAvailable.mockResolvedValue(true);
122
+ }
123
+
124
+ get status() {
125
+ return this._status.asReadonly();
126
+ }
127
+
128
+ get accounts() {
129
+ return this._accounts.asReadonly();
130
+ }
131
+
132
+ get error() {
133
+ return this._error.asReadonly();
134
+ }
135
+
136
+ // Helper methods for tests
137
+ setStatus(status: ConnectionStatus): void {
138
+ this._status.set(status);
139
+ }
140
+
141
+ setAccounts(accounts: UnifiedAccount[]): void {
142
+ this._accounts.set(accounts);
143
+ }
144
+
145
+ setError(error: string | null): void {
146
+ this._error.set(error);
147
+ }
148
+
149
+ isConnected(): boolean {
150
+ return this._status() === 'connected';
151
+ }
152
+
153
+ getPrimaryAccount(): UnifiedAccount | null {
154
+ const accts = this._accounts();
155
+ return accts.length > 0 ? accts[0] : null;
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Mock WalletEventBus
161
+ */
162
+ class MockWalletEventBus {
163
+ connected = { emit: vi.fn() };
164
+ disconnected = { emit: vi.fn() };
165
+ accountChanged = { emit: vi.fn() };
166
+ accountsUpdated = { emit: vi.fn() };
167
+ sessionCreated = { emit: vi.fn() };
168
+ sessionDeleted = { emit: vi.fn() };
169
+ sessionRestored = { emit: vi.fn() };
170
+ providerStatusChanged = { emit: vi.fn() };
171
+ providerError = { emit: vi.fn() };
172
+ }
173
+
174
+ /**
175
+ * Create test accounts
176
+ */
177
+ function createMockAccount(overrides: Partial<UnifiedAccount> = {}): UnifiedAccount {
178
+ return {
179
+ id: 'account-1',
180
+ address: '0.0.12345',
181
+ ledgerId: 'hedera',
182
+ networkId: 'hedera:testnet',
183
+ providerId: 'hsuite-native',
184
+ providerType: 'hsuite-native',
185
+ ...overrides,
186
+ };
187
+ }
188
+
189
+ /**
190
+ * UnifiedWalletService test harness
191
+ *
192
+ * Since the actual service uses Angular DI, we create a simplified test version
193
+ * that mirrors the core functionality for unit testing.
194
+ */
195
+ class TestableUnifiedWalletService {
196
+ private readonly providers = new Map<string, MockWalletProvider>();
197
+ private readonly _providerIds = signal<string[]>([]);
198
+ private readonly _activeAccount = signal<UnifiedAccount | null>(null);
199
+ private readonly eventBus: MockWalletEventBus;
200
+
201
+ constructor(
202
+ hsuiteProvider: MockWalletProvider,
203
+ walletConnectProvider: MockWalletProvider,
204
+ p2pNativeProvider: MockWalletProvider,
205
+ eventBus: MockWalletEventBus,
206
+ ) {
207
+ this.eventBus = eventBus;
208
+
209
+ // Auto-register built-in providers
210
+ this.registerProvider(hsuiteProvider);
211
+ this.registerProvider(walletConnectProvider);
212
+ this.registerProvider(p2pNativeProvider);
213
+ }
214
+
215
+ get allAccounts(): UnifiedAccount[] {
216
+ const providers = Array.from(this.providers.values());
217
+ return providers.flatMap((p) => p.accounts());
218
+ }
219
+
220
+ get activeAccount(): UnifiedAccount | null {
221
+ return this._activeAccount();
222
+ }
223
+
224
+ get connectedProviders(): MockWalletProvider[] {
225
+ return Array.from(this.providers.values()).filter((p) => p.isConnected());
226
+ }
227
+
228
+ get transportState(): string {
229
+ // Check HSuite Native provider first
230
+ const hsuiteProvider = this.providers.get('hsuite-native');
231
+ if (hsuiteProvider) {
232
+ const status = hsuiteProvider.status();
233
+ if ((status === 'connecting' || status === 'connected') && hsuiteProvider.transportState) {
234
+ return hsuiteProvider.transportState();
235
+ }
236
+ }
237
+
238
+ // Check P2P Native provider
239
+ const p2pProvider = this.providers.get('mobile-native');
240
+ if (p2pProvider) {
241
+ const status = p2pProvider.status();
242
+ if ((status === 'connecting' || status === 'connected') && p2pProvider.transportState) {
243
+ return p2pProvider.transportState();
244
+ }
245
+ }
246
+
247
+ return 'idle';
248
+ }
249
+
250
+ getProviderSummaries() {
251
+ return Array.from(this.providers.values()).map((provider) => ({
252
+ id: provider.id,
253
+ name: provider.metadata.name,
254
+ providerType: provider.metadata.type,
255
+ status: provider.status(),
256
+ accounts: provider.accounts(),
257
+ supportsReconnect: typeof provider.attemptReconnect === 'function',
258
+ }));
259
+ }
260
+
261
+ async reconnectProvider(providerId: string): Promise<boolean> {
262
+ const provider = this.providers.get(providerId);
263
+ if (!provider) {
264
+ return false;
265
+ }
266
+ if (typeof provider.attemptReconnect === 'function') {
267
+ try {
268
+ return await provider.attemptReconnect();
269
+ } catch {
270
+ return false;
271
+ }
272
+ }
273
+ return false;
274
+ }
275
+
276
+ registerProvider(provider: MockWalletProvider): void {
277
+ if (this.providers.has(provider.id)) {
278
+ return;
279
+ }
280
+ this.providers.set(provider.id, provider);
281
+ this._providerIds.set(Array.from(this.providers.keys()));
282
+ }
283
+
284
+ unregisterProvider(providerId: string): void {
285
+ this.providers.delete(providerId);
286
+ this._providerIds.set(Array.from(this.providers.keys()));
287
+ }
288
+
289
+ getProvider(providerId: string): MockWalletProvider | undefined {
290
+ return this.providers.get(providerId);
291
+ }
292
+
293
+ getProvidersByType(type: ProviderType): MockWalletProvider[] {
294
+ return Array.from(this.providers.values()).filter((p) => p.metadata.type === type);
295
+ }
296
+
297
+ async connect(providerId: string, config: ConnectionConfig): Promise<void> {
298
+ const provider = this.providers.get(providerId);
299
+ if (!provider) {
300
+ throw new Error(`Provider ${providerId} not found`);
301
+ }
302
+
303
+ await provider.connect(config);
304
+
305
+ const accounts = provider.accounts();
306
+
307
+ // Emit connection event
308
+ this.eventBus.connected.emit({
309
+ providerId,
310
+ providerType: provider.metadata.type,
311
+ accounts,
312
+ timestamp: Date.now(),
313
+ });
314
+
315
+ // Emit accounts updated
316
+ this.eventBus.accountsUpdated.emit({
317
+ providerId,
318
+ accounts,
319
+ totalAccounts: this.allAccounts.length,
320
+ timestamp: Date.now(),
321
+ });
322
+
323
+ // Auto-select first account if none is active
324
+ if (!this._activeAccount() && provider.accounts().length > 0) {
325
+ this.setActiveAccount(provider.accounts()[0]);
326
+ }
327
+ }
328
+
329
+ async disconnect(providerId: string): Promise<void> {
330
+ const provider = this.providers.get(providerId);
331
+ if (!provider) {
332
+ throw new Error(`Provider ${providerId} not found`);
333
+ }
334
+
335
+ await provider.disconnect();
336
+
337
+ // If the active account belonged to this provider, clear it
338
+ const active = this._activeAccount();
339
+ if (active && active.providerId === providerId) {
340
+ const accounts = this.allAccounts;
341
+ const nextAccount = accounts.find((a) => a.providerId !== providerId);
342
+ this.setActiveAccount(nextAccount || null);
343
+ }
344
+ }
345
+
346
+ async disconnectAll(): Promise<void> {
347
+ const disconnectPromises = Array.from(this.providers.values())
348
+ .filter((p) => p.isConnected())
349
+ .map((p) => p.disconnect());
350
+
351
+ await Promise.all(disconnectPromises);
352
+ this.setActiveAccount(null);
353
+ }
354
+
355
+ setActiveAccount(account: UnifiedAccount | null): void {
356
+ const previous = this._activeAccount();
357
+ this._activeAccount.set(account);
358
+
359
+ this.eventBus.accountChanged.emit({
360
+ previousAccount: previous,
361
+ newAccount: account,
362
+ timestamp: Date.now(),
363
+ });
364
+ }
365
+
366
+ async signTransaction(payload: string): Promise<unknown> {
367
+ const account = this._activeAccount();
368
+ if (!account) {
369
+ throw new Error('No active account');
370
+ }
371
+
372
+ const provider = this.providers.get(account.providerId);
373
+ if (!provider) {
374
+ throw new Error(`Provider ${account.providerId} not found`);
375
+ }
376
+
377
+ return await provider.signTransaction({
378
+ accountAddress: account.address,
379
+ payload,
380
+ ledgerId: account.ledgerId,
381
+ networkId: account.networkId,
382
+ });
383
+ }
384
+
385
+ async submitTransaction(
386
+ payload: string,
387
+ options?: {
388
+ isBatch?: boolean;
389
+ batchKey?: string;
390
+ innerTransactions?: Array<{ payload: string; description?: string }>;
391
+ },
392
+ ): Promise<unknown> {
393
+ const account = this._activeAccount();
394
+ if (!account) {
395
+ throw new Error('No active account');
396
+ }
397
+
398
+ const provider = this.providers.get(account.providerId);
399
+ if (!provider) {
400
+ throw new Error(`Provider ${account.providerId} not found`);
401
+ }
402
+
403
+ return await provider.submitTransaction({
404
+ accountAddress: account.address,
405
+ payload,
406
+ ledgerId: account.ledgerId,
407
+ networkId: account.networkId,
408
+ ...options,
409
+ });
410
+ }
411
+
412
+ async signMessage(message: string, encoding?: 'utf-8' | 'base64'): Promise<unknown> {
413
+ const account = this._activeAccount();
414
+ if (!account) {
415
+ throw new Error('No active account');
416
+ }
417
+
418
+ const provider = this.providers.get(account.providerId);
419
+ if (!provider) {
420
+ throw new Error(`Provider ${account.providerId} not found`);
421
+ }
422
+
423
+ return await provider.signMessage({
424
+ accountAddress: account.address,
425
+ message,
426
+ encoding,
427
+ ledgerId: account.ledgerId,
428
+ networkId: account.networkId,
429
+ });
430
+ }
431
+
432
+ async signAndExecuteTransaction(
433
+ payload: string,
434
+ options?: Record<string, unknown>,
435
+ ): Promise<unknown> {
436
+ const account = this._activeAccount();
437
+ if (!account) {
438
+ throw new Error('No active account');
439
+ }
440
+
441
+ const provider = this.providers.get(account.providerId);
442
+ if (!provider) {
443
+ throw new Error(`Provider ${account.providerId} not found`);
444
+ }
445
+
446
+ // Check if provider has native signAndExecute support
447
+ if (typeof provider.signAndExecuteTransaction === 'function') {
448
+ return await provider.signAndExecuteTransaction({
449
+ accountAddress: account.address,
450
+ payload,
451
+ ledgerId: account.ledgerId,
452
+ networkId: account.networkId,
453
+ ...options,
454
+ });
455
+ }
456
+
457
+ // Fallback: sign then submit
458
+ const signResult = await provider.signTransaction({
459
+ accountAddress: account.address,
460
+ payload,
461
+ ledgerId: account.ledgerId,
462
+ networkId: account.networkId,
463
+ ...options,
464
+ });
465
+
466
+ return await provider.submitTransaction({
467
+ accountAddress: account.address,
468
+ payload: signResult.signedPayload || payload,
469
+ ledgerId: account.ledgerId,
470
+ networkId: account.networkId,
471
+ ...options,
472
+ });
473
+ }
474
+
475
+ getActiveSessions(): Array<{
476
+ providerId: string;
477
+ providerType: string;
478
+ providerName: string;
479
+ accounts: UnifiedAccount[];
480
+ metadata: unknown;
481
+ }> {
482
+ const sessions: Array<{
483
+ providerId: string;
484
+ providerType: string;
485
+ providerName: string;
486
+ accounts: UnifiedAccount[];
487
+ metadata: unknown;
488
+ }> = [];
489
+
490
+ for (const provider of this.providers.values()) {
491
+ const accounts = provider.accounts();
492
+
493
+ if (accounts.length > 0) {
494
+ if (provider.id === 'walletconnect-v2' && provider.getActiveSessions) {
495
+ const wcSessions = provider.getActiveSessions();
496
+ for (const wcSession of wcSessions) {
497
+ sessions.push({
498
+ providerId: provider.id,
499
+ providerType: provider.metadata.type,
500
+ providerName: provider.metadata.name,
501
+ accounts: (wcSession as { accounts?: UnifiedAccount[] }).accounts || [],
502
+ metadata: {
503
+ sessionKey: (wcSession as { sessionKey?: string }).sessionKey,
504
+ ledgerId: (wcSession as { ledgerId?: string }).ledgerId,
505
+ networkId: (wcSession as { networkId?: string }).networkId,
506
+ topic: (wcSession as { topic?: string }).topic,
507
+ },
508
+ });
509
+ }
510
+ } else {
511
+ sessions.push({
512
+ providerId: provider.id,
513
+ providerType: provider.metadata.type,
514
+ providerName: provider.metadata.name,
515
+ accounts,
516
+ metadata: {
517
+ type: provider.metadata.type,
518
+ },
519
+ });
520
+ }
521
+ }
522
+ }
523
+
524
+ return sessions;
525
+ }
526
+
527
+ async disconnectSession(providerId: string, sessionKey?: string): Promise<void> {
528
+ const provider = this.providers.get(providerId);
529
+ if (!provider) {
530
+ throw new Error(`Provider ${providerId} not found`);
531
+ }
532
+
533
+ if (provider.disconnectSession && sessionKey) {
534
+ await provider.disconnectSession(sessionKey);
535
+ } else {
536
+ await provider.disconnect();
537
+ }
538
+
539
+ this.eventBus.disconnected.emit({
540
+ providerId,
541
+ providerType: provider.metadata.type,
542
+ sessionKey,
543
+ reason: 'user_initiated',
544
+ timestamp: Date.now(),
545
+ });
546
+
547
+ // Update active account if needed
548
+ const active = this._activeAccount();
549
+ if (active && active.providerId === providerId) {
550
+ const accounts = this.allAccounts;
551
+ const nextAccount = accounts.find(
552
+ (a) =>
553
+ a.providerId !== providerId ||
554
+ (sessionKey && (a.metadata as { sessionKey?: string })?.sessionKey !== sessionKey),
555
+ );
556
+ this.setActiveAccount(nextAccount || null);
557
+ }
558
+ }
559
+
560
+ isAnyConnected(): boolean {
561
+ return this.connectedProviders.length > 0;
562
+ }
563
+
564
+ isConnected(): boolean {
565
+ return this.isAnyConnected();
566
+ }
567
+
568
+ getAccountById(accountId: string): UnifiedAccount | undefined {
569
+ return this.allAccounts.find((a) => a.id === accountId);
570
+ }
571
+
572
+ getConnectedLedgers(): string[] {
573
+ const accounts = this.allAccounts;
574
+ const ledgerIds = new Set(accounts.map((a) => a.ledgerId).filter(Boolean));
575
+ return Array.from(ledgerIds);
576
+ }
577
+
578
+ getAccountsByLedger(ledgerId: string): UnifiedAccount[] {
579
+ return this.allAccounts.filter((a) => a.ledgerId === ledgerId);
580
+ }
581
+
582
+ switchAccount(accountId: string): boolean {
583
+ const account = this.getAccountById(accountId);
584
+ if (!account) {
585
+ return false;
586
+ }
587
+ this.setActiveAccount(account);
588
+ return true;
589
+ }
590
+
591
+ getPrimaryAccountForLedger(ledgerId: string): UnifiedAccount | undefined {
592
+ return this.allAccounts.find((a) => a.ledgerId === ledgerId);
593
+ }
594
+
595
+ hasAccountsForLedger(ledgerId: string): boolean {
596
+ return this.getAccountsByLedger(ledgerId).length > 0;
597
+ }
598
+ }
599
+
600
+ describe('UnifiedWalletService', () => {
601
+ let service: TestableUnifiedWalletService;
602
+ let mockHsuiteProvider: MockWalletProvider;
603
+ let mockWalletConnectProvider: MockWalletProvider;
604
+ let mockP2PProvider: MockWalletProvider;
605
+ let mockEventBus: MockWalletEventBus;
606
+
607
+ beforeEach(() => {
608
+ mockEventBus = new MockWalletEventBus();
609
+
610
+ mockHsuiteProvider = new MockWalletProvider('hsuite-native', 'hsuite-native', 'HSuite Native', {
611
+ supportsReconnect: true,
612
+ transportState: () => 'nostr-only',
613
+ });
614
+
615
+ mockWalletConnectProvider = new MockWalletProvider(
616
+ 'walletconnect-v2',
617
+ 'walletconnect-v2',
618
+ 'WalletConnect',
619
+ {
620
+ supportsSessions: true,
621
+ supportsSignAndExecute: true,
622
+ },
623
+ );
624
+
625
+ mockP2PProvider = new MockWalletProvider('mobile-native', 'mobile-native', 'HSuite Mobile', {
626
+ transportState: () => 'p2p-connected',
627
+ });
628
+
629
+ service = new TestableUnifiedWalletService(
630
+ mockHsuiteProvider,
631
+ mockWalletConnectProvider,
632
+ mockP2PProvider,
633
+ mockEventBus,
634
+ );
635
+ });
636
+
637
+ // ==================== INITIALIZATION ====================
638
+ describe('initialization', () => {
639
+ it('should create', () => {
640
+ expect(service).toBeTruthy();
641
+ });
642
+
643
+ it('should auto-register built-in providers', () => {
644
+ expect(service.getProvider('hsuite-native')).toBe(mockHsuiteProvider);
645
+ expect(service.getProvider('walletconnect-v2')).toBe(mockWalletConnectProvider);
646
+ expect(service.getProvider('mobile-native')).toBe(mockP2PProvider);
647
+ });
648
+
649
+ it('should start with no active account', () => {
650
+ expect(service.activeAccount).toBeNull();
651
+ });
652
+
653
+ it('should start with no connected providers', () => {
654
+ expect(service.connectedProviders).toHaveLength(0);
655
+ });
656
+
657
+ it('should start with empty accounts list', () => {
658
+ expect(service.allAccounts).toHaveLength(0);
659
+ });
660
+ });
661
+
662
+ // ==================== PROVIDER REGISTRATION ====================
663
+ describe('registerProvider', () => {
664
+ it('should register a new provider', () => {
665
+ const customProvider = new MockWalletProvider(
666
+ 'custom-provider',
667
+ 'hsuite-native',
668
+ 'Custom Provider',
669
+ );
670
+
671
+ service.registerProvider(customProvider);
672
+
673
+ expect(service.getProvider('custom-provider')).toBe(customProvider);
674
+ });
675
+
676
+ it('should not register duplicate providers', () => {
677
+ const duplicateProvider = new MockWalletProvider(
678
+ 'hsuite-native',
679
+ 'hsuite-native',
680
+ 'Duplicate',
681
+ );
682
+
683
+ service.registerProvider(duplicateProvider);
684
+
685
+ // Should still be the original provider
686
+ expect(service.getProvider('hsuite-native')?.metadata.name).toBe('HSuite Native');
687
+ });
688
+ });
689
+
690
+ describe('unregisterProvider', () => {
691
+ it('should unregister a provider', () => {
692
+ service.unregisterProvider('hsuite-native');
693
+
694
+ expect(service.getProvider('hsuite-native')).toBeUndefined();
695
+ });
696
+
697
+ it('should handle unregistering non-existent provider', () => {
698
+ // Should not throw
699
+ expect(() => service.unregisterProvider('non-existent')).not.toThrow();
700
+ });
701
+ });
702
+
703
+ // ==================== PROVIDER QUERIES ====================
704
+ describe('getProvider', () => {
705
+ it('should return provider by ID', () => {
706
+ const provider = service.getProvider('walletconnect-v2');
707
+ expect(provider?.id).toBe('walletconnect-v2');
708
+ });
709
+
710
+ it('should return undefined for unknown provider', () => {
711
+ expect(service.getProvider('unknown')).toBeUndefined();
712
+ });
713
+ });
714
+
715
+ describe('getProvidersByType', () => {
716
+ it('should return providers of given type', () => {
717
+ const providers = service.getProvidersByType('hsuite-native');
718
+ expect(providers).toHaveLength(1);
719
+ expect(providers[0].id).toBe('hsuite-native');
720
+ });
721
+
722
+ it('should return empty array for type with no providers', () => {
723
+ service.unregisterProvider('hsuite-native');
724
+ const providers = service.getProvidersByType('hsuite-native');
725
+ expect(providers).toHaveLength(0);
726
+ });
727
+ });
728
+
729
+ describe('getProviderSummaries', () => {
730
+ it('should return summaries for all providers', () => {
731
+ const summaries = service.getProviderSummaries();
732
+
733
+ expect(summaries).toHaveLength(3);
734
+ expect(summaries.map((s) => s.id)).toContain('hsuite-native');
735
+ expect(summaries.map((s) => s.id)).toContain('walletconnect-v2');
736
+ expect(summaries.map((s) => s.id)).toContain('mobile-native');
737
+ });
738
+
739
+ it('should include correct provider metadata', () => {
740
+ const summaries = service.getProviderSummaries();
741
+ const hsuiteSummary = summaries.find((s) => s.id === 'hsuite-native');
742
+
743
+ expect(hsuiteSummary?.name).toBe('HSuite Native');
744
+ expect(hsuiteSummary?.providerType).toBe('hsuite-native');
745
+ expect(hsuiteSummary?.supportsReconnect).toBe(true);
746
+ });
747
+
748
+ it('should include account list', () => {
749
+ const account = createMockAccount();
750
+ mockHsuiteProvider.setAccounts([account]);
751
+
752
+ const summaries = service.getProviderSummaries();
753
+ const hsuiteSummary = summaries.find((s) => s.id === 'hsuite-native');
754
+
755
+ expect(hsuiteSummary?.accounts).toHaveLength(1);
756
+ });
757
+ });
758
+
759
+ // ==================== CONNECTION MANAGEMENT ====================
760
+ describe('connect', () => {
761
+ it('should connect to provider', async () => {
762
+ const config: ConnectionConfig = {
763
+ type: 'hsuite-native',
764
+ appId: 'test-app',
765
+ appName: 'Test App',
766
+ ledgerId: 'hedera',
767
+ networkId: 'hedera:testnet',
768
+ };
769
+
770
+ await service.connect('hsuite-native', config);
771
+
772
+ expect(mockHsuiteProvider.connect).toHaveBeenCalledWith(config);
773
+ });
774
+
775
+ it('should throw error for unknown provider', async () => {
776
+ const config: ConnectionConfig = {
777
+ type: 'hsuite-native',
778
+ appId: 'test-app',
779
+ appName: 'Test App',
780
+ ledgerId: 'hedera',
781
+ networkId: 'hedera:testnet',
782
+ };
783
+
784
+ await expect(service.connect('unknown', config)).rejects.toThrow(
785
+ 'Provider unknown not found',
786
+ );
787
+ });
788
+
789
+ it('should emit connected event', async () => {
790
+ const config: ConnectionConfig = {
791
+ type: 'hsuite-native',
792
+ appId: 'test-app',
793
+ appName: 'Test App',
794
+ ledgerId: 'hedera',
795
+ networkId: 'hedera:testnet',
796
+ };
797
+
798
+ await service.connect('hsuite-native', config);
799
+
800
+ expect(mockEventBus.connected.emit).toHaveBeenCalledWith(
801
+ expect.objectContaining({
802
+ providerId: 'hsuite-native',
803
+ providerType: 'hsuite-native',
804
+ }),
805
+ );
806
+ });
807
+
808
+ it('should emit accountsUpdated event', async () => {
809
+ const config: ConnectionConfig = {
810
+ type: 'hsuite-native',
811
+ appId: 'test-app',
812
+ appName: 'Test App',
813
+ ledgerId: 'hedera',
814
+ networkId: 'hedera:testnet',
815
+ };
816
+
817
+ await service.connect('hsuite-native', config);
818
+
819
+ expect(mockEventBus.accountsUpdated.emit).toHaveBeenCalled();
820
+ });
821
+
822
+ it('should auto-select first account after connect', async () => {
823
+ const account = createMockAccount();
824
+ mockHsuiteProvider.setAccounts([account]);
825
+
826
+ const config: ConnectionConfig = {
827
+ type: 'hsuite-native',
828
+ appId: 'test-app',
829
+ appName: 'Test App',
830
+ ledgerId: 'hedera',
831
+ networkId: 'hedera:testnet',
832
+ };
833
+
834
+ await service.connect('hsuite-native', config);
835
+
836
+ expect(service.activeAccount).toEqual(account);
837
+ });
838
+ });
839
+
840
+ describe('disconnect', () => {
841
+ it('should disconnect from provider', async () => {
842
+ await service.disconnect('hsuite-native');
843
+
844
+ expect(mockHsuiteProvider.disconnect).toHaveBeenCalled();
845
+ });
846
+
847
+ it('should throw error for unknown provider', async () => {
848
+ await expect(service.disconnect('unknown')).rejects.toThrow('Provider unknown not found');
849
+ });
850
+
851
+ it('should clear active account if it belonged to disconnected provider', async () => {
852
+ const account = createMockAccount({ providerId: 'hsuite-native' });
853
+ mockHsuiteProvider.setAccounts([account]);
854
+ mockHsuiteProvider.setStatus('connected');
855
+ service.setActiveAccount(account);
856
+
857
+ await service.disconnect('hsuite-native');
858
+
859
+ expect(service.activeAccount).toBeNull();
860
+ });
861
+
862
+ it('should switch to another account if available', async () => {
863
+ const hsuiteAccount = createMockAccount({ id: 'hsuite-1', providerId: 'hsuite-native' });
864
+ const wcAccount = createMockAccount({ id: 'wc-1', providerId: 'walletconnect-v2' });
865
+
866
+ mockHsuiteProvider.setAccounts([hsuiteAccount]);
867
+ mockWalletConnectProvider.setAccounts([wcAccount]);
868
+ mockHsuiteProvider.setStatus('connected');
869
+ mockWalletConnectProvider.setStatus('connected');
870
+ service.setActiveAccount(hsuiteAccount);
871
+
872
+ await service.disconnect('hsuite-native');
873
+
874
+ expect(service.activeAccount).toEqual(wcAccount);
875
+ });
876
+ });
877
+
878
+ describe('disconnectAll', () => {
879
+ it('should disconnect all connected providers', async () => {
880
+ mockHsuiteProvider.setStatus('connected');
881
+ mockWalletConnectProvider.setStatus('connected');
882
+
883
+ await service.disconnectAll();
884
+
885
+ expect(mockHsuiteProvider.disconnect).toHaveBeenCalled();
886
+ expect(mockWalletConnectProvider.disconnect).toHaveBeenCalled();
887
+ });
888
+
889
+ it('should clear active account', async () => {
890
+ const account = createMockAccount();
891
+ service.setActiveAccount(account);
892
+
893
+ await service.disconnectAll();
894
+
895
+ expect(service.activeAccount).toBeNull();
896
+ });
897
+
898
+ it('should only disconnect connected providers', async () => {
899
+ mockHsuiteProvider.setStatus('connected');
900
+ mockWalletConnectProvider.setStatus('disconnected');
901
+
902
+ await service.disconnectAll();
903
+
904
+ expect(mockHsuiteProvider.disconnect).toHaveBeenCalled();
905
+ expect(mockWalletConnectProvider.disconnect).not.toHaveBeenCalled();
906
+ });
907
+ });
908
+
909
+ // ==================== RECONNECTION ====================
910
+ describe('reconnectProvider', () => {
911
+ it('should attempt reconnection on supported providers', async () => {
912
+ const result = await service.reconnectProvider('hsuite-native');
913
+
914
+ expect(mockHsuiteProvider.attemptReconnect).toHaveBeenCalled();
915
+ expect(result).toBe(true);
916
+ });
917
+
918
+ it('should return false for providers without reconnect support', async () => {
919
+ const result = await service.reconnectProvider('mobile-native');
920
+
921
+ expect(result).toBe(false);
922
+ });
923
+
924
+ it('should return false for unknown provider', async () => {
925
+ const result = await service.reconnectProvider('unknown');
926
+
927
+ expect(result).toBe(false);
928
+ });
929
+
930
+ it('should handle reconnection failure', async () => {
931
+ mockHsuiteProvider.attemptReconnect!.mockRejectedValueOnce(new Error('Failed'));
932
+
933
+ const result = await service.reconnectProvider('hsuite-native');
934
+
935
+ expect(result).toBe(false);
936
+ });
937
+ });
938
+
939
+ // ==================== ACCOUNT MANAGEMENT ====================
940
+ describe('setActiveAccount', () => {
941
+ it('should set active account', () => {
942
+ const account = createMockAccount();
943
+
944
+ service.setActiveAccount(account);
945
+
946
+ expect(service.activeAccount).toEqual(account);
947
+ });
948
+
949
+ it('should emit accountChanged event', () => {
950
+ const account = createMockAccount();
951
+
952
+ service.setActiveAccount(account);
953
+
954
+ expect(mockEventBus.accountChanged.emit).toHaveBeenCalledWith(
955
+ expect.objectContaining({
956
+ previousAccount: null,
957
+ newAccount: account,
958
+ }),
959
+ );
960
+ });
961
+
962
+ it('should include previous account in event', () => {
963
+ const account1 = createMockAccount({ id: 'account-1' });
964
+ const account2 = createMockAccount({ id: 'account-2' });
965
+
966
+ service.setActiveAccount(account1);
967
+ service.setActiveAccount(account2);
968
+
969
+ expect(mockEventBus.accountChanged.emit).toHaveBeenLastCalledWith(
970
+ expect.objectContaining({
971
+ previousAccount: account1,
972
+ newAccount: account2,
973
+ }),
974
+ );
975
+ });
976
+
977
+ it('should allow setting to null', () => {
978
+ const account = createMockAccount();
979
+ service.setActiveAccount(account);
980
+
981
+ service.setActiveAccount(null);
982
+
983
+ expect(service.activeAccount).toBeNull();
984
+ });
985
+ });
986
+
987
+ describe('switchAccount', () => {
988
+ it('should switch to account by ID', () => {
989
+ const account = createMockAccount({ id: 'target-account' });
990
+ mockHsuiteProvider.setAccounts([account]);
991
+
992
+ const result = service.switchAccount('target-account');
993
+
994
+ expect(result).toBe(true);
995
+ expect(service.activeAccount).toEqual(account);
996
+ });
997
+
998
+ it('should return false for unknown account', () => {
999
+ const result = service.switchAccount('unknown-account');
1000
+
1001
+ expect(result).toBe(false);
1002
+ });
1003
+ });
1004
+
1005
+ describe('getAccountById', () => {
1006
+ it('should return account by ID', () => {
1007
+ const account = createMockAccount({ id: 'test-account' });
1008
+ mockHsuiteProvider.setAccounts([account]);
1009
+
1010
+ const result = service.getAccountById('test-account');
1011
+
1012
+ expect(result).toEqual(account);
1013
+ });
1014
+
1015
+ it('should return undefined for unknown ID', () => {
1016
+ const result = service.getAccountById('unknown');
1017
+
1018
+ expect(result).toBeUndefined();
1019
+ });
1020
+ });
1021
+
1022
+ describe('getConnectedLedgers', () => {
1023
+ it('should return unique ledger IDs', () => {
1024
+ mockHsuiteProvider.setAccounts([
1025
+ createMockAccount({ id: 'h1', ledgerId: 'hedera' }),
1026
+ createMockAccount({ id: 'h2', ledgerId: 'hedera' }),
1027
+ ]);
1028
+ mockWalletConnectProvider.setAccounts([createMockAccount({ id: 'x1', ledgerId: 'xrpl' })]);
1029
+
1030
+ const ledgers = service.getConnectedLedgers();
1031
+
1032
+ expect(ledgers).toHaveLength(2);
1033
+ expect(ledgers).toContain('hedera');
1034
+ expect(ledgers).toContain('xrpl');
1035
+ });
1036
+
1037
+ it('should return empty array when no accounts', () => {
1038
+ const ledgers = service.getConnectedLedgers();
1039
+
1040
+ expect(ledgers).toHaveLength(0);
1041
+ });
1042
+ });
1043
+
1044
+ describe('getAccountsByLedger', () => {
1045
+ it('should filter accounts by ledger', () => {
1046
+ mockHsuiteProvider.setAccounts([createMockAccount({ id: 'h1', ledgerId: 'hedera' })]);
1047
+ mockWalletConnectProvider.setAccounts([createMockAccount({ id: 'x1', ledgerId: 'xrpl' })]);
1048
+
1049
+ const hederaAccounts = service.getAccountsByLedger('hedera');
1050
+ const xrplAccounts = service.getAccountsByLedger('xrpl');
1051
+
1052
+ expect(hederaAccounts).toHaveLength(1);
1053
+ expect(hederaAccounts[0].id).toBe('h1');
1054
+ expect(xrplAccounts).toHaveLength(1);
1055
+ expect(xrplAccounts[0].id).toBe('x1');
1056
+ });
1057
+ });
1058
+
1059
+ describe('getPrimaryAccountForLedger', () => {
1060
+ it('should return first account for ledger', () => {
1061
+ mockHsuiteProvider.setAccounts([
1062
+ createMockAccount({ id: 'h1', ledgerId: 'hedera' }),
1063
+ createMockAccount({ id: 'h2', ledgerId: 'hedera' }),
1064
+ ]);
1065
+
1066
+ const primary = service.getPrimaryAccountForLedger('hedera');
1067
+
1068
+ expect(primary?.id).toBe('h1');
1069
+ });
1070
+
1071
+ it('should return undefined when no accounts for ledger', () => {
1072
+ const primary = service.getPrimaryAccountForLedger('xrpl');
1073
+
1074
+ expect(primary).toBeUndefined();
1075
+ });
1076
+ });
1077
+
1078
+ describe('hasAccountsForLedger', () => {
1079
+ it('should return true when accounts exist', () => {
1080
+ mockHsuiteProvider.setAccounts([createMockAccount({ ledgerId: 'hedera' })]);
1081
+
1082
+ expect(service.hasAccountsForLedger('hedera')).toBe(true);
1083
+ });
1084
+
1085
+ it('should return false when no accounts', () => {
1086
+ expect(service.hasAccountsForLedger('xrpl')).toBe(false);
1087
+ });
1088
+ });
1089
+
1090
+ // ==================== CONNECTION STATUS ====================
1091
+ describe('isConnected / isAnyConnected', () => {
1092
+ it('should return false when no providers connected', () => {
1093
+ expect(service.isConnected()).toBe(false);
1094
+ expect(service.isAnyConnected()).toBe(false);
1095
+ });
1096
+
1097
+ it('should return true when any provider is connected', () => {
1098
+ mockHsuiteProvider.setStatus('connected');
1099
+
1100
+ expect(service.isConnected()).toBe(true);
1101
+ expect(service.isAnyConnected()).toBe(true);
1102
+ });
1103
+ });
1104
+
1105
+ describe('connectedProviders', () => {
1106
+ it('should return list of connected providers', () => {
1107
+ mockHsuiteProvider.setStatus('connected');
1108
+ mockWalletConnectProvider.setStatus('connected');
1109
+
1110
+ const connected = service.connectedProviders;
1111
+
1112
+ expect(connected).toHaveLength(2);
1113
+ });
1114
+
1115
+ it('should return empty array when none connected', () => {
1116
+ expect(service.connectedProviders).toHaveLength(0);
1117
+ });
1118
+ });
1119
+
1120
+ // ==================== TRANSPORT STATE ====================
1121
+ describe('transportState', () => {
1122
+ it('should return idle when no native providers connected', () => {
1123
+ expect(service.transportState).toBe('idle');
1124
+ });
1125
+
1126
+ it('should return hsuite transport state when connected', () => {
1127
+ mockHsuiteProvider.setStatus('connected');
1128
+
1129
+ expect(service.transportState).toBe('nostr-only');
1130
+ });
1131
+
1132
+ it('should return p2p transport state when connected', () => {
1133
+ mockP2PProvider.setStatus('connected');
1134
+
1135
+ expect(service.transportState).toBe('p2p-connected');
1136
+ });
1137
+
1138
+ it('should prioritize hsuite over p2p', () => {
1139
+ mockHsuiteProvider.setStatus('connected');
1140
+ mockP2PProvider.setStatus('connected');
1141
+
1142
+ expect(service.transportState).toBe('nostr-only');
1143
+ });
1144
+ });
1145
+
1146
+ // ==================== TRANSACTION OPERATIONS ====================
1147
+ describe('signTransaction', () => {
1148
+ it('should sign transaction with active account provider', async () => {
1149
+ const account = createMockAccount({ providerId: 'hsuite-native' });
1150
+ mockHsuiteProvider.setAccounts([account]);
1151
+ service.setActiveAccount(account);
1152
+
1153
+ await service.signTransaction('payload-123');
1154
+
1155
+ expect(mockHsuiteProvider.signTransaction).toHaveBeenCalledWith(
1156
+ expect.objectContaining({
1157
+ accountAddress: account.address,
1158
+ payload: 'payload-123',
1159
+ ledgerId: account.ledgerId,
1160
+ networkId: account.networkId,
1161
+ }),
1162
+ );
1163
+ });
1164
+
1165
+ it('should throw when no active account', async () => {
1166
+ await expect(service.signTransaction('payload')).rejects.toThrow('No active account');
1167
+ });
1168
+
1169
+ it('should throw when provider not found', async () => {
1170
+ const account = createMockAccount({ providerId: 'non-existent' });
1171
+ service.setActiveAccount(account);
1172
+
1173
+ await expect(service.signTransaction('payload')).rejects.toThrow(
1174
+ 'Provider non-existent not found',
1175
+ );
1176
+ });
1177
+ });
1178
+
1179
+ describe('submitTransaction', () => {
1180
+ it('should submit transaction with active account provider', async () => {
1181
+ const account = createMockAccount({ providerId: 'hsuite-native' });
1182
+ mockHsuiteProvider.setAccounts([account]);
1183
+ service.setActiveAccount(account);
1184
+
1185
+ await service.submitTransaction('payload-123');
1186
+
1187
+ expect(mockHsuiteProvider.submitTransaction).toHaveBeenCalledWith(
1188
+ expect.objectContaining({
1189
+ accountAddress: account.address,
1190
+ payload: 'payload-123',
1191
+ }),
1192
+ );
1193
+ });
1194
+
1195
+ it('should pass batch options', async () => {
1196
+ const account = createMockAccount({ providerId: 'hsuite-native' });
1197
+ mockHsuiteProvider.setAccounts([account]);
1198
+ service.setActiveAccount(account);
1199
+
1200
+ const options = {
1201
+ isBatch: true,
1202
+ batchKey: 'batch-key-123',
1203
+ innerTransactions: [{ payload: 'inner-tx' }],
1204
+ };
1205
+
1206
+ await service.submitTransaction('payload', options);
1207
+
1208
+ expect(mockHsuiteProvider.submitTransaction).toHaveBeenCalledWith(
1209
+ expect.objectContaining(options),
1210
+ );
1211
+ });
1212
+
1213
+ it('should throw when no active account', async () => {
1214
+ await expect(service.submitTransaction('payload')).rejects.toThrow('No active account');
1215
+ });
1216
+ });
1217
+
1218
+ describe('signMessage', () => {
1219
+ it('should sign message with active account provider', async () => {
1220
+ const account = createMockAccount({ providerId: 'hsuite-native' });
1221
+ mockHsuiteProvider.setAccounts([account]);
1222
+ service.setActiveAccount(account);
1223
+
1224
+ await service.signMessage('Hello, World!', 'utf-8');
1225
+
1226
+ expect(mockHsuiteProvider.signMessage).toHaveBeenCalledWith(
1227
+ expect.objectContaining({
1228
+ accountAddress: account.address,
1229
+ message: 'Hello, World!',
1230
+ encoding: 'utf-8',
1231
+ }),
1232
+ );
1233
+ });
1234
+
1235
+ it('should throw when no active account', async () => {
1236
+ await expect(service.signMessage('message')).rejects.toThrow('No active account');
1237
+ });
1238
+ });
1239
+
1240
+ describe('signAndExecuteTransaction', () => {
1241
+ it('should use native signAndExecute when available', async () => {
1242
+ const account = createMockAccount({ providerId: 'walletconnect-v2' });
1243
+ mockWalletConnectProvider.setAccounts([account]);
1244
+ service.setActiveAccount(account);
1245
+
1246
+ await service.signAndExecuteTransaction('payload-123');
1247
+
1248
+ expect(mockWalletConnectProvider.signAndExecuteTransaction).toHaveBeenCalledWith(
1249
+ expect.objectContaining({
1250
+ accountAddress: account.address,
1251
+ payload: 'payload-123',
1252
+ }),
1253
+ );
1254
+ });
1255
+
1256
+ it('should fallback to sign then submit when not available', async () => {
1257
+ const account = createMockAccount({ providerId: 'hsuite-native' });
1258
+ mockHsuiteProvider.setAccounts([account]);
1259
+ service.setActiveAccount(account);
1260
+
1261
+ await service.signAndExecuteTransaction('payload-123');
1262
+
1263
+ expect(mockHsuiteProvider.signTransaction).toHaveBeenCalled();
1264
+ expect(mockHsuiteProvider.submitTransaction).toHaveBeenCalled();
1265
+ });
1266
+
1267
+ it('should throw when no active account', async () => {
1268
+ await expect(service.signAndExecuteTransaction('payload')).rejects.toThrow(
1269
+ 'No active account',
1270
+ );
1271
+ });
1272
+ });
1273
+
1274
+ // ==================== SESSION MANAGEMENT ====================
1275
+ describe('getActiveSessions', () => {
1276
+ it('should return sessions from all providers', () => {
1277
+ const hsuiteAccount = createMockAccount({ providerId: 'hsuite-native' });
1278
+ mockHsuiteProvider.setAccounts([hsuiteAccount]);
1279
+
1280
+ const sessions = service.getActiveSessions();
1281
+
1282
+ expect(sessions).toHaveLength(1);
1283
+ expect(sessions[0].providerId).toBe('hsuite-native');
1284
+ });
1285
+
1286
+ it('should include WalletConnect sessions with metadata', () => {
1287
+ const wcAccount = createMockAccount({ providerId: 'walletconnect-v2' });
1288
+ mockWalletConnectProvider.setAccounts([wcAccount]);
1289
+ mockWalletConnectProvider.getActiveSessions!.mockReturnValue([
1290
+ {
1291
+ sessionKey: 'session-123',
1292
+ ledgerId: 'hedera',
1293
+ networkId: 'hedera:testnet',
1294
+ topic: 'topic-abc',
1295
+ accounts: [wcAccount],
1296
+ },
1297
+ ]);
1298
+
1299
+ const sessions = service.getActiveSessions();
1300
+
1301
+ expect(sessions).toHaveLength(1);
1302
+ expect(sessions[0].metadata).toEqual(
1303
+ expect.objectContaining({
1304
+ sessionKey: 'session-123',
1305
+ topic: 'topic-abc',
1306
+ }),
1307
+ );
1308
+ });
1309
+
1310
+ it('should return empty array when no accounts', () => {
1311
+ const sessions = service.getActiveSessions();
1312
+
1313
+ expect(sessions).toHaveLength(0);
1314
+ });
1315
+ });
1316
+
1317
+ describe('disconnectSession', () => {
1318
+ it('should use provider disconnectSession when available', async () => {
1319
+ mockWalletConnectProvider.setAccounts([
1320
+ createMockAccount({ providerId: 'walletconnect-v2' }),
1321
+ ]);
1322
+
1323
+ await service.disconnectSession('walletconnect-v2', 'session-key');
1324
+
1325
+ expect(mockWalletConnectProvider.disconnectSession).toHaveBeenCalledWith('session-key');
1326
+ });
1327
+
1328
+ it('should fallback to provider disconnect when sessionKey not provided', async () => {
1329
+ await service.disconnectSession('walletconnect-v2');
1330
+
1331
+ expect(mockWalletConnectProvider.disconnect).toHaveBeenCalled();
1332
+ });
1333
+
1334
+ it('should emit disconnected event', async () => {
1335
+ await service.disconnectSession('hsuite-native');
1336
+
1337
+ expect(mockEventBus.disconnected.emit).toHaveBeenCalledWith(
1338
+ expect.objectContaining({
1339
+ providerId: 'hsuite-native',
1340
+ reason: 'user_initiated',
1341
+ }),
1342
+ );
1343
+ });
1344
+
1345
+ it('should throw for unknown provider', async () => {
1346
+ await expect(service.disconnectSession('unknown')).rejects.toThrow(
1347
+ 'Provider unknown not found',
1348
+ );
1349
+ });
1350
+
1351
+ it('should update active account if needed', async () => {
1352
+ const account = createMockAccount({ providerId: 'hsuite-native' });
1353
+ mockHsuiteProvider.setAccounts([account]);
1354
+ service.setActiveAccount(account);
1355
+
1356
+ await service.disconnectSession('hsuite-native');
1357
+
1358
+ expect(service.activeAccount).toBeNull();
1359
+ });
1360
+ });
1361
+
1362
+ // ==================== ALL ACCOUNTS ====================
1363
+ describe('allAccounts', () => {
1364
+ it('should aggregate accounts from all providers', () => {
1365
+ mockHsuiteProvider.setAccounts([
1366
+ createMockAccount({ id: 'h1', providerId: 'hsuite-native' }),
1367
+ createMockAccount({ id: 'h2', providerId: 'hsuite-native' }),
1368
+ ]);
1369
+ mockWalletConnectProvider.setAccounts([
1370
+ createMockAccount({ id: 'wc1', providerId: 'walletconnect-v2' }),
1371
+ ]);
1372
+
1373
+ const accounts = service.allAccounts;
1374
+
1375
+ expect(accounts).toHaveLength(3);
1376
+ });
1377
+
1378
+ it('should return empty array when no accounts', () => {
1379
+ expect(service.allAccounts).toHaveLength(0);
1380
+ });
1381
+ });
1382
+ });