Skip to content

Commit

Permalink
Merge branch 'main' into HEAD
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito committed Nov 5, 2022
2 parents 59969c8 + 409512b commit 2755f43
Show file tree
Hide file tree
Showing 17 changed files with 190 additions and 62 deletions.
1 change: 1 addition & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'body-max-line-length': [0, 'always', Infinity],
'footer-max-line-length': [1, 'always'],
},
}
9 changes: 6 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "msw",
"version": "0.47.3",
"version": "0.47.4",
"description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
Expand All @@ -9,9 +9,11 @@
"default": "./lib/index.js"
},
"./native": {
"types": "./lib/native/index.d.ts",
"default": "./lib/native/index.js"
},
"./node": {
"types": "./lib/node/index.d.ts",
"require": "./lib/node/index.js",
"default": "./lib/node/index.mjs"
}
Expand All @@ -31,7 +33,7 @@
"test:unit": "cross-env BABEL_ENV=test jest --maxWorkers=3",
"test:integration": "jest --config=./test/jest.config.js --maxWorkers=1",
"test:smoke": "./config/scripts/smoke.sh",
"test:ts": "yarn tsc -p ./test/typings/tsconfig.json",
"test:ts": "ts-node test/typings/run.ts",
"prepare": "yarn simple-git-hooks init",
"prepack": "yarn build",
"release": "release publish",
Expand Down Expand Up @@ -100,7 +102,8 @@
"node-fetch": "^2.6.7",
"outvariant": "^1.3.0",
"path-to-regexp": "^6.2.0",
"strict-event-emitter": "^0.2.0",
"statuses": "^2.0.0",
"strict-event-emitter": "^0.2.6",
"type-fest": "^2.19.0",
"yargs": "^17.3.1"
},
Expand Down
2 changes: 1 addition & 1 deletion src/setupWorker/glossary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ export interface StartOptions extends SharedOptions {
serviceWorker?: {
/**
* Custom url to the worker script.
* @default "./mockServiceWorker.js"
* @default "/mockServiceWorker.js"
*/
url?: string
options?: RegistrationOptions
Expand Down
2 changes: 1 addition & 1 deletion src/setupWorker/start/createRequestListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ export const createRequestListener = (
This exception has been gracefully handled as a 500 response, however, it's strongly recommended to resolve this error, as it indicates a mistake in your code. If you wish to mock an error response, please see this guide: https://mswjs.io/docs/recipes/mocking-error-responses`,
request.method,
request.url,
error,
error.stack ?? error,
)

// Treat all other exceptions in a request handler as unintended,
Expand Down
72 changes: 45 additions & 27 deletions src/utils/handleRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,6 @@ import { response } from '../response'
import { context, MockedRequest } from '..'
import { RequiredDeep } from '../typeUtils'

const emitter = new StrictEventEmitter<ServerLifecycleEventsMap>()
const listener = jest.fn()
function createMockListener(name: string) {
return (...args: any) => {
listener(name, ...args)
}
}
function getEmittedEvents() {
return listener.mock.calls
}

const options: RequiredDeep<SharedOptions> = {
onUnhandledRequest: jest.fn(),
}
Expand All @@ -28,27 +17,44 @@ const callbacks: Partial<Record<keyof HandleRequestOptions<any>, any>> = {
onMockedResponse: jest.fn(),
}

beforeEach(() => {
jest.spyOn(global.console, 'warn').mockImplementation()
function setup() {
const emitter = new StrictEventEmitter<ServerLifecycleEventsMap>()
const listener = jest.fn()

const createMockListener = (name: string) => {
return (...args: any) => {
listener(name, ...args)
}
}

emitter.on('request:start', createMockListener('request:start'))
emitter.on('request:match', createMockListener('request:match'))
emitter.on('request:unhandled', createMockListener('request:unhandled'))
emitter.on('request:end', createMockListener('request:end'))
emitter.on('response:mocked', createMockListener('response:mocked'))
emitter.on('response:bypass', createMockListener('response:bypass'))

const events = listener.mock.calls
return { emitter, events }
}

beforeEach(() => {
jest.spyOn(global.console, 'warn').mockImplementation()
})

afterEach(() => {
jest.resetAllMocks()
emitter.removeAllListeners()
})

test('returns undefined for a request with the "x-msw-bypass" header equal to "true"', async () => {
const { emitter, events } = setup()

const request = new MockedRequest(new URL('http://localhost/user'), {
headers: new Headers({
'x-msw-bypass': 'true',
}),
})
const handlers: RequestHandler[] = []
const handlers: Array<RequestHandler> = []

const result = await handleRequest(
request,
Expand All @@ -59,7 +65,7 @@ test('returns undefined for a request with the "x-msw-bypass" header equal to "t
)

expect(result).toBeUndefined()
expect(getEmittedEvents()).toEqual([
expect(events).toEqual([
['request:start', request],
['request:end', request],
])
Expand All @@ -69,12 +75,14 @@ test('returns undefined for a request with the "x-msw-bypass" header equal to "t
})

test('does not bypass a request with "x-msw-bypass" header set to arbitrary value', async () => {
const { emitter } = setup()

const request = new MockedRequest(new URL('http://localhost/user'), {
headers: new Headers({
'x-msw-bypass': 'anything',
}),
})
const handlers: RequestHandler[] = [
const handlers: Array<RequestHandler> = [
rest.get('/user', (req, res, ctx) => {
return res(ctx.text('hello world'))
}),
Expand All @@ -94,8 +102,10 @@ test('does not bypass a request with "x-msw-bypass" header set to arbitrary valu
})

test('reports request as unhandled when it has no matching request handlers', async () => {
const { emitter, events } = setup()

const request = new MockedRequest(new URL('http://localhost/user'))
const handlers: RequestHandler[] = []
const handlers: Array<RequestHandler> = []

const result = await handleRequest(
request,
Expand All @@ -106,7 +116,7 @@ test('reports request as unhandled when it has no matching request handlers', as
)

expect(result).toBeUndefined()
expect(getEmittedEvents()).toEqual([
expect(events).toEqual([
['request:start', request],
['request:unhandled', request],
['request:end', request],
Expand All @@ -120,8 +130,10 @@ test('reports request as unhandled when it has no matching request handlers', as
})

test('returns undefined and warns on a request handler that returns no response', async () => {
const { emitter, events } = setup()

const request = new MockedRequest(new URL('http://localhost/user'))
const handlers: RequestHandler[] = [
const handlers: Array<RequestHandler> = [
rest.get('/user', () => {
// Intentionally blank response resolver.
return
Expand All @@ -137,7 +149,7 @@ test('returns undefined and warns on a request handler that returns no response'
)

expect(result).toBeUndefined()
expect(getEmittedEvents()).toEqual([
expect(events).toEqual([
['request:start', request],
['request:end', request],
])
Expand All @@ -156,9 +168,11 @@ test('returns undefined and warns on a request handler that returns no response'
})

test('returns the mocked response for a request with a matching request handler', async () => {
const { emitter, events } = setup()

const request = new MockedRequest(new URL('http://localhost/user'))
const mockedResponse = await response(context.json({ firstName: 'John' }))
const handlers: RequestHandler[] = [
const handlers: Array<RequestHandler> = [
rest.get('/user', () => {
return mockedResponse
}),
Expand All @@ -179,7 +193,7 @@ test('returns the mocked response for a request with a matching request handler'
)

expect(result).toEqual(mockedResponse)
expect(getEmittedEvents()).toEqual([
expect(events).toEqual([
['request:start', request],
['request:match', request],
['request:end', request],
Expand All @@ -193,9 +207,11 @@ test('returns the mocked response for a request with a matching request handler'
})

test('returns a transformed response if the "transformResponse" option is provided', async () => {
const { emitter, events } = setup()

const request = new MockedRequest(new URL('http://localhost/user'))
const mockedResponse = await response(context.json({ firstName: 'John' }))
const handlers: RequestHandler[] = [
const handlers: Array<RequestHandler> = [
rest.get('/user', () => {
return mockedResponse
}),
Expand All @@ -217,7 +233,7 @@ test('returns a transformed response if the "transformResponse" option is provid
})

expect(result).toEqual(finalResponse)
expect(getEmittedEvents()).toEqual([
expect(events).toEqual([
['request:start', request],
['request:match', request],
['request:end', request],
Expand All @@ -232,8 +248,10 @@ test('returns a transformed response if the "transformResponse" option is provid
})

it('returns undefined without warning on a passthrough request', async () => {
const { emitter, events } = setup()

const request = new MockedRequest(new URL('http://localhost/user'))
const handlers: RequestHandler[] = [
const handlers: Array<RequestHandler> = [
rest.get('/user', (req) => {
return req.passthrough()
}),
Expand All @@ -248,7 +266,7 @@ it('returns undefined without warning on a passthrough request', async () => {
)

expect(result).toBeUndefined()
expect(getEmittedEvents()).toEqual([
expect(events).toEqual([
['request:start', request],
['request:end', request],
])
Expand Down
2 changes: 1 addition & 1 deletion src/utils/internal/getCallFrame.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Ignore the source files traces for local testing.
const SOURCE_FRAME = /\/msw\/src\/(.+)/
const SOURCE_FRAME = /[\/\\]msw[\/\\]src[\/\\](.+)/

const BUILD_FRAME =
/(node_modules)?[\/\\]lib[\/\\](umd|esm|iief|cjs)[\/\\]|^[^\/\\]*$/
Expand Down
2 changes: 1 addition & 1 deletion test/msw-api/cli/init.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ test('does not produce eslint errors or warnings', async () => {
expect(init.stderr).toEqual('')

const eslint = await promisifyChildProcess(
exec(`node_modules/.bin/eslint ${fsMock.resolve()}`),
exec(path.resolve(`node_modules/.bin/eslint ${fsMock.resolve()}`)),
)
expect(eslint.stdout).toEqual('')
expect(eslint.stderr).toEqual('')
Expand Down
9 changes: 9 additions & 0 deletions test/msw-api/exception-handling.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,13 @@ test('transforms uncaught exceptions into a 500 response', async () => {
'ReferenceError: nonExisting is not defined',
),
})

const errors = runtime.consoleSpy.get('error')

expect(errors).toEqual(
expect.arrayContaining([
expect.stringContaining('ReferenceError: nonExisting is not defined'),
expect.stringContaining(' at '),
]),
)
})
46 changes: 46 additions & 0 deletions test/msw-api/setup-worker/response-logging.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { pageWith } from 'page-with'
import { waitFor } from '../../support/waitFor'

function createResponseLogRegexp(username: string): RegExp {
return new RegExp(
`^\\[MSW\\] \\d{2}:\\d{2}:\\d{2} GET https://api\\.github\\.com/users/${username} 200 OK$`,
)
}

test('prints the response info to the console', async () => {
const runtime = await pageWith({
example: require.resolve('../../rest-api/basic.mocks.ts'),
})

const waitForResponseLog = async (exp: RegExp) => {
await waitFor(() => {
expect(runtime.consoleSpy.get('startGroupCollapsed')).toEqual(
expect.arrayContaining([expect.stringMatching(exp)]),
)
})
}

const getResponseLogs = (exp: RegExp) => {
return runtime.consoleSpy.get('startGroupCollapsed').filter((log) => {
return exp.test(log)
})
}

const firstResponseLogRegexp = createResponseLogRegexp('octocat')
await runtime.request('https://api.github.com/users/octocat')
await waitForResponseLog(firstResponseLogRegexp)

// Must print the response summary to the console.
expect(getResponseLogs(firstResponseLogRegexp)).toHaveLength(1)

const secondResopnseLogRegexp = createResponseLogRegexp('john.doe')
await runtime.request('https://api.github.com/users/john.doe')
await waitForResponseLog(secondResopnseLogRegexp)

/**
* Must not duplicate response logs for the current and previous requests.
* @see https://github.com/mswjs/msw/issues/1411
*/
expect(getResponseLogs(secondResopnseLogRegexp)).toHaveLength(1)
expect(getResponseLogs(firstResponseLogRegexp)).toHaveLength(1)
})
38 changes: 20 additions & 18 deletions test/rest-api/body.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,30 @@ const handleRequestBody: ResponseResolver<MockedRequest, RestContext> = (
return res(ctx.json({ body }))
}

const handleMultipartRequestBody: ResponseResolver<MockedRequest, RestContext> =
async (req, res, ctx) => {
const { body } = req

if (typeof body !== 'object') {
throw new Error(
'Expected multipart request body to be parsed but got string',
)
}
const handleMultipartRequestBody: ResponseResolver<
MockedRequest,
RestContext
> = async (req, res, ctx) => {
const { body } = req

const resBody: Record<string, string> = {}
for (const [name, value] of Object.entries(body)) {
if (value instanceof File) {
resBody[name] = await value.text()
} else {
resBody[name] = value as string
}
}
if (typeof body !== 'object') {
throw new Error(
'Expected multipart request body to be parsed but got string',
)
}

return res(ctx.json({ body: resBody }))
const resBody: Record<string, string> = {}
for (const [name, value] of Object.entries(body)) {
if (value instanceof File) {
resBody[name] = await value.text()
} else {
resBody[name] = value as string
}
}

return res(ctx.json({ body: resBody }))
}

const worker = setupWorker(
rest.get('/login', handleRequestBody),
rest.post('/login', handleRequestBody),
Expand Down
3 changes: 3 additions & 0 deletions test/typings/index.test-d.cts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from 'msw'
export { setupServer } from 'msw/node'
export { setupServer as setupNativeServer } from 'msw/native'
3 changes: 3 additions & 0 deletions test/typings/index.test-d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from 'msw'
export { setupServer } from 'msw/node'
export { setupServer as setupNativeServer } from 'msw/native'
Loading

0 comments on commit 2755f43

Please sign in to comment.