@peers-app/peers-ui 0.27.3 → 0.27.5

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 (311) hide show
  1. package/dist/screens/network-viewer/group-details-modal.d.ts +4 -0
  2. package/dist/screens/network-viewer/group-details-modal.js +6 -46
  3. package/dist/screens/network-viewer/network-viewer-ipc.d.ts +3 -2
  4. package/dist/screens/network-viewer/network-viewer-ipc.js +6 -0
  5. package/dist/screens/network-viewer/sync-recovery.js +103 -1
  6. package/dist/screens/variables/variable-details.js +2 -1
  7. package/dist/tabs-layout/network-status-indicator.js +26 -12
  8. package/dist/tabs-layout/use-network-status.d.ts +6 -1
  9. package/dist/tabs-layout/use-network-status.js +27 -4
  10. package/package.json +9 -3
  11. package/.claude/settings.local.json +0 -34
  12. package/.github/README.md +0 -52
  13. package/.github/workflows/delete-all-artifacts.yml +0 -49
  14. package/.github/workflows/publish.yml +0 -51
  15. package/babel.config.js +0 -7
  16. package/biome.json +0 -198
  17. package/dist/app-updates/app-updates-consumer.test.d.ts +0 -1
  18. package/dist/app-updates/app-updates-consumer.test.js +0 -56
  19. package/dist/app-updates/app-updates-settings.test.d.ts +0 -1
  20. package/dist/app-updates/app-updates-settings.test.js +0 -93
  21. package/dist/app-updates/remote-app-update-panel.test.d.ts +0 -1
  22. package/dist/app-updates/remote-app-update-panel.test.js +0 -165
  23. package/dist/components/access-level-field.test.d.ts +0 -1
  24. package/dist/components/access-level-field.test.js +0 -28
  25. package/dist/components/message-logs/tool-access-approval-card.test.d.ts +0 -1
  26. package/dist/components/message-logs/tool-access-approval-card.test.js +0 -102
  27. package/dist/components/responsive-section-nav.test.d.ts +0 -1
  28. package/dist/components/responsive-section-nav.test.js +0 -45
  29. package/dist/globals-hash.test.d.ts +0 -1
  30. package/dist/globals-hash.test.js +0 -16
  31. package/dist/hooks/use-observable-state.test.d.ts +0 -1
  32. package/dist/hooks/use-observable-state.test.js +0 -64
  33. package/dist/hooks/use-observable.test.d.ts +0 -1
  34. package/dist/hooks/use-observable.test.js +0 -191
  35. package/dist/hooks/use-rows.test.d.ts +0 -1
  36. package/dist/hooks/use-rows.test.js +0 -175
  37. package/dist/hooks/use-tool.test.d.ts +0 -1
  38. package/dist/hooks/use-tool.test.js +0 -190
  39. package/dist/host-shell/host-shell-consumer.test.d.ts +0 -1
  40. package/dist/host-shell/host-shell-consumer.test.js +0 -28
  41. package/dist/root-layout/root-layout-host.test.d.ts +0 -1
  42. package/dist/root-layout/root-layout-host.test.js +0 -86
  43. package/dist/screens/account/account-screen.test.d.ts +0 -1
  44. package/dist/screens/account/account-screen.test.js +0 -51
  45. package/dist/screens/assistants/assistant-config.test.d.ts +0 -1
  46. package/dist/screens/assistants/assistant-config.test.js +0 -57
  47. package/dist/screens/contacts/contact-list.test.d.ts +0 -1
  48. package/dist/screens/contacts/contact-list.test.js +0 -48
  49. package/dist/screens/device-pairing/device-pairing.test.d.ts +0 -1
  50. package/dist/screens/device-pairing/device-pairing.test.js +0 -119
  51. package/dist/screens/devices/devices-list.test.d.ts +0 -1
  52. package/dist/screens/devices/devices-list.test.js +0 -53
  53. package/dist/screens/groups/group-list.test.d.ts +0 -1
  54. package/dist/screens/groups/group-list.test.js +0 -38
  55. package/dist/screens/identity/identity-routes.test.d.ts +0 -1
  56. package/dist/screens/identity/identity-routes.test.js +0 -51
  57. package/dist/screens/identity/identity-screen.test.d.ts +0 -1
  58. package/dist/screens/identity/identity-screen.test.js +0 -103
  59. package/dist/screens/invites/accept-invite.test.d.ts +0 -1
  60. package/dist/screens/invites/accept-invite.test.js +0 -103
  61. package/dist/screens/invites/activity-view.test.d.ts +0 -1
  62. package/dist/screens/invites/activity-view.test.js +0 -108
  63. package/dist/screens/invites/invite-classify.test.d.ts +0 -1
  64. package/dist/screens/invites/invite-classify.test.js +0 -115
  65. package/dist/screens/network-viewer/network-viewer-ipc.test.d.ts +0 -1
  66. package/dist/screens/network-viewer/network-viewer-ipc.test.js +0 -61
  67. package/dist/screens/network-viewer/network-viewer.section.test.d.ts +0 -1
  68. package/dist/screens/network-viewer/network-viewer.section.test.js +0 -63
  69. package/dist/screens/network-viewer/sync-recovery.test.d.ts +0 -1
  70. package/dist/screens/network-viewer/sync-recovery.test.js +0 -104
  71. package/dist/screens/packages/package-helpers.test.d.ts +0 -1
  72. package/dist/screens/packages/package-helpers.test.js +0 -53
  73. package/dist/screens/packages/package-runtime-status.test.d.ts +0 -1
  74. package/dist/screens/packages/package-runtime-status.test.js +0 -50
  75. package/dist/screens/packages/packages-ipc.test.d.ts +0 -1
  76. package/dist/screens/packages/packages-ipc.test.js +0 -43
  77. package/dist/screens/settings/settings-page.test.d.ts +0 -1
  78. package/dist/screens/settings/settings-page.test.js +0 -19
  79. package/dist/screens/tools/tool-access-ipc.test.d.ts +0 -1
  80. package/dist/screens/tools/tool-access-ipc.test.js +0 -40
  81. package/dist/screens/tools/tool-catalog-ipc.test.d.ts +0 -1
  82. package/dist/screens/tools/tool-catalog-ipc.test.js +0 -52
  83. package/dist/tabs-layout/network-status-indicator.test.d.ts +0 -1
  84. package/dist/tabs-layout/network-status-indicator.test.js +0 -665
  85. package/dist/tabs-layout/tabs-state.test.d.ts +0 -1
  86. package/dist/tabs-layout/tabs-state.test.js +0 -110
  87. package/dist/tabs-layout/use-network-status.test.d.ts +0 -1
  88. package/dist/tabs-layout/use-network-status.test.js +0 -45
  89. package/dist/ui-router/ui-loader.test.d.ts +0 -1
  90. package/dist/ui-router/ui-loader.test.js +0 -91
  91. package/jest.config.js +0 -25
  92. package/release-guard.js +0 -10
  93. package/src/app-updates/app-update-controls.tsx +0 -73
  94. package/src/app-updates/app-updates-consumer.test.ts +0 -83
  95. package/src/app-updates/app-updates-consumer.ts +0 -121
  96. package/src/app-updates/app-updates-settings.test.tsx +0 -109
  97. package/src/app-updates/app-updates-settings.tsx +0 -136
  98. package/src/app-updates/remote-app-update-panel.test.tsx +0 -205
  99. package/src/app-updates/remote-app-update-panel.tsx +0 -210
  100. package/src/app-updates/use-app-update-status.ts +0 -16
  101. package/src/command-palette/command-palette-ui.tsx +0 -564
  102. package/src/command-palette/command-palette.ts +0 -242
  103. package/src/components/access-level-field.test.tsx +0 -32
  104. package/src/components/access-level-field.tsx +0 -68
  105. package/src/components/checkbox.tsx +0 -25
  106. package/src/components/group-switcher.tsx +0 -526
  107. package/src/components/input-date.tsx +0 -29
  108. package/src/components/input-number.tsx +0 -83
  109. package/src/components/input.tsx +0 -25
  110. package/src/components/inverse-lazy-list.tsx +0 -107
  111. package/src/components/io-schema-values.tsx +0 -103
  112. package/src/components/io-schema.tsx +0 -259
  113. package/src/components/lazy-list.tsx +0 -115
  114. package/src/components/list-screen.tsx +0 -126
  115. package/src/components/loading-indicator.tsx +0 -7
  116. package/src/components/markdown-editor/autolink-plugin.tsx +0 -29
  117. package/src/components/markdown-editor/editor-inline.tsx +0 -9
  118. package/src/components/markdown-editor/editor.tsx +0 -158
  119. package/src/components/markdown-editor/markdown-plugin.tsx +0 -202
  120. package/src/components/markdown-editor/mention-node.ts +0 -204
  121. package/src/components/markdown-editor/mentions-plugin.tsx +0 -354
  122. package/src/components/markdown-editor/move-line-plugin.tsx +0 -186
  123. package/src/components/markdown-editor/select-line-boundary-plugin.tsx +0 -95
  124. package/src/components/markdown-editor/theme.ts +0 -46
  125. package/src/components/markdown-editor/toolbar.tsx +0 -269
  126. package/src/components/markdown-with-mentions.tsx +0 -198
  127. package/src/components/message-logs/message-logs.tsx +0 -523
  128. package/src/components/message-logs/tool-access-approval-card.test.tsx +0 -127
  129. package/src/components/message-logs/tool-access-approval-card.tsx +0 -178
  130. package/src/components/messages/avatar.tsx +0 -171
  131. package/src/components/messages/channel-message-list.tsx +0 -189
  132. package/src/components/messages/channel-view.tsx +0 -92
  133. package/src/components/messages/message-compose.tsx +0 -192
  134. package/src/components/messages/message-display.tsx +0 -231
  135. package/src/components/messages/thread-message-list.tsx +0 -135
  136. package/src/components/qr-scanner.tsx +0 -180
  137. package/src/components/responsive-section-nav.test.tsx +0 -88
  138. package/src/components/responsive-section-nav.tsx +0 -92
  139. package/src/components/router.tsx +0 -175
  140. package/src/components/save-button.tsx +0 -134
  141. package/src/components/sortable-list.tsx +0 -130
  142. package/src/components/tabs.tsx +0 -76
  143. package/src/components/tooltip.tsx +0 -43
  144. package/src/components/trust-level-badge.tsx +0 -44
  145. package/src/components/trust-level-dropdown.tsx +0 -136
  146. package/src/components/typeahead.tsx +0 -357
  147. package/src/globals-hash.test.ts +0 -19
  148. package/src/globals.tsx +0 -226
  149. package/src/hooks/index.ts +0 -8
  150. package/src/hooks/use-host-contract.ts +0 -75
  151. package/src/hooks/use-observable-state.test.tsx +0 -101
  152. package/src/hooks/use-observable-state.ts +0 -29
  153. package/src/hooks/use-observable.test.tsx +0 -258
  154. package/src/hooks/use-observable.ts +0 -67
  155. package/src/hooks/use-on-screen.ts +0 -24
  156. package/src/hooks/use-promise.ts +0 -34
  157. package/src/hooks/use-rows.test.tsx +0 -196
  158. package/src/hooks/use-rows.ts +0 -120
  159. package/src/hooks/use-subscription.ts +0 -24
  160. package/src/hooks/use-tool.test.tsx +0 -225
  161. package/src/hooks/use-tool.ts +0 -116
  162. package/src/host-shell/host-shell-consumer.test.ts +0 -32
  163. package/src/host-shell/host-shell-consumer.ts +0 -53
  164. package/src/index.tsx +0 -42
  165. package/src/mention-configs.ts +0 -185
  166. package/src/operator-console/operator-console-state.ts +0 -119
  167. package/src/operator-console/operator-console.tsx +0 -233
  168. package/src/peers-services-registration.ts +0 -6
  169. package/src/root-layout/global-overlays.ts +0 -50
  170. package/src/root-layout/layout-picker.tsx +0 -47
  171. package/src/root-layout/layouts/full-screen-layout.tsx +0 -144
  172. package/src/root-layout/layouts/tabs-with-console-layout.tsx +0 -260
  173. package/src/root-layout/root-layout-host.test.tsx +0 -108
  174. package/src/root-layout/root-layout-host.tsx +0 -147
  175. package/src/root-layout/root-layout-registry.ts +0 -51
  176. package/src/root-layout/root-layout-state.ts +0 -55
  177. package/src/root-layout/root-layout.ts +0 -32
  178. package/src/screens/account/account-screen.test.tsx +0 -65
  179. package/src/screens/account/account-screen.tsx +0 -942
  180. package/src/screens/assistants/assistant-config.test.tsx +0 -54
  181. package/src/screens/assistants/assistant-config.tsx +0 -121
  182. package/src/screens/assistants/assistant-details.tsx +0 -89
  183. package/src/screens/assistants/assistant-info.tsx +0 -66
  184. package/src/screens/assistants/assistant-list.tsx +0 -145
  185. package/src/screens/assistants/assistant-tools.tsx +0 -82
  186. package/src/screens/console-logs/console-logs-list.tsx +0 -374
  187. package/src/screens/console-logs/log-display.tsx +0 -137
  188. package/src/screens/console-logs/log-filters.tsx +0 -64
  189. package/src/screens/console-logs/mobile-log-card.tsx +0 -161
  190. package/src/screens/console-logs/resizable-table-header.tsx +0 -110
  191. package/src/screens/contacts/contact-details.tsx +0 -123
  192. package/src/screens/contacts/contact-list.test.tsx +0 -59
  193. package/src/screens/contacts/contact-list.tsx +0 -286
  194. package/src/screens/contacts/index.ts +0 -2
  195. package/src/screens/data-explorer/data-explorer.tsx +0 -633
  196. package/src/screens/data-explorer/index.ts +0 -6
  197. package/src/screens/data-explorer/query-executor.tsx +0 -520
  198. package/src/screens/device-pairing/device-pairing.test.tsx +0 -143
  199. package/src/screens/device-pairing/device-pairing.tsx +0 -461
  200. package/src/screens/device-pairing/identity-ipc.ts +0 -25
  201. package/src/screens/device-pairing/index.ts +0 -2
  202. package/src/screens/device-pairing/use-device-pairing-state.ts +0 -15
  203. package/src/screens/devices/devices-list.test.tsx +0 -68
  204. package/src/screens/devices/devices-list.tsx +0 -365
  205. package/src/screens/devices/index.ts +0 -1
  206. package/src/screens/groups/group-details.tsx +0 -160
  207. package/src/screens/groups/group-invite-panel.tsx +0 -311
  208. package/src/screens/groups/group-list.test.tsx +0 -44
  209. package/src/screens/groups/group-list.tsx +0 -317
  210. package/src/screens/groups/group-members.tsx +0 -343
  211. package/src/screens/groups/index.ts +0 -3
  212. package/src/screens/identity/identity-routes.test.ts +0 -70
  213. package/src/screens/identity/identity-routes.ts +0 -301
  214. package/src/screens/identity/identity-screen.test.tsx +0 -138
  215. package/src/screens/identity/identity-screen.tsx +0 -282
  216. package/src/screens/identity/index.ts +0 -2
  217. package/src/screens/identity/invite-contact-panel.tsx +0 -45
  218. package/src/screens/identity/profile-invite.tsx +0 -93
  219. package/src/screens/invites/accept-invite.test.tsx +0 -115
  220. package/src/screens/invites/accept-invite.tsx +0 -318
  221. package/src/screens/invites/activity-view.test.tsx +0 -127
  222. package/src/screens/invites/activity-view.tsx +0 -162
  223. package/src/screens/invites/index.ts +0 -6
  224. package/src/screens/invites/invite-capture.ts +0 -104
  225. package/src/screens/invites/invite-classify.test.ts +0 -129
  226. package/src/screens/invites/invite-share.tsx +0 -104
  227. package/src/screens/invites/invites-ipc.ts +0 -25
  228. package/src/screens/invites/pending-invites-list.tsx +0 -256
  229. package/src/screens/network-viewer/connection-troubleshooter.tsx +0 -395
  230. package/src/screens/network-viewer/cpu-usage-graph.tsx +0 -294
  231. package/src/screens/network-viewer/device-details-modal.tsx +0 -339
  232. package/src/screens/network-viewer/group-details-modal.tsx +0 -421
  233. package/src/screens/network-viewer/index.ts +0 -8
  234. package/src/screens/network-viewer/network-viewer-ipc.test.ts +0 -72
  235. package/src/screens/network-viewer/network-viewer-ipc.ts +0 -39
  236. package/src/screens/network-viewer/network-viewer-section.ts +0 -12
  237. package/src/screens/network-viewer/network-viewer.section.test.tsx +0 -68
  238. package/src/screens/network-viewer/network-viewer.tsx +0 -708
  239. package/src/screens/network-viewer/remote-logs-panel.tsx +0 -190
  240. package/src/screens/network-viewer/sync-recovery.test.tsx +0 -114
  241. package/src/screens/network-viewer/sync-recovery.tsx +0 -461
  242. package/src/screens/network-viewer/usage-graph.tsx +0 -455
  243. package/src/screens/packages/local-folder-grants-panel.tsx +0 -290
  244. package/src/screens/packages/local-folders-ipc.ts +0 -28
  245. package/src/screens/packages/package-details.tsx +0 -166
  246. package/src/screens/packages/package-helpers.test.ts +0 -57
  247. package/src/screens/packages/package-helpers.ts +0 -137
  248. package/src/screens/packages/package-info.tsx +0 -354
  249. package/src/screens/packages/package-list.tsx +0 -519
  250. package/src/screens/packages/package-network-panel.tsx +0 -82
  251. package/src/screens/packages/package-new-local.tsx +0 -100
  252. package/src/screens/packages/package-runtime-status.test.tsx +0 -58
  253. package/src/screens/packages/package-runtime-status.tsx +0 -68
  254. package/src/screens/packages/package-versions.tsx +0 -298
  255. package/src/screens/packages/packages-ipc.test.ts +0 -47
  256. package/src/screens/packages/packages-ipc.ts +0 -125
  257. package/src/screens/peer-types/peer-type-details.tsx +0 -172
  258. package/src/screens/peer-types/peer-type-list.tsx +0 -64
  259. package/src/screens/search/global-search.tsx +0 -406
  260. package/src/screens/settings/color-mode-dropdown.tsx +0 -82
  261. package/src/screens/settings/settings-page.test.tsx +0 -24
  262. package/src/screens/settings/settings-page.tsx +0 -426
  263. package/src/screens/setup-user.tsx +0 -355
  264. package/src/screens/tools/tool-access-ipc.test.ts +0 -55
  265. package/src/screens/tools/tool-access-ipc.ts +0 -27
  266. package/src/screens/tools/tool-catalog-ipc.test.ts +0 -52
  267. package/src/screens/tools/tool-catalog-ipc.ts +0 -51
  268. package/src/screens/tools/tool-details.tsx +0 -52
  269. package/src/screens/tools/tool-info.tsx +0 -45
  270. package/src/screens/tools/tool-list.tsx +0 -106
  271. package/src/screens/tools/tool-schema.tsx +0 -44
  272. package/src/screens/tools/tool-test-details.tsx +0 -89
  273. package/src/screens/tools/tool-test-list.tsx +0 -73
  274. package/src/screens/variables/variable-details.tsx +0 -289
  275. package/src/screens/variables/variable-list.tsx +0 -110
  276. package/src/screens/welcome-modal.tsx +0 -294
  277. package/src/screens/workflows/workflow-details.tsx +0 -115
  278. package/src/screens/workflows/workflow-info.tsx +0 -22
  279. package/src/screens/workflows/workflow-instructions.tsx +0 -134
  280. package/src/screens/workflows/workflow-list.tsx +0 -117
  281. package/src/setupTests.ts +0 -38
  282. package/src/system-apps/assistants.app.ts +0 -7
  283. package/src/system-apps/console-logs.app.ts +0 -7
  284. package/src/system-apps/data-explorer.app.ts +0 -8
  285. package/src/system-apps/identity.app.ts +0 -9
  286. package/src/system-apps/index.ts +0 -77
  287. package/src/system-apps/mobile-settings.app.ts +0 -7
  288. package/src/system-apps/network-viewer.app.ts +0 -8
  289. package/src/system-apps/packages.app.ts +0 -7
  290. package/src/system-apps/search.app.ts +0 -8
  291. package/src/system-apps/settings.app.ts +0 -7
  292. package/src/system-apps/threads.app.ts +0 -7
  293. package/src/system-apps/tools.app.ts +0 -7
  294. package/src/system-apps/types.app.ts +0 -7
  295. package/src/system-apps/variables.app.ts +0 -7
  296. package/src/system-apps/workflows.app.ts +0 -7
  297. package/src/tabs-layout/network-status-indicator.test.tsx +0 -799
  298. package/src/tabs-layout/network-status-indicator.tsx +0 -756
  299. package/src/tabs-layout/tabs-layout.tsx +0 -857
  300. package/src/tabs-layout/tabs-state.test.ts +0 -152
  301. package/src/tabs-layout/tabs-state.ts +0 -587
  302. package/src/tabs-layout/use-network-status.test.tsx +0 -58
  303. package/src/tabs-layout/use-network-status.ts +0 -68
  304. package/src/ui-defaults/index.ts +0 -2
  305. package/src/ui-defaults/list-screen.tsx +0 -94
  306. package/src/ui-defaults/markdown-field.tsx +0 -87
  307. package/src/ui-router/routes-loader.ts +0 -193
  308. package/src/ui-router/ui-loader.test.tsx +0 -108
  309. package/src/ui-router/ui-loader.tsx +0 -606
  310. package/src/utils.ts +0 -95
  311. package/tsconfig.json +0 -18
@@ -1,665 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const jsx_runtime_1 = require("react/jsx-runtime");
7
- const peers_sdk_1 = require("@peers-app/peers-sdk");
8
- const react_1 = require("@testing-library/react");
9
- const moment_1 = __importDefault(require("moment"));
10
- const identity_routes_1 = require("../screens/identity/identity-routes");
11
- const network_viewer_section_1 = require("../screens/network-viewer/network-viewer-section");
12
- const network_status_indicator_1 = require("./network-status-indicator");
13
- const goToTabPath = jest.fn();
14
- const getConnections = jest.fn();
15
- let mockViewState = { availability: "loading" };
16
- let mockAppUpdate;
17
- const installUpdateAndRestart = jest.fn();
18
- jest.mock("./tabs-state", () => ({
19
- goToTabPath: (...args) => goToTabPath(...args),
20
- }));
21
- jest.mock("./use-network-status", () => ({
22
- useNetworkStatus: () => mockViewState,
23
- }));
24
- jest.mock("../app-updates/use-app-update-status", () => ({
25
- useAppUpdateStatus: () => mockAppUpdate,
26
- }));
27
- jest.mock("../app-updates/app-updates-consumer", () => ({
28
- ...jest.requireActual("../app-updates/app-updates-consumer"),
29
- getAppUpdatesConsumer: () => Promise.resolve({
30
- tools: {
31
- installUpdateAndRestart: (...args) => installUpdateAndRestart(...args),
32
- },
33
- }),
34
- }));
35
- function appUpdate(overrides = {}) {
36
- return {
37
- supported: true,
38
- canRestart: true,
39
- platform: "darwin",
40
- currentVersion: "1.0.0",
41
- latestVersion: "1.1.0",
42
- downloadedVersion: "1.1.0",
43
- state: "downloaded",
44
- installOnRestart: true,
45
- installOnQuitArmed: true,
46
- remoteRestartAllowed: true,
47
- ...overrides,
48
- };
49
- }
50
- jest.mock("../screens/network-viewer/network-viewer-ipc", () => ({
51
- getNetworkDiagnosticsConsumer: () => Promise.resolve({
52
- tools: {
53
- getConnections: (...args) => getConnections(...args),
54
- },
55
- }),
56
- }));
57
- function status(overrides) {
58
- return {
59
- state: "synced",
60
- connectionCount: 2,
61
- syncingContextCount: 0,
62
- errorCount: 0,
63
- disconnectedContextCount: 0,
64
- connectedContextCount: 0,
65
- stuckRecoveryCount: 0,
66
- errors: [],
67
- ...overrides,
68
- };
69
- }
70
- function connection(overrides = {}) {
71
- return {
72
- deviceId: "device-1",
73
- userId: "user-1",
74
- userName: "Ada",
75
- deviceName: "Laptop",
76
- userTrustLevel: peers_sdk_1.TrustLevel.Trusted,
77
- deviceTrustLevel: peers_sdk_1.TrustLevel.Trusted,
78
- connectionState: "connected",
79
- sharedGroups: ["group-a"],
80
- roles: {},
81
- pathType: "direct",
82
- latencyMs: 10,
83
- errorRate: 0,
84
- isServer: false,
85
- ...overrides,
86
- };
87
- }
88
- describe("formatRelativeSyncTime", () => {
89
- it("returns now for very recent syncs", () => {
90
- const now = Date.parse("2026-07-28T23:43:06.000");
91
- expect((0, network_status_indicator_1.formatRelativeSyncTime)(now - 10_000, now)).toBe("now");
92
- });
93
- it("returns yesterday for the previous calendar day", () => {
94
- const now = (0, moment_1.default)("2026-07-28T23:43:06").valueOf();
95
- const yesterdayAfternoon = (0, moment_1.default)("2026-07-28T23:43:06")
96
- .subtract(1, "day")
97
- .hour(18)
98
- .minute(0)
99
- .second(0)
100
- .valueOf();
101
- expect((0, network_status_indicator_1.formatRelativeSyncTime)(yesterdayAfternoon, now)).toBe("yesterday");
102
- });
103
- it("returns a short relative phrase otherwise", () => {
104
- const now = Date.parse("2026-07-28T23:43:06.000");
105
- expect((0, network_status_indicator_1.formatRelativeSyncTime)(now - 5 * 60_000, now)).toBe("5 minutes ago");
106
- });
107
- });
108
- describe("connectionIdentityLabel", () => {
109
- it("prefers a friendly name", () => {
110
- expect((0, network_status_indicator_1.connectionIdentityLabel)("Ada", "00mabcdefghijklmnopqrs")).toEqual({
111
- text: "Ada",
112
- isId: false,
113
- title: "Ada",
114
- });
115
- });
116
- it("middle-ellipsizes raw ids", () => {
117
- expect((0, network_status_indicator_1.connectionIdentityLabel)(undefined, "00mabcdefghijklmnopqrs")).toEqual({
118
- text: "00ma…pqrs",
119
- isId: true,
120
- title: "00mabcdefghijklmnopqrs",
121
- });
122
- });
123
- });
124
- describe("getNetworkStatusPresentation", () => {
125
- it("uses a compact green check-circle when synced", () => {
126
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({ availability: "ready", status: status({ state: "synced" }) })).toMatchObject({
127
- shortLabel: "Synced",
128
- colorClass: "text-success",
129
- iconClass: "bi-check-circle-fill",
130
- compactIcon: true,
131
- });
132
- });
133
- it("uses a solid red dot when in error", () => {
134
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
135
- availability: "ready",
136
- status: status({
137
- state: "error",
138
- errorCount: 1,
139
- errors: [{ dataContextId: "context", message: "failed", occurredAt: 1 }],
140
- }),
141
- })).toMatchObject({
142
- shortLabel: "Error",
143
- colorClass: "text-danger",
144
- mark: "solid",
145
- });
146
- });
147
- it("pulses green then switches to an indeterminate circle while syncing", () => {
148
- const syncing = {
149
- availability: "ready",
150
- status: status({ state: "syncing", syncingContextCount: 1 }),
151
- };
152
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)(syncing)).toMatchObject({
153
- colorClass: "text-success",
154
- mark: "pulse",
155
- });
156
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)(syncing, { syncingLongerThanGreenMs: true })).toMatchObject({
157
- colorClass: "text-primary",
158
- mark: "indeterminate",
159
- });
160
- });
161
- it("turns yellow for stuck recovery without hiding sync activity or errors", () => {
162
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
163
- availability: "ready",
164
- status: status({ state: "synced", stuckRecoveryCount: 2 }),
165
- })).toMatchObject({
166
- colorClass: "text-warning",
167
- iconClass: "bi-check-circle-fill",
168
- label: expect.stringContaining("2 records need attention"),
169
- });
170
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
171
- availability: "ready",
172
- status: status({ state: "syncing", syncingContextCount: 1, stuckRecoveryCount: 1 }),
173
- }, { syncingLongerThanGreenMs: true })).toMatchObject({
174
- colorClass: "text-warning",
175
- mark: "indeterminate",
176
- label: expect.stringContaining("1 record needs attention"),
177
- });
178
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
179
- availability: "ready",
180
- status: status({ state: "error", errorCount: 1, stuckRecoveryCount: 4 }),
181
- }).colorClass).toBe("text-danger");
182
- });
183
- it("holds a green pulse while waiting for stable synced", () => {
184
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({ availability: "ready", status: status({ state: "synced" }) }, { holdSyncingVisual: true, syncingLongerThanGreenMs: true })).toMatchObject({
185
- shortLabel: "Syncing",
186
- colorClass: "text-success",
187
- mark: "pulse",
188
- });
189
- });
190
- it("flags a disconnected-group count when some groups lack a connection", () => {
191
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
192
- availability: "ready",
193
- status: status({ state: "synced", disconnectedContextCount: 2 }),
194
- })).toMatchObject({
195
- disconnectedGroupsOverlay: { count: 2 },
196
- label: expect.stringContaining("2 groups without a connection"),
197
- });
198
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
199
- availability: "ready",
200
- status: status({ state: "offline", connectionCount: 0, disconnectedContextCount: 2 }),
201
- })).toMatchObject({
202
- iconClass: "bi-cloud-slash-fill",
203
- });
204
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
205
- availability: "ready",
206
- status: status({ state: "offline", connectionCount: 0, disconnectedContextCount: 2 }),
207
- }).disconnectedGroupsOverlay).toBeUndefined();
208
- });
209
- it("shows a green connected-groups count or red zero when the device is online", () => {
210
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
211
- availability: "ready",
212
- status: status({
213
- state: "synced",
214
- connectionCount: 2,
215
- connectedContextCount: 3,
216
- }),
217
- })).toMatchObject({
218
- connectedGroupsOverlay: { count: 3, colorClass: "text-success" },
219
- label: expect.stringContaining("3 groups connected"),
220
- });
221
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
222
- availability: "ready",
223
- status: status({
224
- state: "synced",
225
- connectionCount: 1,
226
- connectedContextCount: 0,
227
- }),
228
- })).toMatchObject({
229
- connectedGroupsOverlay: { count: 0, colorClass: "text-danger" },
230
- label: expect.stringContaining("0 groups connected"),
231
- });
232
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
233
- availability: "ready",
234
- status: status({
235
- state: "offline",
236
- connectionCount: 0,
237
- connectedContextCount: 0,
238
- }),
239
- }).connectedGroupsOverlay).toBeUndefined();
240
- });
241
- it("turns an offline indicator yellow when recovery is stuck and keeps the offline icon", () => {
242
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
243
- availability: "ready",
244
- status: status({ state: "offline", connectionCount: 0, stuckRecoveryCount: 2 }),
245
- })).toMatchObject({
246
- iconClass: "bi-cloud-slash-fill",
247
- colorClass: "text-warning",
248
- label: expect.stringContaining("2 records need attention"),
249
- });
250
- expect((0, network_status_indicator_1.getNetworkStatusPresentation)({
251
- availability: "ready",
252
- status: status({ state: "error", errorCount: 1, stuckRecoveryCount: 2 }),
253
- }).colorClass).toBe("text-danger");
254
- });
255
- });
256
- describe("withAppUpdate", () => {
257
- const synced = (0, network_status_indicator_1.getNetworkStatusPresentation)({
258
- availability: "ready",
259
- status: status({ state: "synced" }),
260
- });
261
- it("leaves the presentation alone without an available update", () => {
262
- expect((0, network_status_indicator_1.withAppUpdate)(synced, undefined)).toBe(synced);
263
- expect((0, network_status_indicator_1.withAppUpdate)(synced, appUpdate({ state: "up-to-date" }))).toBe(synced);
264
- expect((0, network_status_indicator_1.withAppUpdate)(synced, appUpdate({ supported: false }))).toBe(synced);
265
- expect((0, network_status_indicator_1.withAppUpdate)(synced, appUpdate({
266
- state: "error",
267
- latestVersion: "1.0.0",
268
- downloadedVersion: undefined,
269
- error: "feed unreachable",
270
- }))).toBe(synced);
271
- });
272
- it("keeps the update badge after a failed download while a newer version is known", () => {
273
- const presentation = (0, network_status_indicator_1.withAppUpdate)(synced, appUpdate({ state: "error", downloadedVersion: undefined, error: "download failed" }));
274
- expect(presentation.updateAvailableOverlay).toBe(true);
275
- expect(presentation.label).toBe(`${synced.label}; Version 1.1.0 is available. Update problem: download failed`);
276
- });
277
- it("adds the update badge and label while an update is available, downloading, or ready", () => {
278
- for (const state of ["available", "downloading", "downloaded"]) {
279
- const presentation = (0, network_status_indicator_1.withAppUpdate)(synced, appUpdate({ state }));
280
- expect(presentation.updateAvailableOverlay).toBe(true);
281
- expect(presentation.label).toMatch(/^Network synced; .*1\.1\.0/);
282
- expect(presentation.colorClass).toBe(synced.colorClass);
283
- }
284
- });
285
- it("applies offline too", () => {
286
- const offline = (0, network_status_indicator_1.getNetworkStatusPresentation)({
287
- availability: "ready",
288
- status: status({ state: "offline", connectionCount: 0 }),
289
- });
290
- expect((0, network_status_indicator_1.withAppUpdate)(offline, appUpdate()).updateAvailableOverlay).toBe(true);
291
- });
292
- });
293
- describe("NetworkStatusIndicator", () => {
294
- beforeEach(() => {
295
- mockViewState = { availability: "loading" };
296
- mockAppUpdate = undefined;
297
- installUpdateAndRestart.mockReset();
298
- installUpdateAndRestart.mockResolvedValue({ action: "installing" });
299
- (0, network_viewer_section_1.networkViewerSection)("main");
300
- getConnections.mockReset();
301
- getConnections.mockResolvedValue({ connections: [] });
302
- jest.clearAllMocks();
303
- });
304
- afterEach(() => {
305
- jest.useRealTimers();
306
- });
307
- it.each([
308
- [{ availability: "loading" }, /Network status loading/],
309
- [{ availability: "unavailable" }, /Network status unavailable/],
310
- [
311
- { availability: "ready", status: status({ state: "offline", connectionCount: 0 }) },
312
- /Network offline/,
313
- ],
314
- [
315
- {
316
- availability: "ready",
317
- status: status({ state: "syncing", syncingContextCount: 1 }),
318
- },
319
- /Network syncing/,
320
- ],
321
- [{ availability: "ready", status: status({ state: "synced" }) }, /Network synced/],
322
- [
323
- {
324
- availability: "ready",
325
- status: status({
326
- state: "error",
327
- errorCount: 1,
328
- errors: [{ dataContextId: "context", message: "failed", occurredAt: 1 }],
329
- }),
330
- },
331
- /Network error/,
332
- ],
333
- ])("renders an accessible state for %#", (viewState, accessibleName) => {
334
- mockViewState = viewState;
335
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
336
- expect(view.getByRole("button", { name: accessibleName })).toBeTruthy();
337
- });
338
- it("renders a compact green check-circle when synced and a solid red error dot", () => {
339
- mockViewState = { availability: "ready", status: status({ state: "synced" }) };
340
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
341
- const syncedIcon = view.container.querySelector("i.bi-check-circle-fill");
342
- expect(syncedIcon).toBeTruthy();
343
- expect(syncedIcon).toHaveStyle({ fontSize: "0.7rem" });
344
- expect(view.container.querySelector(".network-status-pulse-dot")).toBeNull();
345
- expect(view.container.querySelector(".network-status-disconnected-count")).toBeNull();
346
- mockViewState = {
347
- availability: "ready",
348
- status: status({
349
- state: "error",
350
- errorCount: 1,
351
- errors: [{ dataContextId: "context", message: "failed", occurredAt: 1 }],
352
- }),
353
- };
354
- view.rerender((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
355
- expect(view.container.querySelector(".bg-danger.rounded-circle")).toBeTruthy();
356
- });
357
- it("shows a bottom-left update badge and an Update & restart action when an update is ready", async () => {
358
- mockViewState = { availability: "ready", status: status({ state: "synced" }) };
359
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
360
- expect(view.container.querySelector(".network-status-update-badge")).toBeNull();
361
- mockAppUpdate = appUpdate();
362
- view.rerender((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
363
- const badge = view.container.querySelector(".network-status-update-badge");
364
- expect(badge).toHaveClass("bi-arrow-up-circle-fill", "text-warning");
365
- expect(badge).toHaveStyle({ left: "-0.46rem", bottom: "-0.08rem" });
366
- react_1.fireEvent.click(view.getByRole("button", { name: /Version 1\.1\.0 is ready to install/ }));
367
- const section = view.getByTestId("app-update-section");
368
- expect(section).toHaveTextContent("Installs automatically the next time Peers restarts");
369
- react_1.fireEvent.click(view.getByRole("button", { name: /Update & restart/ }));
370
- await (0, react_1.waitFor)(() => expect(installUpdateAndRestart).toHaveBeenCalledTimes(1));
371
- });
372
- it("omits the install-on-restart note when nothing is armed to install", async () => {
373
- mockViewState = { availability: "ready", status: status({ state: "synced" }) };
374
- mockAppUpdate = appUpdate({ installOnRestart: false, installOnQuitArmed: false });
375
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
376
- await openNetworkDetails(view, /ready to install/);
377
- expect(view.getByTestId("app-update-section")).not.toHaveTextContent("Installs automatically");
378
- });
379
- it("shows a yellow disconnected-groups count when synced with offline groups", () => {
380
- mockViewState = {
381
- availability: "ready",
382
- status: status({ state: "synced", disconnectedContextCount: 1 }),
383
- };
384
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
385
- expect(view.container.querySelector("i.bi-check-circle-fill")).toBeTruthy();
386
- const overlay = view.container.querySelector(".network-status-disconnected-count.text-warning");
387
- expect(overlay?.textContent).toBe("1");
388
- expect(view.getByRole("button", { name: /1 group without a connection/ })).toBeTruthy();
389
- });
390
- it("shows a green connected-groups overlay when groups are live", () => {
391
- mockViewState = {
392
- availability: "ready",
393
- status: status({
394
- state: "synced",
395
- connectionCount: 2,
396
- connectedContextCount: 2,
397
- }),
398
- };
399
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
400
- const overlay = view.container.querySelector(".network-status-connected-count.text-success");
401
- expect(overlay?.textContent).toBe("2");
402
- });
403
- it("shows a red zero when the device is connected but no groups are live", () => {
404
- mockViewState = {
405
- availability: "ready",
406
- status: status({
407
- state: "synced",
408
- connectionCount: 1,
409
- connectedContextCount: 0,
410
- }),
411
- };
412
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
413
- const overlay = view.container.querySelector(".network-status-connected-count.text-danger");
414
- expect(overlay?.textContent).toBe("0");
415
- });
416
- it("hides the connected-groups overlay when fully offline", () => {
417
- mockViewState = {
418
- availability: "ready",
419
- status: status({
420
- state: "offline",
421
- connectionCount: 0,
422
- connectedContextCount: 0,
423
- }),
424
- };
425
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
426
- expect(view.container.querySelector(".network-status-connected-count")).toBeNull();
427
- });
428
- it("does not show the disconnected-groups count when fully offline", () => {
429
- mockViewState = {
430
- availability: "ready",
431
- status: status({
432
- state: "offline",
433
- connectionCount: 0,
434
- disconnectedContextCount: 3,
435
- }),
436
- };
437
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
438
- expect(view.container.querySelector("i.bi-cloud-slash-fill")).toBeTruthy();
439
- expect(view.container.querySelector(".network-status-disconnected-count")).toBeNull();
440
- });
441
- it("uses a calm opacity pulse while syncing", () => {
442
- mockViewState = {
443
- availability: "ready",
444
- status: status({ state: "syncing", syncingContextCount: 1 }),
445
- };
446
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
447
- const pulse = view.container.querySelector(".network-status-pulse-dot.bg-success");
448
- expect(pulse).toHaveStyle({
449
- animation: "peers-network-pulse-dot 2s ease-in-out infinite",
450
- });
451
- });
452
- it("pulses green while syncing, then an indeterminate circle after the green window", () => {
453
- jest.useFakeTimers();
454
- mockViewState = {
455
- availability: "ready",
456
- status: status({ state: "syncing", syncingContextCount: 1 }),
457
- };
458
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
459
- expect(view.container.querySelector(".network-status-pulse-dot.bg-success")).toBeTruthy();
460
- (0, react_1.act)(() => {
461
- jest.advanceTimersByTime(network_status_indicator_1.SYNCING_GREEN_MS);
462
- });
463
- expect(view.container.querySelector(".spinner-border.text-primary")).toBeTruthy();
464
- expect(view.container.querySelector(".network-status-pulse-dot")).toBeNull();
465
- });
466
- it("keeps the green syncing pulse through brief synced flaps until synced is stable", () => {
467
- jest.useFakeTimers();
468
- mockViewState = {
469
- availability: "ready",
470
- status: status({ state: "syncing", syncingContextCount: 1 }),
471
- };
472
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
473
- expect(view.container.querySelector(".network-status-pulse-dot.bg-success")).toBeTruthy();
474
- mockViewState = { availability: "ready", status: status({ state: "synced" }) };
475
- view.rerender((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
476
- (0, react_1.act)(() => {
477
- jest.advanceTimersByTime(network_status_indicator_1.SYNCED_STABLE_MS - 1);
478
- });
479
- expect(view.container.querySelector(".network-status-pulse-dot.bg-success")).toBeTruthy();
480
- expect(view.container.querySelector("i.bi-check-circle-fill")).toBeNull();
481
- mockViewState = {
482
- availability: "ready",
483
- status: status({ state: "syncing", syncingContextCount: 1 }),
484
- };
485
- view.rerender((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
486
- expect(view.container.querySelector(".network-status-pulse-dot.bg-success")).toBeTruthy();
487
- mockViewState = { availability: "ready", status: status({ state: "synced" }) };
488
- view.rerender((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
489
- (0, react_1.act)(() => {
490
- jest.advanceTimersByTime(network_status_indicator_1.SYNCED_STABLE_MS);
491
- });
492
- expect(view.container.querySelector("i.bi-check-circle-fill")).toBeTruthy();
493
- expect(view.container.querySelector(".network-status-pulse-dot")).toBeNull();
494
- });
495
- it("lists connected devices in the details popover", async () => {
496
- getConnections.mockResolvedValue({
497
- connections: [
498
- connection({
499
- deviceId: "device-1",
500
- userName: "Ada",
501
- deviceName: "Laptop",
502
- sharedGroups: ["g1", "g2"],
503
- }),
504
- ],
505
- });
506
- mockViewState = { availability: "ready", status: status({ state: "synced" }) };
507
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
508
- react_1.fireEvent.click(view.getByRole("button", { name: /Network synced/ }));
509
- await (0, react_1.waitFor)(() => {
510
- expect(view.getByRole("dialog", { name: "Network status details" })).toHaveTextContent("Ada");
511
- });
512
- expect(view.getByRole("dialog")).toHaveTextContent("Laptop");
513
- expect(view.getByRole("dialog")).toHaveTextContent("2g");
514
- expect(getConnections).toHaveBeenCalled();
515
- });
516
- it("shows middle-ellipsized ids when names are missing", async () => {
517
- getConnections.mockResolvedValue({
518
- connections: [
519
- connection({
520
- deviceId: "00mdeviceidabcdefghijklm",
521
- userId: "00museridabcdefghijklmn",
522
- userName: undefined,
523
- deviceName: undefined,
524
- sharedGroups: [],
525
- }),
526
- ],
527
- });
528
- mockViewState = { availability: "ready", status: status({ state: "synced" }) };
529
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
530
- react_1.fireEvent.click(view.getByRole("button", { name: /Network synced/ }));
531
- await (0, react_1.waitFor)(() => {
532
- expect(view.getByRole("dialog")).toHaveTextContent("00mu…klmn");
533
- });
534
- expect(view.getByRole("dialog")).toHaveTextContent("00md…jklm");
535
- });
536
- it("shows an empty connected-devices message when there are none", async () => {
537
- getConnections.mockResolvedValue({ connections: [] });
538
- mockViewState = {
539
- availability: "ready",
540
- status: status({ state: "offline", connectionCount: 0 }),
541
- };
542
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
543
- react_1.fireEvent.click(view.getByRole("button", { name: /Network offline/ }));
544
- await (0, react_1.waitFor)(() => {
545
- expect(view.getByRole("dialog")).toHaveTextContent("No connected devices");
546
- });
547
- });
548
- it("shows an error when connected devices fail to load", async () => {
549
- getConnections.mockRejectedValue(new Error("offline"));
550
- mockViewState = { availability: "ready", status: status({ state: "synced" }) };
551
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
552
- react_1.fireEvent.click(view.getByRole("button", { name: /Network synced/ }));
553
- await (0, react_1.waitFor)(() => {
554
- expect(view.getByRole("dialog")).toHaveTextContent("Couldn't load devices");
555
- });
556
- });
557
- it("offers shortcuts to use an invite and open the Identity account", () => {
558
- mockViewState = { availability: "ready", status: status({ state: "synced" }) };
559
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
560
- react_1.fireEvent.click(view.getByRole("button", { name: /Network synced/ }));
561
- expect(view.getByRole("button", { name: "Use Invite" }).querySelector(".bi-qr-code")).toBeTruthy();
562
- expect(view.getByRole("button", { name: "Identity" }).querySelector(".bi-person-badge")).toBeTruthy();
563
- react_1.fireEvent.click(view.getByRole("button", { name: "Use Invite" }));
564
- expect(goToTabPath).toHaveBeenCalledWith(identity_routes_1.IDENTITY_ACCEPT_PATH);
565
- expect(view.queryByRole("dialog", { name: "Network status details" })).toBeNull();
566
- react_1.fireEvent.click(view.getByRole("button", { name: /Network synced/ }));
567
- react_1.fireEvent.click(view.getByRole("button", { name: "Identity" }));
568
- expect(goToTabPath).toHaveBeenCalledWith(identity_routes_1.IDENTITY_ACCOUNT_PATH);
569
- expect(view.queryByRole("dialog", { name: "Network status details" })).toBeNull();
570
- });
571
- it("updates on rerender and opens details before navigating to Network Viewer", async () => {
572
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
573
- expect(view.getByRole("button", { name: /loading/ })).toBeTruthy();
574
- mockViewState = {
575
- availability: "ready",
576
- status: status({
577
- state: "error",
578
- connectionCount: 3,
579
- errorCount: 1,
580
- errors: [{ dataContextId: "context", message: "sync failed", occurredAt: 1 }],
581
- }),
582
- };
583
- view.rerender((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
584
- react_1.fireEvent.click(view.getByRole("button", { name: /Network error/ }));
585
- expect(view.getByRole("dialog", { name: "Network status details" })).toHaveTextContent("sync failed");
586
- await (0, react_1.waitFor)(() => {
587
- expect(getConnections).toHaveBeenCalled();
588
- });
589
- react_1.fireEvent.keyDown(document, { key: "Escape" });
590
- expect(view.queryByRole("dialog", { name: "Network status details" })).toBeNull();
591
- react_1.fireEvent.click(view.getByRole("button", { name: /Network error/ }));
592
- react_1.fireEvent.click(view.getByRole("button", { name: "Open Network Viewer" }));
593
- expect(goToTabPath).toHaveBeenCalledWith("network-viewer");
594
- expect((0, network_viewer_section_1.networkViewerSection)()).toBe("main");
595
- });
596
- it("hides zero problem counts and Sync recovery when nothing needs attention", async () => {
597
- mockViewState = {
598
- availability: "ready",
599
- status: status({
600
- state: "synced",
601
- connectionCount: 4,
602
- connectedContextCount: 5,
603
- lastSuccessfulSyncAt: Date.parse("2026-09-27T16:00:00.000Z"),
604
- }),
605
- };
606
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
607
- await openNetworkDetails(view, /Network synced/);
608
- const labels = [...view.getByRole("dialog").querySelectorAll("dt")].map((el) => el.textContent);
609
- expect(labels).toEqual(["Connections", "Groups connected", "Last sync"]);
610
- expect(view.queryByRole("button", { name: "Sync recovery" })).toBeNull();
611
- });
612
- it("orders non-zero status rows and offers Sync recovery when records are stuck", async () => {
613
- mockViewState = {
614
- availability: "ready",
615
- status: status({
616
- state: "error",
617
- connectionCount: 4,
618
- connectedContextCount: 5,
619
- disconnectedContextCount: 1,
620
- syncingContextCount: 2,
621
- errorCount: 3,
622
- stuckRecoveryCount: 1,
623
- lastSuccessfulSyncAt: Date.parse("2026-09-27T16:00:00.000Z"),
624
- errors: [{ dataContextId: "context", message: "sync failed", occurredAt: 1 }],
625
- }),
626
- };
627
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
628
- await openNetworkDetails(view, /Network error/);
629
- const labels = [...view.getByRole("dialog").querySelectorAll("dt")].map((el) => el.textContent);
630
- expect(labels).toEqual([
631
- "Connections",
632
- "Groups connected",
633
- "Syncing contexts",
634
- "Groups offline",
635
- "Sync errors",
636
- "Last sync",
637
- ]);
638
- const groupsOffline = view.getByText("Groups offline");
639
- expect(groupsOffline.className).toContain("text-warning");
640
- expect(groupsOffline.nextElementSibling?.textContent).toBe("1");
641
- expect(groupsOffline.nextElementSibling?.className).toContain("text-warning");
642
- expect(view.getByRole("button", { name: "Sync recovery" }).className).toContain("btn-warning");
643
- });
644
- it("opens Sync recovery inside the Network tab", async () => {
645
- (0, network_viewer_section_1.networkViewerSection)("main");
646
- mockViewState = {
647
- availability: "ready",
648
- status: status({ state: "synced", stuckRecoveryCount: 2 }),
649
- };
650
- const view = (0, react_1.render)((0, jsx_runtime_1.jsx)(network_status_indicator_1.NetworkStatusIndicator, {}));
651
- await openNetworkDetails(view, /Network synced/);
652
- react_1.fireEvent.click(view.getByRole("button", { name: "Sync recovery" }));
653
- expect((0, network_viewer_section_1.networkViewerSection)()).toBe("sync-recovery");
654
- expect(goToTabPath).toHaveBeenCalledWith("network-viewer");
655
- expect(goToTabPath).not.toHaveBeenCalledWith("sync-recovery");
656
- expect(view.queryByRole("dialog", { name: "Network status details" })).toBeNull();
657
- });
658
- });
659
- async function openNetworkDetails(view, name) {
660
- react_1.fireEvent.click(view.getByRole("button", { name }));
661
- await (0, react_1.waitFor)(() => expect(getConnections).toHaveBeenCalled());
662
- await (0, react_1.act)(async () => {
663
- await Promise.resolve();
664
- });
665
- }
@@ -1 +0,0 @@
1
- export {};