import { http, HttpResponse } from "msw"; import preview from "../../.storybook/preview.tsx"; import { sleep } from "../sleep.ts"; import type { CurrentUser, SessionInfo } from "../types.ts"; import { JoinCard } from "./JoinCard.tsx"; function createMocks(options?: { responseSpeed?: number }) { const simulateNetwork = () => sleep(options?.responseSpeed ?? 2000); const john: CurrentUser = { id: "john", email: "john@example.com", isVerified: true, }; const sessionInfo: SessionInfo = { createdTs: 123, expiresTs: 123, }; return { data: { john }, handlers: { refreshTokens: { failed: () => { return http.post( "http://mock.api/v1/sessions/access-tokens", async () => { await simulateNetwork(); return HttpResponse.text("Refresh token expired or missing", { status: 401, }); }, ); }, }, getCurrentUser: { success: (currentUser: CurrentUser) => { return http.get("http://mock.api/v1/users/me", async () => { await simulateNetwork(); return HttpResponse.json(currentUser); }); }, noConnection: () => { return http.get("http://mock.api/v1/users/me", async () => { return HttpResponse.error(); }); }, unknownFailure: () => { return http.get("http://mock.api/v1/users/me", async () => { await simulateNetwork(); return HttpResponse.text("Internal server error", { status: 500 }); }); }, /** * Auth cookies no longer valid to make this request. */ notAuthenticated: () => { return http.get("http://mock.api/v1/users/me", async () => { await simulateNetwork(); return HttpResponse.text("Not authenticated", { status: 401 }); }); }, }, join: { success(currentUser: CurrentUser) { return http.post("http://mock.api/v1/users", async () => { await simulateNetwork(); return HttpResponse.json({ currentUser, sessionInfo, tokenId: "1", }); }); }, emailTaken() { return http.post("http://mock.api/v1/users", async () => { await simulateNetwork(); return HttpResponse.text("Duplicate unique key", { status: 409 }); }); }, unknownFailure() { return http.post("http://mock.api/v1/users", async () => { await simulateNetwork(); return HttpResponse.text("Internal server error", { status: 500, }); }); }, }, verify: { success() { return http.put( "http://mock.api/v1/user-verification-tokens/:id", async () => { await simulateNetwork(); return HttpResponse.json({ message: "OK" }); }, ); }, notFoundOrExpired() { return http.put( "http://mock.api/v1/user-verification-tokens/:id", async () => { await simulateNetwork(); return HttpResponse.text("Not found", { status: 404 }); }, ); }, unknownFailure() { return http.put( "http://mock.api/v1/user-verification-tokens/:id", async () => { await simulateNetwork(); return HttpResponse.text("Internal server error", { status: 500, }); }, ); }, }, }, }; } const { data, handlers } = createMocks({ responseSpeed: 700 }); const meta = preview.meta({ title: "Account/Join Card", component: JoinCard, tags: ["autodocs"], args: { defaultValues: {}, }, parameters: { msw: { handlers: { refresh: handlers.refreshTokens.failed(), currentUser: handlers.getCurrentUser.notAuthenticated(), join: handlers.join.success(data.john), verify: handlers.verify.success(), }, }, }, }); /** * The default case in which all steps of the flow succeed. */ export const Success = meta.story({}); /** * In this case, the initial step fails because the email address is associated * with an existing user. */ export const EmailTakenOnJoin = meta.story({ parameters: { msw: { handlers: { join: handlers.join.emailTaken(), }, }, }, }); /** * In this case, the initial step fails for a reason that doesn't have any * special handling in this location. E.g. network error, server error, etc. */ export const UnknownFailureOnJoin = meta.story({ parameters: { msw: { handlers: { join: handlers.join.unknownFailure(), }, }, }, }); /** * In this case, the verify step fails because the token has expired, * or is incorrect. */ export const NotFoundOrExpiredOnVerify = meta.story({ parameters: { msw: { handlers: { verify: handlers.verify.notFoundOrExpired(), }, }, }, }); /** * In this case, the verify step fails for a reason that doesn't have any * special handling in this location. E.g. network error, server error, etc. */ export const UnknownFailureOnVerify = meta.story({ parameters: { msw: { handlers: { verify: handlers.verify.unknownFailure(), }, }, }, }); /** * The proactive user-session check returns a user account. * * The user can continue to use the app, or log out. */ export const AlreadyLoggedIn = meta.story({ parameters: { msw: { handlers: { currentUser: handlers.getCurrentUser.success(data.john), }, }, }, }); /** * The proactive user session check has failed due to connection issues. */ export const NoConnection = meta.story({ parameters: { msw: { handlers: { currentUser: handlers.getCurrentUser.noConnection(), }, }, }, }); /** * The proactive user session check has failed due to an error that doesn't * carry any special meaning. */ export const UnknownFailure = meta.story({ parameters: { msw: { handlers: { currentUser: handlers.getCurrentUser.unknownFailure(), }, }, }, });