Skip to content

Commit

Permalink
rest: Adds a new ".all()" API
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito committed Sep 6, 2021
1 parent cdca909 commit 172bc10
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 18 deletions.
1 change: 1 addition & 0 deletions src/rest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { rest } from './rest'
test('exports all REST API methods', () => {
expect(rest).toBeDefined()
expect(Object.keys(rest)).toEqual([
'all',
'head',
'get',
'post',
Expand Down
3 changes: 2 additions & 1 deletion src/rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from './handlers/RestHandler'
import { Path } from './utils/matching/matchRequestUrl'

function createRestHandler(method: RESTMethods) {
function createRestHandler(method: RESTMethods | RegExp) {
return <
RequestBodyType extends DefaultRequestBody = DefaultRequestBody,
ResponseBody extends DefaultRequestBody = any,
Expand All @@ -26,6 +26,7 @@ function createRestHandler(method: RESTMethods) {
}

export const rest = {
all: createRestHandler(/.+/),
head: createRestHandler(RESTMethods.HEAD),
get: createRestHandler(RESTMethods.GET),
post: createRestHandler(RESTMethods.POST),
Expand Down
19 changes: 2 additions & 17 deletions test/rest-api/query-params-warning.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,8 @@ test('warns when a request handler URL contains query parameters', async () => {
})

expect(consoleSpy.get('warning')).toEqual([
`\
[MSW] Found a redundant usage of query parameters in the request handler URL for "GET /user?name=admin". Please match against a path instead, and access query parameters in the response resolver function:
rest.get("/user", (req, res, ctx) => {
const query = req.url.searchParams
const name = query.get("name")
})\
`,
`\
[MSW] Found a redundant usage of query parameters in the request handler URL for "POST /login?id=123&type=auth". Please match against a path instead, and access query parameters in the response resolver function:
rest.post("/login", (req, res, ctx) => {
const query = req.url.searchParams
const id = query.get("id")
const type = query.get("type")
})\
`,
`[MSW] Found a redundant usage of query parameters in the request handler URL for "GET /user?name=admin". Please match against a path instead and access query parameters in the response resolver function using "req.url.searchParams".`,
`[MSW] Found a redundant usage of query parameters in the request handler URL for "POST /login?id=123&type=auth". Please match against a path instead and access query parameters in the response resolver function using "req.url.searchParams".`,
])

await request('/user?name=admin').then(async (res) => {
Expand Down
12 changes: 12 additions & 0 deletions test/rest-api/request/matching/all.mocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { setupWorker, rest } from 'msw'

const worker = setupWorker(
rest.all('*/api/*', (req, res, ctx) => {
return res(ctx.text('hello world'))
}),
rest.all('*', (req, res, ctx) => {
return res(ctx.text('welcome to the jungle'))
}),
)

worker.start()
92 changes: 92 additions & 0 deletions test/rest-api/request/matching/all.node.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/**
* @jest-environment node
*/
import fetch, { Response } from 'node-fetch'
import { createServer, ServerApi } from '@open-draft/test-server'
import { RESTMethods, rest } from 'msw'
import { setupServer } from 'msw/node'

let httpServer: ServerApi

const server = setupServer()

beforeAll(async () => {
httpServer = await createServer((app) => {
// Responding with "204 No Content" because the "OPTIONS"
// request returns 204 without an obvious way to override that.
app.all('*', (req, res) => res.status(204).end())
})
server.listen({
onUnhandledRequest: 'bypass',
})
})

afterEach(() => {
server.resetHandlers()
})

afterAll(async () => {
server.close()
await httpServer.close()
})

async function forEachMethod(callback: (method: RESTMethods) => unknown) {
for (const method of Object.values(RESTMethods)) {
await callback(method)
}
}

test('matches all requests given no custom path', async () => {
server.use(
rest.all('*', (req, res, ctx) => {
return res(ctx.text('welcome to the jungle'))
}),
)

const responses = await Promise.all(
Object.values(RESTMethods).reduce<Promise<Response>[]>((all, method) => {
return all.concat(
[
httpServer.http.makeUrl('/'),
httpServer.http.makeUrl('/foo'),
'https://example.com',
].map((url) => fetch(url, { method })),
)
}, []),
)

for (const response of responses) {
expect(response.status).toEqual(200)
expect(await response.text()).toEqual('welcome to the jungle')
}
})

test('respects custom path when matching requests', async () => {
server.use(
rest.all(httpServer.http.makeUrl('/api/*'), (req, res, ctx) => {
return res(ctx.text('hello world'))
}),
)

// Root requests.
await forEachMethod(async (method) => {
const response = await fetch(httpServer.http.makeUrl('/api/'), { method })
expect(response.status).toEqual(200)
expect(await response.text()).toEqual('hello world')
})

// Nested requests.
await forEachMethod(async (method) => {
const response = await fetch(httpServer.http.makeUrl('/api/foo'), {
method,
})
expect(response.status).toEqual(200)
expect(await response.text()).toEqual('hello world')
})

// Mismatched requests.
await forEachMethod(async (method) => {
const response = await fetch(httpServer.http.makeUrl('/foo'), { method })
expect(response.status).toEqual(204)
})
})
60 changes: 60 additions & 0 deletions test/rest-api/request/matching/all.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* @jest-environment node
*/
import * as path from 'path'
import { ScenarioApi, pageWith } from 'page-with'
import { Response } from 'playwright'

function createRuntime() {
return pageWith({
example: path.resolve(__dirname, 'all.mocks.ts'),
})
}

function requestAllMethods(
runtime: ScenarioApi,
url: string,
): Promise<Response[]> {
return Promise.all(
['HEAD', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'].map((method) =>
runtime.request(url, { method }),
),
)
}

test('respects custom path when matching requests', async () => {
const runtime = await createRuntime()

// Root request.
const rootResponses = await requestAllMethods(
runtime,
'http://localhost/api/',
)

for (const response of rootResponses) {
expect(response.status()).toEqual(200)
expect(await response.text()).toEqual('hello world')
}

// Nested request.
const nestedResponses = await requestAllMethods(
runtime,
'http://localhost/api/user',
)
for (const response of nestedResponses) {
expect(response.status()).toEqual(200)
expect(await response.text()).toEqual('hello world')
}

// Mismatched request.
// There's a fallback "rest.all()" in this test that acts
// as a fallback request handler for all otherwise mismatched requests.
const mismatchedResponses = await requestAllMethods(
runtime,
'http://localhost/foo',
)
for (const response of mismatchedResponses) {
expect(response.status()).toEqual(200)
expect(await response.text()).toEqual('welcome to the jungle')
}
})

0 comments on commit 172bc10

Please sign in to comment.