import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { render } from '@testing-library/react' import { HttpResponse, http } from 'msw' import { setupServer } from 'msw/node' import type { ReactNode } from 'react' import { AccountCenterLayout } from './account-surface' const base = 'http://localhost:3000' export type AccountStore = ReturnType export function createAccountStore() { return { activeOrganizationId: null as string | null, access: { canCreateOrganization: true, showOrganizations: false, platformOperator: false, consoleOrganizations: [] as Array<{ organizationId: string; level: string }>, }, profile: { id: 'user-1', email: 'jane@example.com', emailVerified: true, displayName: 'Jane Stone', username: 'jane' as string | null, avatarAssetId: null as string | null, image: null as string | null, bio: null as string | null, location: null as string | null, links: [] as import('@shared/api/public-profiles').AccountProfileLink[], role: 'user' as string | null, }, security: { mfa: { enabled: false, factors: [] as unknown[] }, passkeys: { enabled: true, count: 0 }, policy: { mfa: { mode: 'optional' }, passkeys: { enabled: true, rpName: 'Realmroot' } }, }, passkeys: [] as Array<{ id: string name: string | null deviceType: string backedUp: boolean createdAt: string | null }>, sessions: [] as Array<{ id: string userAgent: string | null ipAddress: string | null expiresAt: string current?: boolean }>, linkedAccounts: [] as Array<{ id: string accountId: string providerId: string createdAt: string }>, applications: [] as Array<{ id: string applicationName: string scopes: string[] grantedAt: string expiresAt?: string | null }>, agents: [] as Array<{ id: string name: string status: string host: { id: string; name: string | null } capabilityGrants: Array<{ id: string; capability: string }> }>, agentIdentities: [] as Array<{ id: string issuer: string subject: string name: string homeSpace: { type: 'personal'; userId: string } status: 'active' | 'inactive' createdAt: string updatedAt: string bindings: Array<{ id: string protocolAgentId: string hostId: string status: 'active' | 'revoked' boundAt: string revokedAt: string | null }> }>, } } export function configz(overrides: Partial> = {}) { return { ...baseConfigz(), ...overrides } } function baseConfigz() { return { onboarding: { required: false, href: '/onboarding' }, signIn: { passwordEnabled: true, signupEnabled: true, socialLoginEnabled: true, emailOtpEnabled: true, usernameEnabled: true, identifierFirst: false, }, builtInProviders: { email: { enabled: true }, phone: { enabled: false }, web3Wallet: { enabled: true, allowSignUp: true, chains: [1] }, passkey: { allowSignUp: true }, oneTap: { enabled: false, clientId: '', autoSelect: false, cancelOnTapOutside: true, uxMode: 'popup', context: 'signin', promptBaseDelayMs: 0, promptMaxAttempts: 1, }, }, branding: { logoUrl: null, faviconUrl: null, primaryColor: null, backgroundColor: null, customCss: null }, identityProviders: [ { slug: 'github', providerType: 'social', providerId: 'github', displayName: 'GitHub', icon: 'github' }, ], links: { termsUri: null, privacyUri: null, supportEmail: null }, copy: { productName: 'Realmroot', headline: 'Sign in', description: 'Secure access' }, auth: { basePath: '/api/auth', signInEmailPath: '/api/auth/sign-in/email', signInUsernamePath: '/api/auth/sign-in/username', signUpEmailPath: '/api/auth/sign-up/email', signOutPath: '/api/auth/sign-out', sendVerificationEmailPath: '/api/auth/send-verification-email', verifyEmailPath: '/api/auth/verify-email', emailOtpPath: '/api/auth/email-otp/send-verification-otp', emailOtpSignInPath: '/api/auth/sign-in/email-otp', emailOtpVerificationPath: '/api/auth/email-otp/verify-email', emailOtpPasswordResetRequestPath: '/api/auth/email-otp/request-password-reset', emailOtpPasswordResetPath: '/api/auth/email-otp/reset-password', }, oidc: { issuer: 'https://auth.example.com', discoveryUrl: '/.well-known/openid-configuration', authorizationEndpoint: '/oauth2/authorize', tokenEndpoint: '/oauth2/token', jwksUri: '/jwks', userInfoEndpoint: '/userinfo', endSessionEndpoint: '/logout', }, security: { mfaRequired: false, sessionExpiresInSeconds: 3600, passkeysEnabled: true }, navigation: { externalLinks: [] }, accountCenter: { profileEditingEnabled: true, displayNameEditable: true, usernameEditable: true, avatarEditable: true, emailChangeEnabled: true, passwordChangeEnabled: true, connectedAccountsEnabled: true, sessionsViewEnabled: true, dangerZoneEnabled: false, }, captcha: { enabled: false, provider: 'turnstile', siteKey: '' }, } } type Handlers = Parameters export function accountHandlers(store: AccountStore, config: ReturnType = configz()): Handlers { return [ http.get(`${base}/api/configz`, () => HttpResponse.json(config)), http.get(`${base}/api/account/profile`, () => HttpResponse.json({ user: store.profile })), http.get(`${base}/api/account/developer-console-access`, () => HttpResponse.json(store.access)), http.get(`${base}/api/account/organization-context`, () => HttpResponse.json({ activeOrganizationId: store.activeOrganizationId }), ), http.get(`${base}/api/auth/organization/list`, () => HttpResponse.json([])), http.get(`${base}/api/auth/organization/list-user-invitations`, () => HttpResponse.json([])), http.patch(`${base}/api/account/profile`, async ({ request }) => { const body = (await request.json()) as Record Object.assign(store.profile, body) return HttpResponse.json({ user: store.profile }) }), http.get(`${base}/api/account/security`, () => HttpResponse.json({ security: store.security })), http.get(`${base}/api/account/security/passkeys`, () => HttpResponse.json({ passkeys: store.passkeys })), http.get(`${base}/api/account/sessions`, () => HttpResponse.json({ items: store.sessions, pagination: { page: Math.floor(0 / 50) + 1, pageSize: 50, totalItems: store.sessions.length, totalPages: Math.ceil(store.sessions.length / 50), }, }), ), http.get(`${base}/api/account/linked-accounts`, () => HttpResponse.json({ items: store.linkedAccounts, pagination: { page: Math.floor(0 / 50) + 1, pageSize: 50, totalItems: store.linkedAccounts.length, totalPages: Math.ceil(store.linkedAccounts.length / 50), }, }), ), http.get(`${base}/api/account/provider-connectors`, () => HttpResponse.json({ items: [], pagination: { page: Math.floor(0 / 50) + 1, pageSize: 50, totalItems: 0, totalPages: Math.ceil(0 / 50) }, }), ), http.get(`${base}/api/account/provider-connections`, () => HttpResponse.json({ items: [], pagination: { page: Math.floor(0 / 50) + 1, pageSize: 50, totalItems: 0, totalPages: Math.ceil(0 / 50) }, }), ), http.get(`${base}/api/account/application-authorizations`, () => HttpResponse.json({ items: store.applications.map((authorization) => ({ ...authorization, applicationId: `app-${authorization.id}`, application: { id: `app-${authorization.id}`, name: authorization.applicationName, slug: authorization.applicationName.toLowerCase().replaceAll(' ', '-'), }, user: { id: store.profile.id, displayName: store.profile.displayName, email: store.profile.email }, resourceServerId: null, revokedAt: null, status: 'active', })), pagination: { page: 1, pageSize: 50, totalItems: store.applications.length, totalPages: store.applications.length === 0 ? 0 : 1, }, }), ), http.get(`${base}/api/account/agents`, () => HttpResponse.json({ items: store.agentIdentities, pagination: { page: Math.floor(0 / 50) + 1, pageSize: 50, totalItems: store.agentIdentities.length, totalPages: Math.ceil(store.agentIdentities.length / 50), }, }), ), http.get(`${base}/api/account/api-resources`, () => HttpResponse.json({ items: [], pagination: { page: Math.floor(0 / 50) + 1, pageSize: 50, totalItems: 0, totalPages: Math.ceil(0 / 50) }, }), ), http.get(`${base}/api/account/account-connections`, () => HttpResponse.json({ items: [], pagination: { page: Math.floor(0 / 50) + 1, pageSize: 50, totalItems: 0, totalPages: Math.ceil(0 / 50) }, }), ), ] as unknown as Handlers } export function createAccountServer(store: AccountStore, config?: ReturnType) { return setupServer(...(accountHandlers(store, config) as unknown as Parameters)) } export function renderWithClient( ui: ReactNode, layout: { organizationId?: string pathname?: string section?: Parameters[0]['section'] } = {}, ) { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }) return { queryClient, ...render( {ui} , ), } } export const json = HttpResponse.json export { base, HttpResponse, http }