Skip to content

Commit

Permalink
feat: adhere to Fetch API "Request" in response resolver (#1316)
Browse files Browse the repository at this point in the history
* feat: support isomorphic request api

* fix(test): integration tests

* chore: update "cookies", fix failing unit tests

* chore: update "interceptors", encode/decode buffer imports

* test: add "req.text()" node tests

* chore: mark request body tests as deprecated

* test: add "req.text()" browser tests

* test: fix array buffer request body browser test

* test: fix "res.status()" function in tests

* fix(test): add tests

* chore: fix

* fix: remove comment

Co-authored-by: Gurwinder Singh <vargwin@gmail.com>
  • Loading branch information
kettanaito and 95th committed Jul 11, 2022
1 parent bb4bfac commit fc7f00c
Show file tree
Hide file tree
Showing 49 changed files with 1,385 additions and 657 deletions.
6 changes: 3 additions & 3 deletions package.json
Expand Up @@ -66,8 +66,8 @@
],
"sideEffects": false,
"dependencies": {
"@mswjs/cookies": "^0.2.0",
"@mswjs/interceptors": "^0.16.3",
"@mswjs/cookies": "^0.2.2",
"@mswjs/interceptors": "^0.17.2",
"@open-draft/until": "^1.0.3",
"@types/cookie": "^0.4.1",
"@types/js-levenshtein": "^1.1.1",
Expand Down Expand Up @@ -146,4 +146,4 @@
"path": "./node_modules/cz-conventional-changelog"
}
}
}
}
2 changes: 1 addition & 1 deletion src/context/fetch.ts
@@ -1,6 +1,6 @@
import { isNodeProcess } from 'is-node-process'
import { Headers } from 'headers-polyfill'
import type { MockedRequest } from '../handlers/RequestHandler'
import { MockedRequest } from '../utils/request/MockedRequest'

const useFetch: (input: RequestInfo, init?: RequestInit) => Promise<Response> =
isNodeProcess() ? require('node-fetch') : window.fetch
Expand Down
23 changes: 10 additions & 13 deletions src/handlers/GraphQLHandler.test.ts
@@ -1,10 +1,10 @@
/**
* @jest-environment jsdom
*/
import { encodeBuffer } from '@mswjs/interceptors'
import { OperationTypeNode, parse } from 'graphql'
import { Headers } from 'headers-polyfill/lib'
import { context } from '..'
import { createMockedRequest } from '../../test/support/utils'
import { context, MockedRequest, MockedRequestInit } from '..'
import { response } from '../response'
import {
GraphQLContext,
Expand All @@ -13,7 +13,7 @@ import {
GraphQLRequestBody,
isDocumentNode,
} from './GraphQLHandler'
import { MockedRequest, ResponseResolver } from './RequestHandler'
import { ResponseResolver } from './RequestHandler'

const resolver: ResponseResolver<
GraphQLRequest<{ userId: string }>,
Expand All @@ -33,22 +33,19 @@ function createGetGraphQLRequest(
const requestUrl = new URL(hostname)
requestUrl.searchParams.set('query', body?.query)
requestUrl.searchParams.set('variables', JSON.stringify(body?.variables))
return createMockedRequest({
url: requestUrl,
})
return new MockedRequest(requestUrl)
}

function createPostGraphQLRequest(
body: GraphQLRequestBody<any>,
hostname = 'https://example.com',
initMockedRequest: Partial<MockedRequest> = {},
requestInit: MockedRequestInit = {},
) {
return createMockedRequest({
return new MockedRequest(new URL(hostname), {
method: 'POST',
url: new URL(hostname),
...initMockedRequest,
headers: new Headers({ 'Content-Type': 'application/json ' }),
body,
...requestInit,
headers: new Headers({ 'Content-Type': 'application/json' }),
body: encodeBuffer(JSON.stringify(body)),
})
}

Expand Down Expand Up @@ -491,7 +488,7 @@ describe('run', () => {
})
const result = await handler.run(request)

expect(result).toEqual({
expect(result).toMatchObject({
handler,
request: {
...request,
Expand Down
26 changes: 16 additions & 10 deletions src/handlers/GraphQLHandler.ts
Expand Up @@ -9,7 +9,6 @@ import { cookie } from '../context/cookie'
import {
defaultContext,
DefaultContext,
MockedRequest,
RequestHandler,
RequestHandlerDefaultInfo,
ResponseResolver,
Expand All @@ -28,6 +27,7 @@ import {
import { getPublicUrlFromRequest } from '../utils/request/getPublicUrlFromRequest'
import { tryCatch } from '../utils/internal/tryCatch'
import { devUtils } from '../utils/internal/devUtils'
import { MockedRequest } from '../utils/request/MockedRequest'

export type ExpectedOperationTypeNode = OperationTypeNode | 'all'
export type GraphQLHandlerNameSelector = DocumentNode | RegExp | string
Expand Down Expand Up @@ -71,11 +71,6 @@ export interface GraphQLJsonRequestBody<Variables extends GraphQLVariables> {
variables?: Variables
}

export interface GraphQLRequest<Variables extends GraphQLVariables>
extends MockedRequest<GraphQLRequestBody<Variables>> {
variables: Variables
}

export function isDocumentNode(
value: DocumentNode | any,
): value is DocumentNode {
Expand All @@ -86,6 +81,20 @@ export function isDocumentNode(
return typeof value === 'object' && 'kind' in value && 'definitions' in value
}

export class GraphQLRequest<
Variables extends GraphQLVariables,
> extends MockedRequest<GraphQLRequestBody<Variables>> {
constructor(request: MockedRequest, public readonly variables: Variables) {
super(request.url, {
...request,
/**
* TODO(https://github.com/mswjs/msw/issues/1318): Cleanup
*/
body: request['_body'],
})
}
}

export class GraphQLHandler<
Request extends GraphQLRequest<any> = GraphQLRequest<any>,
> extends RequestHandler<
Expand Down Expand Up @@ -151,10 +160,7 @@ export class GraphQLHandler<
request: Request,
parsedResult: ParsedGraphQLRequest,
): GraphQLRequest<any> {
return {
...request,
variables: parsedResult?.variables || {},
}
return new GraphQLRequest(request, parsedResult?.variables || {})
}

predicate(request: MockedRequest, parsedResult: ParsedGraphQLRequest) {
Expand Down
21 changes: 1 addition & 20 deletions src/handlers/RequestHandler.ts
Expand Up @@ -13,6 +13,7 @@ import { delay } from '../context/delay'
import { fetch } from '../context/fetch'
import { ResponseResolutionContext } from '../utils/getResponse'
import { SerializedResponse } from '../setupWorker/glossary'
import { MockedRequest } from '../utils/request/MockedRequest'

export type DefaultContext = {
status: typeof status
Expand Down Expand Up @@ -42,26 +43,6 @@ export type DefaultBodyType =
| null
| undefined

export interface MockedRequest<Body = DefaultBodyType> {
id: string
url: URL
method: Request['method']
headers: Headers
cookies: Record<string, string>
mode: Request['mode']
keepalive: Request['keepalive']
cache: Request['cache']
destination: Request['destination']
integrity: Request['integrity']
credentials: Request['credentials']
redirect: Request['redirect']
referrer: Request['referrer']
referrerPolicy: Request['referrerPolicy']
body: Body
bodyUsed: Request['bodyUsed']
passthrough: typeof passthrough
}

export interface RequestHandlerDefaultInfo {
header: string
}
Expand Down
70 changes: 18 additions & 52 deletions src/handlers/RestHandler.test.ts
Expand Up @@ -2,9 +2,8 @@
* @jest-environment jsdom
*/
import { RestHandler, RestRequest, RestContext } from './RestHandler'
import { createMockedRequest } from '../../test/support/utils'
import { response } from '../response'
import { context } from '..'
import { context, MockedRequest } from '..'
import { ResponseResolver } from './RequestHandler'

const resolver: ResponseResolver<
Expand Down Expand Up @@ -38,9 +37,7 @@ describe('info', () => {
describe('parse', () => {
test('parses a URL given a matching request', () => {
const handler = new RestHandler('GET', '/user/:userId', resolver)
const request = createMockedRequest({
url: new URL('/user/abc-123', location.href),
})
const request = new MockedRequest(new URL('/user/abc-123', location.href))

expect(handler.parse(request)).toEqual({
matches: true,
Expand All @@ -52,9 +49,8 @@ describe('parse', () => {

test('parses a URL and ignores the request method', () => {
const handler = new RestHandler('GET', '/user/:userId', resolver)
const request = createMockedRequest({
const request = new MockedRequest(new URL('/user/def-456', location.href), {
method: 'POST',
url: new URL('/user/def-456', location.href),
})

expect(handler.parse(request)).toEqual({
Expand All @@ -67,9 +63,7 @@ describe('parse', () => {

test('returns negative match result given a non-matching request', () => {
const handler = new RestHandler('GET', '/user/:userId', resolver)
const request = createMockedRequest({
url: new URL('/login', location.href),
})
const request = new MockedRequest(new URL('/login', location.href))

expect(handler.parse(request)).toEqual({
matches: false,
Expand All @@ -81,9 +75,8 @@ describe('parse', () => {
describe('predicate', () => {
test('returns true given a matching request', () => {
const handler = new RestHandler('POST', '/login', resolver)
const request = createMockedRequest({
const request = new MockedRequest(new URL('/login', location.href), {
method: 'POST',
url: new URL('/login', location.href),
})

expect(handler.predicate(request, handler.parse(request))).toBe(true)
Expand All @@ -92,15 +85,9 @@ describe('predicate', () => {
test('respects RegExp as the request method', () => {
const handler = new RestHandler(/.+/, '/login', resolver)
const requests = [
createMockedRequest({ url: new URL('/login', location.href) }),
createMockedRequest({
method: 'POST',
url: new URL('/login', location.href),
}),
createMockedRequest({
method: 'DELETE',
url: new URL('/login', location.href),
}),
new MockedRequest(new URL('/login', location.href)),
new MockedRequest(new URL('/login', location.href), { method: 'POST' }),
new MockedRequest(new URL('/login', location.href), { method: 'DELETE' }),
]

for (const request of requests) {
Expand All @@ -110,9 +97,7 @@ describe('predicate', () => {

test('returns false given a non-matching request', () => {
const handler = new RestHandler('POST', '/login', resolver)
const request = createMockedRequest({
url: new URL('/user/abc-123', location.href),
})
const request = new MockedRequest(new URL('/user/abc-123', location.href))

expect(handler.predicate(request, handler.parse(request))).toBe(false)
})
Expand All @@ -122,14 +107,10 @@ describe('test', () => {
test('returns true given a matching request', () => {
const handler = new RestHandler('GET', '/user/:userId', resolver)
const firstTest = handler.test(
createMockedRequest({
url: new URL('/user/abc-123', location.href),
}),
new MockedRequest(new URL('/user/abc-123', location.href)),
)
const secondTest = handler.test(
createMockedRequest({
url: new URL('/user/def-456', location.href),
}),
new MockedRequest(new URL('/user/def-456', location.href)),
)

expect(firstTest).toBe(true)
Expand All @@ -139,19 +120,13 @@ describe('test', () => {
test('returns false given a non-matching request', () => {
const handler = new RestHandler('GET', '/user/:userId', resolver)
const firstTest = handler.test(
createMockedRequest({
url: new URL('/login', location.href),
}),
new MockedRequest(new URL('/login', location.href)),
)
const secondTest = handler.test(
createMockedRequest({
url: new URL('/user/', location.href),
}),
new MockedRequest(new URL('/user/', location.href)),
)
const thirdTest = handler.test(
createMockedRequest({
url: new URL('/user/abc-123/extra', location.href),
}),
new MockedRequest(new URL('/user/abc-123/extra', location.href)),
)

expect(firstTest).toBe(false)
Expand All @@ -163,9 +138,7 @@ describe('test', () => {
describe('run', () => {
test('returns a mocked response given a matching request', async () => {
const handler = new RestHandler('GET', '/user/:userId', resolver)
const request = createMockedRequest({
url: new URL('/user/abc-123', location.href),
})
const request = new MockedRequest(new URL('/user/abc-123', location.href))
const result = await handler.run(request)

expect(result).toEqual({
Expand All @@ -189,10 +162,7 @@ describe('run', () => {
test('returns null given a non-matching request', async () => {
const handler = new RestHandler('POST', '/login', resolver)
const result = await handler.run(
createMockedRequest({
method: 'GET',
url: new URL('/users', location.href),
}),
new MockedRequest(new URL('/users', location.href)),
)

expect(result).toBeNull()
Expand All @@ -201,9 +171,7 @@ describe('run', () => {
test('returns an empty object as "req.params" given request with no URL parameters', async () => {
const handler = new RestHandler('GET', '/users', resolver)
const result = await handler.run(
createMockedRequest({
url: new URL('/users', location.href),
}),
new MockedRequest(new URL('/users', location.href)),
)

expect(result?.request.params).toEqual({})
Expand All @@ -215,9 +183,7 @@ describe('run with generator', () => {
const handler = new RestHandler('GET', '/users', generatorResolver)
const run = async () => {
const result = await handler.run(
createMockedRequest({
url: new URL('/users', location.href),
}),
new MockedRequest(new URL('/users', location.href)),
)
return result?.response?.body
}
Expand Down

0 comments on commit fc7f00c

Please sign in to comment.