Skip to content

Commit

Permalink
feat(onunhandledrequest): allow default behaviour fallback for onunha…
Browse files Browse the repository at this point in the history
…ndledrequest

Allow users to fall back to default behaviour for onunhandledrequest if they just want to cover a
small number of scenarios, then hand back control to msw

addresses #1080
  • Loading branch information
James authored and kettanaito committed Feb 12, 2022
1 parent 6e7ec28 commit 6247e45
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 65 deletions.
6 changes: 5 additions & 1 deletion src/utils/handleRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,11 @@ test('reports request as unhandled when it has no matching request handlers', as
['request:unhandled', request],
['request:end', request],
])
expect(options.onUnhandledRequest).toHaveBeenNthCalledWith(1, request)
expect(options.onUnhandledRequest).toHaveBeenNthCalledWith(
1,
request,
expect.anything(),
)
expect(callbacks.onBypassResponse).toHaveBeenNthCalledWith(1, request)
expect(callbacks.onMockedResponse).not.toHaveBeenCalled()
expect(callbacks.onMockedResponseSent).not.toHaveBeenCalled()
Expand Down
2 changes: 1 addition & 1 deletion src/utils/request/onUnhandledRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ test('supports custom callback function', () => {
const request = createMockedRequest()
onUnhandledRequest(request, [], handleRequest)

expect(handleRequest).toHaveBeenCalledWith(request)
expect(handleRequest).toHaveBeenCalledWith(request, expect.anything())
})

test('does not print any suggestions given no handlers to suggest', () => {
Expand Down
148 changes: 85 additions & 63 deletions src/utils/request/onUnhandledRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ const MAX_MATCH_SCORE = 3
const MAX_SUGGESTION_COUNT = 4
const TYPE_MATCH_DELTA = 0.5

export type UnhandledRequestCallback = (request: MockedRequest) => void
type HandleNext = () => {
error: (strategy: UnhandledRequestStrategy) => void
warn: (strategy: UnhandledRequestStrategy) => void
}
export type UnhandledRequestCallback = (
request: MockedRequest,
next: HandleNext,
) => void

export type UnhandledRequestStrategy =
| 'bypass'
Expand Down Expand Up @@ -131,78 +138,93 @@ export function onUnhandledRequest(
handlers: RequestHandler[],
strategy: UnhandledRequestStrategy = 'warn',
): void {
if (typeof strategy === 'function') {
strategy(request)
return
}

/**
* @note Ignore exceptions during GraphQL request parsing because at this point
* we cannot assume the unhandled request is a valid GraphQL request.
* If the GraphQL parsing fails, just don't treat it as a GraphQL request.
*/
const parsedGraphQLQuery = tryCatch(() => parseGraphQLRequest(request))
const handlerGroups = groupHandlersByType(handlers)
const relevantHandlers = parsedGraphQLQuery
? handlerGroups.graphql
: handlerGroups.rest

const suggestedHandlers = getSuggestedHandler(
request,
relevantHandlers,
parsedGraphQLQuery
? getGraphQLHandlerScore(parsedGraphQLQuery)
: getRestHandlerScore(),
)

const handlerSuggestion =
suggestedHandlers.length > 0
function generateHandlerSuggestion(): string {
/**
* @note Ignore exceptions during GraphQL request parsing because at this point
* we cannot assume the unhandled request is a valid GraphQL request.
* If the GraphQL parsing fails, just don't treat it as a GraphQL request.
*/
const handlerGroups = groupHandlersByType(handlers)
const relevantHandlers = parsedGraphQLQuery
? handlerGroups.graphql
: handlerGroups.rest

const suggestedHandlers = getSuggestedHandler(
request,
relevantHandlers,
parsedGraphQLQuery
? getGraphQLHandlerScore(parsedGraphQLQuery)
: getRestHandlerScore(),
)

return suggestedHandlers.length > 0
? getSuggestedHandlersMessage(suggestedHandlers)
: ''
}

const publicUrl = getPublicUrlFromRequest(request)
const requestHeader = parsedGraphQLQuery
? `${parsedGraphQLQuery.operationType} ${parsedGraphQLQuery.operationName} (${request.method} ${publicUrl})`
: `${request.method} ${publicUrl}`

const messageTemplate = [
`captured a request without a matching request handler:`,
` \u2022 ${requestHeader}`,
handlerSuggestion,
`\
function generateUnhandledRequestMessage(): string {
const publicUrl = getPublicUrlFromRequest(request)
const requestHeader = parsedGraphQLQuery
? `${parsedGraphQLQuery.operationType} ${parsedGraphQLQuery.operationName} (${request.method} ${publicUrl})`
: `${request.method} ${publicUrl}`
const handlerSuggestion = generateHandlerSuggestion()

const messageTemplate = [
`captured a request without a matching request handler:`,
` \u2022 ${requestHeader}`,
handlerSuggestion,
`\
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\
`,
].filter(Boolean)
const message = messageTemplate.join('\n\n')

switch (strategy) {
case 'error': {
// Print a developer-friendly error.
devUtils.error('Error: %s', message)

// Throw an exception to halt request processing and not perform the original request.
throw new Error(
devUtils.formatMessage(
'Cannot bypass a request when using the "error" strategy for the "onUnhandledRequest" option.',
),
)
}
].filter(Boolean)
return messageTemplate.join('\n\n')
}

case 'warn': {
devUtils.warn('Warning: %s', message)
break
}
function actionStrategy(strategy: UnhandledRequestStrategy) {
const message = generateUnhandledRequestMessage()
switch (strategy) {
case 'error': {
// Print a developer-friendly error.
devUtils.error('Error: %s', message)

// Throw an exception to halt request processing and not perform the original request.
throw new Error(
devUtils.formatMessage(
'Cannot bypass a request when using the "error" strategy for the "onUnhandledRequest" option.',
),
)
}

case 'warn': {
devUtils.warn('Warning: %s', message)
break
}

case 'bypass':
break
case 'bypass':
break

default:
throw new Error(
devUtils.formatMessage(
'Failed to react to an unhandled request: unknown strategy "%s". Please provide one of the supported strategies ("bypass", "warn", "error") or a custom callback function as the value of the "onUnhandledRequest" option.',
strategy,
),
)
default:
throw new Error(
devUtils.formatMessage(
'Failed to react to an unhandled request: unknown strategy "%s". Please provide one of the supported strategies ("bypass", "warn", "error") or a custom callback function as the value of the "onUnhandledRequest" option.',
strategy,
),
)
}
}

if (typeof strategy === 'function') {
strategy(request, () => {
return {
error: () => actionStrategy('error'),
warn: () => actionStrategy('warn'),
}
})
return
}

actionStrategy(strategy)
}

0 comments on commit 6247e45

Please sign in to comment.