@wopr-network/platform-ui-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.paperclip +18 -0
- package/.env.wopr +18 -0
- package/README.md +36 -0
- package/biome.json +52 -0
- package/next.config.ts +45 -0
- package/package.json +84 -0
- package/postcss.config.mjs +7 -0
- package/public/file.svg +1 -0
- package/public/globe.svg +1 -0
- package/public/window.svg +1 -0
- package/src/__tests__/__snapshots__/layout-snapshots.test.tsx.snap +741 -0
- package/src/__tests__/account-page-redirect.test.tsx +73 -0
- package/src/__tests__/account-switcher.test.tsx +85 -0
- package/src/__tests__/activity-page.test.tsx +176 -0
- package/src/__tests__/add-payment-method-dialog.test.tsx +160 -0
- package/src/__tests__/admin-api.test.ts +244 -0
- package/src/__tests__/admin-gpu-api.test.ts +188 -0
- package/src/__tests__/admin-guard.test.tsx +79 -0
- package/src/__tests__/admin-marketplace-api.test.ts +179 -0
- package/src/__tests__/admin-middleware.test.ts +157 -0
- package/src/__tests__/admin-tenant-table.test.tsx +95 -0
- package/src/__tests__/affiliate-dashboard.test.tsx +178 -0
- package/src/__tests__/api-401-redirect.test.ts +78 -0
- package/src/__tests__/api-client.test.ts +316 -0
- package/src/__tests__/api-config.test.ts +89 -0
- package/src/__tests__/api-control-instance.test.ts +69 -0
- package/src/__tests__/api-fleet-resources.test.ts +52 -0
- package/src/__tests__/api-fleet-trpc.test.ts +252 -0
- package/src/__tests__/api-get-instance-config.test.ts +41 -0
- package/src/__tests__/api-null-guards.test.ts +244 -0
- package/src/__tests__/api-rename-instance.test.ts +60 -0
- package/src/__tests__/api-update-instance-config.test.ts +60 -0
- package/src/__tests__/audit-log-table-pagination.test.tsx +136 -0
- package/src/__tests__/auth-client.test.ts +87 -0
- package/src/__tests__/auth-password-reset.test.tsx +435 -0
- package/src/__tests__/auth-redirect.test.tsx +60 -0
- package/src/__tests__/auth.test.tsx +269 -0
- package/src/__tests__/auto-topup-card.test.tsx +257 -0
- package/src/__tests__/backups-tab.test.tsx +221 -0
- package/src/__tests__/billing-byok-callout.test.tsx +76 -0
- package/src/__tests__/billing-layout-nav-hidden.test.tsx +47 -0
- package/src/__tests__/billing-payment-org-invoices.test.tsx +123 -0
- package/src/__tests__/billing.test.tsx +509 -0
- package/src/__tests__/bot-settings/resources-tab.test.tsx +119 -0
- package/src/__tests__/bot-settings/storage-tab.test.tsx +80 -0
- package/src/__tests__/bot-settings/vps-info-panel.test.tsx +108 -0
- package/src/__tests__/bot-settings/vps-upgrade-card.test.tsx +52 -0
- package/src/__tests__/bot-settings-data-control.test.ts +49 -0
- package/src/__tests__/bot-settings-restart.test.tsx +149 -0
- package/src/__tests__/bot-settings.test.tsx +678 -0
- package/src/__tests__/brand.test.ts +335 -0
- package/src/__tests__/buy-credits-panel.test.tsx +249 -0
- package/src/__tests__/buy-crypto-credits-panel.test.tsx +178 -0
- package/src/__tests__/capability-conflicts.test.ts +88 -0
- package/src/__tests__/capability-resolver.test.tsx +173 -0
- package/src/__tests__/changeset-detail.test.tsx +156 -0
- package/src/__tests__/channel-setup-logger.test.ts +22 -0
- package/src/__tests__/channel-setup-toast.test.tsx +60 -0
- package/src/__tests__/channel-wizard.test.tsx +505 -0
- package/src/__tests__/chat/ambient-dot.test.tsx +35 -0
- package/src/__tests__/chat/chat-input.test.tsx +78 -0
- package/src/__tests__/chat/chat-message.test.tsx +45 -0
- package/src/__tests__/chat/chat-panel.test.tsx +111 -0
- package/src/__tests__/chat/chat-widget.test.tsx +82 -0
- package/src/__tests__/chat-store.test.ts +87 -0
- package/src/__tests__/command-center.test.tsx +246 -0
- package/src/__tests__/compliance-retention-edit.test.tsx +134 -0
- package/src/__tests__/coupon-input.test.tsx +119 -0
- package/src/__tests__/create-instance.test.tsx +96 -0
- package/src/__tests__/create-org-wizard.test.tsx +200 -0
- package/src/__tests__/credit-balance.test.tsx +103 -0
- package/src/__tests__/credits.test.tsx +376 -0
- package/src/__tests__/csrf-middleware.test.ts +198 -0
- package/src/__tests__/degraded-state-banner.test.tsx +130 -0
- package/src/__tests__/dividend-calculator.test.tsx +20 -0
- package/src/__tests__/dividend-stats.test.tsx +64 -0
- package/src/__tests__/dividend.test.tsx +169 -0
- package/src/__tests__/dockerfile.test.ts +110 -0
- package/src/__tests__/email-verification-banner.test.tsx +64 -0
- package/src/__tests__/env-example.test.ts +25 -0
- package/src/__tests__/error-boundaries.test.tsx +64 -0
- package/src/__tests__/fetch-pricing.test.ts +121 -0
- package/src/__tests__/field-oauth.test.tsx +302 -0
- package/src/__tests__/fixtures/mock-manifests-data.js +372 -0
- package/src/__tests__/fixtures/mock-manifests.ts +24 -0
- package/src/__tests__/fleet-health-timestamp.test.tsx +101 -0
- package/src/__tests__/fleet-health-update.test.tsx +83 -0
- package/src/__tests__/format-credit.test.ts +58 -0
- package/src/__tests__/gpu-dashboard.test.tsx +236 -0
- package/src/__tests__/hosted-usage-date-range.test.tsx +54 -0
- package/src/__tests__/instance-detail.test.tsx +571 -0
- package/src/__tests__/instance-list.test.tsx +230 -0
- package/src/__tests__/landing-hero.test.tsx +27 -0
- package/src/__tests__/landing-nav.test.tsx +24 -0
- package/src/__tests__/layout-snapshots.test.tsx +167 -0
- package/src/__tests__/logger.test.ts +54 -0
- package/src/__tests__/login-page-redirect.test.tsx +142 -0
- package/src/__tests__/manifest-validation.test.ts +126 -0
- package/src/__tests__/marketplace-admin.test.tsx +151 -0
- package/src/__tests__/marketplace.test.tsx +609 -0
- package/src/__tests__/merge-api-rates.test.ts +70 -0
- package/src/__tests__/middleware.test.ts +690 -0
- package/src/__tests__/network-page.test.tsx +100 -0
- package/src/__tests__/next-config-headers.test.ts +28 -0
- package/src/__tests__/not-found.test.tsx +26 -0
- package/src/__tests__/notifications.test.tsx +128 -0
- package/src/__tests__/oauth-buttons.test.tsx +101 -0
- package/src/__tests__/oauth-error-mapping.test.tsx +97 -0
- package/src/__tests__/observability.test.tsx +541 -0
- package/src/__tests__/onboarding-data.test.ts +363 -0
- package/src/__tests__/onboarding-page.test.tsx +113 -0
- package/src/__tests__/onboarding-store.test.ts +121 -0
- package/src/__tests__/org-billing-api.test.tsx +70 -0
- package/src/__tests__/org-billing-null-guards.test.ts +64 -0
- package/src/__tests__/org-billing-page.test.tsx +124 -0
- package/src/__tests__/plugin-definition.test.ts +43 -0
- package/src/__tests__/plugin-install-flow.test.tsx +535 -0
- package/src/__tests__/plugin-registry.test.tsx +475 -0
- package/src/__tests__/plugin-setup/setup-chat-panel.test.ts +142 -0
- package/src/__tests__/plugin-setup/use-plugin-setup-chat.test.ts +49 -0
- package/src/__tests__/plugin-tool-definitions.test.ts +51 -0
- package/src/__tests__/plugin-tool-sync.test.ts +59 -0
- package/src/__tests__/portfolio-chart.test.tsx +24 -0
- package/src/__tests__/pricing.test.tsx +107 -0
- package/src/__tests__/promotion-form.test.tsx +180 -0
- package/src/__tests__/promotions-list.test.tsx +194 -0
- package/src/__tests__/provider-key-api.test.ts +134 -0
- package/src/__tests__/resend-verification-button.test.tsx +104 -0
- package/src/__tests__/sanitize-redirect-url.test.ts +47 -0
- package/src/__tests__/secrets-audit-pagination.test.tsx +139 -0
- package/src/__tests__/settings.test.tsx +937 -0
- package/src/__tests__/setup-checklist.test.tsx +274 -0
- package/src/__tests__/setup.ts +82 -0
- package/src/__tests__/smoke.test.tsx +10 -0
- package/src/__tests__/snapshot-api.test.ts +104 -0
- package/src/__tests__/status-api.test.ts +46 -0
- package/src/__tests__/status-badge.test.tsx +33 -0
- package/src/__tests__/status-colors.test.ts +83 -0
- package/src/__tests__/status-page.test.tsx +86 -0
- package/src/__tests__/step-superpowers.test.tsx +218 -0
- package/src/__tests__/story-sections.test.tsx +24 -0
- package/src/__tests__/superpower-content-sanitize.test.tsx +87 -0
- package/src/__tests__/superpower-content.test.tsx +44 -0
- package/src/__tests__/suspension-banner.test.tsx +140 -0
- package/src/__tests__/tenant-context.test.tsx +146 -0
- package/src/__tests__/tenant-keys-api.test.ts +114 -0
- package/src/__tests__/tenant-table-pagination.test.tsx +124 -0
- package/src/__tests__/terminal-log-cleanup.test.tsx +51 -0
- package/src/__tests__/terminal-sequence.test.tsx +28 -0
- package/src/__tests__/transaction-history.test.tsx +325 -0
- package/src/__tests__/trpc-types.test.ts +102 -0
- package/src/__tests__/use-capability-meta.test.ts +161 -0
- package/src/__tests__/use-chat.test.ts +616 -0
- package/src/__tests__/use-has-org.test.ts +44 -0
- package/src/__tests__/use-image-status.test.ts +77 -0
- package/src/__tests__/use-pagination-params.test.ts +88 -0
- package/src/__tests__/use-plugin-setup-chat-stale-closure.test.ts +53 -0
- package/src/__tests__/use-webmcp.test.ts +119 -0
- package/src/__tests__/validate-elevenlabs-key.test.ts +95 -0
- package/src/__tests__/validate-redirect-url.test.ts +61 -0
- package/src/__tests__/verify-page.test.tsx +140 -0
- package/src/__tests__/verify-redirect.test.tsx +41 -0
- package/src/__tests__/verify-result-banner.test.tsx +66 -0
- package/src/__tests__/webmcp-feature-detect.test.ts +54 -0
- package/src/__tests__/webmcp-hook.test.tsx +72 -0
- package/src/__tests__/webmcp-marketplace-onboarding-tools.test.ts +185 -0
- package/src/__tests__/webmcp-register.test.ts +103 -0
- package/src/__tests__/webmcp-set-provider.test.ts +47 -0
- package/src/__tests__/webmcp-tools.test.ts +348 -0
- package/src/app/(auth)/error.tsx +72 -0
- package/src/app/(auth)/forgot-password/page.tsx +137 -0
- package/src/app/(auth)/layout.tsx +14 -0
- package/src/app/(auth)/loading.tsx +26 -0
- package/src/app/(auth)/login/page.tsx +188 -0
- package/src/app/(auth)/reset-password/page.tsx +169 -0
- package/src/app/(auth)/signup/page.tsx +309 -0
- package/src/app/(dashboard)/billing/credits/page.tsx +209 -0
- package/src/app/(dashboard)/billing/error.tsx +72 -0
- package/src/app/(dashboard)/billing/layout.tsx +73 -0
- package/src/app/(dashboard)/billing/loading.tsx +41 -0
- package/src/app/(dashboard)/billing/payment/page.tsx +639 -0
- package/src/app/(dashboard)/billing/plans/page.tsx +58 -0
- package/src/app/(dashboard)/billing/referrals/page.tsx +7 -0
- package/src/app/(dashboard)/billing/usage/hosted/page.tsx +348 -0
- package/src/app/(dashboard)/billing/usage/page.tsx +663 -0
- package/src/app/(dashboard)/changesets/[id]/changeset-detail-client.tsx +400 -0
- package/src/app/(dashboard)/changesets/[id]/error.tsx +57 -0
- package/src/app/(dashboard)/changesets/[id]/loading.tsx +23 -0
- package/src/app/(dashboard)/changesets/[id]/page.tsx +10 -0
- package/src/app/(dashboard)/changesets/error.tsx +72 -0
- package/src/app/(dashboard)/changesets/page.tsx +10 -0
- package/src/app/(dashboard)/chat/page.tsx +74 -0
- package/src/app/(dashboard)/dashboard/bots/[id]/settings/page.tsx +10 -0
- package/src/app/(dashboard)/dashboard/network/page.tsx +97 -0
- package/src/app/(dashboard)/dashboard/page.tsx +13 -0
- package/src/app/(dashboard)/error.tsx +72 -0
- package/src/app/(dashboard)/layout.tsx +113 -0
- package/src/app/(dashboard)/loading.tsx +27 -0
- package/src/app/(dashboard)/marketplace/[plugin]/page.tsx +548 -0
- package/src/app/(dashboard)/marketplace/error.tsx +72 -0
- package/src/app/(dashboard)/marketplace/loading.tsx +27 -0
- package/src/app/(dashboard)/marketplace/page.tsx +268 -0
- package/src/app/(dashboard)/not-found.tsx +46 -0
- package/src/app/(dashboard)/onboarding/page.tsx +267 -0
- package/src/app/(dashboard)/settings/account/page.tsx +132 -0
- package/src/app/(dashboard)/settings/activity/page.tsx +280 -0
- package/src/app/(dashboard)/settings/api-keys/page.tsx +530 -0
- package/src/app/(dashboard)/settings/brain/page.tsx +412 -0
- package/src/app/(dashboard)/settings/error.tsx +72 -0
- package/src/app/(dashboard)/settings/layout.tsx +114 -0
- package/src/app/(dashboard)/settings/loading.tsx +31 -0
- package/src/app/(dashboard)/settings/notifications/page.tsx +216 -0
- package/src/app/(dashboard)/settings/org/page.tsx +617 -0
- package/src/app/(dashboard)/settings/profile/page.tsx +510 -0
- package/src/app/(dashboard)/settings/providers/page.tsx +842 -0
- package/src/app/(dashboard)/settings/secrets/page.tsx +658 -0
- package/src/app/(dashboard)/settings/security/page.tsx +1133 -0
- package/src/app/admin/accounting/loading.tsx +32 -0
- package/src/app/admin/accounting/page.tsx +5 -0
- package/src/app/admin/affiliates/loading.tsx +32 -0
- package/src/app/admin/affiliates/page.tsx +5 -0
- package/src/app/admin/audit/loading.tsx +32 -0
- package/src/app/admin/audit/page.tsx +5 -0
- package/src/app/admin/billing-health/loading.tsx +17 -0
- package/src/app/admin/billing-health/page.tsx +10 -0
- package/src/app/admin/compliance/page.tsx +5 -0
- package/src/app/admin/error.tsx +72 -0
- package/src/app/admin/gpu/loading.tsx +38 -0
- package/src/app/admin/gpu/page.tsx +5 -0
- package/src/app/admin/incidents/page.tsx +10 -0
- package/src/app/admin/inference/loading.tsx +32 -0
- package/src/app/admin/inference/page.tsx +5 -0
- package/src/app/admin/layout.tsx +44 -0
- package/src/app/admin/loading.tsx +32 -0
- package/src/app/admin/marketplace/loading.tsx +32 -0
- package/src/app/admin/marketplace/page.tsx +5 -0
- package/src/app/admin/migrations/loading.tsx +22 -0
- package/src/app/admin/migrations/page.tsx +5 -0
- package/src/app/admin/onboarding/loading.tsx +18 -0
- package/src/app/admin/onboarding/page.tsx +5 -0
- package/src/app/admin/promotions/[id]/edit/loading.tsx +16 -0
- package/src/app/admin/promotions/[id]/edit/page.tsx +56 -0
- package/src/app/admin/promotions/[id]/loading.tsx +15 -0
- package/src/app/admin/promotions/[id]/page.tsx +311 -0
- package/src/app/admin/promotions/loading.tsx +21 -0
- package/src/app/admin/promotions/new/loading.tsx +16 -0
- package/src/app/admin/promotions/new/page.tsx +12 -0
- package/src/app/admin/promotions/page.tsx +266 -0
- package/src/app/admin/rate-overrides/loading.tsx +17 -0
- package/src/app/admin/rate-overrides/page.tsx +290 -0
- package/src/app/admin/roles/loading.tsx +27 -0
- package/src/app/admin/roles/page.tsx +5 -0
- package/src/app/admin/tenants/loading.tsx +32 -0
- package/src/app/admin/tenants/page.tsx +5 -0
- package/src/app/apple-icon.tsx +32 -0
- package/src/app/auth/callback/[provider]/page.tsx +104 -0
- package/src/app/auth/verify/page.tsx +224 -0
- package/src/app/channels/error.tsx +72 -0
- package/src/app/channels/loading.tsx +29 -0
- package/src/app/channels/page.tsx +262 -0
- package/src/app/channels/setup/[plugin]/page.tsx +136 -0
- package/src/app/error.tsx +72 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/fleet/error.tsx +72 -0
- package/src/app/fleet/health/page.tsx +9 -0
- package/src/app/fleet/layout.tsx +14 -0
- package/src/app/fleet/loading.tsx +33 -0
- package/src/app/fleet/page.tsx +5 -0
- package/src/app/global-error.tsx +96 -0
- package/src/app/globals.css +251 -0
- package/src/app/icon.svg +4 -0
- package/src/app/instances/[id]/instance-detail-client.tsx +1298 -0
- package/src/app/instances/[id]/page.tsx +10 -0
- package/src/app/instances/error.tsx +72 -0
- package/src/app/instances/instance-list-client.tsx +540 -0
- package/src/app/instances/loading.tsx +33 -0
- package/src/app/instances/new/create-instance-client.tsx +377 -0
- package/src/app/instances/new/page.tsx +9 -0
- package/src/app/instances/page.tsx +9 -0
- package/src/app/layout.tsx +83 -0
- package/src/app/not-found.tsx +38 -0
- package/src/app/og/route.tsx +50 -0
- package/src/app/page.tsx +39 -0
- package/src/app/plugins/error.tsx +72 -0
- package/src/app/plugins/layout.tsx +14 -0
- package/src/app/plugins/loading.tsx +30 -0
- package/src/app/plugins/page.tsx +555 -0
- package/src/app/pricing/error.tsx +72 -0
- package/src/app/pricing/loading.tsx +25 -0
- package/src/app/pricing/page.tsx +20 -0
- package/src/app/privacy/page.tsx +406 -0
- package/src/app/robots.ts +9 -0
- package/src/app/sitemap.ts +11 -0
- package/src/app/status/error.tsx +72 -0
- package/src/app/status/loading.tsx +21 -0
- package/src/app/status/page.tsx +20 -0
- package/src/app/terms/page.tsx +414 -0
- package/src/components/account-switcher.tsx +82 -0
- package/src/components/admin/accounting-dashboard.tsx +190 -0
- package/src/components/admin/admin-guard.tsx +36 -0
- package/src/components/admin/admin-nav.tsx +71 -0
- package/src/components/admin/affiliate-dashboard.tsx +564 -0
- package/src/components/admin/audit-log-table.tsx +336 -0
- package/src/components/admin/billing-health-dashboard.test.tsx +40 -0
- package/src/components/admin/billing-health-dashboard.tsx +416 -0
- package/src/components/admin/bulk-actions-bar.test.tsx +92 -0
- package/src/components/admin/bulk-actions-bar.tsx +80 -0
- package/src/components/admin/bulk-export-dialog.test.tsx +75 -0
- package/src/components/admin/bulk-export-dialog.tsx +189 -0
- package/src/components/admin/bulk-grant-dialog.test.tsx +81 -0
- package/src/components/admin/bulk-grant-dialog.tsx +147 -0
- package/src/components/admin/bulk-preview-dialog.test.tsx +72 -0
- package/src/components/admin/bulk-preview-dialog.tsx +106 -0
- package/src/components/admin/bulk-reactivate-dialog.test.tsx +51 -0
- package/src/components/admin/bulk-reactivate-dialog.tsx +55 -0
- package/src/components/admin/bulk-select-all-banner.test.tsx +36 -0
- package/src/components/admin/bulk-select-all-banner.tsx +44 -0
- package/src/components/admin/bulk-suspend-dialog.test.tsx +77 -0
- package/src/components/admin/bulk-suspend-dialog.tsx +129 -0
- package/src/components/admin/bulk-undo-toast.test.tsx +66 -0
- package/src/components/admin/bulk-undo-toast.tsx +121 -0
- package/src/components/admin/compliance-dashboard.tsx +1341 -0
- package/src/components/admin/gpu-dashboard.tsx +552 -0
- package/src/components/admin/grant-credits-dialog.tsx +121 -0
- package/src/components/admin/incident-dashboard.test.tsx +44 -0
- package/src/components/admin/incident-dashboard.tsx +717 -0
- package/src/components/admin/inference-dashboard.tsx +415 -0
- package/src/components/admin/marketplace-admin.tsx +765 -0
- package/src/components/admin/migrations-dashboard.tsx +404 -0
- package/src/components/admin/onboarding-dashboard.tsx +422 -0
- package/src/components/admin/promotions/promotion-form.tsx +440 -0
- package/src/components/admin/roles-dashboard.tsx +278 -0
- package/src/components/admin/suspend-dialog.tsx +98 -0
- package/src/components/admin/tenant-notes-panel.tsx +134 -0
- package/src/components/admin/tenant-row-actions.tsx +78 -0
- package/src/components/admin/tenant-table.tsx +339 -0
- package/src/components/auth/auth-error.tsx +22 -0
- package/src/components/auth/auth-redirect.tsx +18 -0
- package/src/components/auth/auth-shell.tsx +25 -0
- package/src/components/auth/email-verification-banner.tsx +25 -0
- package/src/components/auth/email-verification-result-banner.tsx +70 -0
- package/src/components/auth/resend-verification-button.tsx +94 -0
- package/src/components/auth/wopr-wordmark.tsx +19 -0
- package/src/components/billing/add-payment-method-dialog.tsx +267 -0
- package/src/components/billing/affiliate-dashboard.tsx +300 -0
- package/src/components/billing/auto-topup-card.tsx +432 -0
- package/src/components/billing/buy-credits-panel.tsx +180 -0
- package/src/components/billing/buy-crypto-credits-panel.tsx +96 -0
- package/src/components/billing/byok-callout.tsx +87 -0
- package/src/components/billing/coupon-input.tsx +86 -0
- package/src/components/billing/credit-balance.tsx +79 -0
- package/src/components/billing/degraded-state-banner.tsx +95 -0
- package/src/components/billing/dividend-banner.tsx +97 -0
- package/src/components/billing/dividend-eligibility.tsx +86 -0
- package/src/components/billing/dividend-pool-stats.tsx +86 -0
- package/src/components/billing/first-dividend-dialog.tsx +109 -0
- package/src/components/billing/low-balance-banner.tsx +50 -0
- package/src/components/billing/org-billing-page.tsx +360 -0
- package/src/components/billing/suspension-banner.tsx +53 -0
- package/src/components/billing/transaction-history.tsx +239 -0
- package/src/components/bot-settings/__tests__/bot-settings-client.test.tsx +205 -0
- package/src/components/bot-settings/backups-tab.tsx +377 -0
- package/src/components/bot-settings/bot-settings-client.tsx +1712 -0
- package/src/components/bot-settings/resources-tab.tsx +203 -0
- package/src/components/bot-settings/storage-tab.tsx +248 -0
- package/src/components/bot-settings/vps-info-panel.tsx +132 -0
- package/src/components/bot-settings/vps-upgrade-card.tsx +110 -0
- package/src/components/capability/CapabilityResolver.tsx +113 -0
- package/src/components/channel-wizard/field-interactive.tsx +48 -0
- package/src/components/channel-wizard/field-oauth.tsx +181 -0
- package/src/components/channel-wizard/field-paste.tsx +47 -0
- package/src/components/channel-wizard/field-qr.tsx +302 -0
- package/src/components/channel-wizard/index.ts +6 -0
- package/src/components/channel-wizard/step-renderer.tsx +103 -0
- package/src/components/channel-wizard/wizard.tsx +200 -0
- package/src/components/chat/ambient-dot.tsx +32 -0
- package/src/components/chat/chat-input.tsx +56 -0
- package/src/components/chat/chat-message.tsx +36 -0
- package/src/components/chat/chat-panel.tsx +138 -0
- package/src/components/chat/chat-widget.tsx +41 -0
- package/src/components/chat/index.ts +5 -0
- package/src/components/dashboard/command-center.tsx +614 -0
- package/src/components/instances/friends-tab.test.tsx +265 -0
- package/src/components/instances/friends-tab.tsx +721 -0
- package/src/components/landing/hero.tsx +53 -0
- package/src/components/landing/landing-nav.tsx +21 -0
- package/src/components/landing/landing-page.tsx +71 -0
- package/src/components/landing/portfolio-chart.tsx +349 -0
- package/src/components/landing/story-sections.tsx +50 -0
- package/src/components/landing/terminal-lines.ts +99 -0
- package/src/components/landing/terminal-sequence.tsx +453 -0
- package/src/components/landing/typing-effect.tsx +43 -0
- package/src/components/marketplace/category-filter.tsx +61 -0
- package/src/components/marketplace/empty-state.tsx +61 -0
- package/src/components/marketplace/featured-heroes.tsx +84 -0
- package/src/components/marketplace/first-visit-hero.tsx +110 -0
- package/src/components/marketplace/index.ts +9 -0
- package/src/components/marketplace/install-wizard.tsx +782 -0
- package/src/components/marketplace/marketplace-tabs.tsx +54 -0
- package/src/components/marketplace/plugin-card.tsx +129 -0
- package/src/components/marketplace/superpower-card.tsx +104 -0
- package/src/components/marketplace/superpower-content.tsx +117 -0
- package/src/components/marketplace/terminal-search.tsx +67 -0
- package/src/components/oauth-buttons.tsx +75 -0
- package/src/components/observability/fleet-health.tsx +370 -0
- package/src/components/observability/health-overview.tsx +246 -0
- package/src/components/observability/logs-viewer.tsx +215 -0
- package/src/components/observability/metrics-dashboard.tsx +288 -0
- package/src/components/onboarding/fallback-setup.tsx +137 -0
- package/src/components/onboarding/index.ts +3 -0
- package/src/components/onboarding/setup-checklist.tsx +333 -0
- package/src/components/onboarding/step-superpowers.tsx +122 -0
- package/src/components/plugin-setup/index.ts +1 -0
- package/src/components/plugin-setup/setup-chat-panel.tsx +188 -0
- package/src/components/pricing/dividend-calculator.tsx +47 -0
- package/src/components/pricing/dividend-stats.tsx +117 -0
- package/src/components/pricing/pricing-page.tsx +229 -0
- package/src/components/settings/create-org-wizard.tsx +225 -0
- package/src/components/sidebar.tsx +202 -0
- package/src/components/status/status-page.tsx +209 -0
- package/src/components/status-badge.tsx +28 -0
- package/src/components/theme-provider.tsx +8 -0
- package/src/components/ui/alert-dialog.tsx +141 -0
- package/src/components/ui/badge.tsx +47 -0
- package/src/components/ui/banner.tsx +36 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/card.tsx +75 -0
- package/src/components/ui/checkbox.tsx +52 -0
- package/src/components/ui/collapsible.tsx +31 -0
- package/src/components/ui/credit-detailed.tsx +33 -0
- package/src/components/ui/dialog.tsx +143 -0
- package/src/components/ui/dropdown-menu.tsx +228 -0
- package/src/components/ui/form.tsx +151 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +21 -0
- package/src/components/ui/popover.tsx +74 -0
- package/src/components/ui/progress.tsx +28 -0
- package/src/components/ui/radio-group.tsx +45 -0
- package/src/components/ui/select.tsx +175 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +125 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/switch.tsx +35 -0
- package/src/components/ui/table.tsx +92 -0
- package/src/components/ui/tabs.tsx +81 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +44 -0
- package/src/config/provider-docs.ts +17 -0
- package/src/hooks/__tests__/use-async.test.ts +127 -0
- package/src/hooks/__tests__/use-count-up.test.ts +129 -0
- package/src/hooks/__tests__/use-debounce.test.ts +105 -0
- package/src/hooks/__tests__/use-fleet-sse.test.ts +216 -0
- package/src/hooks/__tests__/use-local-storage.test.ts +74 -0
- package/src/hooks/__tests__/use-mobile.test.ts +86 -0
- package/src/hooks/__tests__/use-save-queue.test.ts +159 -0
- package/src/hooks/use-async.ts +54 -0
- package/src/hooks/use-capability-meta.ts +99 -0
- package/src/hooks/use-count-up.ts +23 -0
- package/src/hooks/use-debounce.ts +12 -0
- package/src/hooks/use-fleet-sse.ts +47 -0
- package/src/hooks/use-has-org.ts +18 -0
- package/src/hooks/use-image-status.ts +36 -0
- package/src/hooks/use-local-storage.ts +36 -0
- package/src/hooks/use-mobile.ts +17 -0
- package/src/hooks/use-page-context.ts +24 -0
- package/src/hooks/use-pagination-params.ts +30 -0
- package/src/hooks/use-plugin-registry.ts +247 -0
- package/src/hooks/use-plugin-setup-chat.ts +211 -0
- package/src/hooks/use-save-queue.ts +54 -0
- package/src/hooks/use-webmcp.ts +40 -0
- package/src/lib/__tests__/__snapshots__/pricing-data.test.ts.snap +112 -0
- package/src/lib/__tests__/admin-api.test.ts +487 -0
- package/src/lib/__tests__/api-bot-crud.test.ts +391 -0
- package/src/lib/__tests__/api-fetch.test.ts +196 -0
- package/src/lib/__tests__/bot-settings-data.test.ts +352 -0
- package/src/lib/__tests__/org-api.test.ts +281 -0
- package/src/lib/__tests__/org-billing-api.test.ts +242 -0
- package/src/lib/__tests__/pricing-data.test.ts +32 -0
- package/src/lib/__tests__/settings-api.test.ts +272 -0
- package/src/lib/admin-affiliate-api.ts +51 -0
- package/src/lib/admin-api.ts +325 -0
- package/src/lib/admin-compliance-api.ts +127 -0
- package/src/lib/admin-gpu-api.ts +82 -0
- package/src/lib/admin-incident-api.ts +121 -0
- package/src/lib/admin-inference-api.ts +47 -0
- package/src/lib/admin-marketplace-api.ts +97 -0
- package/src/lib/api-config.test.ts +111 -0
- package/src/lib/api-config.ts +65 -0
- package/src/lib/api-errors.test.ts +43 -0
- package/src/lib/api.ts +2011 -0
- package/src/lib/auth-client.ts +11 -0
- package/src/lib/bot-settings-data.ts +342 -0
- package/src/lib/brand-config.ts +145 -0
- package/src/lib/brand.ts +669 -0
- package/src/lib/changeset-api.ts +29 -0
- package/src/lib/changeset-types.ts +56 -0
- package/src/lib/channel-manifests.ts +50 -0
- package/src/lib/chat/chat-context.tsx +70 -0
- package/src/lib/chat/chat-store.ts +62 -0
- package/src/lib/chat/types.ts +35 -0
- package/src/lib/chat/use-chat.ts +255 -0
- package/src/lib/cost-comparison-data.test.ts +95 -0
- package/src/lib/cost-comparison-data.ts +54 -0
- package/src/lib/errors.test.ts +64 -0
- package/src/lib/errors.ts +52 -0
- package/src/lib/fetch-utils.test.ts +57 -0
- package/src/lib/fetch-utils.ts +25 -0
- package/src/lib/format-credit.test.ts +66 -0
- package/src/lib/format-credit.ts +24 -0
- package/src/lib/format.test.ts +62 -0
- package/src/lib/format.ts +17 -0
- package/src/lib/logger.ts +28 -0
- package/src/lib/marketplace-data.ts +346 -0
- package/src/lib/oauth-errors.ts +19 -0
- package/src/lib/onboarding-data.ts +1265 -0
- package/src/lib/onboarding-store.ts +233 -0
- package/src/lib/org-api.ts +74 -0
- package/src/lib/org-billing-api.ts +81 -0
- package/src/lib/page-prompts.test.ts +32 -0
- package/src/lib/page-prompts.ts +23 -0
- package/src/lib/plugin/index.ts +32 -0
- package/src/lib/plugin/tool-definitions.ts +306 -0
- package/src/lib/pricing-data.ts +115 -0
- package/src/lib/promotions-types.ts +58 -0
- package/src/lib/settings-api.ts +63 -0
- package/src/lib/status-colors.ts +38 -0
- package/src/lib/tenant-context.tsx +134 -0
- package/src/lib/trpc-types.ts +173 -0
- package/src/lib/trpc.tsx +86 -0
- package/src/lib/utils.test.ts +55 -0
- package/src/lib/utils.ts +18 -0
- package/src/lib/validate-redirect-url.ts +39 -0
- package/src/lib/webmcp/feature-detect.ts +13 -0
- package/src/lib/webmcp/marketplace-onboarding-tools.ts +202 -0
- package/src/lib/webmcp/register.ts +44 -0
- package/src/lib/webmcp/tools.ts +422 -0
- package/src/proxy.ts +258 -0
- package/src/types/missing-deps.d.ts +160 -0
- package/src/types/motion-dom.d.ts +162 -0
- package/src/types/vitest-matchers.d.ts +40 -0
- package/src/types/web-mcp.d.ts +22 -0
- package/tsconfig.json +34 -0
- package/vitest.config.ts +26 -0
|
@@ -0,0 +1,678 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import type { BotSettings } from "@/lib/bot-settings-data";
|
|
5
|
+
import { PERSONALITY_TEMPLATES } from "@/lib/bot-settings-data";
|
|
6
|
+
|
|
7
|
+
const TEST_BOT_SETTINGS = vi.hoisted(
|
|
8
|
+
(): BotSettings => ({
|
|
9
|
+
id: "bot-001",
|
|
10
|
+
identity: {
|
|
11
|
+
name: "Jarvis",
|
|
12
|
+
avatar: "robot",
|
|
13
|
+
personality:
|
|
14
|
+
'You are Jarvis, a helpful AI assistant. You are witty, concise, and slightly sarcastic. You help with coding, research, and creative tasks. You call your owner "sir."',
|
|
15
|
+
},
|
|
16
|
+
brain: {
|
|
17
|
+
provider: "Anthropic",
|
|
18
|
+
model: "Claude Sonnet 4",
|
|
19
|
+
mode: "hosted",
|
|
20
|
+
costPerMessage: "~$0.01/message",
|
|
21
|
+
description: "Excellent at reasoning and code",
|
|
22
|
+
},
|
|
23
|
+
channels: [
|
|
24
|
+
{
|
|
25
|
+
id: "ch-1",
|
|
26
|
+
type: "Discord",
|
|
27
|
+
name: "My Server",
|
|
28
|
+
status: "connected",
|
|
29
|
+
stats: "3 channels, 142 messages today",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
id: "ch-2",
|
|
33
|
+
type: "Web UI",
|
|
34
|
+
name: "chat.localhost/jarvis",
|
|
35
|
+
status: "always-on",
|
|
36
|
+
stats: "12 sessions today",
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
availableChannels: [
|
|
40
|
+
{ type: "Slack", label: "Slack" },
|
|
41
|
+
{ type: "Telegram", label: "Telegram" },
|
|
42
|
+
{ type: "WhatsApp", label: "WhatsApp" },
|
|
43
|
+
],
|
|
44
|
+
activeSuperpowers: [
|
|
45
|
+
{
|
|
46
|
+
id: "image-gen",
|
|
47
|
+
name: "ImageGen",
|
|
48
|
+
icon: "image",
|
|
49
|
+
mode: "hosted",
|
|
50
|
+
provider: "Replicate",
|
|
51
|
+
model: "SDXL",
|
|
52
|
+
usageCount: 47,
|
|
53
|
+
usageLabel: "images this week",
|
|
54
|
+
spend: 2.35,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "voice",
|
|
58
|
+
name: "Voice (STT+TTS)",
|
|
59
|
+
icon: "mic",
|
|
60
|
+
mode: "hosted",
|
|
61
|
+
provider: "Deepgram + ElevenLabs",
|
|
62
|
+
model: "STT + TTS",
|
|
63
|
+
usageCount: 34,
|
|
64
|
+
usageLabel: "min this week",
|
|
65
|
+
spend: 1.2,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: "memory",
|
|
69
|
+
name: "Memory",
|
|
70
|
+
icon: "brain",
|
|
71
|
+
mode: "hosted",
|
|
72
|
+
provider: "OpenAI",
|
|
73
|
+
model: "ada-002",
|
|
74
|
+
usageCount: 2847,
|
|
75
|
+
usageLabel: "memories stored",
|
|
76
|
+
spend: 0.45,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
availableSuperpowers: [
|
|
80
|
+
{
|
|
81
|
+
id: "video-gen",
|
|
82
|
+
name: "VideoGen",
|
|
83
|
+
icon: "video",
|
|
84
|
+
description: "Generate videos from text in any channel",
|
|
85
|
+
pricing: "~$0.50/vid",
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
id: "phone",
|
|
89
|
+
name: "Phone Calls",
|
|
90
|
+
icon: "phone",
|
|
91
|
+
description: "Your bot answers the phone and calls people",
|
|
92
|
+
pricing: "~$0.10/min",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
id: "sms",
|
|
96
|
+
name: "SMS",
|
|
97
|
+
icon: "message-square",
|
|
98
|
+
description: "Your bot sends and receives texts",
|
|
99
|
+
pricing: "~$0.02/msg",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: "search",
|
|
103
|
+
name: "Web Search",
|
|
104
|
+
icon: "search",
|
|
105
|
+
description: "Web + doc search in any channel",
|
|
106
|
+
pricing: "~$0.01/search",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
id: "analytics",
|
|
110
|
+
name: "Analytics",
|
|
111
|
+
icon: "bar-chart",
|
|
112
|
+
description: "Usage insights and reports",
|
|
113
|
+
pricing: "Free",
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
installedPlugins: [
|
|
117
|
+
{
|
|
118
|
+
id: "meeting-transcriber",
|
|
119
|
+
name: "Meeting Transcriber",
|
|
120
|
+
description: "Transcribes voice calls to searchable notes",
|
|
121
|
+
icon: "file-text",
|
|
122
|
+
status: "active",
|
|
123
|
+
capabilities: ["Voice (STT)", "Memory"],
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: "art-director",
|
|
127
|
+
name: "Art Director",
|
|
128
|
+
description: "Style-consistent image generation with presets",
|
|
129
|
+
icon: "palette",
|
|
130
|
+
status: "active",
|
|
131
|
+
capabilities: ["ImageGen"],
|
|
132
|
+
},
|
|
133
|
+
],
|
|
134
|
+
discoverPlugins: [
|
|
135
|
+
{
|
|
136
|
+
id: "analytics-pro",
|
|
137
|
+
name: "Analytics Pro",
|
|
138
|
+
description: "Deep usage analytics & reports",
|
|
139
|
+
icon: "bar-chart",
|
|
140
|
+
needs: ["Hosted"],
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "music-bot",
|
|
144
|
+
name: "Music Bot",
|
|
145
|
+
description: "Play music in voice channels",
|
|
146
|
+
icon: "music",
|
|
147
|
+
needs: ["Voice", "Hosted"],
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
id: "scheduler",
|
|
151
|
+
name: "Scheduler",
|
|
152
|
+
description: "Schedule messages & reminders",
|
|
153
|
+
icon: "calendar",
|
|
154
|
+
needs: [],
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
usage: {
|
|
158
|
+
totalSpend: 4.0,
|
|
159
|
+
creditBalance: 7.42,
|
|
160
|
+
capabilities: [
|
|
161
|
+
{ capability: "ImageGen", icon: "image", spend: 2.35, percent: 59 },
|
|
162
|
+
{ capability: "Voice", icon: "mic", spend: 1.2, percent: 30 },
|
|
163
|
+
{ capability: "Memory", icon: "brain", spend: 0.45, percent: 11 },
|
|
164
|
+
],
|
|
165
|
+
trend: [
|
|
166
|
+
{ date: "Feb 1", spend: 0.5 },
|
|
167
|
+
{ date: "Feb 3", spend: 0.8 },
|
|
168
|
+
{ date: "Feb 5", spend: 0.3 },
|
|
169
|
+
{ date: "Feb 7", spend: 1.2 },
|
|
170
|
+
{ date: "Feb 9", spend: 0.6 },
|
|
171
|
+
{ date: "Feb 11", spend: 0.4 },
|
|
172
|
+
{ date: "Feb 13", spend: 0.2 },
|
|
173
|
+
],
|
|
174
|
+
},
|
|
175
|
+
status: "running",
|
|
176
|
+
}),
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// Mock next/navigation
|
|
180
|
+
vi.mock("next/navigation", () => ({
|
|
181
|
+
useRouter: () => ({ push: vi.fn() }),
|
|
182
|
+
useSearchParams: () => new URLSearchParams(),
|
|
183
|
+
usePathname: () => "/dashboard/bots/bot-001/settings",
|
|
184
|
+
}));
|
|
185
|
+
|
|
186
|
+
// Mock better-auth/react
|
|
187
|
+
vi.mock("better-auth/react", () => ({
|
|
188
|
+
createAuthClient: () => ({
|
|
189
|
+
useSession: () => ({ data: null, isPending: false, error: null }),
|
|
190
|
+
signIn: { email: vi.fn(), social: vi.fn() },
|
|
191
|
+
signUp: { email: vi.fn() },
|
|
192
|
+
signOut: vi.fn(),
|
|
193
|
+
}),
|
|
194
|
+
}));
|
|
195
|
+
|
|
196
|
+
// Mock bot-settings-data API functions
|
|
197
|
+
vi.mock("@/lib/bot-settings-data", async (importOriginal) => {
|
|
198
|
+
const actual = await importOriginal<typeof import("@/lib/bot-settings-data")>();
|
|
199
|
+
return {
|
|
200
|
+
...actual,
|
|
201
|
+
getBotSettings: vi.fn().mockResolvedValue(TEST_BOT_SETTINGS),
|
|
202
|
+
updateBotIdentity: vi.fn().mockResolvedValue(TEST_BOT_SETTINGS.identity),
|
|
203
|
+
activateSuperpower: vi.fn().mockResolvedValue({ success: true }),
|
|
204
|
+
controlBot: vi.fn().mockResolvedValue(undefined),
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe("BotSettingsClient", () => {
|
|
209
|
+
it("renders loading state initially", async () => {
|
|
210
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
211
|
+
const { container } = render(<BotSettingsClient botId="bot-001" />);
|
|
212
|
+
expect(container.querySelectorAll('[data-slot="skeleton"]').length).toBeGreaterThan(0);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("renders bot name and status after loading", async () => {
|
|
216
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
217
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
218
|
+
expect(await screen.findByText("Jarvis")).toBeInTheDocument();
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("renders all 7 tab triggers", async () => {
|
|
222
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
223
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
224
|
+
await screen.findByText("Jarvis");
|
|
225
|
+
|
|
226
|
+
expect(screen.getByRole("tab", { name: "Identity" })).toBeInTheDocument();
|
|
227
|
+
expect(screen.getByRole("tab", { name: "Brain" })).toBeInTheDocument();
|
|
228
|
+
expect(screen.getByRole("tab", { name: "Channels" })).toBeInTheDocument();
|
|
229
|
+
expect(screen.getByRole("tab", { name: "Superpowers" })).toBeInTheDocument();
|
|
230
|
+
expect(screen.getByRole("tab", { name: "Plugins" })).toBeInTheDocument();
|
|
231
|
+
expect(screen.getByRole("tab", { name: "Usage" })).toBeInTheDocument();
|
|
232
|
+
expect(screen.getByRole("tab", { name: "Danger Zone" })).toBeInTheDocument();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("renders back to fleet link", async () => {
|
|
236
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
237
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
238
|
+
await screen.findByText("Jarvis");
|
|
239
|
+
|
|
240
|
+
const backLink = screen.getByRole("link", { name: /Back to Fleet/ });
|
|
241
|
+
expect(backLink).toBeInTheDocument();
|
|
242
|
+
expect(backLink).toHaveAttribute("href", "/dashboard");
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe("Identity tab", () => {
|
|
247
|
+
it("renders identity form fields", async () => {
|
|
248
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
249
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
250
|
+
await screen.findByText("Jarvis");
|
|
251
|
+
|
|
252
|
+
expect(screen.getByLabelText("Name")).toBeInTheDocument();
|
|
253
|
+
expect(screen.getByLabelText("Personality")).toBeInTheDocument();
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it("renders personality template buttons", async () => {
|
|
257
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
258
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
259
|
+
await screen.findByText("Jarvis");
|
|
260
|
+
|
|
261
|
+
for (const template of PERSONALITY_TEMPLATES) {
|
|
262
|
+
expect(screen.getByRole("button", { name: template.label })).toBeInTheDocument();
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
it("renders save changes button", async () => {
|
|
267
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
268
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
269
|
+
await screen.findByText("Jarvis");
|
|
270
|
+
|
|
271
|
+
expect(screen.getByRole("button", { name: "Save changes" })).toBeInTheDocument();
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe("Brain tab", () => {
|
|
276
|
+
it("renders current model and provider mode", async () => {
|
|
277
|
+
const user = userEvent.setup();
|
|
278
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
279
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
280
|
+
await screen.findByText("Jarvis");
|
|
281
|
+
|
|
282
|
+
await user.click(screen.getByRole("tab", { name: "Brain" }));
|
|
283
|
+
|
|
284
|
+
expect(screen.getByText("Model")).toBeInTheDocument();
|
|
285
|
+
expect(screen.getByDisplayValue("Claude Sonnet 4")).toBeInTheDocument();
|
|
286
|
+
expect(screen.getByText("Provider Mode")).toBeInTheDocument();
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it("renders Platform Hosted and BYOK options", async () => {
|
|
290
|
+
const user = userEvent.setup();
|
|
291
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
292
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
293
|
+
await screen.findByText("Jarvis");
|
|
294
|
+
|
|
295
|
+
await user.click(screen.getByRole("tab", { name: "Brain" }));
|
|
296
|
+
|
|
297
|
+
expect(screen.getByText("Platform Hosted")).toBeInTheDocument();
|
|
298
|
+
expect(screen.getByText("Bring Your Own Key")).toBeInTheDocument();
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe("Channels tab", () => {
|
|
303
|
+
it("renders connected channels", async () => {
|
|
304
|
+
const user = userEvent.setup();
|
|
305
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
306
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
307
|
+
await screen.findByText("Jarvis");
|
|
308
|
+
|
|
309
|
+
await user.click(screen.getByRole("tab", { name: "Channels" }));
|
|
310
|
+
|
|
311
|
+
expect(screen.getByText("Discord")).toBeInTheDocument();
|
|
312
|
+
expect(screen.getByText("Web UI")).toBeInTheDocument();
|
|
313
|
+
expect(screen.getByText("Connected")).toBeInTheDocument();
|
|
314
|
+
expect(screen.getByText("Always On")).toBeInTheDocument();
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it("renders add channel buttons", async () => {
|
|
318
|
+
const user = userEvent.setup();
|
|
319
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
320
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
321
|
+
await screen.findByText("Jarvis");
|
|
322
|
+
|
|
323
|
+
await user.click(screen.getByRole("tab", { name: "Channels" }));
|
|
324
|
+
|
|
325
|
+
expect(screen.getByRole("button", { name: "+ Add Slack" })).toBeInTheDocument();
|
|
326
|
+
expect(screen.getByRole("button", { name: "+ Add Telegram" })).toBeInTheDocument();
|
|
327
|
+
expect(screen.getByRole("button", { name: "+ Add WhatsApp" })).toBeInTheDocument();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("renders free channels messaging", async () => {
|
|
331
|
+
const user = userEvent.setup();
|
|
332
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
333
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
334
|
+
await screen.findByText("Jarvis");
|
|
335
|
+
|
|
336
|
+
await user.click(screen.getByRole("tab", { name: "Channels" }));
|
|
337
|
+
|
|
338
|
+
expect(
|
|
339
|
+
screen.getByText("Your Platform works everywhere you do. All channels are free."),
|
|
340
|
+
).toBeInTheDocument();
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
describe("Superpowers tab", () => {
|
|
345
|
+
it("renders active superpowers with usage stats", async () => {
|
|
346
|
+
const user = userEvent.setup();
|
|
347
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
348
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
349
|
+
await screen.findByText("Jarvis");
|
|
350
|
+
|
|
351
|
+
await user.click(screen.getByRole("tab", { name: "Superpowers" }));
|
|
352
|
+
|
|
353
|
+
expect(screen.getByText("ImageGen")).toBeInTheDocument();
|
|
354
|
+
expect(screen.getByText(/47 images this week/)).toBeInTheDocument();
|
|
355
|
+
expect(screen.getByText(/\$2\.35/)).toBeInTheDocument();
|
|
356
|
+
|
|
357
|
+
expect(screen.getByText("Voice (STT+TTS)")).toBeInTheDocument();
|
|
358
|
+
expect(screen.getByText("Memory")).toBeInTheDocument();
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it("renders available superpowers with pricing", async () => {
|
|
362
|
+
const user = userEvent.setup();
|
|
363
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
364
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
365
|
+
await screen.findByText("Jarvis");
|
|
366
|
+
|
|
367
|
+
await user.click(screen.getByRole("tab", { name: "Superpowers" }));
|
|
368
|
+
|
|
369
|
+
expect(screen.getByText("VideoGen")).toBeInTheDocument();
|
|
370
|
+
expect(screen.getByText("~$0.50/vid")).toBeInTheDocument();
|
|
371
|
+
expect(screen.getByText("Phone Calls")).toBeInTheDocument();
|
|
372
|
+
expect(screen.getByText("~$0.10/min")).toBeInTheDocument();
|
|
373
|
+
expect(screen.getByText("SMS")).toBeInTheDocument();
|
|
374
|
+
expect(screen.getByText("Web Search")).toBeInTheDocument();
|
|
375
|
+
expect(screen.getByText("Analytics")).toBeInTheDocument();
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
it("renders add buttons for available superpowers", async () => {
|
|
379
|
+
const user = userEvent.setup();
|
|
380
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
381
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
382
|
+
await screen.findByText("Jarvis");
|
|
383
|
+
|
|
384
|
+
await user.click(screen.getByRole("tab", { name: "Superpowers" }));
|
|
385
|
+
|
|
386
|
+
const addButtons = screen.getAllByRole("button", { name: "+ Add" });
|
|
387
|
+
expect(addButtons.length).toBe(5); // 5 available superpowers
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
it("renders 'Your Platform can do more' section", async () => {
|
|
391
|
+
const user = userEvent.setup();
|
|
392
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
393
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
394
|
+
await screen.findByText("Jarvis");
|
|
395
|
+
|
|
396
|
+
await user.click(screen.getByRole("tab", { name: "Superpowers" }));
|
|
397
|
+
|
|
398
|
+
expect(screen.getByText("Your Platform can do more")).toBeInTheDocument();
|
|
399
|
+
expect(screen.getByText("One click to activate. Uses your credits.")).toBeInTheDocument();
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it("calls activateSuperpower on add button click", async () => {
|
|
403
|
+
const user = userEvent.setup();
|
|
404
|
+
const { activateSuperpower } = await import("@/lib/bot-settings-data");
|
|
405
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
406
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
407
|
+
await screen.findByText("Jarvis");
|
|
408
|
+
|
|
409
|
+
await user.click(screen.getByRole("tab", { name: "Superpowers" }));
|
|
410
|
+
|
|
411
|
+
const addButtons = screen.getAllByRole("button", { name: "+ Add" });
|
|
412
|
+
await user.click(addButtons[0]);
|
|
413
|
+
|
|
414
|
+
expect(activateSuperpower).toHaveBeenCalledWith("bot-001", "video-gen");
|
|
415
|
+
});
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
describe("Plugins tab", () => {
|
|
419
|
+
it("renders installed plugins", async () => {
|
|
420
|
+
const user = userEvent.setup();
|
|
421
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
422
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
423
|
+
await screen.findByText("Jarvis");
|
|
424
|
+
|
|
425
|
+
await user.click(screen.getByRole("tab", { name: "Plugins" }));
|
|
426
|
+
|
|
427
|
+
expect(screen.getByText("Meeting Transcriber")).toBeInTheDocument();
|
|
428
|
+
expect(screen.getByText("Art Director")).toBeInTheDocument();
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it("renders plugin capabilities", async () => {
|
|
432
|
+
const user = userEvent.setup();
|
|
433
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
434
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
435
|
+
await screen.findByText("Jarvis");
|
|
436
|
+
|
|
437
|
+
await user.click(screen.getByRole("tab", { name: "Plugins" }));
|
|
438
|
+
|
|
439
|
+
expect(screen.getByText("Uses: Voice (STT), Memory")).toBeInTheDocument();
|
|
440
|
+
expect(screen.getByText("Uses: ImageGen")).toBeInTheDocument();
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it("renders discover plugins section", async () => {
|
|
444
|
+
const user = userEvent.setup();
|
|
445
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
446
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
447
|
+
await screen.findByText("Jarvis");
|
|
448
|
+
|
|
449
|
+
await user.click(screen.getByRole("tab", { name: "Plugins" }));
|
|
450
|
+
|
|
451
|
+
expect(screen.getByText("Discover Plugins")).toBeInTheDocument();
|
|
452
|
+
expect(screen.getByText("Analytics Pro")).toBeInTheDocument();
|
|
453
|
+
expect(screen.getByText("Music Bot")).toBeInTheDocument();
|
|
454
|
+
expect(screen.getByText("Scheduler")).toBeInTheDocument();
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it("renders needs badges on discover plugins", async () => {
|
|
458
|
+
const user = userEvent.setup();
|
|
459
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
460
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
461
|
+
await screen.findByText("Jarvis");
|
|
462
|
+
|
|
463
|
+
await user.click(screen.getByRole("tab", { name: "Plugins" }));
|
|
464
|
+
|
|
465
|
+
const hostedBadges = screen.getAllByText("Needs: Hosted");
|
|
466
|
+
expect(hostedBadges.length).toBeGreaterThanOrEqual(1);
|
|
467
|
+
expect(screen.getByText("Needs: Voice")).toBeInTheDocument();
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it("renders browse all plugins link", async () => {
|
|
471
|
+
const user = userEvent.setup();
|
|
472
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
473
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
474
|
+
await screen.findByText("Jarvis");
|
|
475
|
+
|
|
476
|
+
await user.click(screen.getByRole("tab", { name: "Plugins" }));
|
|
477
|
+
|
|
478
|
+
const browseLink = screen.getByRole("link", { name: "Browse all plugins" });
|
|
479
|
+
expect(browseLink).toHaveAttribute("href", "/marketplace");
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
describe("Usage tab", () => {
|
|
484
|
+
it("renders usage summary", async () => {
|
|
485
|
+
const user = userEvent.setup();
|
|
486
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
487
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
488
|
+
await screen.findByText("Jarvis");
|
|
489
|
+
|
|
490
|
+
await user.click(screen.getByRole("tab", { name: "Usage" }));
|
|
491
|
+
|
|
492
|
+
expect(screen.getByText(/\$4\.00/)).toBeInTheDocument();
|
|
493
|
+
expect(screen.getByText(/\$7\.42/)).toBeInTheDocument();
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
it("renders per-capability breakdown", async () => {
|
|
497
|
+
const user = userEvent.setup();
|
|
498
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
499
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
500
|
+
await screen.findByText("Jarvis");
|
|
501
|
+
|
|
502
|
+
await user.click(screen.getByRole("tab", { name: "Usage" }));
|
|
503
|
+
|
|
504
|
+
expect(screen.getByText("By Capability")).toBeInTheDocument();
|
|
505
|
+
// Capability names appear in the breakdown
|
|
506
|
+
const usageSection = screen.getByText("By Capability").closest("[data-slot='card']");
|
|
507
|
+
expect(usageSection).toBeInTheDocument();
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it("renders trend chart", async () => {
|
|
511
|
+
const user = userEvent.setup();
|
|
512
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
513
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
514
|
+
await screen.findByText("Jarvis");
|
|
515
|
+
|
|
516
|
+
await user.click(screen.getByRole("tab", { name: "Usage" }));
|
|
517
|
+
|
|
518
|
+
expect(screen.getByText("Trend")).toBeInTheDocument();
|
|
519
|
+
expect(screen.getByText("Daily spend over the last 14 days")).toBeInTheDocument();
|
|
520
|
+
});
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
describe("Danger Zone tab", () => {
|
|
524
|
+
it("renders danger zone heading", async () => {
|
|
525
|
+
const user = userEvent.setup();
|
|
526
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
527
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
528
|
+
await screen.findByText("Jarvis");
|
|
529
|
+
|
|
530
|
+
await user.click(screen.getByRole("tab", { name: "Danger Zone" }));
|
|
531
|
+
|
|
532
|
+
expect(screen.getByRole("heading", { name: "Danger Zone" })).toBeInTheDocument();
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it("renders stop, archive, and delete buttons", async () => {
|
|
536
|
+
const user = userEvent.setup();
|
|
537
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
538
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
539
|
+
await screen.findByText("Jarvis");
|
|
540
|
+
|
|
541
|
+
await user.click(screen.getByRole("tab", { name: "Danger Zone" }));
|
|
542
|
+
|
|
543
|
+
expect(screen.getByRole("button", { name: "Stop Jarvis" })).toBeInTheDocument();
|
|
544
|
+
expect(screen.getByRole("button", { name: "Archive Jarvis" })).toBeInTheDocument();
|
|
545
|
+
expect(screen.getByRole("button", { name: "Delete Jarvis" })).toBeInTheDocument();
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
it("opens confirmation dialog on stop click", async () => {
|
|
549
|
+
const user = userEvent.setup();
|
|
550
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
551
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
552
|
+
await screen.findByText("Jarvis");
|
|
553
|
+
|
|
554
|
+
await user.click(screen.getByRole("tab", { name: "Danger Zone" }));
|
|
555
|
+
await user.click(screen.getByRole("button", { name: "Stop Jarvis" }));
|
|
556
|
+
|
|
557
|
+
expect(screen.getByText("Stop Jarvis?")).toBeInTheDocument();
|
|
558
|
+
expect(screen.getByRole("button", { name: "Stop bot" })).toBeInTheDocument();
|
|
559
|
+
expect(screen.getByRole("button", { name: "Cancel" })).toBeInTheDocument();
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
it("opens delete dialog with name confirmation requirement", async () => {
|
|
563
|
+
const user = userEvent.setup();
|
|
564
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
565
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
566
|
+
await screen.findByText("Jarvis");
|
|
567
|
+
|
|
568
|
+
await user.click(screen.getByRole("tab", { name: "Danger Zone" }));
|
|
569
|
+
await user.click(screen.getByRole("button", { name: "Delete Jarvis" }));
|
|
570
|
+
|
|
571
|
+
expect(screen.getByText("Delete Jarvis permanently?")).toBeInTheDocument();
|
|
572
|
+
expect(screen.getByPlaceholderText('Type "Jarvis" to confirm')).toBeInTheDocument();
|
|
573
|
+
|
|
574
|
+
// Delete button should be disabled until name is typed
|
|
575
|
+
const deleteBtn = screen.getByRole("button", { name: "Delete permanently" });
|
|
576
|
+
expect(deleteBtn).toBeDisabled();
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it("enables delete button when name matches", async () => {
|
|
580
|
+
const user = userEvent.setup();
|
|
581
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
582
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
583
|
+
await screen.findByText("Jarvis");
|
|
584
|
+
|
|
585
|
+
await user.click(screen.getByRole("tab", { name: "Danger Zone" }));
|
|
586
|
+
await user.click(screen.getByRole("button", { name: "Delete Jarvis" }));
|
|
587
|
+
|
|
588
|
+
const input = screen.getByPlaceholderText('Type "Jarvis" to confirm');
|
|
589
|
+
await user.type(input, "Jarvis");
|
|
590
|
+
|
|
591
|
+
const deleteBtn = screen.getByRole("button", { name: "Delete permanently" });
|
|
592
|
+
expect(deleteBtn).not.toBeDisabled();
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
it("calls controlBot on stop confirmation", async () => {
|
|
596
|
+
const user = userEvent.setup();
|
|
597
|
+
const { controlBot } = await import("@/lib/bot-settings-data");
|
|
598
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
599
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
600
|
+
await screen.findByText("Jarvis");
|
|
601
|
+
|
|
602
|
+
await user.click(screen.getByRole("tab", { name: "Danger Zone" }));
|
|
603
|
+
await user.click(screen.getByRole("button", { name: "Stop Jarvis" }));
|
|
604
|
+
await user.click(screen.getByRole("button", { name: "Stop bot" }));
|
|
605
|
+
|
|
606
|
+
expect(controlBot).toHaveBeenCalledWith("bot-001", "stop");
|
|
607
|
+
});
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
describe("Error handling", () => {
|
|
611
|
+
it("shows error when getBotSettings fails", async () => {
|
|
612
|
+
const { getBotSettings } = await import("@/lib/bot-settings-data");
|
|
613
|
+
vi.mocked(getBotSettings).mockRejectedValueOnce(
|
|
614
|
+
new Error("API error: 500 Internal Server Error"),
|
|
615
|
+
);
|
|
616
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
617
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
618
|
+
expect(await screen.findByText("API error: 500 Internal Server Error")).toBeInTheDocument();
|
|
619
|
+
expect(screen.getByRole("link", { name: /Back to Dashboard/ })).toBeInTheDocument();
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
it("shows error when updateBotIdentity fails", async () => {
|
|
623
|
+
const user = userEvent.setup();
|
|
624
|
+
const { updateBotIdentity } = await import("@/lib/bot-settings-data");
|
|
625
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
626
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
627
|
+
await screen.findByText("Jarvis");
|
|
628
|
+
|
|
629
|
+
vi.mocked(updateBotIdentity).mockRejectedValueOnce(new Error("Save failed"));
|
|
630
|
+
await user.click(screen.getByRole("button", { name: "Save changes" }));
|
|
631
|
+
|
|
632
|
+
expect(await screen.findByText(/Failed to save/)).toBeInTheDocument();
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it("does not show Saved! when save fails", async () => {
|
|
636
|
+
const user = userEvent.setup();
|
|
637
|
+
const { updateBotIdentity } = await import("@/lib/bot-settings-data");
|
|
638
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
639
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
640
|
+
await screen.findByText("Jarvis");
|
|
641
|
+
|
|
642
|
+
vi.mocked(updateBotIdentity).mockRejectedValueOnce(new Error("Save failed"));
|
|
643
|
+
await user.click(screen.getByRole("button", { name: "Save changes" }));
|
|
644
|
+
|
|
645
|
+
await screen.findByText(/Failed to save/);
|
|
646
|
+
expect(screen.queryByText("Saved!")).not.toBeInTheDocument();
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
it("shows error when activateSuperpower fails", async () => {
|
|
650
|
+
const user = userEvent.setup();
|
|
651
|
+
const { activateSuperpower } = await import("@/lib/bot-settings-data");
|
|
652
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
653
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
654
|
+
await screen.findByText("Jarvis");
|
|
655
|
+
|
|
656
|
+
await user.click(screen.getByRole("tab", { name: "Superpowers" }));
|
|
657
|
+
vi.mocked(activateSuperpower).mockRejectedValueOnce(new Error("Activation failed"));
|
|
658
|
+
const addButtons = screen.getAllByRole("button", { name: "+ Add" });
|
|
659
|
+
await user.click(addButtons[0]);
|
|
660
|
+
|
|
661
|
+
expect(await screen.findByText(/Failed to activate/)).toBeInTheDocument();
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
it("shows error when controlBot fails", async () => {
|
|
665
|
+
const user = userEvent.setup();
|
|
666
|
+
const { controlBot } = await import("@/lib/bot-settings-data");
|
|
667
|
+
const { BotSettingsClient } = await import("../components/bot-settings/bot-settings-client");
|
|
668
|
+
render(<BotSettingsClient botId="bot-001" />);
|
|
669
|
+
await screen.findByText("Jarvis");
|
|
670
|
+
|
|
671
|
+
await user.click(screen.getByRole("tab", { name: "Danger Zone" }));
|
|
672
|
+
vi.mocked(controlBot).mockRejectedValueOnce(new Error("Control failed"));
|
|
673
|
+
await user.click(screen.getByRole("button", { name: "Stop Jarvis" }));
|
|
674
|
+
await user.click(screen.getByRole("button", { name: "Stop bot" }));
|
|
675
|
+
|
|
676
|
+
expect(await screen.findByText(/Failed to stop bot/)).toBeInTheDocument();
|
|
677
|
+
});
|
|
678
|
+
});
|