Skip to content

Commit

Permalink
fix: does not warn on unhandled "file://" requests (#1997)
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito authored Jan 25, 2024
1 parent 82ab765 commit 5afedb1
Show file tree
Hide file tree
Showing 10 changed files with 108 additions and 64 deletions.
4 changes: 2 additions & 2 deletions src/core/handlers/GraphQLHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
parseGraphQLRequest,
parseDocumentNode,
} from '../utils/internal/parseGraphQLRequest'
import { getPublicUrlFromRequest } from '../utils/request/getPublicUrlFromRequest'
import { toPublicUrl } from '../utils/request/toPublicUrl'
import { devUtils } from '../utils/internal/devUtils'
import { getAllRequestCookies } from '../utils/request/getRequestCookies'

Expand Down Expand Up @@ -200,7 +200,7 @@ export class GraphQLHandler extends RequestHandler<
}

if (!args.parsedResult.operationName && this.info.operationType !== 'all') {
const publicUrl = getPublicUrlFromRequest(args.request)
const publicUrl = toPublicUrl(args.request.url)

devUtils.warn(`\
Failed to intercept a GraphQL request at "${args.request.method} ${publicUrl}": anonymous GraphQL operations are not supported.
Expand Down
4 changes: 2 additions & 2 deletions src/core/handlers/HttpHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Path,
PathParams,
} from '../utils/matching/matchRequestUrl'
import { getPublicUrlFromRequest } from '../utils/request/getPublicUrlFromRequest'
import { toPublicUrl } from '../utils/request/toPublicUrl'
import { getAllRequestCookies } from '../utils/request/getRequestCookies'
import { cleanUrl, getSearchParams } from '../utils/url/cleanUrl'
import {
Expand Down Expand Up @@ -147,7 +147,7 @@ export class HttpHandler extends RequestHandler<
}

async log(args: { request: Request; response: Response }) {
const publicUrl = getPublicUrlFromRequest(args.request)
const publicUrl = toPublicUrl(args.request.url)
const loggedRequest = await serializeRequest(args.request)
const loggedResponse = await serializeResponse(args.response)
const statusColor = getStatusCodeColor(loggedResponse.status)
Expand Down
4 changes: 2 additions & 2 deletions src/core/utils/internal/parseGraphQLRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
} from 'graphql'
import { parse } from 'graphql'
import type { GraphQLVariables } from '../../handlers/GraphQLHandler'
import { getPublicUrlFromRequest } from '../request/getPublicUrlFromRequest'
import { toPublicUrl } from '../request/toPublicUrl'
import { devUtils } from './devUtils'
import { jsonParse } from './jsonParse'
import { parseMultipartData } from './parseMultipartData'
Expand Down Expand Up @@ -184,7 +184,7 @@ export async function parseGraphQLRequest(
const parsedResult = parseQuery(query)

if (parsedResult instanceof Error) {
const requestPublicUrl = getPublicUrlFromRequest(request)
const requestPublicUrl = toPublicUrl(request.url)

throw new Error(
devUtils.formatMessage(
Expand Down
26 changes: 0 additions & 26 deletions src/core/utils/request/getPublicUrlFromRequest.test.ts

This file was deleted.

15 changes: 0 additions & 15 deletions src/core/utils/request/getPublicUrlFromRequest.ts

This file was deleted.

16 changes: 14 additions & 2 deletions src/core/utils/request/onUnhandledRequest.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getPublicUrlFromRequest } from './getPublicUrlFromRequest'
import { toPublicUrl } from './toPublicUrl'
import { devUtils } from '../internal/devUtils'

export interface UnhandledRequestPrint {
Expand All @@ -21,7 +21,9 @@ export async function onUnhandledRequest(
request: Request,
strategy: UnhandledRequestStrategy = 'warn',
): Promise<void> {
const publicUrl = getPublicUrlFromRequest(request)
const url = new URL(request.url)
const publicUrl = toPublicUrl(url)

const unhandledRequestMessage = `intercepted a request without a matching request handler:\n\n \u2022 ${request.method} ${publicUrl}\n\nIf you still wish to intercept this unhandled request, please create a request handler for it.\nRead more: https://mswjs.io/docs/getting-started/mocks`

function applyStrategy(strategy: UnhandledRequestStrategy) {
Expand Down Expand Up @@ -64,5 +66,15 @@ export async function onUnhandledRequest(
return
}

/**
* @note Ignore "file://" requests.
* Those often are an implementation detail of modern tooling
* that fetches modules via HTTP. Developers don't issue those
* requests and so they mustn't be warned about them.
*/
if (url.protocol === 'file:') {
return
}

applyStrategy(strategy)
}
18 changes: 18 additions & 0 deletions src/core/utils/request/toPublicUrl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @vitest-environment jsdom
*/
import { toPublicUrl } from './toPublicUrl'

test('returns an absolute request URL withouth search params', () => {
expect(toPublicUrl(new URL('https://test.mswjs.io/path'))).toBe(
'https://test.mswjs.io/path',
)

expect(toPublicUrl(new URL('http://localhost/path'))).toBe('/path')

expect(toPublicUrl(new URL('http://localhost/path?foo=bar'))).toBe('/path')
})

it('returns a relative URL given the request to the same origin', () => {
expect(toPublicUrl('http://localhost/user')).toBe('/user')
})
15 changes: 15 additions & 0 deletions src/core/utils/request/toPublicUrl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Returns a relative URL if the given request URL is relative
* to the current origin. Otherwise returns an absolute URL.
*/
export function toPublicUrl(url: string | URL): string {
if (typeof location === 'undefined') {
return url.toString()
}

const urlInstance = url instanceof URL ? url : new URL(url)

return urlInstance.origin === location.origin
? urlInstance.pathname
: urlInstance.origin + urlInstance.pathname
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,48 @@ const server = setupServer(
}),
)

const logs: Array<string> = []
const unhandledListener = vi.fn()

beforeAll(() =>
beforeAll(() => {
server.listen({
onUnhandledRequest(request) {
logs.push(`${request.method} ${request.url}`)
},
}),
)
afterAll(() => server.close())
onUnhandledRequest: unhandledListener,
})
})

afterEach(() => {
vi.resetAllMocks()
})

afterAll(() => {
server.close()
})

test('executes given callback function on un unmatched request', async () => {
const res = await fetch('https://test.mswjs.io')
it('calls the given callback function on un unhandled request', async () => {
const response = await fetch('https://test.mswjs.io')

// Request should be performed as-is, since the callback didn't throw.
expect(res).toHaveProperty('status', 404)
expect(logs).toEqual(['GET https://test.mswjs.io/'])
expect(response).toHaveProperty('status', 404)
expect(unhandledListener).toHaveBeenCalledTimes(1)

const [request, print] = unhandledListener.mock.calls[0]
expect(request.method).toBe('GET')
expect(request.url).toBe('https://test.mswjs.io/')
expect(print).toEqual({
error: expect.any(Function),
warning: expect.any(Function),
})
})

it('calls the given callback on unhandled "file://" requests', async () => {
await fetch('file:///does/not/exist').catch(() => void 0)

expect(unhandledListener).toHaveBeenCalledTimes(1)

const [request, print] = unhandledListener.mock.calls[0]
expect(request.method).toBe('GET')
expect(request.url).toBe('file:///does/not/exist')
expect(print).toEqual({
error: expect.any(Function),
warning: expect.any(Function),
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ beforeAll(() => {
vi.spyOn(global.console, 'warn').mockImplementation(() => void 0)
})

afterEach(() => {
vi.resetAllMocks()
})

afterAll(() => {
server.close()
vi.restoreAllMocks()
})

test('warns on unhandled requests by default', async () => {
const res = await fetch('https://test.mswjs.io')
it('warns on unhandled requests by default', async () => {
const response = await fetch('https://test.mswjs.io')

// Request should be performed as-is
expect(res).toHaveProperty('status', 404)
expect(response).toHaveProperty('status', 404)

expect(console.error).not.toBeCalled()
expect(console.warn).toBeCalledWith(`\
Expand All @@ -36,3 +40,12 @@ test('warns on unhandled requests by default', async () => {
If you still wish to intercept this unhandled request, please create a request handler for it.
Read more: https://mswjs.io/docs/getting-started/mocks`)
})

it('does not warn on unhandled "file://" requests', async () => {
// This request is expected to fail:
// Fetching non-existing file URL.
await fetch('file:///file/does/not/exist').catch(() => void 0)

expect(console.error).not.toBeCalled()
expect(console.warn).not.toBeCalled()
})

0 comments on commit 5afedb1

Please sign in to comment.