From 7edfc1951cf6d8310f677b20fd0594a9782decf9 Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Mon, 22 Jan 2024 01:42:25 +0000 Subject: [PATCH 1/2] chore(release): v2.1.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 012b9d791..d81590541 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "msw", - "version": "2.1.2", + "version": "2.1.3", "description": "Seamless REST/GraphQL API mocking library for browser and Node.js.", "main": "./lib/core/index.js", "module": "./lib/core/index.mjs", From bc0bea6325f61d677d21422ab7e503310f999b00 Mon Sep 17 00:00:00 2001 From: Artem Zakharchenko Date: Mon, 22 Jan 2024 15:10:58 +0100 Subject: [PATCH 2/2] fix: removes request parsing from "onUnhandledRequest" (#1990) --- global.d.ts | 5 - package.json | 3 - pnpm-lock.yaml | 21 -- src/core/utils/handleRequest.ts | 2 +- .../utils/request/onUnhandledRequest.test.ts | 106 +-------- src/core/utils/request/onUnhandledRequest.ts | 187 +--------------- .../graphql-api/anonymous-operation.test.ts | 14 +- .../suggestions.graphql.test.ts | 205 ------------------ .../on-unhandled-request/suggestions.mocks.ts | 18 -- .../suggestions.rest.test.ts | 148 ------------- .../callback.node.test.ts | 4 +- .../many-request-handlers-jsdom.test.ts | 4 +- .../regressions/many-request-handlers.test.ts | 7 +- 13 files changed, 19 insertions(+), 705 deletions(-) delete mode 100644 test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.graphql.test.ts delete mode 100644 test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.mocks.ts delete mode 100644 test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.rest.test.ts diff --git a/global.d.ts b/global.d.ts index 1b6b2563b..b35933e17 100644 --- a/global.d.ts +++ b/global.d.ts @@ -14,8 +14,3 @@ declare module 'babel-minify' { babelOpts: Record, ): { code: string } } - -declare module '@bundled-es-modules/js-levenshtein' { - import levenshtein from 'js-levenshtein' - export default levenshtein -} diff --git a/package.json b/package.json index d81590541..08b05f836 100644 --- a/package.json +++ b/package.json @@ -113,13 +113,11 @@ "sideEffects": false, "dependencies": { "@bundled-es-modules/cookie": "^2.0.0", - "@bundled-es-modules/js-levenshtein": "^2.0.1", "@bundled-es-modules/statuses": "^1.0.1", "@mswjs/cookies": "^1.1.0", "@mswjs/interceptors": "^0.25.14", "@open-draft/until": "^2.1.0", "@types/cookie": "^0.6.0", - "@types/js-levenshtein": "^1.1.3", "@types/statuses": "^2.0.4", "chalk": "^4.1.2", "chokidar": "^3.4.2", @@ -127,7 +125,6 @@ "headers-polyfill": "^4.0.2", "inquirer": "^8.2.0", "is-node-process": "^1.2.0", - "js-levenshtein": "^1.1.6", "outvariant": "^1.4.2", "path-to-regexp": "^6.2.0", "strict-event-emitter": "^0.5.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 588dfcee8..933269b99 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,6 @@ overrides: specifiers: '@bundled-es-modules/cookie': ^2.0.0 - '@bundled-es-modules/js-levenshtein': ^2.0.1 '@bundled-es-modules/statuses': ^1.0.1 '@commitlint/cli': ^18.4.4 '@commitlint/config-conventional': ^18.4.4 @@ -20,7 +19,6 @@ specifiers: '@types/express': ^4.17.21 '@types/fs-extra': ^11.0.4 '@types/glob': ^8.1.0 - '@types/js-levenshtein': ^1.1.3 '@types/json-bigint': ^1.0.4 '@types/node': 18.x '@types/statuses': ^2.0.4 @@ -48,7 +46,6 @@ specifiers: headers-polyfill: ^4.0.2 inquirer: ^8.2.0 is-node-process: ^1.2.0 - js-levenshtein: ^1.1.6 jsdom: ^23.2.0 json-bigint: ^1.0.0 lint-staged: ^15.2.0 @@ -74,13 +71,11 @@ specifiers: dependencies: '@bundled-es-modules/cookie': 2.0.0 - '@bundled-es-modules/js-levenshtein': 2.0.1 '@bundled-es-modules/statuses': 1.0.1 '@mswjs/cookies': 1.1.0 '@mswjs/interceptors': 0.25.14 '@open-draft/until': 2.1.0 '@types/cookie': 0.6.0 - '@types/js-levenshtein': 1.1.3 '@types/statuses': 2.0.4 chalk: 4.1.2 chokidar: 3.4.1 @@ -88,7 +83,6 @@ dependencies: headers-polyfill: 4.0.2 inquirer: 8.2.5 is-node-process: 1.2.0 - js-levenshtein: 1.1.6 outvariant: 1.4.2 path-to-regexp: 6.2.1 strict-event-emitter: 0.5.1 @@ -392,12 +386,6 @@ packages: cookie: 0.5.0 dev: false - /@bundled-es-modules/js-levenshtein/2.0.1: - resolution: {integrity: sha512-DERMS3yfbAljKsQc0U2wcqGKUWpdFjwqWuoMugEJlqBnKO180/n+4SR/J8MRDt1AN48X1ovgoD9KrdVXcaa3Rg==} - dependencies: - js-levenshtein: 1.1.6 - dev: false - /@bundled-es-modules/statuses/1.0.1: resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} dependencies: @@ -1814,10 +1802,6 @@ packages: resolution: {integrity: sha512-cdggbeJIxWoIB8CB57BvenONrQZcBuEf2uddxMRNIy2jgdcnSxnY71tQcNrxdqTG4VmQP5fdLLE9E+jCnMK0Fg==} dev: true - /@types/js-levenshtein/1.1.3: - resolution: {integrity: sha512-jd+Q+sD20Qfu9e2aEXogiO3vpOC1PYJOUdyN9gvs4Qrvkg4wF43L5OhqrPeokdv8TL0/mXoYfpkcoGZMNN2pkQ==} - dev: false - /@types/json-bigint/1.0.4: resolution: {integrity: sha512-ydHooXLbOmxBbubnA7Eh+RpBzuaIiQjh8WGJYQB50JFGFrdxW7JzVlyEV7fAXw0T2sqJ1ysTneJbiyNLqZRAag==} dev: true @@ -5111,11 +5095,6 @@ packages: engines: {node: '>=10'} dev: true - /js-levenshtein/1.1.6: - resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} - engines: {node: '>=0.10.0'} - dev: false - /js-tokens/4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} dev: true diff --git a/src/core/utils/handleRequest.ts b/src/core/utils/handleRequest.ts index 0ab4727d5..45f9ebe6f 100644 --- a/src/core/utils/handleRequest.ts +++ b/src/core/utils/handleRequest.ts @@ -82,7 +82,7 @@ export async function handleRequest( // If the handler lookup returned nothing, no request handler was found // matching this request. Report the request as unhandled. if (!lookupResult.data) { - await onUnhandledRequest(request, handlers, options.onUnhandledRequest) + await onUnhandledRequest(request, options.onUnhandledRequest) emitter.emit('request:unhandled', { request, requestId }) emitter.emit('request:end', { request, requestId }) handleRequestOptions?.onPassthroughResponse?.(request) diff --git a/src/core/utils/request/onUnhandledRequest.test.ts b/src/core/utils/request/onUnhandledRequest.test.ts index 52dc2a21c..fc717c952 100644 --- a/src/core/utils/request/onUnhandledRequest.test.ts +++ b/src/core/utils/request/onUnhandledRequest.test.ts @@ -5,10 +5,6 @@ import { onUnhandledRequest, UnhandledRequestCallback, } from './onUnhandledRequest' -import { HttpHandler, HttpMethods } from '../../handlers/HttpHandler' -import { ResponseResolver } from '../../handlers/RequestHandler' - -const resolver: ResponseResolver = () => void 0 const fixtures = { warningWithoutSuggestions: `\ @@ -24,18 +20,6 @@ Read more: https://mswjs.io/docs/getting-started/mocks`, • GET /api -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`, - - warningWithSuggestions: (suggestions: string) => `\ -[MSW] Warning: intercepted a request without a matching request handler: - - • GET /api - -Did you mean to request one of the following resources instead? - -${suggestions} - 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`, } @@ -52,7 +36,6 @@ afterEach(() => { test('supports the "bypass" request strategy', async () => { await onUnhandledRequest( new Request(new URL('http://localhost/api')), - [], 'bypass', ) @@ -61,22 +44,14 @@ test('supports the "bypass" request strategy', async () => { }) test('supports the "warn" request strategy', async () => { - await onUnhandledRequest( - new Request(new URL('http://localhost/api')), - [], - 'warn', - ) + await onUnhandledRequest(new Request(new URL('http://localhost/api')), 'warn') expect(console.warn).toHaveBeenCalledWith(fixtures.warningWithoutSuggestions) }) test('supports the "error" request strategy', async () => { await expect( - onUnhandledRequest( - new Request(new URL('http://localhost/api')), - [], - 'error', - ), + onUnhandledRequest(new Request(new URL('http://localhost/api')), 'error'), ).rejects.toThrow( '[MSW] Cannot bypass a request when using the "error" strategy for the "onUnhandledRequest" option.', ) @@ -89,7 +64,7 @@ test('supports a custom callback function', async () => { console.warn(`callback: ${request.method} ${request.url}`) }) const request = new Request(new URL('/user', 'http://localhost:3000')) - await onUnhandledRequest(request, [], callback) + await onUnhandledRequest(request, callback) expect(callback).toHaveBeenCalledTimes(1) expect(callback).toHaveBeenCalledWith(request, { @@ -111,7 +86,7 @@ test('supports calling default strategies from the custom callback function', as }, ) const request = new Request(new URL('http://localhost/api')) - await expect(onUnhandledRequest(request, [], callback)).rejects.toThrow( + await expect(onUnhandledRequest(request, callback)).rejects.toThrow( `[MSW] Cannot bypass a request when using the "error" strategy for the "onUnhandledRequest" option.`, ) @@ -126,86 +101,15 @@ test('supports calling default strategies from the custom callback function', as }) test('does not print any suggestions given no handlers to suggest', async () => { - await onUnhandledRequest( - new Request(new URL('http://localhost/api')), - [], - 'warn', - ) - - expect(console.warn).toHaveBeenCalledWith(fixtures.warningWithoutSuggestions) -}) - -test('does not print any suggestions given no handlers are similar', async () => { - await onUnhandledRequest( - new Request(new URL('http://localhost/api')), - [ - // None of the defined request handlers match the actual request URL - // to be used as suggestions. - new HttpHandler(HttpMethods.GET, 'https://api.github.com', resolver), - new HttpHandler(HttpMethods.GET, 'https://api.stripe.com', resolver), - ], - 'warn', - ) + await onUnhandledRequest(new Request(new URL('http://localhost/api')), 'warn') expect(console.warn).toHaveBeenCalledWith(fixtures.warningWithoutSuggestions) }) -test('respects RegExp as a request handler method', async () => { - await onUnhandledRequest( - new Request(new URL('http://localhost/api')), - [new HttpHandler(/^GE/, 'http://localhost/api', resolver)], - 'warn', - ) - - expect(console.warn).toHaveBeenCalledWith(fixtures.warningWithoutSuggestions) -}) - -test('sorts the suggestions by relevance', async () => { - await onUnhandledRequest( - new Request(new URL('http://localhost/api')), - [ - new HttpHandler(HttpMethods.GET, '/', resolver), - new HttpHandler(HttpMethods.GET, 'https://api.example.com/api', resolver), - new HttpHandler(HttpMethods.POST, '/api', resolver), - ], - 'warn', - ) - - expect(console.warn).toHaveBeenCalledWith( - fixtures.warningWithSuggestions(`\ - • POST /api - • GET /`), - ) -}) - -test('does not print more than 4 suggestions', async () => { - await onUnhandledRequest( - new Request(new URL('http://localhost/api')), - [ - new HttpHandler(HttpMethods.GET, '/ap', resolver), - new HttpHandler(HttpMethods.GET, '/api', resolver), - new HttpHandler(HttpMethods.GET, '/api-1', resolver), - new HttpHandler(HttpMethods.GET, '/api-2', resolver), - new HttpHandler(HttpMethods.GET, '/api-3', resolver), - new HttpHandler(HttpMethods.GET, '/api-4', resolver), - ], - 'warn', - ) - - expect(console.warn).toHaveBeenCalledWith( - fixtures.warningWithSuggestions(`\ - • GET /api - • GET /ap - • GET /api-1 - • GET /api-2`), - ) -}) - test('throws an exception given unknown request strategy', async () => { await expect( onUnhandledRequest( new Request(new URL('http://localhost/api')), - [], // @ts-expect-error Intentional unknown strategy. 'invalid-strategy', ), diff --git a/src/core/utils/request/onUnhandledRequest.ts b/src/core/utils/request/onUnhandledRequest.ts index 36dc15aba..f731fbc9c 100644 --- a/src/core/utils/request/onUnhandledRequest.ts +++ b/src/core/utils/request/onUnhandledRequest.ts @@ -1,22 +1,6 @@ -import jsLevenshtein from '@bundled-es-modules/js-levenshtein' -import { RequestHandler } from '../../handlers/RequestHandler' -import { HttpHandler } from '../../handlers/HttpHandler' -import { GraphQLHandler } from '../../handlers/GraphQLHandler' -import { - ParsedGraphQLQuery, - ParsedGraphQLRequest, - parseGraphQLRequest, -} from '../internal/parseGraphQLRequest' import { getPublicUrlFromRequest } from './getPublicUrlFromRequest' -import { isStringEqual } from '../internal/isStringEqual' import { devUtils } from '../internal/devUtils' -const getStringMatchScore = jsLevenshtein - -const MAX_MATCH_SCORE = 3 -const MAX_SUGGESTION_COUNT = 4 -const TYPE_MATCH_DELTA = 0.5 - export interface UnhandledRequestPrint { warning(): void error(): void @@ -33,183 +17,18 @@ export type UnhandledRequestStrategy = | 'error' | UnhandledRequestCallback -interface RequestHandlerGroups { - http: Array - graphql: Array -} - -function groupHandlersByType( - handlers: Array, -): RequestHandlerGroups { - return handlers.reduce( - (groups, handler) => { - if (handler instanceof HttpHandler) { - groups.http.push(handler) - } - - if (handler instanceof GraphQLHandler) { - groups.graphql.push(handler) - } - - return groups - }, - { - http: [], - graphql: [], - }, - ) -} - -type RequestHandlerSuggestion = [number, RequestHandler] - -type ScoreGetterFn = ( - request: Request, - handler: RequestHandlerType, -) => number - -function getHttpHandlerScore(): ScoreGetterFn { - return (request, handler) => { - const { path, method } = handler.info - - if (path instanceof RegExp || method instanceof RegExp) { - return Infinity - } - - const hasSameMethod = isStringEqual(request.method, method) - - // Always treat a handler with the same method as a more similar one. - const methodScoreDelta = hasSameMethod ? TYPE_MATCH_DELTA : 0 - const requestPublicUrl = getPublicUrlFromRequest(request) - const score = getStringMatchScore(requestPublicUrl, path) - - return score - methodScoreDelta - } -} - -function getGraphQLHandlerScore( - parsedQuery: ParsedGraphQLQuery, -): ScoreGetterFn { - return (_, handler) => { - if (typeof parsedQuery.operationName === 'undefined') { - return Infinity - } - - const { operationType, operationName } = handler.info - - if (typeof operationName !== 'string') { - return Infinity - } - - const hasSameOperationType = parsedQuery.operationType === operationType - // Always treat a handler with the same operation type as a more similar one. - const operationTypeScoreDelta = hasSameOperationType ? TYPE_MATCH_DELTA : 0 - const score = getStringMatchScore(parsedQuery.operationName, operationName) - - return score - operationTypeScoreDelta - } -} - -function getSuggestedHandler( - request: Request, - handlers: Array | Array, - getScore: ScoreGetterFn | ScoreGetterFn, -): Array { - const suggestedHandlers = (handlers as Array) - .reduce>((suggestions, handler) => { - const score = getScore(request, handler as any) - return suggestions.concat([[score, handler]]) - }, []) - .sort(([leftScore], [rightScore]) => leftScore - rightScore) - .filter(([score]) => score <= MAX_MATCH_SCORE) - .slice(0, MAX_SUGGESTION_COUNT) - .map(([, handler]) => handler) - - return suggestedHandlers -} - -function getSuggestedHandlersMessage(handlers: RequestHandler[]) { - if (handlers.length > 1) { - return `\ -Did you mean to request one of the following resources instead? - -${handlers.map((handler) => ` • ${handler.info.header}`).join('\n')}` - } - - return `Did you mean to request "${handlers[0].info.header}" instead?` -} - export async function onUnhandledRequest( request: Request, - handlers: Array, strategy: UnhandledRequestStrategy = 'warn', ): Promise { - const parsedGraphQLQuery = await parseGraphQLRequest(request).catch( - () => null, - ) const publicUrl = getPublicUrlFromRequest(request) - - 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.http - - const suggestedHandlers = getSuggestedHandler( - request, - relevantHandlers, - parsedGraphQLQuery - ? getGraphQLHandlerScore(parsedGraphQLQuery) - : getHttpHandlerScore(), - ) - - return suggestedHandlers.length > 0 - ? getSuggestedHandlersMessage(suggestedHandlers) - : '' - } - - function getGraphQLRequestHeader( - parsedGraphQLRequest: ParsedGraphQLRequest, - ): string { - if (!parsedGraphQLRequest?.operationName) { - return `anonymous ${parsedGraphQLRequest?.operationType} (${request.method} ${publicUrl})` - } - - return `${parsedGraphQLRequest.operationType} ${parsedGraphQLRequest.operationName} (${request.method} ${publicUrl})` - } - - function generateUnhandledRequestMessage(): string { - const requestHeader = parsedGraphQLQuery - ? getGraphQLRequestHeader(parsedGraphQLQuery) - : `${request.method} ${publicUrl}` - const handlerSuggestion = generateHandlerSuggestion() - - const messageTemplate = [ - `intercepted 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) - return messageTemplate.join('\n\n') - } + 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) { - // Generate handler suggestions only when applying the strategy. - // This saves bandwidth for scenarios when developers opt-out - // from the default unhandled request handling strategy. - const message = generateUnhandledRequestMessage() - switch (strategy) { case 'error': { // Print a developer-friendly error. - devUtils.error('Error: %s', message) + devUtils.error('Error: %s', unhandledRequestMessage) // Throw an exception to halt request processing and not perform the original request. throw new Error( @@ -220,7 +39,7 @@ Read more: https://mswjs.io/docs/getting-started/mocks\ } case 'warn': { - devUtils.warn('Warning: %s', message) + devUtils.warn('Warning: %s', unhandledRequestMessage) break } diff --git a/test/browser/graphql-api/anonymous-operation.test.ts b/test/browser/graphql-api/anonymous-operation.test.ts index 4ba3119a3..6cef3e90a 100644 --- a/test/browser/graphql-api/anonymous-operation.test.ts +++ b/test/browser/graphql-api/anonymous-operation.test.ts @@ -69,24 +69,12 @@ test('does not warn on anonymous GraphQL operation when no GraphQL handlers are `\ [MSW] Warning: intercepted a request without a matching request handler: - • anonymous query (POST ${endpointUrl}) + • POST ${endpointUrl} 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`, ]) }) - - // // Must print the warning because anonymous operations cannot be intercepted - // // using standard "graphql.query()" and "graphql.mutation()" handlers. - // await waitFor(() => { - // expect(consoleSpy.get('warning')).toEqual( - // expect.arrayContaining([ - // `[MSW] Failed to intercept a GraphQL request at "POST ${endpointUrl}": anonymous GraphQL operations are not supported. - - // Consider naming this operation or using "graphql.operation()" request handler to intercept GraphQL requests regardless of their operation name/type. Read more: https://mswjs.io/docs/api/graphql/#graphqloperationresolver`, - // ]), - // ) - // }) }) test('warns on handled anonymous GraphQL operation', async ({ diff --git a/test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.graphql.test.ts b/test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.graphql.test.ts deleted file mode 100644 index 77bd1c96c..000000000 --- a/test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.graphql.test.ts +++ /dev/null @@ -1,205 +0,0 @@ -import { graphql } from 'msw' -import { SetupWorkerApi } from 'msw/browser' -import { test, expect } from '../../../../playwright.extend' - -declare namespace window { - export const msw: { - worker: SetupWorkerApi - graphql: typeof graphql - } -} - -test.describe('GraphQL API', () => { - test('does not suggest any handlers when there are no similar ones', async ({ - loadExample, - spyOnConsole, - fetch, - page, - }) => { - const consoleSpy = spyOnConsole() - await loadExample(require.resolve('./suggestions.mocks.ts')) - - page.evaluate(() => { - const { worker, graphql } = window.msw - worker.use( - graphql.mutation('SubmitCheckout', () => void 0), - graphql.query('GetUserPaymentHistory', () => void 0), - ) - }) - - await fetch('/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: ` - query PaymentHistory { - creditCard { - number - } - } - `, - }), - }) - - expect(consoleSpy.get('warning')).toEqual( - expect.arrayContaining([ - expect.stringContaining(`\ -[MSW] Warning: intercepted a request without a matching request handler: - - • query PaymentHistory (POST /graphql) - -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`), - ]), - ) - }) - - test('suggests a similar request handler of the same operation type', async ({ - loadExample, - spyOnConsole, - fetch, - page, - }) => { - const consoleSpy = spyOnConsole() - await loadExample(require.resolve('./suggestions.mocks.ts')) - - page.evaluate(() => { - const { worker, graphql } = window.msw - worker.use( - graphql.mutation('GetLatestActiveUser', () => void 0), - graphql.query('GetUser', () => void 0), - ) - }) - - await fetch('/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: ` - query GetUsers { - user { - firstName - } - } - `, - }), - }) - - expect(consoleSpy.get('warning')).toEqual( - expect.arrayContaining([ - expect.stringContaining(`\ -[MSW] Warning: intercepted a request without a matching request handler: - - • query GetUsers (POST /graphql) - -Did you mean to request "query GetUser (origin: *)" instead? - -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`), - ]), - ) - }) - - test('suggests a handler of a different operation type if its name is similar', async ({ - loadExample, - spyOnConsole, - fetch, - page, - }) => { - const consoleSpy = spyOnConsole() - await loadExample(require.resolve('./suggestions.mocks.ts')) - - page.evaluate(() => { - const { worker, graphql } = window.msw - worker.use( - graphql.query('GetCheckoutSummary', () => void 0), - graphql.mutation('SubmitCheckout', () => void 0), - ) - }) - - await fetch('/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: ` - query SubmitCheckout { - checkout { - status - } - } - `, - }), - }) - - expect(consoleSpy.get('warning')).toEqual( - expect.arrayContaining([ - expect.stringContaining(`\ -[MSW] Warning: intercepted a request without a matching request handler: - - • query SubmitCheckout (POST /graphql) - -Did you mean to request "mutation SubmitCheckout (origin: *)" instead? - -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`), - ]), - ) - }) - - test('suggests multiple similar handlers regardless of their operation type', async ({ - loadExample, - spyOnConsole, - fetch, - page, - }) => { - const consoleSpy = spyOnConsole() - await loadExample(require.resolve('./suggestions.mocks.ts')) - - page.evaluate(() => { - const { worker, graphql } = window.msw - worker.use( - graphql.mutation('ActivateUser', () => void 0), - graphql.query('ActiveUser', () => void 0), - ) - }) - - await fetch('/graphql', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: ` - query ActiveUsers { - checkout { - status - } - } - `, - }), - }) - - expect(consoleSpy.get('warning')).toEqual( - expect.arrayContaining([ - expect.stringContaining(`\ -[MSW] Warning: intercepted a request without a matching request handler: - - • query ActiveUsers (POST /graphql) - -Did you mean to request one of the following resources instead? - - • query ActiveUser (origin: *) - • mutation ActivateUser (origin: *) - -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`), - ]), - ) - }) -}) diff --git a/test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.mocks.ts b/test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.mocks.ts deleted file mode 100644 index 260bedf1d..000000000 --- a/test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.mocks.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { http, graphql } from 'msw' -import { setupWorker } from 'msw/browser' - -const worker = setupWorker() - -worker.start({ - // Set the `onUnhandledRequest` option to enable - // smart suggestions for unhandled requests. - onUnhandledRequest: 'warn', -}) - -Object.assign(window, { - msw: { - worker, - http, - graphql, - }, -}) diff --git a/test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.rest.test.ts b/test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.rest.test.ts deleted file mode 100644 index cec86e941..000000000 --- a/test/browser/msw-api/setup-worker/start/on-unhandled-request/suggestions.rest.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { http } from 'msw' -import { SetupWorkerApi } from 'msw/browser' -import { test, expect } from '../../../../playwright.extend' - -declare namespace window { - export const msw: { - worker: SetupWorkerApi - http: typeof http - } -} - -test.describe('REST API', () => { - test('does not suggest any handlers when there are no similar ones', async ({ - loadExample, - spyOnConsole, - fetch, - page, - }) => { - const consoleSpy = spyOnConsole() - await loadExample(require.resolve('./suggestions.mocks.ts')) - - page.evaluate(() => { - const { worker, http } = window.msw - worker.use( - http.get('/user', () => void 0), - http.post('/user-contact-details', () => void 0), - ) - }) - - await fetch('/user-details') - - expect(consoleSpy.get('warning')).toEqual( - expect.arrayContaining([ - expect.stringContaining(`\ -[MSW] Warning: intercepted a request without a matching request handler: - - • GET /user-details - -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`), - ]), - ) - }) - - test('suggests a similar request handler of the same method', async ({ - loadExample, - spyOnConsole, - fetch, - page, - }) => { - const consoleSpy = spyOnConsole() - await loadExample(require.resolve('./suggestions.mocks.ts')) - - page.evaluate(() => { - const { worker, http } = window.msw - worker.use(http.get('/user', () => void 0)) - }) - - await fetch('/users') - - expect(consoleSpy.get('warning')).toEqual( - expect.arrayContaining([ - expect.stringContaining(`\ -[MSW] Warning: intercepted a request without a matching request handler: - - • GET /users - -Did you mean to request "GET /user" instead? - -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`), - ]), - ) - }) - - test('suggest a handler of a different method if its URL is similar', async ({ - loadExample, - spyOnConsole, - fetch, - page, - }) => { - const consoleSpy = spyOnConsole() - await loadExample(require.resolve('./suggestions.mocks.ts')) - - page.evaluate(() => { - const { worker, http } = window.msw - worker.use( - http.get('/user', () => void 0), - http.post('/user-contact-details', () => void 0), - ) - }) - - await fetch('/users', { - method: 'POST', - }) - - expect(consoleSpy.get('warning')).toEqual( - expect.arrayContaining([ - expect.stringContaining(`\ -[MSW] Warning: intercepted a request without a matching request handler: - - • POST /users - -Did you mean to request "GET /user" instead? - -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`), - ]), - ) - }) - - test('suggests multiple similar handlers regardless of their method', async ({ - loadExample, - spyOnConsole, - fetch, - page, - }) => { - const consoleSpy = spyOnConsole() - await loadExample(require.resolve('./suggestions.mocks.ts')) - - page.evaluate(() => { - const { worker, http } = window.msw - worker.use( - http.post('/payment', () => void 0), - http.get('/payments', () => void 0), - ) - }) - - await fetch('/pamyents') - - expect(consoleSpy.get('warning')).toEqual( - expect.arrayContaining([ - expect.stringContaining(`\ -[MSW] Warning: intercepted a request without a matching request handler: - - • GET /pamyents - -Did you mean to request one of the following resources instead? - - • GET /payments - • POST /payment - -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`), - ]), - ) - }) -}) diff --git a/test/node/msw-api/setup-server/scenarios/on-unhandled-request/callback.node.test.ts b/test/node/msw-api/setup-server/scenarios/on-unhandled-request/callback.node.test.ts index 6f6dc6e40..06804f612 100644 --- a/test/node/msw-api/setup-server/scenarios/on-unhandled-request/callback.node.test.ts +++ b/test/node/msw-api/setup-server/scenarios/on-unhandled-request/callback.node.test.ts @@ -14,8 +14,8 @@ const logs: Array = [] beforeAll(() => server.listen({ - onUnhandledRequest(req) { - logs.push(`${req.method} ${req.url}`) + onUnhandledRequest(request) { + logs.push(`${request.method} ${request.url}`) }, }), ) diff --git a/test/node/regressions/many-request-handlers-jsdom.test.ts b/test/node/regressions/many-request-handlers-jsdom.test.ts index 982c0e609..5324fd705 100644 --- a/test/node/regressions/many-request-handlers-jsdom.test.ts +++ b/test/node/regressions/many-request-handlers-jsdom.test.ts @@ -75,7 +75,7 @@ describe('http handlers', () => { }, ) // Each clone is a new AbortSignal listener which needs to be registered - expect(requestCloneSpy).toHaveBeenCalledTimes(2) + expect(requestCloneSpy).toHaveBeenCalledTimes(1) expect(httpResponse.status).toBe(500) expect(processErrorSpy).not.toHaveBeenCalled() }) @@ -122,7 +122,7 @@ describe('graphql handlers', () => { }) expect(unhandledResponse.status).toEqual(500) - expect(requestCloneSpy).toHaveBeenCalledTimes(3) + expect(requestCloneSpy).toHaveBeenCalledTimes(2) // Must not print any memory leak warnings. expect(processErrorSpy).not.toHaveBeenCalled() }) diff --git a/test/node/regressions/many-request-handlers.test.ts b/test/node/regressions/many-request-handlers.test.ts index 455e6c18c..3079ebc9c 100644 --- a/test/node/regressions/many-request-handlers.test.ts +++ b/test/node/regressions/many-request-handlers.test.ts @@ -60,6 +60,7 @@ describe('http handlers', () => { body: 'request-body-', }, ).then((response) => response.text()) + // Each clone is a new AbortSignal listener which needs to be registered expect(requestCloneSpy).toHaveBeenCalledTimes(1) expect(httpResponse).toBe(`request-body-${NUMBER_OF_REQUEST_HANDLERS - 1}`) @@ -75,7 +76,7 @@ describe('http handlers', () => { }, ) // Each clone is a new AbortSignal listener which needs to be registered - expect(requestCloneSpy).toHaveBeenCalledTimes(2) + expect(requestCloneSpy).toHaveBeenCalledTimes(1) expect(httpResponse.status).toBe(500) expect(stdErrSpy).not.toHaveBeenCalled() }) @@ -91,6 +92,7 @@ describe('graphql handlers', () => { }), ) }) + it('does not print a memory leak warning', async () => { const graphqlResponse = await fetch(httpServer.http.url('/graphql'), { method: 'POST', @@ -109,6 +111,7 @@ describe('graphql handlers', () => { }) expect(stdErrSpy).not.toHaveBeenCalled() }) + it('does not print a memory leak warning for onUnhandledRequest', async () => { const unhandledResponse = await fetch(httpServer.http.url('/graphql'), { method: 'POST', @@ -121,7 +124,7 @@ describe('graphql handlers', () => { }) expect(unhandledResponse.status).toEqual(500) - expect(requestCloneSpy).toHaveBeenCalledTimes(3) + expect(requestCloneSpy).toHaveBeenCalledTimes(2) // Must not print any memory leak warnings. expect(stdErrSpy).not.toHaveBeenCalled() })