@wopr-network/platform-ui-core 1.27.8 → 1.27.9

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 (353) hide show
  1. package/next.config.ts +1 -2
  2. package/package.json +17 -17
  3. package/src/__tests__/account-switcher.test.tsx +21 -20
  4. package/src/__tests__/activity-page.test.tsx +2 -6
  5. package/src/__tests__/add-payment-method-dialog.test.tsx +9 -32
  6. package/src/__tests__/admin-api.test.ts +1 -6
  7. package/src/__tests__/admin-gpu-api.test.ts +1 -3
  8. package/src/__tests__/admin-marketplace-api.test.ts +1 -4
  9. package/src/__tests__/admin-middleware.test.ts +76 -83
  10. package/src/__tests__/affiliate-dashboard.test.tsx +3 -3
  11. package/src/__tests__/api-401-redirect.test.ts +46 -9
  12. package/src/__tests__/api-client.test.ts +3 -5
  13. package/src/__tests__/api-config.test.ts +22 -42
  14. package/src/__tests__/api-fleet-resources.test.ts +1 -2
  15. package/src/__tests__/api-fleet-trpc.test.ts +2 -8
  16. package/src/__tests__/api-null-guards.test.ts +3 -1
  17. package/src/__tests__/audit-log-table-pagination.test.tsx +2 -6
  18. package/src/__tests__/auth-password-reset.test.tsx +7 -21
  19. package/src/__tests__/auth-redirect.test.tsx +8 -2
  20. package/src/__tests__/auth.test.tsx +25 -23
  21. package/src/__tests__/auto-topup-card.test.tsx +4 -12
  22. package/src/__tests__/backups-tab.test.tsx +3 -4
  23. package/src/__tests__/billing-layout-nav-hidden.test.tsx +5 -37
  24. package/src/__tests__/billing-payment-org-invoices.test.tsx +2 -18
  25. package/src/__tests__/billing.test.tsx +8 -39
  26. package/src/__tests__/bot-settings/resources-tab.test.tsx +1 -3
  27. package/src/__tests__/bot-settings/storage-tab.test.tsx +1 -3
  28. package/src/__tests__/bot-settings/vps-upgrade-card.test.tsx +1 -3
  29. package/src/__tests__/bot-settings-restart.test.tsx +1 -3
  30. package/src/__tests__/bot-settings.test.tsx +2 -6
  31. package/src/__tests__/brand.test.ts +6 -26
  32. package/src/__tests__/buy-credits-panel.test.tsx +1 -3
  33. package/src/__tests__/buy-crypto-credits-panel.test.tsx +101 -119
  34. package/src/__tests__/capability-conflicts.test.ts +2 -8
  35. package/src/__tests__/capability-resolver.test.tsx +2 -12
  36. package/src/__tests__/channel-wizard.test.tsx +4 -17
  37. package/src/__tests__/chat/chat-panel.test.tsx +1 -4
  38. package/src/__tests__/chat-store.test.ts +5 -15
  39. package/src/__tests__/command-center.test.tsx +10 -12
  40. package/src/__tests__/compliance-retention-edit.test.tsx +3 -6
  41. package/src/__tests__/confirmation-tracker.test.tsx +3 -18
  42. package/src/__tests__/coupon-input.test.tsx +1 -3
  43. package/src/__tests__/create-instance.test.tsx +1 -3
  44. package/src/__tests__/credit-balance.test.tsx +4 -12
  45. package/src/__tests__/credits.test.tsx +32 -85
  46. package/src/__tests__/email-verification-banner.test.tsx +2 -6
  47. package/src/__tests__/error-boundaries.test.tsx +0 -1
  48. package/src/__tests__/fetch-pricing.test.ts +2 -1
  49. package/src/__tests__/field-oauth.test.tsx +2 -6
  50. package/src/__tests__/fixtures/mock-manifests-data.js +1 -3
  51. package/src/__tests__/fixtures/mock-manifests.ts +2 -4
  52. package/src/__tests__/fleet-health-timestamp.test.tsx +1 -8
  53. package/src/__tests__/fleet-health-update.test.tsx +1 -8
  54. package/src/__tests__/gpu-dashboard.test.tsx +2 -6
  55. package/src/__tests__/instance-detail.test.tsx +3 -9
  56. package/src/__tests__/instance-list.test.tsx +1 -5
  57. package/src/__tests__/layout-snapshots.test.tsx +64 -11
  58. package/src/__tests__/marketplace-admin.test.tsx +2 -6
  59. package/src/__tests__/marketplace.test.tsx +11 -35
  60. package/src/__tests__/merge-api-rates.test.ts +1 -6
  61. package/src/__tests__/middleware.test.ts +32 -219
  62. package/src/__tests__/next-config-headers.test.ts +1 -3
  63. package/src/__tests__/notifications.test.tsx +4 -11
  64. package/src/__tests__/oauth-buttons.test.tsx +36 -59
  65. package/src/__tests__/oauth-error-mapping.test.tsx +2 -6
  66. package/src/__tests__/observability.test.tsx +23 -36
  67. package/src/__tests__/onboarding-page.test.tsx +4 -6
  68. package/src/__tests__/org-billing-api.test.tsx +1 -6
  69. package/src/__tests__/plugin-install-flow.test.tsx +28 -58
  70. package/src/__tests__/plugin-registry.test.tsx +3 -11
  71. package/src/__tests__/plugin-tool-sync.test.ts +1 -3
  72. package/src/__tests__/plugins-catalog-error.test.tsx +2 -6
  73. package/src/__tests__/plugins-toggle-race.test.tsx +3 -5
  74. package/src/__tests__/portfolio-chart.test.tsx +2 -6
  75. package/src/__tests__/promotion-form.test.tsx +2 -6
  76. package/src/__tests__/promotions-list.test.tsx +1 -3
  77. package/src/__tests__/provider-key-api.test.ts +2 -1
  78. package/src/__tests__/resend-verification-button.test.tsx +8 -24
  79. package/src/__tests__/secrets-audit-pagination.test.tsx +1 -3
  80. package/src/__tests__/settings.test.tsx +11 -21
  81. package/src/__tests__/setup-checklist.test.tsx +3 -9
  82. package/src/__tests__/setup.ts +25 -6
  83. package/src/__tests__/snapshot-api.test.ts +2 -1
  84. package/src/__tests__/step-superpowers.test.tsx +1 -3
  85. package/src/__tests__/tenant-context.test.tsx +1 -6
  86. package/src/__tests__/tenant-keys-api.test.ts +3 -4
  87. package/src/__tests__/tenant-table-pagination.test.tsx +2 -6
  88. package/src/__tests__/terminal-log-cleanup.test.tsx +0 -1
  89. package/src/__tests__/transaction-history.test.tsx +190 -238
  90. package/src/__tests__/trpc-types.test.ts +2 -6
  91. package/src/__tests__/use-chat.test.ts +1 -3
  92. package/src/__tests__/use-plugin-setup-chat-stale-closure.test.ts +1 -4
  93. package/src/__tests__/use-sidecar-bridge.test.tsx +105 -0
  94. package/src/__tests__/use-webmcp.test.ts +1 -3
  95. package/src/__tests__/validate-elevenlabs-key.test.ts +2 -1
  96. package/src/__tests__/verify-page.test.tsx +4 -13
  97. package/src/__tests__/verify-redirect.test.tsx +2 -6
  98. package/src/app/(auth)/error.tsx +1 -7
  99. package/src/app/(auth)/forgot-password/page.tsx +4 -18
  100. package/src/app/(auth)/login/page.tsx +5 -22
  101. package/src/app/(auth)/reset-password/page.tsx +2 -12
  102. package/src/app/(auth)/signup/page.tsx +10 -44
  103. package/src/app/(auth)/verify/page.tsx +47 -0
  104. package/src/app/(dashboard)/billing/credits/page.tsx +14 -67
  105. package/src/app/(dashboard)/billing/error.tsx +2 -10
  106. package/src/app/(dashboard)/billing/layout.tsx +12 -62
  107. package/src/app/(dashboard)/billing/payment/page.tsx +17 -68
  108. package/src/app/(dashboard)/billing/plans/page.tsx +3 -9
  109. package/src/app/(dashboard)/billing/usage/hosted/page.tsx +8 -25
  110. package/src/app/(dashboard)/billing/usage/page.tsx +63 -103
  111. package/src/app/(dashboard)/changesets/[id]/changeset-detail-client.tsx +9 -27
  112. package/src/app/(dashboard)/changesets/[id]/error.tsx +2 -6
  113. package/src/app/(dashboard)/changesets/error.tsx +1 -7
  114. package/src/app/(dashboard)/chat/page.tsx +2 -6
  115. package/src/app/(dashboard)/dashboard/network/page.tsx +5 -19
  116. package/src/app/(dashboard)/error.tsx +1 -7
  117. package/src/app/(dashboard)/layout.tsx +15 -36
  118. package/src/app/(dashboard)/marketplace/[plugin]/page.tsx +14 -51
  119. package/src/app/(dashboard)/marketplace/error.tsx +1 -7
  120. package/src/app/(dashboard)/marketplace/page.tsx +6 -27
  121. package/src/app/(dashboard)/not-found.tsx +2 -5
  122. package/src/app/(dashboard)/onboarding/page.tsx +5 -22
  123. package/src/app/(dashboard)/settings/account/page.tsx +1 -6
  124. package/src/app/(dashboard)/settings/activity/page.tsx +8 -34
  125. package/src/app/(dashboard)/settings/api-keys/page.tsx +15 -60
  126. package/src/app/(dashboard)/settings/brain/page.tsx +9 -31
  127. package/src/app/(dashboard)/settings/error.tsx +2 -10
  128. package/src/app/(dashboard)/settings/notifications/page.tsx +2 -6
  129. package/src/app/(dashboard)/settings/org/page.tsx +13 -56
  130. package/src/app/(dashboard)/settings/page.tsx +1 -0
  131. package/src/app/(dashboard)/settings/profile/page.tsx +126 -73
  132. package/src/app/(dashboard)/settings/providers/page.tsx +21 -78
  133. package/src/app/(dashboard)/settings/secrets/page.tsx +13 -58
  134. package/src/app/(dashboard)/settings/security/page.tsx +31 -111
  135. package/src/app/admin/email-templates/email-templates-client.tsx +15 -58
  136. package/src/app/admin/error.tsx +1 -7
  137. package/src/app/admin/fleet-updates/error.tsx +1 -7
  138. package/src/app/admin/fleet-updates/fleet-updates-client.tsx +10 -50
  139. package/src/app/admin/layout.tsx +4 -0
  140. package/src/app/admin/payment-methods/page.tsx +9 -38
  141. package/src/app/admin/products/error.tsx +2 -7
  142. package/src/app/admin/products/page.tsx +1 -4
  143. package/src/app/admin/promotions/[id]/page.tsx +9 -38
  144. package/src/app/admin/promotions/page.tsx +9 -36
  145. package/src/app/admin/rate-overrides/page.tsx +9 -45
  146. package/src/app/auth/callback/[provider]/page.tsx +1 -8
  147. package/src/app/auth/verify/page.tsx +9 -36
  148. package/src/app/channels/error.tsx +2 -10
  149. package/src/app/channels/layout.tsx +9 -0
  150. package/src/app/channels/page.tsx +8 -20
  151. package/src/app/channels/setup/[plugin]/page.tsx +3 -5
  152. package/src/app/error.tsx +1 -7
  153. package/src/app/fleet/error.tsx +1 -7
  154. package/src/app/fleet/layout.tsx +5 -0
  155. package/src/app/fleet/settings/page.tsx +1 -3
  156. package/src/app/global-error.tsx +2 -10
  157. package/src/app/globals.css +1 -4
  158. package/src/app/instances/[id]/instance-detail-client.tsx +51 -125
  159. package/src/app/instances/error.tsx +2 -10
  160. package/src/app/instances/instance-list-client.tsx +20 -69
  161. package/src/app/instances/layout.tsx +9 -0
  162. package/src/app/instances/new/create-instance-client.tsx +10 -31
  163. package/src/app/layout.tsx +2 -10
  164. package/src/app/not-found.tsx +1 -3
  165. package/src/app/page.tsx +1 -2
  166. package/src/app/plugins/error.tsx +2 -10
  167. package/src/app/plugins/layout.tsx +5 -0
  168. package/src/app/plugins/page.tsx +16 -48
  169. package/src/app/pricing/error.tsx +1 -7
  170. package/src/app/privacy/page.tsx +93 -150
  171. package/src/app/status/error.tsx +1 -7
  172. package/src/app/terms/page.tsx +89 -144
  173. package/src/components/account-switcher.tsx +25 -52
  174. package/src/components/admin/accounting-dashboard.tsx +1 -3
  175. package/src/components/admin/admin-guard.tsx +1 -3
  176. package/src/components/admin/admin-nav.tsx +1 -3
  177. package/src/components/admin/affiliate-dashboard.tsx +25 -94
  178. package/src/components/admin/audit-log-table.tsx +13 -49
  179. package/src/components/admin/billing-health-dashboard.tsx +7 -25
  180. package/src/components/admin/bulk-actions-bar.test.tsx +1 -7
  181. package/src/components/admin/bulk-actions-bar.tsx +1 -3
  182. package/src/components/admin/bulk-export-dialog.test.tsx +1 -7
  183. package/src/components/admin/bulk-export-dialog.tsx +6 -32
  184. package/src/components/admin/bulk-grant-dialog.test.tsx +2 -6
  185. package/src/components/admin/bulk-grant-dialog.tsx +4 -15
  186. package/src/components/admin/bulk-preview-dialog.tsx +3 -12
  187. package/src/components/admin/bulk-reactivate-dialog.tsx +1 -7
  188. package/src/components/admin/bulk-select-all-banner.tsx +1 -6
  189. package/src/components/admin/bulk-suspend-dialog.tsx +5 -12
  190. package/src/components/admin/bulk-undo-toast.tsx +1 -2
  191. package/src/components/admin/compliance-dashboard.tsx +31 -101
  192. package/src/components/admin/gpu-dashboard.tsx +21 -70
  193. package/src/components/admin/grant-credits-dialog.tsx +4 -17
  194. package/src/components/admin/incident-dashboard.tsx +10 -25
  195. package/src/components/admin/inference-dashboard.tsx +14 -54
  196. package/src/components/admin/marketplace-admin.tsx +18 -60
  197. package/src/components/admin/migrations-dashboard.tsx +9 -42
  198. package/src/components/admin/onboarding-dashboard.tsx +14 -64
  199. package/src/components/admin/pool-config-dashboard.tsx +4 -10
  200. package/src/components/admin/products/fleet-form.tsx +2 -11
  201. package/src/components/admin/products/nav-editor.tsx +3 -10
  202. package/src/components/admin/promotions/promotion-form.tsx +9 -42
  203. package/src/components/admin/roles-dashboard.tsx +7 -34
  204. package/src/components/admin/suspend-dialog.tsx +4 -11
  205. package/src/components/admin/tenant-notes-panel.tsx +1 -3
  206. package/src/components/admin/tenant-row-actions.tsx +4 -20
  207. package/src/components/admin/tenant-table.tsx +12 -49
  208. package/src/components/auth/auth-redirect.tsx +11 -3
  209. package/src/components/auth/email-verification-result-banner.tsx +1 -3
  210. package/src/components/auth/resend-verification-button.tsx +2 -10
  211. package/src/components/auth/wopr-wordmark.tsx +1 -3
  212. package/src/components/billing/add-payment-method-dialog.tsx +1 -2
  213. package/src/components/billing/affiliate-dashboard.tsx +4 -16
  214. package/src/components/billing/amount-selector.tsx +1 -3
  215. package/src/components/billing/auto-topup-card.tsx +2 -11
  216. package/src/components/billing/buy-credits-panel.tsx +4 -14
  217. package/src/components/billing/byok-callout.tsx +6 -8
  218. package/src/components/billing/confirmation-tracker.tsx +4 -14
  219. package/src/components/billing/credit-balance-badge.tsx +22 -0
  220. package/src/components/billing/credit-balance.tsx +3 -9
  221. package/src/components/billing/crypto-checkout.tsx +5 -24
  222. package/src/components/billing/degraded-state-banner.tsx +1 -3
  223. package/src/components/billing/deposit-view.tsx +301 -41
  224. package/src/components/billing/dividend-banner.tsx +1 -3
  225. package/src/components/billing/dividend-eligibility.tsx +3 -12
  226. package/src/components/billing/dividend-pool-stats.tsx +6 -20
  227. package/src/components/billing/first-dividend-dialog.tsx +2 -2
  228. package/src/components/billing/org-billing-page.tsx +8 -31
  229. package/src/components/billing/payment-method-picker.tsx +2 -10
  230. package/src/components/billing/suspension-banner.tsx +2 -7
  231. package/src/components/billing/transaction-history.tsx +10 -58
  232. package/src/components/billing/unified-checkout.tsx +547 -0
  233. package/src/components/bot-settings/backups-tab.tsx +9 -33
  234. package/src/components/bot-settings/bot-settings-client.tsx +32 -134
  235. package/src/components/bot-settings/resources-tab.tsx +2 -9
  236. package/src/components/bot-settings/storage-tab.tsx +19 -48
  237. package/src/components/bot-settings/vps-info-panel.tsx +3 -11
  238. package/src/components/bot-settings/vps-upgrade-card.tsx +3 -4
  239. package/src/components/brand-hydrator.tsx +13 -0
  240. package/src/components/channel-wizard/field-interactive.tsx +1 -3
  241. package/src/components/channel-wizard/field-qr.tsx +10 -39
  242. package/src/components/channel-wizard/step-renderer.tsx +5 -28
  243. package/src/components/channel-wizard/wizard.tsx +6 -31
  244. package/src/components/chat/chat-message.tsx +1 -4
  245. package/src/components/chat/chat-panel.tsx +4 -18
  246. package/src/components/chat/chat-widget.tsx +3 -14
  247. package/src/components/dashboard/command-center.tsx +15 -61
  248. package/src/components/fleet/update-settings-card.tsx +7 -23
  249. package/src/components/instance-update-banner.tsx +130 -0
  250. package/src/components/instances/friends-tab.test.tsx +2 -9
  251. package/src/components/instances/friends-tab.tsx +18 -74
  252. package/src/components/instances/update-available-badge.tsx +2 -11
  253. package/src/components/landing/hero.tsx +3 -9
  254. package/src/components/landing/landing-page.tsx +1 -3
  255. package/src/components/landing/portfolio-chart.tsx +4 -9
  256. package/src/components/landing/story-sections.tsx +1 -3
  257. package/src/components/landing/terminal-sequence.tsx +4 -17
  258. package/src/components/marketplace/empty-state.tsx +2 -6
  259. package/src/components/marketplace/first-visit-hero.tsx +1 -3
  260. package/src/components/marketplace/install-wizard.tsx +20 -77
  261. package/src/components/marketplace/marketplace-tabs.tsx +1 -4
  262. package/src/components/marketplace/plugin-card.tsx +2 -9
  263. package/src/components/marketplace/superpower-content.tsx +1 -3
  264. package/src/components/marketplace/terminal-search.tsx +2 -8
  265. package/src/components/oauth-buttons.tsx +29 -14
  266. package/src/components/observability/fleet-health.tsx +5 -18
  267. package/src/components/observability/health-overview.tsx +7 -20
  268. package/src/components/observability/logs-viewer.tsx +8 -32
  269. package/src/components/observability/metrics-dashboard.tsx +2 -15
  270. package/src/components/onboarding/fallback-setup.tsx +6 -25
  271. package/src/components/onboarding/setup-checklist.tsx +18 -51
  272. package/src/components/onboarding/step-superpowers.tsx +1 -4
  273. package/src/components/plugin-setup/setup-chat-panel.tsx +6 -22
  274. package/src/components/pricing/dividend-calculator.tsx +6 -12
  275. package/src/components/pricing/dividend-stats.tsx +5 -17
  276. package/src/components/pricing/pricing-page.tsx +17 -36
  277. package/src/components/settings/create-org-wizard.tsx +2 -5
  278. package/src/components/sidebar.tsx +7 -42
  279. package/src/components/sidecar-frame.tsx +78 -0
  280. package/src/components/status/status-page.tsx +6 -28
  281. package/src/components/ui/alert-dialog.tsx +8 -25
  282. package/src/components/ui/badge.tsx +2 -8
  283. package/src/components/ui/banner.tsx +1 -6
  284. package/src/components/ui/card.tsx +5 -24
  285. package/src/components/ui/checkbox.tsx +1 -5
  286. package/src/components/ui/collapsible.tsx +3 -8
  287. package/src/components/ui/dialog.tsx +4 -10
  288. package/src/components/ui/dropdown-menu.tsx +9 -18
  289. package/src/components/ui/form.tsx +2 -16
  290. package/src/components/ui/popover.tsx +3 -23
  291. package/src/components/ui/progress.tsx +1 -5
  292. package/src/components/ui/radio-group.tsx +3 -15
  293. package/src/components/ui/select.tsx +4 -17
  294. package/src/components/ui/sheet.tsx +5 -19
  295. package/src/components/ui/skeleton.tsx +1 -7
  296. package/src/components/ui/table.tsx +5 -22
  297. package/src/components/ui/tabs.tsx +3 -13
  298. package/src/components/ui/tooltip.tsx +1 -1
  299. package/src/components/unified-sidebar.tsx +493 -0
  300. package/src/hooks/__tests__/use-fleet-sse.test.ts +1 -4
  301. package/src/hooks/__tests__/use-save-queue.test.ts +2 -8
  302. package/src/hooks/use-credit-balance.ts +27 -0
  303. package/src/hooks/use-my-org-role.ts +1 -3
  304. package/src/hooks/use-plugin-registry.ts +8 -14
  305. package/src/hooks/use-plugin-setup-chat.ts +2 -5
  306. package/src/hooks/use-sidecar-bridge.tsx +148 -0
  307. package/src/hooks/use-webmcp.ts +1 -4
  308. package/src/lib/__tests__/admin-api.test.ts +1 -3
  309. package/src/lib/__tests__/api-bot-crud.test.ts +8 -18
  310. package/src/lib/__tests__/api-fetch.test.ts +4 -16
  311. package/src/lib/__tests__/org-billing-api.test.ts +1 -3
  312. package/src/lib/__tests__/pricing-data.test.ts +0 -8
  313. package/src/lib/__tests__/settings-api.test.ts +1 -3
  314. package/src/lib/admin-affiliate-api.ts +2 -7
  315. package/src/lib/admin-api.ts +6 -26
  316. package/src/lib/admin-incident-api.ts +11 -19
  317. package/src/lib/admin-marketplace-api.ts +1 -5
  318. package/src/lib/api-config.test.ts +5 -50
  319. package/src/lib/api.ts +143 -122
  320. package/src/lib/auth-client.ts +1 -2
  321. package/src/lib/bot-settings-data.ts +11 -36
  322. package/src/lib/brand-config.ts +56 -115
  323. package/src/lib/brand.ts +2 -15
  324. package/src/lib/chat/use-chat.ts +2 -7
  325. package/src/lib/cost-comparison-data.test.ts +1 -3
  326. package/src/lib/cost-comparison-data.ts +1 -4
  327. package/src/lib/errors.ts +1 -4
  328. package/src/lib/fetch-utils.test.ts +26 -9
  329. package/src/lib/fetch-utils.ts +40 -11
  330. package/src/lib/logger.ts +2 -0
  331. package/src/lib/marketplace-data.ts +3 -11
  332. package/src/lib/oauth-errors.ts +2 -4
  333. package/src/lib/onboarding-data.ts +3 -11
  334. package/src/lib/org-api.ts +2 -10
  335. package/src/lib/org-billing-api.ts +5 -19
  336. package/src/lib/plugin/tool-definitions.ts +1 -2
  337. package/src/lib/require-auth.ts +57 -0
  338. package/src/lib/settings-api.ts +1 -4
  339. package/src/lib/sidecar-routes.ts +43 -0
  340. package/src/lib/trpc-server.ts +49 -0
  341. package/src/lib/trpc-types.ts +4 -6
  342. package/src/lib/trpc.tsx +12 -4
  343. package/src/lib/validate-redirect-url.ts +1 -4
  344. package/src/lib/webmcp/marketplace-onboarding-tools.ts +6 -16
  345. package/src/lib/webmcp/register.ts +1 -4
  346. package/src/lib/webmcp/tools.ts +2 -9
  347. package/src/proxy.ts +35 -212
  348. package/src/types/missing-deps.d.ts +2 -8
  349. package/tsconfig.json +1 -8
  350. package/biome.json +0 -52
  351. package/src/__tests__/__snapshots__/layout-snapshots.test.tsx.snap +0 -741
  352. package/src/__tests__/billing-byok-callout.test.tsx +0 -76
  353. package/src/lib/__tests__/__snapshots__/pricing-data.test.ts.snap +0 -112
@@ -32,8 +32,7 @@ const DEFAULT_PREFS: NotificationPreferences = {
32
32
  };
33
33
 
34
34
  const mockGetPrefs = vi.fn<() => Promise<NotificationPreferences>>();
35
- const mockUpdatePrefs =
36
- vi.fn<(p: Partial<NotificationPreferences>) => Promise<NotificationPreferences>>();
35
+ const mockUpdatePrefs = vi.fn<(p: Partial<NotificationPreferences>) => Promise<NotificationPreferences>>();
37
36
 
38
37
  vi.mock("@/lib/settings-api", async (importOriginal) => {
39
38
  const actual = await importOriginal<typeof import("@/lib/settings-api")>();
@@ -60,9 +59,7 @@ describe("Notifications page - toggle behavior", () => {
60
59
  mockUpdatePrefs.mockResolvedValue({ ...DEFAULT_PREFS, billing_low_balance: false });
61
60
 
62
61
  const user = userEvent.setup();
63
- const { default: NotificationsPage } = await import(
64
- "../app/(dashboard)/settings/notifications/page"
65
- );
62
+ const { default: NotificationsPage } = await import("../app/(dashboard)/settings/notifications/page");
66
63
  render(<NotificationsPage />);
67
64
 
68
65
  const toggle = await screen.findByRole("switch", { name: "Low balance alerts" });
@@ -90,9 +87,7 @@ describe("Notifications page - toggle behavior", () => {
90
87
  mockUpdatePrefs.mockReturnValueOnce(pending);
91
88
 
92
89
  const user = userEvent.setup();
93
- const { default: NotificationsPage } = await import(
94
- "../app/(dashboard)/settings/notifications/page"
95
- );
90
+ const { default: NotificationsPage } = await import("../app/(dashboard)/settings/notifications/page");
96
91
  render(<NotificationsPage />);
97
92
 
98
93
  const toggle = await screen.findByRole("switch", { name: "Low balance alerts" });
@@ -111,9 +106,7 @@ describe("Notifications page - toggle behavior", () => {
111
106
  mockUpdatePrefs.mockRejectedValueOnce(new Error("Network error"));
112
107
 
113
108
  const user = userEvent.setup();
114
- const { default: NotificationsPage } = await import(
115
- "../app/(dashboard)/settings/notifications/page"
116
- );
109
+ const { default: NotificationsPage } = await import("../app/(dashboard)/settings/notifications/page");
117
110
  render(<NotificationsPage />);
118
111
 
119
112
  const toggle = await screen.findByRole("switch", { name: "Low balance alerts" });
@@ -1,14 +1,8 @@
1
1
  import { render, screen } from "@testing-library/react";
2
- import { beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { afterEach, describe, expect, it, vi } from "vitest";
3
3
 
4
- vi.mock("@/lib/trpc", () => ({
5
- trpc: {
6
- authSocial: {
7
- enabledSocialProviders: {
8
- useQuery: vi.fn(),
9
- },
10
- },
11
- },
4
+ vi.mock("@/lib/api-config", () => ({
5
+ API_BASE_URL: "https://api.test/api",
12
6
  }));
13
7
 
14
8
  vi.mock("@/lib/auth-client", () => ({
@@ -18,84 +12,67 @@ vi.mock("@/lib/auth-client", () => ({
18
12
  }));
19
13
 
20
14
  import { OAuthButtons } from "@/components/oauth-buttons";
21
- import { trpc } from "@/lib/trpc";
22
15
 
23
16
  describe("OAuthButtons", () => {
24
- beforeEach(() => {
25
- vi.clearAllMocks();
17
+ afterEach(() => {
18
+ vi.unstubAllGlobals();
26
19
  });
27
20
 
28
21
  it("renders nothing while loading", () => {
29
- vi.mocked(trpc.authSocial.enabledSocialProviders.useQuery).mockReturnValue({
30
- data: undefined,
31
- isLoading: true,
32
- } as ReturnType<typeof trpc.authSocial.enabledSocialProviders.useQuery>);
33
-
22
+ // fetch never resolves — component stays in loading state
23
+ vi.stubGlobal("fetch", vi.fn().mockReturnValue(new Promise(() => {})));
34
24
  const { container } = render(<OAuthButtons />);
35
25
  expect(container.querySelector("button")).toBeNull();
36
26
  });
37
27
 
38
- it("renders nothing when no providers are enabled", () => {
39
- vi.mocked(trpc.authSocial.enabledSocialProviders.useQuery).mockReturnValue({
40
- data: [],
41
- isLoading: false,
42
- } as ReturnType<typeof trpc.authSocial.enabledSocialProviders.useQuery>);
43
-
28
+ it("renders nothing when no providers are enabled", async () => {
29
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve([]) }));
44
30
  const { container } = render(<OAuthButtons />);
45
- expect(container.querySelector("button")).toBeNull();
31
+ // Wait for useEffect to settle
32
+ await vi.waitFor(() => {
33
+ expect(container.querySelector("button")).toBeNull();
34
+ });
46
35
  });
47
36
 
48
- it("renders nothing when data is undefined and not loading", () => {
49
- vi.mocked(trpc.authSocial.enabledSocialProviders.useQuery).mockReturnValue({
50
- data: undefined,
51
- isLoading: false,
52
- } as ReturnType<typeof trpc.authSocial.enabledSocialProviders.useQuery>);
53
-
37
+ it("renders nothing when fetch fails", async () => {
38
+ vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("Network error")));
54
39
  const { container } = render(<OAuthButtons />);
55
- expect(container.querySelector("button")).toBeNull();
40
+ await vi.waitFor(() => {
41
+ expect(container.querySelector("button")).toBeNull();
42
+ });
56
43
  });
57
44
 
58
- it("renders only enabled providers", () => {
59
- vi.mocked(trpc.authSocial.enabledSocialProviders.useQuery).mockReturnValue({
60
- data: ["github", "discord"],
61
- isLoading: false,
62
- } as ReturnType<typeof trpc.authSocial.enabledSocialProviders.useQuery>);
63
-
45
+ it("renders only enabled providers", async () => {
46
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve(["github", "discord"]) }));
64
47
  render(<OAuthButtons />);
65
- expect(screen.getByRole("button", { name: "Continue with GitHub" })).toBeInTheDocument();
48
+ expect(await screen.findByRole("button", { name: "Continue with GitHub" })).toBeInTheDocument();
66
49
  expect(screen.getByRole("button", { name: "Continue with Discord" })).toBeInTheDocument();
67
50
  expect(screen.queryByRole("button", { name: /Google/ })).not.toBeInTheDocument();
68
51
  });
69
52
 
70
- it("renders all three providers when all are enabled", () => {
71
- vi.mocked(trpc.authSocial.enabledSocialProviders.useQuery).mockReturnValue({
72
- data: ["github", "discord", "google"],
73
- isLoading: false,
74
- } as ReturnType<typeof trpc.authSocial.enabledSocialProviders.useQuery>);
75
-
53
+ it("renders all three providers when all are enabled", async () => {
54
+ vi.stubGlobal(
55
+ "fetch",
56
+ vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve(["github", "discord", "google"]) }),
57
+ );
76
58
  render(<OAuthButtons />);
77
- expect(screen.getByRole("button", { name: "Continue with GitHub" })).toBeInTheDocument();
59
+ expect(await screen.findByRole("button", { name: "Continue with GitHub" })).toBeInTheDocument();
78
60
  expect(screen.getByRole("button", { name: "Continue with Discord" })).toBeInTheDocument();
79
61
  expect(screen.getByRole("button", { name: "Continue with Google" })).toBeInTheDocument();
80
62
  });
81
63
 
82
- it("renders the separator when providers are available", () => {
83
- vi.mocked(trpc.authSocial.enabledSocialProviders.useQuery).mockReturnValue({
84
- data: ["github"],
85
- isLoading: false,
86
- } as ReturnType<typeof trpc.authSocial.enabledSocialProviders.useQuery>);
87
-
64
+ it("renders the separator when providers are available", async () => {
65
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve(["github"]) }));
88
66
  render(<OAuthButtons />);
89
- expect(screen.getByText(/or continue with/i)).toBeInTheDocument();
67
+ expect(await screen.findByText(/or continue with/i)).toBeInTheDocument();
90
68
  });
91
69
 
92
- it("does not render the separator when no providers", () => {
93
- vi.mocked(trpc.authSocial.enabledSocialProviders.useQuery).mockReturnValue({
94
- data: [],
95
- isLoading: false,
96
- } as ReturnType<typeof trpc.authSocial.enabledSocialProviders.useQuery>);
97
-
70
+ it("does not render the separator when no providers", async () => {
71
+ vi.stubGlobal("fetch", vi.fn().mockResolvedValue({ ok: true, json: () => Promise.resolve([]) }));
98
72
  render(<OAuthButtons />);
99
- expect(screen.queryByText(/or continue with/i)).not.toBeInTheDocument();
73
+ // Wait for the fetch to resolve and state to settle
74
+ await vi.waitFor(() => {
75
+ expect(screen.queryByText(/or continue with/i)).not.toBeInTheDocument();
76
+ });
100
77
  });
101
78
  });
@@ -56,9 +56,7 @@ describe("getOAuthErrorMessage", () => {
56
56
  });
57
57
 
58
58
  it("returns generic fallback for unknown code", () => {
59
- expect(getOAuthErrorMessage("something_random")).toBe(
60
- "Authentication failed. Please try again.",
61
- );
59
+ expect(getOAuthErrorMessage("something_random")).toBe("Authentication failed. Please try again.");
62
60
  });
63
61
 
64
62
  it("returns generic fallback for malicious input — never reflects raw value", () => {
@@ -76,9 +74,7 @@ const { default: OAuthCallbackPage } = await import("@/app/auth/callback/[provid
76
74
  describe("OAuthCallbackPage — error reflection", () => {
77
75
  it("never renders raw malicious error param in the DOM", () => {
78
76
  const malicious = "<img src=x onerror=alert(1)>";
79
- vi.mocked(useSearchParams).mockReturnValue(
80
- new URLSearchParams(`error=${encodeURIComponent(malicious)}`) as never,
81
- );
77
+ vi.mocked(useSearchParams).mockReturnValue(new URLSearchParams(`error=${encodeURIComponent(malicious)}`) as never);
82
78
 
83
79
  render(<OAuthCallbackPage />);
84
80
 
@@ -8,12 +8,8 @@ vi.mock("recharts", () => ({
8
8
  ResponsiveContainer: ({ children }: { children: React.ReactNode }) => (
9
9
  <div data-testid="responsive-container">{children}</div>
10
10
  ),
11
- LineChart: ({ children }: { children: React.ReactNode }) => (
12
- <div data-testid="line-chart">{children}</div>
13
- ),
14
- BarChart: ({ children }: { children: React.ReactNode }) => (
15
- <div data-testid="bar-chart">{children}</div>
16
- ),
11
+ LineChart: ({ children }: { children: React.ReactNode }) => <div data-testid="line-chart">{children}</div>,
12
+ BarChart: ({ children }: { children: React.ReactNode }) => <div data-testid="bar-chart">{children}</div>,
17
13
  Line: () => <div data-testid="line" />,
18
14
  Bar: () => <div data-testid="bar" />,
19
15
  XAxis: () => <div data-testid="x-axis" />,
@@ -117,9 +113,7 @@ vi.mock("@/lib/api", () => ({
117
113
  memoryMb: 256,
118
114
  },
119
115
  ],
120
- tokenUsage: [
121
- { provider: "anthropic", inputTokens: 125000, outputTokens: 89000, totalCost: 4.28 },
122
- ],
116
+ tokenUsage: [{ provider: "anthropic", inputTokens: 125000, outputTokens: 89000, totalCost: 4.28 }],
123
117
  pluginEvents: [{ plugin: "memory", count: 340 }],
124
118
  }),
125
119
  getImageStatus: vi.fn().mockResolvedValue({
@@ -128,19 +122,16 @@ vi.mock("@/lib/api", () => ({
128
122
  updateAvailable: false,
129
123
  }),
130
124
  pullImageUpdate: vi.fn().mockResolvedValue(undefined),
131
- mapBotStatusToFleetInstance: vi.fn(
132
- (bot: { id: string; name: string; state: string; health: string | null }) => ({
133
- id: bot.id,
134
- name: bot.name,
135
- status: bot.state === "running" ? "running" : "stopped",
136
- health:
137
- bot.health === "healthy" ? "healthy" : bot.health === "degraded" ? "degraded" : "degraded",
138
- uptime: 86400,
139
- pluginCount: 2,
140
- sessionCount: bot.health === "healthy" ? 2 : 0,
141
- provider: bot.health === "healthy" ? "anthropic" : "openai",
142
- }),
143
- ),
125
+ mapBotStatusToFleetInstance: vi.fn((bot: { id: string; name: string; state: string; health: string | null }) => ({
126
+ id: bot.id,
127
+ name: bot.name,
128
+ status: bot.state === "running" ? "running" : "stopped",
129
+ health: bot.health === "healthy" ? "healthy" : bot.health === "degraded" ? "degraded" : "degraded",
130
+ uptime: 86400,
131
+ pluginCount: 2,
132
+ sessionCount: bot.health === "healthy" ? 2 : 0,
133
+ provider: bot.health === "healthy" ? "anthropic" : "openai",
134
+ })),
144
135
  }));
145
136
 
146
137
  vi.mock("framer-motion", () => ({
@@ -215,17 +206,15 @@ describe("HealthOverview", () => {
215
206
  it("recovers from error on retry", async () => {
216
207
  const user = userEvent.setup();
217
208
  const { getInstanceHealth } = await import("@/lib/api");
218
- vi.mocked(getInstanceHealth)
219
- .mockRejectedValueOnce(new Error("Network error"))
220
- .mockResolvedValueOnce({
221
- status: "healthy",
222
- uptime: 86400,
223
- activeSessions: 2,
224
- totalSessions: 47,
225
- plugins: [],
226
- providers: [],
227
- history: [],
228
- });
209
+ vi.mocked(getInstanceHealth).mockRejectedValueOnce(new Error("Network error")).mockResolvedValueOnce({
210
+ status: "healthy",
211
+ uptime: 86400,
212
+ activeSessions: 2,
213
+ totalSessions: 47,
214
+ plugins: [],
215
+ providers: [],
216
+ history: [],
217
+ });
229
218
 
230
219
  render(<HealthOverview instanceId="inst-001" />);
231
220
  await waitFor(() => {
@@ -377,9 +366,7 @@ describe("MetricsDashboard", () => {
377
366
  memoryMb: 256,
378
367
  },
379
368
  ],
380
- tokenUsage: [
381
- { provider: "anthropic", inputTokens: 125000, outputTokens: 89000, totalCost: 4.28 },
382
- ],
369
+ tokenUsage: [{ provider: "anthropic", inputTokens: 125000, outputTokens: 89000, totalCost: 4.28 }],
383
370
  pluginEvents: [{ plugin: "memory", count: 340 }],
384
371
  });
385
372
 
@@ -9,9 +9,7 @@ vi.mock("next/navigation", () => ({
9
9
 
10
10
  vi.mock("framer-motion", () => ({
11
11
  motion: {
12
- div: ({ children, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
13
- <div {...props}>{children}</div>
14
- ),
12
+ div: ({ children, ...props }: React.HTMLAttributes<HTMLDivElement>) => <div {...props}>{children}</div>,
15
13
  },
16
14
  AnimatePresence: ({ children }: { children: React.ReactNode }) => <>{children}</>,
17
15
  }));
@@ -74,11 +72,11 @@ describe("OnboardingPage", () => {
74
72
  expect(screen.getByRole("textbox")).toBeInTheDocument();
75
73
  });
76
74
 
77
- it("redirects to /marketplace if onboarding already complete", async () => {
75
+ it("redirects to homePath if onboarding already complete", async () => {
78
76
  mockIsComplete = true;
79
77
  render(<OnboardingPage />);
80
78
  await waitFor(() => {
81
- expect(mockPush).toHaveBeenCalledWith("/marketplace");
79
+ expect(mockPush).toHaveBeenCalledWith("/");
82
80
  });
83
81
  });
84
82
 
@@ -108,6 +106,6 @@ describe("OnboardingPage", () => {
108
106
 
109
107
  expect(mockMarkComplete).toHaveBeenCalled();
110
108
  expect(mockSaveState).toHaveBeenCalled();
111
- expect(mockPush).toHaveBeenCalledWith("/marketplace");
109
+ expect(mockPush).toHaveBeenCalledWith("/");
112
110
  });
113
111
  });
@@ -56,12 +56,7 @@ describe("org-billing-api", () => {
56
56
  });
57
57
 
58
58
  it("createOrgTopupCheckout returns checkout URL", async () => {
59
- const result = await createOrgTopupCheckout(
60
- "org-1",
61
- "price_1",
62
- "https://ok.com",
63
- "https://cancel.com",
64
- );
59
+ const result = await createOrgTopupCheckout("org-1", "price_1", "https://ok.com", "https://cancel.com");
65
60
  expect(result.url).toContain("stripe.com");
66
61
  });
67
62
  });
@@ -43,33 +43,25 @@ vi.mock("framer-motion", () => {
43
43
  });
44
44
 
45
45
  // vi.hoisted runs before module imports so TEST_PLUGINS and mocks are available in vi.mock factories
46
- const {
47
- TEST_PLUGINS,
48
- ALL_PLUGINS,
49
- mockInstallPlugin,
50
- mockListBots,
51
- mockListInstalledPlugins,
52
- injectPathlessZodError,
53
- } = vi.hoisted(() => {
54
- const mockInstallPlugin = vi.fn();
55
- const mockListBots = vi
56
- .fn()
57
- .mockResolvedValue([{ id: "bot-001", name: "My Bot", state: "running" }]);
58
- const mockListInstalledPlugins = vi.fn().mockResolvedValue([]);
59
- // eslint-disable-next-line @typescript-eslint/no-require-imports
60
- const { INSTALL_FLOW_TEST_PLUGINS, MARKETPLACE_TEST_PLUGINS } =
61
- require("./fixtures/mock-manifests-data") as typeof import("./fixtures/mock-manifests");
62
- // Flag to conditionally inject a path-less Zod error in the mocked z.object
63
- const injectPathlessZodError = { value: false };
64
- return {
65
- TEST_PLUGINS: INSTALL_FLOW_TEST_PLUGINS,
66
- ALL_PLUGINS: MARKETPLACE_TEST_PLUGINS,
67
- mockInstallPlugin,
68
- mockListBots,
69
- mockListInstalledPlugins,
70
- injectPathlessZodError,
71
- };
72
- });
46
+ const { TEST_PLUGINS, ALL_PLUGINS, mockInstallPlugin, mockListBots, mockListInstalledPlugins, injectPathlessZodError } =
47
+ vi.hoisted(() => {
48
+ const mockInstallPlugin = vi.fn();
49
+ const mockListBots = vi.fn().mockResolvedValue([{ id: "bot-001", name: "My Bot", state: "running" }]);
50
+ const mockListInstalledPlugins = vi.fn().mockResolvedValue([]);
51
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
52
+ const { INSTALL_FLOW_TEST_PLUGINS, MARKETPLACE_TEST_PLUGINS } =
53
+ require("./fixtures/mock-manifests-data") as typeof import("./fixtures/mock-manifests");
54
+ // Flag to conditionally inject a path-less Zod error in the mocked z.object
55
+ const injectPathlessZodError = { value: false };
56
+ return {
57
+ TEST_PLUGINS: INSTALL_FLOW_TEST_PLUGINS,
58
+ ALL_PLUGINS: MARKETPLACE_TEST_PLUGINS,
59
+ mockInstallPlugin,
60
+ mockListBots,
61
+ mockListInstalledPlugins,
62
+ injectPathlessZodError,
63
+ };
64
+ });
73
65
 
74
66
  // Mock zod with a conditional wrapper: when injectPathlessZodError.value is true,
75
67
  // z.object() adds a superRefine that produces a path-less ZodIssue.
@@ -78,10 +70,7 @@ const {
78
70
  vi.mock("zod", async () => {
79
71
  const realZod = await vi.importActual<typeof import("zod")>("zod");
80
72
  const originalObject = realZod.z.object.bind(realZod.z);
81
- const wrappedObject = (
82
- shape?: Record<string, unknown>,
83
- params?: string | Record<string, unknown>,
84
- ) => {
73
+ const wrappedObject = (shape?: Record<string, unknown>, params?: string | Record<string, unknown>) => {
85
74
  const schema = originalObject(shape, params as undefined);
86
75
  if (!injectPathlessZodError.value || !shape || Object.keys(shape).length === 0) return schema;
87
76
  return schema.superRefine((_val, ctx) => {
@@ -105,11 +94,7 @@ vi.mock("next/navigation", () => ({
105
94
  }));
106
95
 
107
96
  vi.mock("next/link", () => ({
108
- default: ({
109
- children,
110
- href,
111
- ...props
112
- }: { children: React.ReactNode; href: string } & Record<string, unknown>) => (
97
+ default: ({ children, href, ...props }: { children: React.ReactNode; href: string } & Record<string, unknown>) => (
113
98
  <a href={href} {...props}>
114
99
  {children}
115
100
  </a>
@@ -197,9 +182,7 @@ describe("Plugin Install Flow", () => {
197
182
 
198
183
  it("renders install button on plugin detail page for uninstalled plugin", async () => {
199
184
  mockParams.plugin = "webhooks";
200
- const { default: PluginDetailPage } = await import(
201
- "../app/(dashboard)/marketplace/[plugin]/page"
202
- );
185
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
203
186
  renderWithQueryClient(<PluginDetailPage />);
204
187
 
205
188
  expect(await screen.findByText("Webhooks")).toBeInTheDocument();
@@ -211,9 +194,7 @@ describe("Plugin Install Flow", () => {
211
194
  it("opens install wizard when install button is clicked", async () => {
212
195
  const user = userEvent.setup();
213
196
  mockParams.plugin = "webhooks";
214
- const { default: PluginDetailPage } = await import(
215
- "../app/(dashboard)/marketplace/[plugin]/page"
216
- );
197
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
217
198
  renderWithQueryClient(<PluginDetailPage />);
218
199
 
219
200
  await screen.findByText("Webhooks");
@@ -228,9 +209,7 @@ describe("Plugin Install Flow", () => {
228
209
 
229
210
  const user = userEvent.setup();
230
211
  mockParams.plugin = "webhooks";
231
- const { default: PluginDetailPage } = await import(
232
- "../app/(dashboard)/marketplace/[plugin]/page"
233
- );
212
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
234
213
  renderWithQueryClient(<PluginDetailPage />);
235
214
 
236
215
  await screen.findByText("Webhooks");
@@ -268,9 +247,7 @@ describe("Plugin Install Flow", () => {
268
247
 
269
248
  const user = userEvent.setup();
270
249
  mockParams.plugin = "webhooks";
271
- const { default: PluginDetailPage } = await import(
272
- "../app/(dashboard)/marketplace/[plugin]/page"
273
- );
250
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
274
251
  renderWithQueryClient(<PluginDetailPage />);
275
252
 
276
253
  await screen.findByText("Webhooks");
@@ -295,9 +272,7 @@ describe("Plugin Install Flow", () => {
295
272
  it("wizard cancel button returns to detail page", async () => {
296
273
  const user = userEvent.setup();
297
274
  mockParams.plugin = "webhooks";
298
- const { default: PluginDetailPage } = await import(
299
- "../app/(dashboard)/marketplace/[plugin]/page"
300
- );
275
+ const { default: PluginDetailPage } = await import("../app/(dashboard)/marketplace/[plugin]/page");
301
276
  renderWithQueryClient(<PluginDetailPage />);
302
277
 
303
278
  await screen.findByText("Webhooks");
@@ -353,9 +328,7 @@ describe("Plugin Toggle (Enable/Disable)", () => {
353
328
 
354
329
  // Complete phase reached — "This plugin requires the following dependencies:" was never shown
355
330
  expect(screen.getByText("Plugin installed successfully")).toBeInTheDocument();
356
- expect(
357
- screen.queryByText("This plugin requires the following dependencies:"),
358
- ).not.toBeInTheDocument();
331
+ expect(screen.queryByText("This plugin requires the following dependencies:")).not.toBeInTheDocument();
359
332
  });
360
333
 
361
334
  it("InstallWizard shows requirements phase for plugins with dependencies", async () => {
@@ -424,10 +397,7 @@ describe("Plugin Toggle (Enable/Disable)", () => {
424
397
  expect(screen.getByText("Plugin installed successfully")).toBeInTheDocument();
425
398
  await user.click(screen.getByText("Done"));
426
399
 
427
- expect(onComplete).toHaveBeenCalledWith(
428
- "bot-001",
429
- expect.objectContaining({ _providerChoices: {} }),
430
- );
400
+ expect(onComplete).toHaveBeenCalledWith("bot-001", expect.objectContaining({ _providerChoices: {} }));
431
401
  });
432
402
 
433
403
  it("InstallWizard shows loading skeleton while bots are loading", async () => {
@@ -192,10 +192,7 @@ describe("usePluginRegistry", () => {
192
192
  it("provides derived pluginOptions from all categories", async () => {
193
193
  const { result } = renderHook(() => usePluginRegistry());
194
194
  await waitFor(() => expect(result.current.categoriesLoaded).toBe(true));
195
- const totalCategoryPlugins = result.current.categories.reduce(
196
- (sum, cat) => sum + cat.plugins.length,
197
- 0,
198
- );
195
+ const totalCategoryPlugins = result.current.categories.reduce((sum, cat) => sum + cat.plugins.length, 0);
199
196
  expect(result.current.pluginOptions.length).toBe(totalCategoryPlugins);
200
197
  });
201
198
 
@@ -414,13 +411,8 @@ describe("usePluginRegistry", () => {
414
411
  await waitFor(() => expect(result.current.loading).toBe(false));
415
412
 
416
413
  const all = result.current.getAllPlugins();
417
- const categoryPluginCount = result.current.categories.reduce(
418
- (sum, cat) => sum + cat.plugins.length,
419
- 0,
420
- );
421
- expect(all.length).toBe(
422
- result.current.channels.length + result.current.providers.length + categoryPluginCount,
423
- );
414
+ const categoryPluginCount = result.current.categories.reduce((sum, cat) => sum + cat.plugins.length, 0);
415
+ expect(all.length).toBe(result.current.channels.length + result.current.providers.length + categoryPluginCount);
424
416
  });
425
417
 
426
418
  it("collectConfigFields returns config fields for selected channels", async () => {
@@ -51,9 +51,7 @@ describe("tool definitions stay in sync with handlers", () => {
51
51
  const handlerNames = new Set(handlerTools.map((t) => t.name));
52
52
 
53
53
  for (const def of getPlatformUIToolDefinitions()) {
54
- expect(handlerNames.has(def.name), `Definition "${def.name}" has no matching handler`).toBe(
55
- true,
56
- );
54
+ expect(handlerNames.has(def.name), `Definition "${def.name}" has no matching handler`).toBe(true);
57
55
  }
58
56
  });
59
57
  });
@@ -118,9 +118,7 @@ describe("plugins catalog error state", () => {
118
118
  };
119
119
 
120
120
  // First call fails, second succeeds
121
- mockListMarketplacePlugins
122
- .mockRejectedValueOnce(new Error("Network error"))
123
- .mockResolvedValueOnce([catalogPlugin]);
121
+ mockListMarketplacePlugins.mockRejectedValueOnce(new Error("Network error")).mockResolvedValueOnce([catalogPlugin]);
124
122
 
125
123
  render(<PluginsPage />);
126
124
 
@@ -184,9 +182,7 @@ describe("plugins catalog error state", () => {
184
182
  };
185
183
 
186
184
  // First call fails, second succeeds
187
- mockListMarketplacePlugins
188
- .mockRejectedValueOnce(new Error("Network error"))
189
- .mockResolvedValueOnce([catalogPlugin]);
185
+ mockListMarketplacePlugins.mockRejectedValueOnce(new Error("Network error")).mockResolvedValueOnce([catalogPlugin]);
190
186
 
191
187
  render(<PluginsPage />);
192
188
 
@@ -98,9 +98,7 @@ describe("togglePlugin race condition", () => {
98
98
  vi.clearAllMocks();
99
99
  mockListInstalledPlugins.mockResolvedValue([{ pluginId: "plugin-1", enabled: true }]);
100
100
  // Make togglePluginEnabled slow so we can test in-flight guard
101
- mockTogglePluginEnabled.mockImplementation(
102
- () => new Promise((resolve) => setTimeout(resolve, 500)),
103
- );
101
+ mockTogglePluginEnabled.mockImplementation(() => new Promise((resolve) => setTimeout(resolve, 500)));
104
102
  });
105
103
 
106
104
  it("blocks a second toggle while the first is in flight (stale closure exposes race)", async () => {
@@ -113,8 +111,8 @@ describe("togglePlugin race condition", () => {
113
111
 
114
112
  render(<PluginsPage />);
115
113
 
116
- // Wait for the page to fully load
117
- const toggle = await screen.findByRole("switch", { name: /toggle test plugin/i });
114
+ // Wait for the page to fully load — extended timeout for CI runners
115
+ const toggle = await screen.findByRole("switch", { name: /toggle test plugin/i }, { timeout: 5000 });
118
116
  expect(toggle).toBeInTheDocument();
119
117
 
120
118
  // Simulate rapid double-click by firing two change events synchronously
@@ -7,9 +7,7 @@ describe("PortfolioChart", () => {
7
7
  it("renders a canvas element", () => {
8
8
  const milestoneRef = createRef<((label: string) => void) | null>();
9
9
  const fadeStartRef = createRef<(() => void) | null>();
10
- const { container } = render(
11
- <PortfolioChart onMilestoneRef={milestoneRef} onFadeStartRef={fadeStartRef} />,
12
- );
10
+ const { container } = render(<PortfolioChart onMilestoneRef={milestoneRef} onFadeStartRef={fadeStartRef} />);
13
11
  const canvas = container.querySelector("canvas");
14
12
  expect(canvas).toBeInTheDocument();
15
13
  });
@@ -17,8 +15,6 @@ describe("PortfolioChart", () => {
17
15
  it("renders without crashing when refs are provided", () => {
18
16
  const milestoneRef = createRef<((label: string) => void) | null>();
19
17
  const fadeStartRef = createRef<(() => void) | null>();
20
- expect(() =>
21
- render(<PortfolioChart onMilestoneRef={milestoneRef} onFadeStartRef={fadeStartRef} />),
22
- ).not.toThrow();
18
+ expect(() => render(<PortfolioChart onMilestoneRef={milestoneRef} onFadeStartRef={fadeStartRef} />)).not.toThrow();
23
19
  });
24
20
  });
@@ -26,9 +26,7 @@ vi.mock("@/lib/trpc", () => ({
26
26
 
27
27
  vi.mock("framer-motion", () => ({
28
28
  motion: {
29
- div: ({ children, ...props }: React.PropsWithChildren<Record<string, unknown>>) => (
30
- <div {...props}>{children}</div>
31
- ),
29
+ div: ({ children, ...props }: React.PropsWithChildren<Record<string, unknown>>) => <div {...props}>{children}</div>,
32
30
  },
33
31
  }));
34
32
 
@@ -59,9 +57,7 @@ vi.mock("@/components/ui/select", () => ({
59
57
  {children}
60
58
  </select>
61
59
  ),
62
- SelectTrigger: ({ children, id }: { children: React.ReactNode; id?: string }) => (
63
- <span id={id}>{children}</span>
64
- ),
60
+ SelectTrigger: ({ children, id }: { children: React.ReactNode; id?: string }) => <span id={id}>{children}</span>,
65
61
  SelectValue: () => null,
66
62
  SelectContent: ({ children }: { children: React.ReactNode }) => <>{children}</>,
67
63
  SelectItem: ({ children, value }: { children: React.ReactNode; value: string }) => (
@@ -31,9 +31,7 @@ vi.mock("@/lib/trpc", () => ({
31
31
 
32
32
  vi.mock("framer-motion", () => ({
33
33
  motion: {
34
- div: ({ children, ...props }: React.PropsWithChildren<Record<string, unknown>>) => (
35
- <div {...props}>{children}</div>
36
- ),
34
+ div: ({ children, ...props }: React.PropsWithChildren<Record<string, unknown>>) => <div {...props}>{children}</div>,
37
35
  },
38
36
  }));
39
37