// Vérifs headless : flux d'auth du mock + rendu de chaque écran. // Lancement : npx tsx --tsconfig tsconfig.app.json scripts/smoke.tsx import { createElement, type ReactNode } from 'react'; import { renderToString } from 'react-dom/server'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { MemoryRouter, RouterProvider, createMemoryRouter } from 'react-router-dom'; import { ApiError, DEMO_COORDINATOR_EMAIL, DEMO_COORDINATOR_PASSWORD, EDITION_ID, api, devMagicToken, setAuthToken, } from '@/api'; import type { AccountRead } from '@/api/schema'; import { queryKeys } from '@/api/hooks'; import { AuthContext, AuthProvider, type AuthContextValue } from '@/auth/auth-context'; import { routes } from '@/app/router'; import { ConfigPage } from '@/pages/ConfigPage'; import { GrillePage } from '@/pages/GrillePage'; import { MobileLeadPage } from '@/pages/MobileLeadPage'; import { MobileVolunteerPage } from '@/pages/MobileVolunteerPage'; import { VolunteerViewPage } from '@/pages/VolunteerViewPage'; const e = EDITION_ID; let failures = 0; const fail = (label: string, err: unknown) => { failures += 1; console.error(`FAIL ${label}`, err); }; let coordinator: AccountRead | null = null; let volunteerAccount: AccountRead | null = null; let leadAccount: AccountRead | null = null; let coordinatorToken = ''; async function checkAuth() { let badRejected = 'non'; try { await api.auth.login(e, { email: DEMO_COORDINATOR_EMAIL, password: 'faux' }); } catch (err) { badRejected = err instanceof ApiError ? err.code : String(err); } const session = await api.auth.login(e, { email: DEMO_COORDINATOR_EMAIL, password: DEMO_COORDINATOR_PASSWORD, }); coordinator = session.account; coordinatorToken = session.access_token; setAuthToken(coordinatorToken); const me = await api.auth.me(); const vols = await api.volunteers.list(e); const volEmail = vols.find((v) => v.email)?.email ?? ''; await api.auth.requestMagicLink(e, { email: volEmail }); const raw = devMagicToken(volEmail); volunteerAccount = raw ? (await api.auth.verifyMagicLink(e, { token: raw })).account : null; await api.auth.requestMagicLink(e, { email: 'referent1@lessonsdautomne.fr' }); const leadRaw = devMagicToken('referent1@lessonsdautomne.fr'); leadAccount = leadRaw ? (await api.auth.verifyMagicLink(e, { token: leadRaw })).account : null; setAuthToken(coordinatorToken); console.log( JSON.stringify( { badPasswordRejectedWith: badRejected, loginRole: session.account.role, meRole: me.role, magicVolunteerRole: volunteerAccount?.role, magicVolunteerLinked: Boolean(volunteerAccount?.volunteer_id), magicLeadRole: leadAccount?.role, }, null, 2, ), ); } async function prefetchCore(client: QueryClient) { await Promise.all([ client.prefetchQuery({ queryKey: queryKeys.volunteers, queryFn: () => api.volunteers.list(e) }), client.prefetchQuery({ queryKey: queryKeys.positions, queryFn: () => api.positions.list(e) }), client.prefetchQuery({ queryKey: queryKeys.shifts(), queryFn: () => api.shifts.list(e) }), client.prefetchQuery({ queryKey: queryKeys.assignments(), queryFn: () => api.assignments.list(e) }), client.prefetchQuery({ queryKey: queryKeys.matrix, queryFn: () => api.matrix.get(e) }), ]); } function authValue(account: AccountRead): AuthContextValue { return { status: 'authed', account, loginWithPassword: async () => account, requestMagicLink: async () => {}, verifyMagicLink: async () => account, logout: () => {}, }; } async function renderProtected( label: string, account: AccountRead, node: ReactNode, extra?: (client: QueryClient) => Promise, ) { try { const client = new QueryClient({ defaultOptions: { queries: { retry: false } } }); await prefetchCore(client); if (extra) await extra(client); const html = renderToString( createElement( QueryClientProvider, { client }, createElement( AuthContext.Provider, { value: authValue(account) }, createElement(MemoryRouter, null, node), ), ), ); console.log(`ok ${label.padEnd(26)} ${html.length} octets`); } catch (err) { fail(label, err); } } function renderPublic(path: string) { try { const client = new QueryClient(); const router = createMemoryRouter(routes, { initialEntries: [path] }); const html = renderToString( createElement( QueryClientProvider, { client }, createElement(AuthProvider, null, createElement(RouterProvider, { router })), ), ); console.log(`ok ${path.padEnd(26)} ${html.length} octets`); } catch (err) { fail(path, err); } } // --------------------------------------------------------------------------- // await checkAuth().catch((err) => fail('checkAuth', err)); renderPublic('/connexion'); renderPublic('/connexion/coordination'); if (coordinator) { setAuthToken(coordinatorToken); await renderProtected('/grille', coordinator, createElement(GrillePage)); await renderProtected('/benevoles', coordinator, createElement(VolunteerViewPage)); await renderProtected('/configuration', coordinator, createElement(ConfigPage)); } if (volunteerAccount) { const vid = volunteerAccount.volunteer_id as string; await renderProtected( '/mobile-benevole', volunteerAccount, createElement(MobileVolunteerPage), async (client) => { await client.prefetchQuery({ queryKey: queryKeys.assignments({ volunteerId: vid }), queryFn: () => api.assignments.list(e, { volunteerId: vid }), }); await client.prefetchQuery({ queryKey: ['volunteer', e, vid], queryFn: () => api.volunteers.get(e, vid), }); }, ); } if (leadAccount) { await renderProtected('/mobile-referent', leadAccount, createElement(MobileLeadPage)); } process.exit(failures ? 1 : 0);