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])
}