mirror of
https://github.com/hanzoai/msw.git
synced 2026-09-24 17:00:34 +00:00
309 lines
7.6 KiB
TypeScript
309 lines
7.6 KiB
TypeScript
// @vitest-environment node
|
|
import { HttpResponse, http } from 'msw'
|
|
import { type SetupServerApi, setupServer } from 'msw/node'
|
|
import { HttpServer } from '@open-draft/test-server/http'
|
|
|
|
const httpServer = new HttpServer((app) => {
|
|
app.get('/user', (req, res) => res.status(500).end())
|
|
app.post('/no-response', (req, res) => res.send('original-response'))
|
|
app.get('/unknown-route', (req, res) => res.send('majestic-unknown'))
|
|
})
|
|
|
|
const server = setupServer()
|
|
|
|
function spyOnEvents(server: SetupServerApi) {
|
|
const listener = vi.fn()
|
|
const wrapListener = (eventName: string, listener: any) => {
|
|
return (...args: Array<unknown>) => listener(eventName, ...args)
|
|
}
|
|
|
|
server.events.on('request:start', wrapListener('request:start', listener))
|
|
server.events.on('request:match', wrapListener('request:match', listener))
|
|
server.events.on(
|
|
'request:unhandled',
|
|
wrapListener('request:unhandled', listener),
|
|
)
|
|
server.events.on('request:end', wrapListener('request:end', listener))
|
|
server.events.on('response:mocked', wrapListener('response:mocked', listener))
|
|
server.events.on('response:bypass', wrapListener('response:bypass', listener))
|
|
server.events.on(
|
|
'unhandledException',
|
|
wrapListener('unhandledException', listener),
|
|
)
|
|
|
|
return listener
|
|
}
|
|
|
|
beforeAll(async () => {
|
|
// Supress "Expected a mocking resolver function to return a mocked response"
|
|
// warnings when hitting intentionally empty resolver.
|
|
vi.spyOn(global.console, 'warn').mockImplementation(() => void 0)
|
|
|
|
await httpServer.listen()
|
|
|
|
server.use(
|
|
http.get(httpServer.http.url('/user'), () => {
|
|
return HttpResponse.text('response-body')
|
|
}),
|
|
http.post(httpServer.http.url('/no-response'), () => {
|
|
return
|
|
}),
|
|
http.get(httpServer.http.url('/unhandled-exception'), () => {
|
|
throw new Error('Unhandled resolver error')
|
|
}),
|
|
)
|
|
server.listen()
|
|
})
|
|
|
|
afterAll(async () => {
|
|
server.close()
|
|
await httpServer.close()
|
|
})
|
|
|
|
test('emits events for a handled request and mocked response', async () => {
|
|
const listener = spyOnEvents(server)
|
|
const url = httpServer.http.url('/user')
|
|
await fetch(url)
|
|
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
1,
|
|
'request:start',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'GET',
|
|
url,
|
|
}),
|
|
requestId: expect.any(String),
|
|
}),
|
|
)
|
|
|
|
const { requestId } = listener.mock.calls[0][1]
|
|
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
2,
|
|
'request:match',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'GET',
|
|
url,
|
|
}),
|
|
requestId,
|
|
}),
|
|
)
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
3,
|
|
'request:end',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'GET',
|
|
url,
|
|
}),
|
|
requestId,
|
|
}),
|
|
)
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
4,
|
|
'response:mocked',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'GET',
|
|
url,
|
|
}),
|
|
requestId,
|
|
response: expect.any(Response),
|
|
}),
|
|
)
|
|
|
|
const { response } = listener.mock.calls[3][1]
|
|
expect(response.status).toBe(200)
|
|
expect(response.statusText).toBe('OK')
|
|
await expect(response.text()).resolves.toBe('response-body')
|
|
|
|
expect(listener).toHaveBeenCalledTimes(4)
|
|
})
|
|
|
|
test('emits events for a handled request with no response', async () => {
|
|
const listener = spyOnEvents(server)
|
|
const url = httpServer.http.url('/no-response')
|
|
await fetch(url, { method: 'POST' })
|
|
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
1,
|
|
'request:start',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'POST',
|
|
url,
|
|
}),
|
|
requestId: expect.any(String),
|
|
}),
|
|
)
|
|
|
|
const { requestId } = listener.mock.calls[0][1]
|
|
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
2,
|
|
'request:match',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'POST',
|
|
url,
|
|
}),
|
|
requestId,
|
|
}),
|
|
)
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
3,
|
|
'request:end',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'POST',
|
|
url,
|
|
}),
|
|
requestId,
|
|
}),
|
|
)
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
4,
|
|
'response:bypass',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'POST',
|
|
url,
|
|
}),
|
|
requestId,
|
|
response: expect.any(Response),
|
|
}),
|
|
)
|
|
|
|
const { response } = listener.mock.calls[3][1]
|
|
expect(response.status).toBe(200)
|
|
expect(response.statusText).toBe('OK')
|
|
await expect(response.text()).resolves.toBe('original-response')
|
|
|
|
expect(listener).toHaveBeenCalledTimes(4)
|
|
})
|
|
|
|
test('emits events for an unhandled request', async () => {
|
|
const listener = spyOnEvents(server)
|
|
const url = httpServer.http.url('/unknown-route')
|
|
await fetch(url)
|
|
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
1,
|
|
'request:start',
|
|
expect.objectContaining({
|
|
request: expect.any(Request),
|
|
requestId: expect.any(String),
|
|
}),
|
|
)
|
|
|
|
const { requestId } = listener.mock.calls[0][1]
|
|
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
2,
|
|
'request:unhandled',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'GET',
|
|
url,
|
|
}),
|
|
requestId,
|
|
}),
|
|
)
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
3,
|
|
'request:end',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'GET',
|
|
url,
|
|
}),
|
|
requestId,
|
|
}),
|
|
)
|
|
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
4,
|
|
'response:bypass',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'GET',
|
|
url,
|
|
}),
|
|
requestId,
|
|
response: expect.any(Response),
|
|
}),
|
|
)
|
|
|
|
const { response } = listener.mock.calls[3][1]
|
|
expect(response.status).toBe(200)
|
|
expect(response.statusText).toBe('OK')
|
|
await expect(response.text()).resolves.toBe('majestic-unknown')
|
|
|
|
expect(listener).toHaveBeenCalledTimes(4)
|
|
})
|
|
|
|
test('emits unhandled exceptions in the request handler', async () => {
|
|
const listener = spyOnEvents(server)
|
|
const url = httpServer.http.url('/unhandled-exception')
|
|
await fetch(url).catch(() => undefined)
|
|
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
1,
|
|
'request:start',
|
|
expect.objectContaining({
|
|
request: expect.any(Request),
|
|
requestId: expect.any(String),
|
|
}),
|
|
)
|
|
|
|
const { requestId } = listener.mock.calls[0][1]
|
|
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
2,
|
|
'unhandledException',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'GET',
|
|
url,
|
|
}),
|
|
requestId,
|
|
error: new Error('Unhandled resolver error'),
|
|
}),
|
|
)
|
|
|
|
/**
|
|
* @note The fallback 500 response still counts as a mocked response.
|
|
* I'm torn on this but I believe we should indicate that the request
|
|
* (a) received a response; (b) that response was mocked.
|
|
*/
|
|
expect(listener).toHaveBeenNthCalledWith(
|
|
3,
|
|
'response:mocked',
|
|
expect.objectContaining({
|
|
request: expect.objectContaining({
|
|
method: 'GET',
|
|
url,
|
|
}),
|
|
requestId,
|
|
response: expect.any(Response),
|
|
}),
|
|
)
|
|
|
|
const { response } = listener.mock.calls[2][1]
|
|
expect(response.status).toBe(500)
|
|
expect(response.statusText).toBe('Unhandled Exception')
|
|
|
|
expect(listener).toHaveBeenCalledTimes(3)
|
|
})
|
|
|
|
test('stops emitting events once the server is stopped', async () => {
|
|
const listener = spyOnEvents(server)
|
|
server.close()
|
|
|
|
await fetch(httpServer.http.url('/user'))
|
|
|
|
expect(listener).not.toHaveBeenCalled()
|
|
})
|