Skip to content

Commit

Permalink
Merge branch 'feat/standard-api' into feat/ws-sync-handlers
Browse files Browse the repository at this point in the history
  • Loading branch information
kettanaito committed Sep 16, 2023
2 parents c5d03b0 + 91989df commit f1ff76a
Show file tree
Hide file tree
Showing 19 changed files with 89 additions and 148 deletions.
17 changes: 17 additions & 0 deletions MIGRATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ To help you navigate, we've structured this guide on the feature basis. You can
- [req.cookies](#request-cookies)
- [req.passthrough](#reqpassthrough)
- [res.once](#resonce)
- [res.networkError](#resnetworkerror)
- [Context utilities](#context-utilities)
- [ctx.status](#ctxstatus)
- [ctx.set](#ctxset)
Expand Down Expand Up @@ -249,6 +250,22 @@ export const handlers = [
]
```
## `res.networkError`
To respond to a request with a network error, use the `HttpResponse.error()` static method:
```js
import { http, HttpResponse } from 'msw'
export const handlers = [
http.get('/resource', () => {
return HttpResponse.error()
}),
]
```
> Note that we are dropping support for custom network error messages to be more compliant with the standard [`Response.error()`](https://developer.mozilla.org/en-US/docs/Web/API/Response/error_static) network errors, which don't support custom error messages.

## `req.passthrough`

```js
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "msw",
"version": "0.0.0-fetch.rc-18",
"version": "0.0.0-fetch.rc-19",
"description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
"main": "./lib/core/index.js",
"module": "./lib/core/index.mjs",
Expand Down Expand Up @@ -114,7 +114,7 @@
"@bundled-es-modules/js-levenshtein": "^2.0.1",
"@bundled-es-modules/statuses": "^1.0.1",
"@mswjs/cookies": "^1.0.0",
"@mswjs/interceptors": "^0.23.0",
"@mswjs/interceptors": "^0.25.1",
"@open-draft/deferred-promise": "^2.1.0",
"@open-draft/until": "^2.1.0",
"@types/cookie": "^0.4.1",
Expand Down
20 changes: 10 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 0 additions & 12 deletions src/browser/setupWorker/start/createRequestListener.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
ServiceWorkerMessage,
WorkerChannel,
} from './utils/createMessageChannel'
import { NetworkError } from '~/core/NetworkError'
import { parseWorkerRequest } from '../../utils/parseWorkerRequest'
import { handleRequest } from '~/core/utils/handleRequest'
import { RequiredDeep } from '~/core/typeUtils'
Expand Down Expand Up @@ -70,17 +69,6 @@ export const createRequestListener = (
},
)
} catch (error) {
if (error instanceof NetworkError) {
// Treat emulated network error differently,
// as it is an intended exception in a request handler.
messageChannel.postMessage('NETWORK_ERROR', {
name: error.name,
message: error.message,
})

return
}

if (error instanceof Error) {
devUtils.error(
`Uncaught exception in the request handler for "%s %s":
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ interface WorkerChannelEventsMap {
transfer?: [ReadableStream<Uint8Array>],
]
NOT_FOUND: []
NETWORK_ERROR: [data: { name: string; message: string }]
}

export class WorkerChannel {
Expand Down
17 changes: 0 additions & 17 deletions src/core/NetworkError.ts

This file was deleted.

16 changes: 13 additions & 3 deletions src/core/graphql.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import type { DocumentNode, OperationTypeNode } from 'graphql'
import { ResponseResolver } from './handlers/RequestHandler'
import {
ResponseResolver,
RequestHandlerOptions,
} from './handlers/RequestHandler'
import {
GraphQLHandler,
GraphQLVariables,
Expand All @@ -8,7 +11,7 @@ import {
GraphQLResolverExtras,
GraphQLResponseBody,
} from './handlers/GraphQLHandler'
import { Path } from './utils/matching/matchRequestUrl'
import type { Path } from './utils/matching/matchRequestUrl'

export interface TypedDocumentNode<
Result = { [key: string]: any },
Expand Down Expand Up @@ -36,8 +39,15 @@ function createScopedGraphQLHandler(
null,
GraphQLResponseBody<Query>
>,
options: RequestHandlerOptions = {},
) => {
return new GraphQLHandler(operationType, operationName, url, resolver)
return new GraphQLHandler(
operationType,
operationName,
url,
resolver,
options,
)
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/core/handlers/GraphQLHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
DefaultBodyType,
RequestHandler,
RequestHandlerDefaultInfo,
RequestHandlerOptions,
ResponseResolver,
} from './RequestHandler'
import { getTimestamp } from '../utils/logging/getTimestamp'
Expand Down Expand Up @@ -73,6 +74,7 @@ export class GraphQLHandler extends RequestHandler<
operationName: GraphQLHandlerNameSelector,
endpoint: Path,
resolver: ResponseResolver<GraphQLResolverExtras<any>, any, any>,
options?: RequestHandlerOptions,
) {
let resolvedOperationName = operationName

Expand Down Expand Up @@ -106,6 +108,7 @@ export class GraphQLHandler extends RequestHandler<
operationName: resolvedOperationName,
},
resolver,
options,
})

this.endpoint = endpoint
Expand Down
8 changes: 4 additions & 4 deletions src/core/handlers/HttpHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import { getTimestamp } from '../utils/logging/getTimestamp'
import { requestToLoggableObject } from '../utils/logging/requestToLoggableObject'
import { responseToLoggableObject } from '../utils/logging/responseToLoggableObject'
import {
Match,
matchRequestUrl,
Match,
Path,
PathParams,
} from '../utils/matching/matchRequestUrl'
Expand All @@ -17,7 +17,7 @@ import { cleanUrl, getSearchParams } from '../utils/url/cleanUrl'
import {
RequestHandler,
RequestHandlerDefaultInfo,
RequestHandlerPublicOptions,
RequestHandlerOptions,
ResponseResolver,
} from './RequestHandler'

Expand Down Expand Up @@ -65,7 +65,7 @@ export class HttpHandler extends RequestHandler<
method: HttpHandlerMethod,
path: Path,
resolver: ResponseResolver<HttpRequestResolverExtras<any>, any, any>,
options?: RequestHandlerPublicOptions,
options?: RequestHandlerOptions,
) {
super({
info: {
Expand All @@ -74,7 +74,7 @@ export class HttpHandler extends RequestHandler<
method,
},
resolver,
once: options?.once,
options,
})

this.checkRedundantQueryParameters()
Expand Down
22 changes: 13 additions & 9 deletions src/core/handlers/RequestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,16 @@ export type ResponseResolver<
info: ResponseResolverInfo<ResolverExtraInfo, RequestBodyType>,
) => AsyncResponseResolverReturnType<ResponseBodyType>

export interface RequestHandlerOptions<HandlerInfo>
extends RequestHandlerPublicOptions {
export interface RequestHandlerArgs<
HandlerInfo,
HandlerOptions extends RequestHandlerOptions,
> {
info: HandlerInfo
resolver: ResponseResolver<any>
options?: HandlerOptions
}

export interface RequestHandlerPublicOptions {
export interface RequestHandlerOptions {
once?: boolean
}

Expand All @@ -84,6 +87,7 @@ export abstract class RequestHandler<
HandlerInfo extends RequestHandlerDefaultInfo = RequestHandlerDefaultInfo,
ParsedResult extends Record<string, any> | undefined = any,
ResolverExtras extends Record<string, unknown> = any,
HandlerOptions extends RequestHandlerOptions = RequestHandlerOptions,
> {
public info: HandlerInfo & RequestHandlerInternalInfo
/**
Expand All @@ -99,16 +103,16 @@ export abstract class RequestHandler<
MaybeAsyncResponseResolverReturnType<any>
>
private resolverGeneratorResult?: Response | StrictResponse<any>
private once: boolean
private options?: HandlerOptions

constructor(options: RequestHandlerOptions<HandlerInfo>) {
this.resolver = options.resolver
this.once = options.once || false
constructor(args: RequestHandlerArgs<HandlerInfo, HandlerOptions>) {
this.resolver = args.resolver
this.options = args.options

const callFrame = getCallFrame(new Error())

this.info = {
...options.info,
...args.info,
callFrame,
}

Expand Down Expand Up @@ -173,7 +177,7 @@ export abstract class RequestHandler<
request: StrictRequest<any>,
resolutionContext?: ResponseResolutionContext,
): Promise<RequestHandlerExecutionResult<ParsedResult> | null> {
if (this.isUsed && this.once) {
if (this.isUsed && this.options?.once) {
return null
}

Expand Down
4 changes: 2 additions & 2 deletions src/core/http.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {
DefaultBodyType,
RequestHandlerPublicOptions,
RequestHandlerOptions,
ResponseResolver,
} from './handlers/RequestHandler'
import {
Expand All @@ -24,7 +24,7 @@ function createHttpHandler<Method extends HttpMethods | RegExp>(
RequestBodyType,
ResponseBodyType
>,
options: RequestHandlerPublicOptions = {},
options: RequestHandlerOptions = {},
) => {
return new HttpHandler(method, path, resolver, options)
}
Expand Down
1 change: 0 additions & 1 deletion src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ export * from './HttpResponse'
export * from './delay'
export { bypass } from './bypass'
export { passthrough } from './passthrough'
export { NetworkError } from './NetworkError'

// Validate environmental globals before executing any code.
// This ensures that the library gives user-friendly errors
Expand Down
33 changes: 1 addition & 32 deletions src/mockServiceWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ self.addEventListener('message', async function (event) {

self.addEventListener('fetch', function (event) {
const { request } = event
const accept = request.headers.get('accept') || ''

// Bypass navigation requests.
if (request.mode === 'navigate') {
Expand All @@ -109,28 +108,7 @@ self.addEventListener('fetch', function (event) {

// Generate unique request ID.
const requestId = Math.random().toString(16).slice(2)

event.respondWith(
handleRequest(event, requestId).catch((error) => {
if (error.name === 'NetworkError') {
console.warn(
'[MSW] Successfully emulated a network error for the "%s %s" request.',
request.method,
request.url,
)
return
}

// At this point, any exception indicates an issue with the original request/response.
console.error(
`\
[MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`,
request.method,
request.url,
`${error.name}: ${error.message}`,
)
}),
)
event.respondWith(handleRequest(event, requestId))
})

async function handleRequest(event, requestId) {
Expand Down Expand Up @@ -270,15 +248,6 @@ async function getResponse(event, client, requestId) {
case 'MOCK_NOT_FOUND': {
return passthrough()
}

case 'NETWORK_ERROR': {
const { name, message } = clientMessage.data
const networkError = new Error(message)
networkError.name = name

// Rejecting a "respondWith" promise emulates a network error.
throw networkError
}
}

return passthrough()
Expand Down

0 comments on commit f1ff76a

Please sign in to comment.