@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,535 @@
|
|
|
1
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
2
|
+
import { render, screen, waitFor } from "@testing-library/react";
|
|
3
|
+
import userEvent from "@testing-library/user-event";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import type { PluginManifest } from "../lib/marketplace-data";
|
|
6
|
+
|
|
7
|
+
// Mock framer-motion to prevent rAF animation loops hanging jsdom tests
|
|
8
|
+
vi.mock("framer-motion", () => {
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
10
|
+
const React = require("react");
|
|
11
|
+
return {
|
|
12
|
+
motion: new Proxy(
|
|
13
|
+
{},
|
|
14
|
+
{
|
|
15
|
+
get:
|
|
16
|
+
(_target: Record<string, unknown>, tag: string) =>
|
|
17
|
+
({ children, ...props }: { children?: unknown; [key: string]: unknown }) =>
|
|
18
|
+
React.createElement(tag, props, children),
|
|
19
|
+
},
|
|
20
|
+
),
|
|
21
|
+
AnimatePresence: ({ children }: { children?: unknown }) => children,
|
|
22
|
+
useMotionValue: (v: number) => ({
|
|
23
|
+
get: () => v,
|
|
24
|
+
on: () => () => {
|
|
25
|
+
/* no-op */
|
|
26
|
+
},
|
|
27
|
+
set: () => {
|
|
28
|
+
/* no-op */
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
31
|
+
useTransform: () => ({
|
|
32
|
+
on: () => () => {
|
|
33
|
+
/* no-op */
|
|
34
|
+
},
|
|
35
|
+
get: () => 0,
|
|
36
|
+
}),
|
|
37
|
+
animate: () => ({
|
|
38
|
+
stop: () => {
|
|
39
|
+
/* no-op */
|
|
40
|
+
},
|
|
41
|
+
}),
|
|
42
|
+
};
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// vi.hoisted runs before module imports so TEST_PLUGINS and mocks are available in vi.mock factories
|
|
46
|
+
const { TEST_PLUGINS, mockInstallPlugin, mockListBots, mockListInstalledPlugins } = vi.hoisted(
|
|
47
|
+
() => {
|
|
48
|
+
const mockInstallPlugin = vi.fn();
|
|
49
|
+
const mockListBots = vi
|
|
50
|
+
.fn()
|
|
51
|
+
.mockResolvedValue([{ id: "bot-001", name: "My Bot", state: "running" }]);
|
|
52
|
+
const mockListInstalledPlugins = vi.fn().mockResolvedValue([]);
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
54
|
+
const { INSTALL_FLOW_TEST_PLUGINS } =
|
|
55
|
+
require("./fixtures/mock-manifests-data") as typeof import("./fixtures/mock-manifests");
|
|
56
|
+
return {
|
|
57
|
+
TEST_PLUGINS: INSTALL_FLOW_TEST_PLUGINS,
|
|
58
|
+
mockInstallPlugin,
|
|
59
|
+
mockListBots,
|
|
60
|
+
mockListInstalledPlugins,
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const mockPush = vi.fn();
|
|
66
|
+
const mockParams: { plugin?: string } = {};
|
|
67
|
+
vi.mock("next/navigation", () => ({
|
|
68
|
+
useRouter: () => ({ push: mockPush }),
|
|
69
|
+
usePathname: () => "/marketplace",
|
|
70
|
+
useParams: () => mockParams,
|
|
71
|
+
useSearchParams: () => ({ get: () => null }),
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
vi.mock("next/link", () => ({
|
|
75
|
+
default: ({
|
|
76
|
+
children,
|
|
77
|
+
href,
|
|
78
|
+
...props
|
|
79
|
+
}: { children: React.ReactNode; href: string } & Record<string, unknown>) => (
|
|
80
|
+
<a href={href} {...props}>
|
|
81
|
+
{children}
|
|
82
|
+
</a>
|
|
83
|
+
),
|
|
84
|
+
}));
|
|
85
|
+
|
|
86
|
+
vi.mock("@/hooks/use-capability-meta", () => ({
|
|
87
|
+
useCapabilityMeta: () => ({
|
|
88
|
+
meta: [
|
|
89
|
+
{
|
|
90
|
+
capability: "llm",
|
|
91
|
+
label: "Platform Hosted LLM",
|
|
92
|
+
description: "Managed LLM.",
|
|
93
|
+
pricing: "Pay-per-token",
|
|
94
|
+
hostedProvider: "OpenRouter",
|
|
95
|
+
icon: "bot",
|
|
96
|
+
sortOrder: 0,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
capability: "stt",
|
|
100
|
+
label: "Platform Hosted STT",
|
|
101
|
+
description: "Managed STT.",
|
|
102
|
+
pricing: "Pay-per-minute",
|
|
103
|
+
hostedProvider: "Whisper",
|
|
104
|
+
icon: "mic",
|
|
105
|
+
sortOrder: 1,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
loading: false,
|
|
109
|
+
error: false,
|
|
110
|
+
getMeta: (cap: string) => ({
|
|
111
|
+
capability: cap,
|
|
112
|
+
label: cap.toUpperCase(),
|
|
113
|
+
description: "",
|
|
114
|
+
pricing: "",
|
|
115
|
+
hostedProvider: "",
|
|
116
|
+
icon: "sparkles",
|
|
117
|
+
sortOrder: 999,
|
|
118
|
+
}),
|
|
119
|
+
}),
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
vi.mock("../lib/marketplace-data", async () => {
|
|
123
|
+
const actual = await vi.importActual("../lib/marketplace-data");
|
|
124
|
+
return {
|
|
125
|
+
...actual,
|
|
126
|
+
listMarketplacePlugins: vi.fn().mockResolvedValue(TEST_PLUGINS),
|
|
127
|
+
getMarketplacePlugin: vi.fn().mockImplementation(async (id: string) => {
|
|
128
|
+
return TEST_PLUGINS.find((p) => p.id === id) ?? null;
|
|
129
|
+
}),
|
|
130
|
+
getPluginContent: vi.fn().mockResolvedValue(null),
|
|
131
|
+
installPlugin: mockInstallPlugin,
|
|
132
|
+
listBots: mockListBots,
|
|
133
|
+
listInstalledPlugins: mockListInstalledPlugins,
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const mockBots = [
|
|
138
|
+
{ id: "bot-001", name: "My Bot", state: "running" },
|
|
139
|
+
{ id: "bot-002", name: "Second Bot", state: "stopped" },
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
function renderWithQueryClient(ui: React.ReactElement) {
|
|
143
|
+
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
|
144
|
+
return render(<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
describe("Plugin Install Flow", () => {
|
|
148
|
+
beforeEach(() => {
|
|
149
|
+
mockPush.mockClear();
|
|
150
|
+
mockInstallPlugin.mockClear();
|
|
151
|
+
mockParams.plugin = undefined;
|
|
152
|
+
vi.stubGlobal(
|
|
153
|
+
"fetch",
|
|
154
|
+
vi.fn().mockResolvedValue({
|
|
155
|
+
ok: true,
|
|
156
|
+
json: async () => ({ bots: mockBots }),
|
|
157
|
+
}),
|
|
158
|
+
);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
afterEach(() => {
|
|
162
|
+
vi.unstubAllGlobals();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it("renders install button on plugin detail page for uninstalled plugin", async () => {
|
|
166
|
+
mockParams.plugin = "webhooks";
|
|
167
|
+
const { default: PluginDetailPage } = await import(
|
|
168
|
+
"../app/(dashboard)/marketplace/[plugin]/page"
|
|
169
|
+
);
|
|
170
|
+
renderWithQueryClient(<PluginDetailPage />);
|
|
171
|
+
|
|
172
|
+
expect(await screen.findByText("Webhooks")).toBeInTheDocument();
|
|
173
|
+
const installBtn = screen.getByText("Give my bot this superpower");
|
|
174
|
+
expect(installBtn).toBeInTheDocument();
|
|
175
|
+
expect(installBtn.closest("button")).not.toBeDisabled();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("opens install wizard when install button is clicked", async () => {
|
|
179
|
+
const user = userEvent.setup();
|
|
180
|
+
mockParams.plugin = "webhooks";
|
|
181
|
+
const { default: PluginDetailPage } = await import(
|
|
182
|
+
"../app/(dashboard)/marketplace/[plugin]/page"
|
|
183
|
+
);
|
|
184
|
+
renderWithQueryClient(<PluginDetailPage />);
|
|
185
|
+
|
|
186
|
+
await screen.findByText("Webhooks");
|
|
187
|
+
await user.click(screen.getByText("Give my bot this superpower"));
|
|
188
|
+
|
|
189
|
+
expect(screen.getByText("Install Webhooks")).toBeInTheDocument();
|
|
190
|
+
expect(screen.getByText("Select which bot to install this plugin on")).toBeInTheDocument();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("install wizard calls installPlugin on completion via detail page", async () => {
|
|
194
|
+
mockInstallPlugin.mockResolvedValueOnce({ success: true });
|
|
195
|
+
|
|
196
|
+
const user = userEvent.setup();
|
|
197
|
+
mockParams.plugin = "webhooks";
|
|
198
|
+
const { default: PluginDetailPage } = await import(
|
|
199
|
+
"../app/(dashboard)/marketplace/[plugin]/page"
|
|
200
|
+
);
|
|
201
|
+
renderWithQueryClient(<PluginDetailPage />);
|
|
202
|
+
|
|
203
|
+
await screen.findByText("Webhooks");
|
|
204
|
+
await user.click(screen.getByText("Give my bot this superpower"));
|
|
205
|
+
|
|
206
|
+
// Phase 1: Bot select — select a bot
|
|
207
|
+
const botButton = await screen.findByText("My Bot");
|
|
208
|
+
await user.click(botButton);
|
|
209
|
+
await user.click(screen.getByText("Continue"));
|
|
210
|
+
|
|
211
|
+
// Phase 2: Setup step "Configure Webhooks" (no fields) — advance
|
|
212
|
+
await user.click(screen.getByText("Continue"));
|
|
213
|
+
|
|
214
|
+
// Phase 3: Setup step "Webhooks Ready" (no fields) — advance to complete
|
|
215
|
+
await user.click(screen.getByText("Continue"));
|
|
216
|
+
|
|
217
|
+
// Complete phase — click Done
|
|
218
|
+
expect(screen.getByText("Plugin installed successfully")).toBeInTheDocument();
|
|
219
|
+
await user.click(screen.getByText("Done"));
|
|
220
|
+
|
|
221
|
+
// Verify installPlugin was called with correct args
|
|
222
|
+
await waitFor(() => {
|
|
223
|
+
expect(mockInstallPlugin).toHaveBeenCalledWith(
|
|
224
|
+
"webhooks",
|
|
225
|
+
"bot-001",
|
|
226
|
+
expect.objectContaining({}),
|
|
227
|
+
expect.any(Object),
|
|
228
|
+
expect.anything(),
|
|
229
|
+
);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it("shows error when installPlugin fails", async () => {
|
|
234
|
+
mockInstallPlugin.mockRejectedValueOnce(new Error("Server error"));
|
|
235
|
+
|
|
236
|
+
const user = userEvent.setup();
|
|
237
|
+
mockParams.plugin = "webhooks";
|
|
238
|
+
const { default: PluginDetailPage } = await import(
|
|
239
|
+
"../app/(dashboard)/marketplace/[plugin]/page"
|
|
240
|
+
);
|
|
241
|
+
renderWithQueryClient(<PluginDetailPage />);
|
|
242
|
+
|
|
243
|
+
await screen.findByText("Webhooks");
|
|
244
|
+
await user.click(screen.getByText("Give my bot this superpower"));
|
|
245
|
+
|
|
246
|
+
// Walk through wizard to completion
|
|
247
|
+
const botButton = await screen.findByText("My Bot");
|
|
248
|
+
await user.click(botButton);
|
|
249
|
+
await user.click(screen.getByText("Continue"));
|
|
250
|
+
await user.click(screen.getByText("Continue"));
|
|
251
|
+
await user.click(screen.getByText("Continue"));
|
|
252
|
+
|
|
253
|
+
// Complete phase — click Done triggers installPlugin
|
|
254
|
+
await user.click(screen.getByText("Done"));
|
|
255
|
+
|
|
256
|
+
// Should show error message (toUserMessage wraps the error)
|
|
257
|
+
await waitFor(() => {
|
|
258
|
+
expect(screen.getByText(/Installation failed|Server error/i)).toBeInTheDocument();
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("wizard cancel button returns to detail page", async () => {
|
|
263
|
+
const user = userEvent.setup();
|
|
264
|
+
mockParams.plugin = "webhooks";
|
|
265
|
+
const { default: PluginDetailPage } = await import(
|
|
266
|
+
"../app/(dashboard)/marketplace/[plugin]/page"
|
|
267
|
+
);
|
|
268
|
+
renderWithQueryClient(<PluginDetailPage />);
|
|
269
|
+
|
|
270
|
+
await screen.findByText("Webhooks");
|
|
271
|
+
await user.click(screen.getByText("Give my bot this superpower"));
|
|
272
|
+
expect(screen.getByText("Install Webhooks")).toBeInTheDocument();
|
|
273
|
+
|
|
274
|
+
await user.click(screen.getByText("Cancel"));
|
|
275
|
+
|
|
276
|
+
// Should return to detail page (install button visible again)
|
|
277
|
+
expect(screen.getByText("Give my bot this superpower")).toBeInTheDocument();
|
|
278
|
+
expect(screen.queryByText("Install Webhooks")).not.toBeInTheDocument();
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
describe("Plugin Toggle (Enable/Disable)", () => {
|
|
283
|
+
beforeEach(() => {
|
|
284
|
+
// Default: single bot for most tests, individual tests override via mockResolvedValueOnce
|
|
285
|
+
mockListBots.mockResolvedValue([{ id: "bot-001", name: "My Bot", state: "running" }]);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("InstallWizard shows multiple bots and allows selection", async () => {
|
|
289
|
+
mockListBots.mockResolvedValueOnce(mockBots);
|
|
290
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
291
|
+
const plugin = TEST_PLUGINS[0] as unknown as PluginManifest; // webhooks
|
|
292
|
+
|
|
293
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
294
|
+
|
|
295
|
+
// Wait for bots to load (listBots mock returns 2 bots)
|
|
296
|
+
expect(await screen.findByText("My Bot")).toBeInTheDocument();
|
|
297
|
+
expect(screen.getByText("Second Bot")).toBeInTheDocument();
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it("RequirementsCheck shows 'No additional dependencies required' when plugin.requires is empty", async () => {
|
|
301
|
+
// RequirementsCheck is not exported — test via InstallWizard with a plugin that has requires: []
|
|
302
|
+
// and manually reach the requirements phase
|
|
303
|
+
// Since requires=[] the requirements phase is skipped in the wizard, so we test the component
|
|
304
|
+
// behavior indirectly: the wizard for TEST_PLUGINS[0] (webhooks, requires:[]) should never show
|
|
305
|
+
// "This plugin requires the following dependencies:" text at all.
|
|
306
|
+
const user = userEvent.setup();
|
|
307
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
308
|
+
const plugin = TEST_PLUGINS[0] as unknown as PluginManifest; // webhooks, requires: []
|
|
309
|
+
|
|
310
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
311
|
+
|
|
312
|
+
// Walk through all wizard phases
|
|
313
|
+
const botButton = await screen.findByText("My Bot");
|
|
314
|
+
await user.click(botButton);
|
|
315
|
+
await user.click(screen.getByText("Continue"));
|
|
316
|
+
|
|
317
|
+
// Advance through setup steps
|
|
318
|
+
await user.click(screen.getByText("Continue"));
|
|
319
|
+
await user.click(screen.getByText("Continue"));
|
|
320
|
+
|
|
321
|
+
// Complete phase reached — "This plugin requires the following dependencies:" was never shown
|
|
322
|
+
expect(screen.getByText("Plugin installed successfully")).toBeInTheDocument();
|
|
323
|
+
expect(
|
|
324
|
+
screen.queryByText("This plugin requires the following dependencies:"),
|
|
325
|
+
).not.toBeInTheDocument();
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("InstallWizard shows requirements phase for plugins with dependencies", async () => {
|
|
329
|
+
const user = userEvent.setup();
|
|
330
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
331
|
+
const plugin = TEST_PLUGINS[1] as unknown as PluginManifest; // meeting-transcriber
|
|
332
|
+
|
|
333
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
334
|
+
|
|
335
|
+
// Select bot
|
|
336
|
+
const botButton = await screen.findByText("My Bot");
|
|
337
|
+
await user.click(botButton);
|
|
338
|
+
await user.click(screen.getByText("Continue"));
|
|
339
|
+
|
|
340
|
+
// Requirements phase
|
|
341
|
+
expect(screen.getByText("Check plugin requirements")).toBeInTheDocument();
|
|
342
|
+
expect(screen.getByText("Discord (for voice channels)")).toBeInTheDocument();
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it("InstallWizard shows provider selector for hosted capabilities", async () => {
|
|
346
|
+
const user = userEvent.setup();
|
|
347
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
348
|
+
const plugin = TEST_PLUGINS[1] as unknown as PluginManifest; // meeting-transcriber (stt + llm)
|
|
349
|
+
// meeting-transcriber requires discord — mock it as installed so requirements phase passes
|
|
350
|
+
mockListInstalledPlugins.mockResolvedValueOnce([{ pluginId: "discord", enabled: true }]);
|
|
351
|
+
|
|
352
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
353
|
+
|
|
354
|
+
// Select bot and advance
|
|
355
|
+
const botButton = await screen.findByText("My Bot");
|
|
356
|
+
await user.click(botButton);
|
|
357
|
+
await user.click(screen.getByText("Continue"));
|
|
358
|
+
|
|
359
|
+
// Skip requirements
|
|
360
|
+
await user.click(screen.getByText("Continue"));
|
|
361
|
+
|
|
362
|
+
// Provider selector
|
|
363
|
+
expect(screen.getByText("Choose provider for each capability")).toBeInTheDocument();
|
|
364
|
+
expect(screen.getByText("Platform Hosted LLM")).toBeInTheDocument();
|
|
365
|
+
expect(screen.getByText("Platform Hosted STT")).toBeInTheDocument();
|
|
366
|
+
|
|
367
|
+
// Can choose BYOK
|
|
368
|
+
const byokButtons = screen.getAllByText("Use your key");
|
|
369
|
+
await user.click(byokButtons[0]);
|
|
370
|
+
// Button toggled to active (no assertion needed — just verifying click doesn't throw)
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it("InstallWizard calls onComplete with botId and config including provider choices", async () => {
|
|
374
|
+
const onComplete = vi.fn();
|
|
375
|
+
const user = userEvent.setup();
|
|
376
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
377
|
+
const plugin = TEST_PLUGINS[0] as unknown as PluginManifest; // webhooks (no requirements, no hosted)
|
|
378
|
+
|
|
379
|
+
render(<InstallWizard plugin={plugin} onComplete={onComplete} onCancel={vi.fn()} />);
|
|
380
|
+
|
|
381
|
+
// Select bot
|
|
382
|
+
const botButton = await screen.findByText("My Bot");
|
|
383
|
+
await user.click(botButton);
|
|
384
|
+
await user.click(screen.getByText("Continue"));
|
|
385
|
+
|
|
386
|
+
// Setup steps (2 steps with no fields) — advance through both
|
|
387
|
+
await user.click(screen.getByText("Continue"));
|
|
388
|
+
await user.click(screen.getByText("Continue"));
|
|
389
|
+
|
|
390
|
+
// Complete phase — click Done
|
|
391
|
+
expect(screen.getByText("Plugin installed successfully")).toBeInTheDocument();
|
|
392
|
+
await user.click(screen.getByText("Done"));
|
|
393
|
+
|
|
394
|
+
expect(onComplete).toHaveBeenCalledWith(
|
|
395
|
+
"bot-001",
|
|
396
|
+
expect.objectContaining({ _providerChoices: {} }),
|
|
397
|
+
);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it("InstallWizard shows loading skeleton while bots are loading", async () => {
|
|
401
|
+
// Make listBots hang indefinitely
|
|
402
|
+
mockListBots.mockReturnValue(
|
|
403
|
+
new Promise(() => {
|
|
404
|
+
/* never resolves */
|
|
405
|
+
}),
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
409
|
+
const plugin = TEST_PLUGINS[0] as unknown as PluginManifest;
|
|
410
|
+
|
|
411
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
412
|
+
|
|
413
|
+
// Should show skeleton while loading
|
|
414
|
+
expect(document.querySelector('[data-slot="skeleton"]')).toBeInTheDocument();
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it("InstallWizard shows error when bot loading fails", async () => {
|
|
418
|
+
mockListBots.mockRejectedValueOnce(new Error("Network failure"));
|
|
419
|
+
|
|
420
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
421
|
+
const plugin = TEST_PLUGINS[0] as unknown as PluginManifest;
|
|
422
|
+
|
|
423
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
424
|
+
|
|
425
|
+
// Should show error after loading fails
|
|
426
|
+
await waitFor(() => {
|
|
427
|
+
expect(screen.getByText(/Failed to load bots|Network failure/i)).toBeInTheDocument();
|
|
428
|
+
});
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
it("InstallWizard shows empty state when no bots exist", async () => {
|
|
432
|
+
mockListBots.mockResolvedValueOnce([]);
|
|
433
|
+
|
|
434
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
435
|
+
const plugin = TEST_PLUGINS[0] as unknown as PluginManifest;
|
|
436
|
+
|
|
437
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
438
|
+
|
|
439
|
+
await waitFor(() => {
|
|
440
|
+
expect(screen.getByText(/No bots found/i)).toBeInTheDocument();
|
|
441
|
+
});
|
|
442
|
+
expect(screen.getByText("Create a Bot")).toBeInTheDocument();
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
describe("Install Wizard Navigation", () => {
|
|
447
|
+
beforeEach(() => {
|
|
448
|
+
mockListBots.mockResolvedValue([{ id: "bot-001", name: "My Bot", state: "running" }]);
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
it("Continue button is disabled until a bot is selected", async () => {
|
|
452
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
453
|
+
const plugin = TEST_PLUGINS[0] as unknown as PluginManifest;
|
|
454
|
+
|
|
455
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
456
|
+
|
|
457
|
+
// Wait for bots to load
|
|
458
|
+
await screen.findByText("My Bot");
|
|
459
|
+
|
|
460
|
+
// Continue button should be disabled when no bot selected
|
|
461
|
+
const continueBtn = screen.getByText("Continue").closest("button");
|
|
462
|
+
expect(continueBtn).toBeDisabled();
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it("Continue button enables after selecting a bot", async () => {
|
|
466
|
+
const user = userEvent.setup();
|
|
467
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
468
|
+
const plugin = TEST_PLUGINS[0] as unknown as PluginManifest;
|
|
469
|
+
|
|
470
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
471
|
+
|
|
472
|
+
const botButton = await screen.findByText("My Bot");
|
|
473
|
+
await user.click(botButton);
|
|
474
|
+
|
|
475
|
+
const continueBtn = screen.getByText("Continue").closest("button");
|
|
476
|
+
expect(continueBtn).not.toBeDisabled();
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
it("back button navigates to previous phase", async () => {
|
|
480
|
+
const user = userEvent.setup();
|
|
481
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
482
|
+
const plugin = TEST_PLUGINS[1] as unknown as PluginManifest; // meeting-transcriber (has requirements)
|
|
483
|
+
|
|
484
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
485
|
+
|
|
486
|
+
// Select bot and advance
|
|
487
|
+
const botButton = await screen.findByText("My Bot");
|
|
488
|
+
await user.click(botButton);
|
|
489
|
+
await user.click(screen.getByText("Continue"));
|
|
490
|
+
|
|
491
|
+
// Now on requirements phase
|
|
492
|
+
expect(screen.getByText("Check plugin requirements")).toBeInTheDocument();
|
|
493
|
+
|
|
494
|
+
// Click Back
|
|
495
|
+
await user.click(screen.getByText("Back"));
|
|
496
|
+
|
|
497
|
+
// Should be back on bot-select
|
|
498
|
+
expect(screen.getByText("Select which bot to install this plugin on")).toBeInTheDocument();
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it("calls onCancel when cancel button is clicked", async () => {
|
|
502
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
503
|
+
const plugin = TEST_PLUGINS[0] as unknown as PluginManifest;
|
|
504
|
+
const onCancel = vi.fn();
|
|
505
|
+
|
|
506
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={onCancel} />);
|
|
507
|
+
|
|
508
|
+
const user = userEvent.setup();
|
|
509
|
+
await user.click(screen.getByText("Cancel"));
|
|
510
|
+
|
|
511
|
+
expect(onCancel).toHaveBeenCalledOnce();
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
it("progress step counter advances through wizard phases", async () => {
|
|
515
|
+
const user = userEvent.setup();
|
|
516
|
+
const { InstallWizard } = await import("../components/marketplace/install-wizard");
|
|
517
|
+
const plugin = TEST_PLUGINS[0] as unknown as PluginManifest; // webhooks
|
|
518
|
+
|
|
519
|
+
render(<InstallWizard plugin={plugin} onComplete={vi.fn()} onCancel={vi.fn()} />);
|
|
520
|
+
|
|
521
|
+
// Bot-select phase — Step 1 of N
|
|
522
|
+
await screen.findByText("My Bot");
|
|
523
|
+
expect(screen.getByText(/Step 1 of/)).toBeInTheDocument();
|
|
524
|
+
|
|
525
|
+
// Select bot and advance
|
|
526
|
+
await user.click(screen.getByText("My Bot"));
|
|
527
|
+
await user.click(screen.getByText("Continue"));
|
|
528
|
+
|
|
529
|
+
// Progress should have advanced past Step 1 (conflicts phase auto-skips to setup)
|
|
530
|
+
await waitFor(() => {
|
|
531
|
+
expect(screen.queryByText(/Step 1 of/)).not.toBeInTheDocument();
|
|
532
|
+
});
|
|
533
|
+
expect(screen.getByText(/Step \d+ of/)).toBeInTheDocument();
|
|
534
|
+
});
|
|
535
|
+
});
|