import { ReactElement, ReactNode } from 'react' import { http, type HttpResponseResolver, type RequestHandler } from 'msw' import { setupServer, type SetupServer } from 'msw/node' import { BrowserRouter as Router } from 'react-router' import { render, RenderResult, cleanup } from '@testing-library/react' import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { beforeAll, afterEach, afterAll } from 'vitest' import { Query } from 'wasp/client/operations/rpc' import { config } from 'wasp/client' import { HttpMethod, Route } from 'wasp/client' import { serialize } from 'wasp/core/serialization' // PRIVATE API export type MockQuery = ( query: Query, resJson: MockOutput ) => void // PRIVATE API export type MockApi = (route: Route, resJson: unknown) => void // PUBLIC API // Inspired by the Tanstack React Query helper: // https://github.com/TanStack/query/blob/4ae99561ca3383d6de3f4aad656a49ba4a17b57a/packages/react-query/src/__tests__/utils.tsx#L7-L26 export function renderInContext(ui: ReactElement): RenderResult { const client = new QueryClient() const { rerender, ...result } = render( {ui} ) return { ...result, rerender: (rerenderUi: ReactNode) => rerender( {rerenderUi} ), } } // PUBLIC API export function mockServer(): { server: SetupServer mockQuery: MockQuery mockApi: MockApi } { const server: SetupServer = setupServer() beforeAll(() => server.listen()) afterEach(() => { server.resetHandlers() cleanup() }) afterAll(() => server.close()) const mockQuery: MockQuery = (query, mockData) => { const route = (query as unknown as { route: Route }).route mockRoute(server, route, () => Response.json(serialize(mockData))) } const mockApi: MockApi = (route, mockData) => { mockRoute(server, route, () => Response.json(mockData)) } return { server, mockQuery, mockApi } } function mockRoute( server: SetupServer, route: Route, responseHandler: HttpResponseResolver ) { if (!Object.values(HttpMethod).includes(route.method)) { throw new Error( `Unsupported query method for mocking: ${ route.method }. Supported method strings are: ${Object.values(HttpMethod).join(', ')}.` ) } const url = `${config.apiUrl}${route.path}` const handlers: Record = { [HttpMethod.Get]: http.get(url, responseHandler), [HttpMethod.Post]: http.post(url, responseHandler), [HttpMethod.Put]: http.put(url, responseHandler), [HttpMethod.Delete]: http.delete(url, responseHandler), } server.use(handlers[route.method]) }