mirror of
https://github.com/hanzoai/msw.git
synced 2026-09-24 17:00:34 +00:00
209 lines
5.4 KiB
TypeScript
209 lines
5.4 KiB
TypeScript
import type { http, HttpResponse } from 'msw'
|
|
import type { setupWorker } from 'msw/browser'
|
|
import { createTeardown } from 'fs-teardown'
|
|
import type { Page } from '@playwright/test'
|
|
import type { HttpServer } from '@open-draft/test-server/lib/http.js'
|
|
import { fromTemp } from '../../../../support/utils'
|
|
import { test, expect } from '../../../playwright.extend'
|
|
|
|
declare namespace window {
|
|
export const msw: {
|
|
setupWorker: typeof setupWorker
|
|
http: typeof http
|
|
HttpResponse: typeof HttpResponse
|
|
}
|
|
}
|
|
|
|
const fsMock = createTeardown({
|
|
rootDir: fromTemp('fallback-mode'),
|
|
})
|
|
|
|
let server: HttpServer
|
|
|
|
async function gotoStaticPage(page: Page, workerIndex: number): Promise<void> {
|
|
await page.goto(
|
|
`file://${fsMock.resolve(`worker-${workerIndex}/index.html`)}`,
|
|
{ waitUntil: 'networkidle' },
|
|
)
|
|
}
|
|
|
|
interface DirectFetchResponse {
|
|
status: number
|
|
statusText: string
|
|
headers: Record<string, string>
|
|
body: Record<string, unknown>
|
|
}
|
|
|
|
function createFetchWithoutNetwork(page: Page) {
|
|
return (
|
|
input: RequestInfo,
|
|
init?: RequestInit,
|
|
): Promise<DirectFetchResponse> => {
|
|
return page.evaluate(
|
|
([input, init]) => {
|
|
return fetch(input, init)
|
|
.then((res) => {
|
|
const headers: Record<string, string> = {}
|
|
res.headers.forEach((value, key) => {
|
|
headers[key] = value
|
|
})
|
|
|
|
return res.json().then((body) => ({
|
|
status: res.status,
|
|
statusText: res.statusText,
|
|
headers,
|
|
body,
|
|
}))
|
|
})
|
|
.catch(() => null as any)
|
|
},
|
|
[input, init] as [RequestInfo, RequestInit],
|
|
)
|
|
}
|
|
}
|
|
|
|
test.beforeAll(async () => {
|
|
await fsMock.prepare()
|
|
})
|
|
|
|
test.beforeEach(async ({ webpackServer }, testInfo) => {
|
|
const compilation = await webpackServer.compile([
|
|
new URL('./fallback-mode.mocks.ts', import.meta.url).pathname,
|
|
])
|
|
const bundleUrl = new URL('./main.js', compilation.previewUrl)
|
|
await fsMock.create({
|
|
// Scope static files per worker to prevent shared state.
|
|
// The tests below are run in parallel.
|
|
[`worker-${testInfo.workerIndex}`]: {
|
|
'index.html': `<script src="${bundleUrl.href}"></script>`,
|
|
},
|
|
})
|
|
})
|
|
|
|
test.beforeEach(async ({ createServer }) => {
|
|
server = await createServer((app) => {
|
|
app.get('/user', (_, res) => {
|
|
res.json({ name: 'Actual User' })
|
|
})
|
|
})
|
|
})
|
|
|
|
test.afterAll(async () => {
|
|
await fsMock.cleanup()
|
|
})
|
|
|
|
test('prints a fallback start message in the console', async ({
|
|
spyOnConsole,
|
|
page,
|
|
waitFor,
|
|
}, testInfo) => {
|
|
const consoleSpy = spyOnConsole()
|
|
await gotoStaticPage(page, testInfo.workerIndex)
|
|
|
|
await page.evaluate(async () => {
|
|
const { setupWorker } = window.msw
|
|
const worker = setupWorker()
|
|
await worker.start()
|
|
})
|
|
|
|
const consoleGroups = consoleSpy.get('startGroupCollapsed')
|
|
|
|
await waitFor(() => {
|
|
expect(consoleGroups).toContain('[MSW] Mocking enabled (fallback mode).')
|
|
})
|
|
})
|
|
|
|
test('responds with a mocked response to a handled request', async ({
|
|
spyOnConsole,
|
|
waitFor,
|
|
page,
|
|
}, testInfo) => {
|
|
const fetch = createFetchWithoutNetwork(page)
|
|
const consoleSpy = spyOnConsole()
|
|
await gotoStaticPage(page, testInfo.workerIndex)
|
|
|
|
await page.evaluate(async () => {
|
|
const { setupWorker, http, HttpResponse } = window.msw
|
|
|
|
const worker = setupWorker(
|
|
http.get('*/user', () => {
|
|
return HttpResponse.json({ name: 'John Maverick' })
|
|
}),
|
|
)
|
|
await worker.start()
|
|
})
|
|
|
|
const response = await fetch(server.https.url('/user'))
|
|
|
|
// Prints the request message group in the console.
|
|
await waitFor(() => {
|
|
expect(consoleSpy.get('startGroupCollapsed')).toEqual(
|
|
expect.arrayContaining([
|
|
expect.stringMatching(
|
|
/\[MSW\] \d{2}:\d{2}:\d{2} GET https:\/\/127\.0\.0\.1:\d+\/user 200 OK/,
|
|
),
|
|
]),
|
|
)
|
|
})
|
|
|
|
// Responds with a mocked response.
|
|
expect(response.status).toEqual(200)
|
|
expect(response.statusText).toEqual('OK')
|
|
expect(response.body).toEqual({
|
|
name: 'John Maverick',
|
|
})
|
|
})
|
|
|
|
test('warns on the unhandled request by default', async ({
|
|
spyOnConsole,
|
|
page,
|
|
}, testInfo) => {
|
|
const fetch = createFetchWithoutNetwork(page)
|
|
const consoleSpy = spyOnConsole()
|
|
await gotoStaticPage(page, testInfo.workerIndex)
|
|
|
|
await page.evaluate(async () => {
|
|
const { setupWorker } = window.msw
|
|
const worker = setupWorker()
|
|
await worker.start()
|
|
})
|
|
|
|
await fetch(server.http.url('/unknown-resource'))
|
|
|
|
expect(consoleSpy.get('warning')).toEqual(
|
|
expect.arrayContaining([
|
|
expect.stringContaining(`\
|
|
[MSW] Warning: intercepted a request without a matching request handler:
|
|
|
|
• GET ${server.http.url('/unknown-resource')}
|
|
|
|
If you still wish to intercept this unhandled request, please create a request handler for it.
|
|
Read more: https://mswjs.io/docs/http/intercepting-requests`),
|
|
]),
|
|
)
|
|
})
|
|
|
|
test('stops the fallback interceptor when called "worker.stop()"', async ({
|
|
spyOnConsole,
|
|
page,
|
|
}, testInfo) => {
|
|
const fetch = createFetchWithoutNetwork(page)
|
|
const consoleSpy = spyOnConsole()
|
|
await gotoStaticPage(page, testInfo.workerIndex)
|
|
|
|
await page.evaluate(async () => {
|
|
const { setupWorker } = window.msw
|
|
const worker = setupWorker()
|
|
await worker.start()
|
|
|
|
worker.stop()
|
|
})
|
|
|
|
expect(consoleSpy.get('log')).toContain('[MSW] Mocking disabled.')
|
|
|
|
const response = await fetch(server.http.url('/user'))
|
|
|
|
expect(response.status).toBe(200)
|
|
expect(response.body).toEqual({ name: 'Actual User' })
|
|
})
|