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