msw 2.5.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/index.d.mts +7 -6
- package/lib/browser/index.d.ts +7 -6
- package/lib/browser/index.js +29 -1
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/index.mjs +29 -1
- package/lib/browser/index.mjs.map +1 -1
- package/lib/core/{GraphQLHandler-ClMB0BOy.d.mts → GraphQLHandler-Doool6Q_.d.mts} +1 -1
- package/lib/core/{GraphQLHandler-D6mLMXGZ.d.ts → GraphQLHandler-udzgBRPf.d.ts} +1 -1
- package/lib/core/{HttpResponse-vn-Pb4Bi.d.mts → HttpResponse-BLGmJolh.d.mts} +1 -1
- package/lib/core/{HttpResponse-DaYkf3ml.d.ts → HttpResponse-Cgbkdkje.d.ts} +1 -1
- package/lib/core/HttpResponse.d.mts +1 -1
- package/lib/core/HttpResponse.d.ts +1 -1
- package/lib/core/SetupApi.d.mts +15 -12
- package/lib/core/SetupApi.d.ts +15 -12
- package/lib/core/SetupApi.js +3 -1
- package/lib/core/SetupApi.js.map +1 -1
- package/lib/core/SetupApi.mjs +3 -1
- package/lib/core/SetupApi.mjs.map +1 -1
- package/lib/core/getResponse.d.mts +1 -1
- package/lib/core/getResponse.d.ts +1 -1
- package/lib/core/graphql.d.mts +2 -2
- package/lib/core/graphql.d.ts +2 -2
- package/lib/core/handlers/GraphQLHandler.d.mts +2 -2
- package/lib/core/handlers/GraphQLHandler.d.ts +2 -2
- package/lib/core/handlers/HttpHandler.d.mts +1 -1
- package/lib/core/handlers/HttpHandler.d.ts +1 -1
- package/lib/core/handlers/RequestHandler.d.mts +1 -1
- package/lib/core/handlers/RequestHandler.d.ts +1 -1
- package/lib/core/handlers/WebSocketHandler.d.mts +33 -0
- package/lib/core/handlers/WebSocketHandler.d.ts +33 -0
- package/lib/core/handlers/WebSocketHandler.js +120 -0
- package/lib/core/handlers/WebSocketHandler.js.map +1 -0
- package/lib/core/handlers/WebSocketHandler.mjs +102 -0
- package/lib/core/handlers/WebSocketHandler.mjs.map +1 -0
- package/lib/core/http.d.mts +1 -1
- package/lib/core/http.d.ts +1 -1
- package/lib/core/index.d.mts +5 -2
- package/lib/core/index.d.ts +5 -2
- package/lib/core/index.js +5 -1
- package/lib/core/index.js.map +1 -1
- package/lib/core/index.mjs +7 -1
- package/lib/core/index.mjs.map +1 -1
- package/lib/core/passthrough.d.mts +1 -1
- package/lib/core/passthrough.d.ts +1 -1
- package/lib/core/utils/HttpResponse/decorators.d.mts +1 -1
- package/lib/core/utils/HttpResponse/decorators.d.ts +1 -1
- package/lib/core/utils/executeHandlers.d.mts +1 -1
- package/lib/core/utils/executeHandlers.d.ts +1 -1
- package/lib/core/utils/executeHandlers.js +4 -0
- package/lib/core/utils/executeHandlers.js.map +1 -1
- package/lib/core/utils/executeHandlers.mjs +6 -0
- package/lib/core/utils/executeHandlers.mjs.map +1 -1
- package/lib/core/utils/handleRequest.d.mts +2 -2
- package/lib/core/utils/handleRequest.d.ts +2 -2
- package/lib/core/utils/handleRequest.js.map +1 -1
- package/lib/core/utils/handleRequest.mjs.map +1 -1
- package/lib/core/utils/internal/parseGraphQLRequest.d.mts +2 -2
- package/lib/core/utils/internal/parseGraphQLRequest.d.ts +2 -2
- package/lib/core/utils/internal/parseMultipartData.d.mts +1 -1
- package/lib/core/utils/internal/parseMultipartData.d.ts +1 -1
- package/lib/core/utils/internal/requestHandlerUtils.d.mts +1 -1
- package/lib/core/utils/internal/requestHandlerUtils.d.ts +1 -1
- package/lib/core/utils/logging/getTimestamp.d.mts +4 -1
- package/lib/core/utils/logging/getTimestamp.d.ts +4 -1
- package/lib/core/utils/logging/getTimestamp.js +6 -2
- package/lib/core/utils/logging/getTimestamp.js.map +1 -1
- package/lib/core/utils/logging/getTimestamp.mjs +6 -2
- package/lib/core/utils/logging/getTimestamp.mjs.map +1 -1
- package/lib/core/utils/matching/matchRequestUrl.d.mts +2 -1
- package/lib/core/utils/matching/matchRequestUrl.d.ts +2 -1
- package/lib/core/utils/matching/matchRequestUrl.js +4 -0
- package/lib/core/utils/matching/matchRequestUrl.js.map +1 -1
- package/lib/core/utils/matching/matchRequestUrl.mjs +4 -0
- package/lib/core/utils/matching/matchRequestUrl.mjs.map +1 -1
- package/lib/core/ws/WebSocketClientManager.d.mts +63 -0
- package/lib/core/ws/WebSocketClientManager.d.ts +63 -0
- package/lib/core/ws/WebSocketClientManager.js +149 -0
- package/lib/core/ws/WebSocketClientManager.js.map +1 -0
- package/lib/core/ws/WebSocketClientManager.mjs +129 -0
- package/lib/core/ws/WebSocketClientManager.mjs.map +1 -0
- package/lib/core/ws/WebSocketClientStore.d.mts +13 -0
- package/lib/core/ws/WebSocketClientStore.d.ts +13 -0
- package/lib/core/ws/WebSocketClientStore.js +26 -0
- package/lib/core/ws/WebSocketClientStore.js.map +1 -0
- package/lib/core/ws/WebSocketClientStore.mjs +6 -0
- package/lib/core/ws/WebSocketClientStore.mjs.map +1 -0
- package/lib/core/ws/WebSocketIndexedDBClientStore.d.mts +15 -0
- package/lib/core/ws/WebSocketIndexedDBClientStore.d.ts +15 -0
- package/lib/core/ws/WebSocketIndexedDBClientStore.js +130 -0
- package/lib/core/ws/WebSocketIndexedDBClientStore.js.map +1 -0
- package/lib/core/ws/WebSocketIndexedDBClientStore.mjs +110 -0
- package/lib/core/ws/WebSocketIndexedDBClientStore.mjs.map +1 -0
- package/lib/core/ws/WebSocketMemoryClientStore.d.mts +13 -0
- package/lib/core/ws/WebSocketMemoryClientStore.d.ts +13 -0
- package/lib/core/ws/WebSocketMemoryClientStore.js +41 -0
- package/lib/core/ws/WebSocketMemoryClientStore.js.map +1 -0
- package/lib/core/ws/WebSocketMemoryClientStore.mjs +21 -0
- package/lib/core/ws/WebSocketMemoryClientStore.mjs.map +1 -0
- package/lib/core/ws/handleWebSocketEvent.d.mts +19 -0
- package/lib/core/ws/handleWebSocketEvent.d.ts +19 -0
- package/lib/core/ws/handleWebSocketEvent.js +73 -0
- package/lib/core/ws/handleWebSocketEvent.js.map +1 -0
- package/lib/core/ws/handleWebSocketEvent.mjs +55 -0
- package/lib/core/ws/handleWebSocketEvent.mjs.map +1 -0
- package/lib/core/ws/utils/attachWebSocketLogger.d.mts +12 -0
- package/lib/core/ws/utils/attachWebSocketLogger.d.ts +12 -0
- package/lib/core/ws/utils/attachWebSocketLogger.js +198 -0
- package/lib/core/ws/utils/attachWebSocketLogger.js.map +1 -0
- package/lib/core/ws/utils/attachWebSocketLogger.mjs +178 -0
- package/lib/core/ws/utils/attachWebSocketLogger.mjs.map +1 -0
- package/lib/core/ws/utils/getMessageLength.d.mts +11 -0
- package/lib/core/ws/utils/getMessageLength.d.ts +11 -0
- package/lib/core/ws/utils/getMessageLength.js +33 -0
- package/lib/core/ws/utils/getMessageLength.js.map +1 -0
- package/lib/core/ws/utils/getMessageLength.mjs +13 -0
- package/lib/core/ws/utils/getMessageLength.mjs.map +1 -0
- package/lib/core/ws/utils/getPublicData.d.mts +5 -0
- package/lib/core/ws/utils/getPublicData.d.ts +5 -0
- package/lib/core/ws/utils/getPublicData.js +36 -0
- package/lib/core/ws/utils/getPublicData.js.map +1 -0
- package/lib/core/ws/utils/getPublicData.mjs +16 -0
- package/lib/core/ws/utils/getPublicData.mjs.map +1 -0
- package/lib/core/ws/utils/truncateMessage.d.mts +3 -0
- package/lib/core/ws/utils/truncateMessage.d.ts +3 -0
- package/lib/core/ws/utils/truncateMessage.js +31 -0
- package/lib/core/ws/utils/truncateMessage.js.map +1 -0
- package/lib/core/ws/utils/truncateMessage.mjs +11 -0
- package/lib/core/ws/utils/truncateMessage.mjs.map +1 -0
- package/lib/core/ws/webSocketInterceptor.d.mts +5 -0
- package/lib/core/ws/webSocketInterceptor.d.ts +5 -0
- package/lib/core/ws/webSocketInterceptor.js +26 -0
- package/lib/core/ws/webSocketInterceptor.js.map +1 -0
- package/lib/core/ws/webSocketInterceptor.mjs +6 -0
- package/lib/core/ws/webSocketInterceptor.mjs.map +1 -0
- package/lib/core/ws.d.mts +75 -0
- package/lib/core/ws.d.ts +75 -0
- package/lib/core/ws.js +71 -0
- package/lib/core/ws.js.map +1 -0
- package/lib/core/ws.mjs +54 -0
- package/lib/core/ws.mjs.map +1 -0
- package/lib/iife/index.js +1413 -85
- package/lib/iife/index.js.map +1 -1
- package/lib/mockServiceWorker.js +1 -1
- package/lib/native/index.d.mts +6 -5
- package/lib/native/index.d.ts +6 -5
- package/lib/native/index.js +22 -4
- package/lib/native/index.js.map +1 -1
- package/lib/native/index.mjs +22 -4
- package/lib/native/index.mjs.map +1 -1
- package/lib/node/index.d.mts +8 -7
- package/lib/node/index.d.ts +8 -7
- package/lib/node/index.js +22 -4
- package/lib/node/index.js.map +1 -1
- package/lib/node/index.mjs +22 -4
- package/lib/node/index.mjs.map +1 -1
- package/package.json +10 -1
- package/src/browser/setupWorker/glossary.ts +10 -10
- package/src/browser/setupWorker/setupWorker.ts +32 -3
- package/src/browser/setupWorker/start/createRequestListener.ts +7 -1
- package/src/browser/setupWorker/start/createStartHandler.ts +5 -0
- package/src/browser/setupWorker/stop/createStop.ts +6 -0
- package/src/core/SetupApi.ts +28 -20
- package/src/core/handlers/WebSocketHandler.ts +142 -0
- package/src/core/index.ts +11 -1
- package/src/core/utils/executeHandlers.ts +6 -2
- package/src/core/utils/handleRequest.ts +1 -1
- package/src/core/utils/logging/getTimestamp.test.ts +20 -6
- package/src/core/utils/logging/getTimestamp.ts +11 -6
- package/src/core/utils/matching/matchRequestUrl.test.ts +44 -0
- package/src/core/utils/matching/matchRequestUrl.ts +4 -0
- package/src/core/ws/WebSocketClientManager.test.ts +164 -0
- package/src/core/ws/WebSocketClientManager.ts +211 -0
- package/src/core/ws/WebSocketClientStore.ts +14 -0
- package/src/core/ws/WebSocketIndexedDBClientStore.ts +145 -0
- package/src/core/ws/WebSocketMemoryClientStore.ts +27 -0
- package/src/core/ws/handleWebSocketEvent.ts +82 -0
- package/src/core/ws/utils/attachWebSocketLogger.ts +259 -0
- package/src/core/ws/utils/getMessageLength.test.ts +16 -0
- package/src/core/ws/utils/getMessageLength.ts +19 -0
- package/src/core/ws/utils/getPublicData.test.ts +38 -0
- package/src/core/ws/utils/getPublicData.ts +17 -0
- package/src/core/ws/utils/truncateMessage.test.ts +12 -0
- package/src/core/ws/utils/truncateMessage.ts +9 -0
- package/src/core/ws/webSocketInterceptor.ts +3 -0
- package/src/core/ws.test.ts +23 -0
- package/src/core/ws.ts +166 -0
- package/src/node/SetupServerApi.ts +8 -7
- package/src/node/SetupServerCommonApi.ts +29 -5
- package/src/node/glossary.ts +5 -7
- package/src/node/setupServer.ts +2 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "msw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.0",
|
|
4
4
|
"description": "Seamless REST/GraphQL API mocking library for browser and Node.js.",
|
|
5
5
|
"main": "./lib/core/index.js",
|
|
6
6
|
"module": "./lib/core/index.mjs",
|
|
@@ -58,6 +58,12 @@
|
|
|
58
58
|
"import": "./lib/core/graphql.mjs",
|
|
59
59
|
"default": "./lib/core/graphql.js"
|
|
60
60
|
},
|
|
61
|
+
"./core/ws": {
|
|
62
|
+
"types": "./lib/core/ws.d.ts",
|
|
63
|
+
"require": "./lib/core/ws.js",
|
|
64
|
+
"import": "./lib/core/ws.mjs",
|
|
65
|
+
"default": "./lib/core/ws.js"
|
|
66
|
+
},
|
|
61
67
|
"./mockServiceWorker.js": "./lib/mockServiceWorker.js",
|
|
62
68
|
"./package.json": "./package.json"
|
|
63
69
|
},
|
|
@@ -118,6 +124,7 @@
|
|
|
118
124
|
"@bundled-es-modules/tough-cookie": "^0.1.6",
|
|
119
125
|
"@inquirer/confirm": "^5.0.0",
|
|
120
126
|
"@mswjs/interceptors": "^0.36.5",
|
|
127
|
+
"@open-draft/deferred-promise": "^2.2.0",
|
|
121
128
|
"@open-draft/until": "^2.1.0",
|
|
122
129
|
"@types/cookie": "^0.6.0",
|
|
123
130
|
"@types/statuses": "^2.0.4",
|
|
@@ -134,6 +141,7 @@
|
|
|
134
141
|
"devDependencies": {
|
|
135
142
|
"@commitlint/cli": "^18.4.4",
|
|
136
143
|
"@commitlint/config-conventional": "^18.4.4",
|
|
144
|
+
"@fastify/websocket": "^8.3.1",
|
|
137
145
|
"@open-draft/test-server": "^0.4.2",
|
|
138
146
|
"@ossjs/release": "^0.8.1",
|
|
139
147
|
"@playwright/test": "^1.48.0",
|
|
@@ -157,6 +165,7 @@
|
|
|
157
165
|
"eslint-config-prettier": "^9.1.0",
|
|
158
166
|
"eslint-plugin-prettier": "^5.2.1",
|
|
159
167
|
"express": "^5.0.0",
|
|
168
|
+
"fastify": "^4.26.0",
|
|
160
169
|
"fs-extra": "^11.2.0",
|
|
161
170
|
"fs-teardown": "^0.3.0",
|
|
162
171
|
"glob": "^11.0.0",
|
|
@@ -5,13 +5,11 @@ import {
|
|
|
5
5
|
SharedOptions,
|
|
6
6
|
} from '~/core/sharedOptions'
|
|
7
7
|
import { ServiceWorkerMessage } from './start/utils/createMessageChannel'
|
|
8
|
-
import {
|
|
9
|
-
RequestHandler,
|
|
10
|
-
RequestHandlerDefaultInfo,
|
|
11
|
-
} from '~/core/handlers/RequestHandler'
|
|
8
|
+
import { RequestHandler } from '~/core/handlers/RequestHandler'
|
|
12
9
|
import type { HttpRequestEventMap, Interceptor } from '@mswjs/interceptors'
|
|
13
|
-
import { Path } from '~/core/utils/matching/matchRequestUrl'
|
|
14
|
-
import { RequiredDeep } from '~/core/typeUtils'
|
|
10
|
+
import type { Path } from '~/core/utils/matching/matchRequestUrl'
|
|
11
|
+
import type { RequiredDeep } from '~/core/typeUtils'
|
|
12
|
+
import type { WebSocketHandler } from '~/core/handlers/WebSocketHandler'
|
|
15
13
|
|
|
16
14
|
export type ResolvedPath = Path | URL
|
|
17
15
|
|
|
@@ -92,7 +90,7 @@ export interface SetupWorkerInternalContext {
|
|
|
92
90
|
startOptions: RequiredDeep<StartOptions>
|
|
93
91
|
worker: ServiceWorker | null
|
|
94
92
|
registration: ServiceWorkerRegistration | null
|
|
95
|
-
getRequestHandlers(): Array<RequestHandler>
|
|
93
|
+
getRequestHandlers(): Array<RequestHandler | WebSocketHandler>
|
|
96
94
|
requests: Map<string, Request>
|
|
97
95
|
emitter: Emitter<LifeCycleEventsMap>
|
|
98
96
|
keepAliveInterval?: number
|
|
@@ -216,7 +214,7 @@ export interface SetupWorker {
|
|
|
216
214
|
*
|
|
217
215
|
* @see {@link https://mswjs.io/docs/api/setup-worker/use `worker.use()` API reference}
|
|
218
216
|
*/
|
|
219
|
-
use: (...handlers: RequestHandler
|
|
217
|
+
use: (...handlers: Array<RequestHandler | WebSocketHandler>) => void
|
|
220
218
|
|
|
221
219
|
/**
|
|
222
220
|
* Marks all request handlers that respond using `res.once()` as unused.
|
|
@@ -231,14 +229,16 @@ export interface SetupWorker {
|
|
|
231
229
|
*
|
|
232
230
|
* @see {@link https://mswjs.io/docs/api/setup-worker/reset-handlers `worker.resetHandlers()` API reference}
|
|
233
231
|
*/
|
|
234
|
-
resetHandlers: (
|
|
232
|
+
resetHandlers: (
|
|
233
|
+
...nextHandlers: Array<RequestHandler | WebSocketHandler>
|
|
234
|
+
) => void
|
|
235
235
|
|
|
236
236
|
/**
|
|
237
237
|
* Returns a readonly list of currently active request handlers.
|
|
238
238
|
*
|
|
239
239
|
* @see {@link https://mswjs.io/docs/api/setup-worker/list-handlers `worker.listHandlers()` API reference}
|
|
240
240
|
*/
|
|
241
|
-
listHandlers(): ReadonlyArray<RequestHandler
|
|
241
|
+
listHandlers(): ReadonlyArray<RequestHandler | WebSocketHandler>
|
|
242
242
|
|
|
243
243
|
/**
|
|
244
244
|
* Life-cycle events.
|
|
@@ -18,9 +18,13 @@ import { createFallbackStop } from './stop/createFallbackStop'
|
|
|
18
18
|
import { devUtils } from '~/core/utils/internal/devUtils'
|
|
19
19
|
import { SetupApi } from '~/core/SetupApi'
|
|
20
20
|
import { mergeRight } from '~/core/utils/internal/mergeRight'
|
|
21
|
-
import { LifeCycleEventsMap } from '~/core/sharedOptions'
|
|
21
|
+
import type { LifeCycleEventsMap } from '~/core/sharedOptions'
|
|
22
|
+
import type { WebSocketHandler } from '~/core/handlers/WebSocketHandler'
|
|
22
23
|
import { SetupWorker } from './glossary'
|
|
23
24
|
import { supportsReadableStreamTransfer } from '../utils/supportsReadableStreamTransfer'
|
|
25
|
+
import { webSocketInterceptor } from '~/core/ws/webSocketInterceptor'
|
|
26
|
+
import { handleWebSocketEvent } from '~/core/ws/handleWebSocketEvent'
|
|
27
|
+
import { attachWebSocketLogger } from '~/core/ws/utils/attachWebSocketLogger'
|
|
24
28
|
|
|
25
29
|
interface Listener {
|
|
26
30
|
target: EventTarget
|
|
@@ -37,7 +41,7 @@ export class SetupWorkerApi
|
|
|
37
41
|
private stopHandler: StopHandler = null as any
|
|
38
42
|
private listeners: Array<Listener>
|
|
39
43
|
|
|
40
|
-
constructor(...handlers: Array<RequestHandler>) {
|
|
44
|
+
constructor(...handlers: Array<RequestHandler | WebSocketHandler>) {
|
|
41
45
|
super(...handlers)
|
|
42
46
|
|
|
43
47
|
invariant(
|
|
@@ -176,6 +180,29 @@ export class SetupWorkerApi
|
|
|
176
180
|
options,
|
|
177
181
|
) as SetupWorkerInternalContext['startOptions']
|
|
178
182
|
|
|
183
|
+
// Enable the WebSocket interception.
|
|
184
|
+
handleWebSocketEvent({
|
|
185
|
+
getUnhandledRequestStrategy: () => {
|
|
186
|
+
return this.context.startOptions.onUnhandledRequest
|
|
187
|
+
},
|
|
188
|
+
getHandlers: () => {
|
|
189
|
+
return this.handlersController.currentHandlers()
|
|
190
|
+
},
|
|
191
|
+
onMockedConnection: (connection) => {
|
|
192
|
+
if (!this.context.startOptions.quiet) {
|
|
193
|
+
// Attach the logger for mocked connections since
|
|
194
|
+
// those won't be visible in the browser's devtools.
|
|
195
|
+
attachWebSocketLogger(connection)
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
onPassthroughConnection() {},
|
|
199
|
+
})
|
|
200
|
+
webSocketInterceptor.apply()
|
|
201
|
+
|
|
202
|
+
this.subscriptions.push(() => {
|
|
203
|
+
webSocketInterceptor.dispose()
|
|
204
|
+
})
|
|
205
|
+
|
|
179
206
|
return await this.startHandler(this.context.startOptions, options)
|
|
180
207
|
}
|
|
181
208
|
|
|
@@ -193,6 +220,8 @@ export class SetupWorkerApi
|
|
|
193
220
|
*
|
|
194
221
|
* @see {@link https://mswjs.io/docs/api/setup-worker `setupWorker()` API reference}
|
|
195
222
|
*/
|
|
196
|
-
export function setupWorker(
|
|
223
|
+
export function setupWorker(
|
|
224
|
+
...handlers: Array<RequestHandler | WebSocketHandler>
|
|
225
|
+
): SetupWorker {
|
|
197
226
|
return new SetupWorkerApi(...handlers)
|
|
198
227
|
}
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
} from './utils/createMessageChannel'
|
|
10
10
|
import { parseWorkerRequest } from '../../utils/parseWorkerRequest'
|
|
11
11
|
import { RequestHandler } from '~/core/handlers/RequestHandler'
|
|
12
|
+
import { HttpHandler } from '~/core/handlers/HttpHandler'
|
|
13
|
+
import { GraphQLHandler } from '~/core/handlers/GraphQLHandler'
|
|
12
14
|
import { handleRequest } from '~/core/utils/handleRequest'
|
|
13
15
|
import { RequiredDeep } from '~/core/typeUtils'
|
|
14
16
|
import { devUtils } from '~/core/utils/internal/devUtils'
|
|
@@ -43,7 +45,11 @@ export const createRequestListener = (
|
|
|
43
45
|
await handleRequest(
|
|
44
46
|
request,
|
|
45
47
|
requestId,
|
|
46
|
-
context.getRequestHandlers()
|
|
48
|
+
context.getRequestHandlers().filter((handler) => {
|
|
49
|
+
return (
|
|
50
|
+
handler instanceof HttpHandler || handler instanceof GraphQLHandler
|
|
51
|
+
)
|
|
52
|
+
}),
|
|
47
53
|
options,
|
|
48
54
|
context.emitter,
|
|
49
55
|
{
|
|
@@ -71,6 +71,11 @@ Please consider using a custom "serviceWorker.url" option to point to the actual
|
|
|
71
71
|
// Make sure we're always clearing the interval - there are reports that not doing this can
|
|
72
72
|
// cause memory leaks in headless browser environments.
|
|
73
73
|
window.clearInterval(context.keepAliveInterval)
|
|
74
|
+
|
|
75
|
+
// Notify others about this client disconnecting.
|
|
76
|
+
// E.g. this will purge the in-memory WebSocket clients since
|
|
77
|
+
// starting the worker again will assign them new IDs.
|
|
78
|
+
window.postMessage({ type: 'msw/worker:stop' })
|
|
74
79
|
})
|
|
75
80
|
|
|
76
81
|
// Check if the active Service Worker has been generated
|
|
@@ -24,6 +24,12 @@ export const createStop = (
|
|
|
24
24
|
context.isMockingEnabled = false
|
|
25
25
|
window.clearInterval(context.keepAliveInterval)
|
|
26
26
|
|
|
27
|
+
// Post the internal stop message on the window
|
|
28
|
+
// to let any logic know when the worker has stopped.
|
|
29
|
+
// E.g. the WebSocket client manager needs this to know
|
|
30
|
+
// when to clear its in-memory clients list.
|
|
31
|
+
window.postMessage({ type: 'msw/worker:stop' })
|
|
32
|
+
|
|
27
33
|
printStopMessage({ quiet: context.startOptions?.quiet })
|
|
28
34
|
}
|
|
29
35
|
}
|
package/src/core/SetupApi.ts
CHANGED
|
@@ -1,38 +1,42 @@
|
|
|
1
1
|
import { invariant } from 'outvariant'
|
|
2
2
|
import { EventMap, Emitter } from 'strict-event-emitter'
|
|
3
|
-
import {
|
|
4
|
-
RequestHandler,
|
|
5
|
-
RequestHandlerDefaultInfo,
|
|
6
|
-
} from './handlers/RequestHandler'
|
|
3
|
+
import { RequestHandler } from './handlers/RequestHandler'
|
|
7
4
|
import { LifeCycleEventEmitter } from './sharedOptions'
|
|
8
5
|
import { devUtils } from './utils/internal/devUtils'
|
|
9
6
|
import { pipeEvents } from './utils/internal/pipeEvents'
|
|
10
7
|
import { toReadonlyArray } from './utils/internal/toReadonlyArray'
|
|
11
8
|
import { Disposable } from './utils/internal/Disposable'
|
|
9
|
+
import type { WebSocketHandler } from './handlers/WebSocketHandler'
|
|
12
10
|
|
|
13
11
|
export abstract class HandlersController {
|
|
14
|
-
abstract prepend(
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
abstract prepend(
|
|
13
|
+
runtimeHandlers: Array<RequestHandler | WebSocketHandler>,
|
|
14
|
+
): void
|
|
15
|
+
abstract reset(nextHandles: Array<RequestHandler | WebSocketHandler>): void
|
|
16
|
+
abstract currentHandlers(): Array<RequestHandler | WebSocketHandler>
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export class InMemoryHandlersController implements HandlersController {
|
|
20
|
-
private handlers: Array<RequestHandler>
|
|
20
|
+
private handlers: Array<RequestHandler | WebSocketHandler>
|
|
21
21
|
|
|
22
|
-
constructor(
|
|
22
|
+
constructor(
|
|
23
|
+
private initialHandlers: Array<RequestHandler | WebSocketHandler>,
|
|
24
|
+
) {
|
|
23
25
|
this.handlers = [...initialHandlers]
|
|
24
26
|
}
|
|
25
27
|
|
|
26
|
-
public prepend(
|
|
28
|
+
public prepend(
|
|
29
|
+
runtimeHandles: Array<RequestHandler | WebSocketHandler>,
|
|
30
|
+
): void {
|
|
27
31
|
this.handlers.unshift(...runtimeHandles)
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
public reset(nextHandlers: Array<RequestHandler>): void {
|
|
34
|
+
public reset(nextHandlers: Array<RequestHandler | WebSocketHandler>): void {
|
|
31
35
|
this.handlers =
|
|
32
36
|
nextHandlers.length > 0 ? [...nextHandlers] : [...this.initialHandlers]
|
|
33
37
|
}
|
|
34
38
|
|
|
35
|
-
public currentHandlers(): Array<RequestHandler> {
|
|
39
|
+
public currentHandlers(): Array<RequestHandler | WebSocketHandler> {
|
|
36
40
|
return this.handlers
|
|
37
41
|
}
|
|
38
42
|
}
|
|
@@ -47,7 +51,7 @@ export abstract class SetupApi<EventsMap extends EventMap> extends Disposable {
|
|
|
47
51
|
|
|
48
52
|
public readonly events: LifeCycleEventEmitter<EventsMap>
|
|
49
53
|
|
|
50
|
-
constructor(...initialHandlers: Array<RequestHandler>) {
|
|
54
|
+
constructor(...initialHandlers: Array<RequestHandler | WebSocketHandler>) {
|
|
51
55
|
super()
|
|
52
56
|
|
|
53
57
|
invariant(
|
|
@@ -71,12 +75,14 @@ export abstract class SetupApi<EventsMap extends EventMap> extends Disposable {
|
|
|
71
75
|
})
|
|
72
76
|
}
|
|
73
77
|
|
|
74
|
-
private validateHandlers(handlers: ReadonlyArray<
|
|
78
|
+
private validateHandlers(handlers: ReadonlyArray<unknown>): boolean {
|
|
75
79
|
// Guard against incorrect call signature of the setup API.
|
|
76
80
|
return handlers.every((handler) => !Array.isArray(handler))
|
|
77
81
|
}
|
|
78
82
|
|
|
79
|
-
public use(
|
|
83
|
+
public use(
|
|
84
|
+
...runtimeHandlers: Array<RequestHandler | WebSocketHandler>
|
|
85
|
+
): void {
|
|
80
86
|
invariant(
|
|
81
87
|
this.validateHandlers(runtimeHandlers),
|
|
82
88
|
devUtils.formatMessage(
|
|
@@ -89,17 +95,19 @@ export abstract class SetupApi<EventsMap extends EventMap> extends Disposable {
|
|
|
89
95
|
|
|
90
96
|
public restoreHandlers(): void {
|
|
91
97
|
this.handlersController.currentHandlers().forEach((handler) => {
|
|
92
|
-
|
|
98
|
+
if ('isUsed' in handler) {
|
|
99
|
+
handler.isUsed = false
|
|
100
|
+
}
|
|
93
101
|
})
|
|
94
102
|
}
|
|
95
103
|
|
|
96
|
-
public resetHandlers(
|
|
104
|
+
public resetHandlers(
|
|
105
|
+
...nextHandlers: Array<RequestHandler | WebSocketHandler>
|
|
106
|
+
): void {
|
|
97
107
|
this.handlersController.reset(nextHandlers)
|
|
98
108
|
}
|
|
99
109
|
|
|
100
|
-
public listHandlers(): ReadonlyArray<
|
|
101
|
-
RequestHandler<RequestHandlerDefaultInfo, any, any>
|
|
102
|
-
> {
|
|
110
|
+
public listHandlers(): ReadonlyArray<RequestHandler | WebSocketHandler> {
|
|
103
111
|
return toReadonlyArray(this.handlersController.currentHandlers())
|
|
104
112
|
}
|
|
105
113
|
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Emitter } from 'strict-event-emitter'
|
|
2
|
+
import { createRequestId } from '@mswjs/interceptors'
|
|
3
|
+
import type { WebSocketConnectionData } from '@mswjs/interceptors/WebSocket'
|
|
4
|
+
import {
|
|
5
|
+
type Match,
|
|
6
|
+
type Path,
|
|
7
|
+
type PathParams,
|
|
8
|
+
matchRequestUrl,
|
|
9
|
+
} from '../utils/matching/matchRequestUrl'
|
|
10
|
+
import { getCallFrame } from '../utils/internal/getCallFrame'
|
|
11
|
+
|
|
12
|
+
type WebSocketHandlerParsedResult = {
|
|
13
|
+
match: Match
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type WebSocketHandlerEventMap = {
|
|
17
|
+
connection: [args: WebSocketHandlerConnection]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface WebSocketHandlerConnection extends WebSocketConnectionData {
|
|
21
|
+
params: PathParams
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const kEmitter = Symbol('kEmitter')
|
|
25
|
+
export const kDispatchEvent = Symbol('kDispatchEvent')
|
|
26
|
+
export const kSender = Symbol('kSender')
|
|
27
|
+
const kStopPropagationPatched = Symbol('kStopPropagationPatched')
|
|
28
|
+
const KOnStopPropagation = Symbol('KOnStopPropagation')
|
|
29
|
+
|
|
30
|
+
export class WebSocketHandler {
|
|
31
|
+
public id: string
|
|
32
|
+
public callFrame?: string
|
|
33
|
+
|
|
34
|
+
protected [kEmitter]: Emitter<WebSocketHandlerEventMap>
|
|
35
|
+
|
|
36
|
+
constructor(private readonly url: Path) {
|
|
37
|
+
this.id = createRequestId()
|
|
38
|
+
|
|
39
|
+
this[kEmitter] = new Emitter()
|
|
40
|
+
this.callFrame = getCallFrame(new Error())
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public parse(args: {
|
|
44
|
+
event: MessageEvent<WebSocketConnectionData>
|
|
45
|
+
}): WebSocketHandlerParsedResult {
|
|
46
|
+
const connection = args.event.data
|
|
47
|
+
const match = matchRequestUrl(connection.client.url, this.url)
|
|
48
|
+
|
|
49
|
+
return {
|
|
50
|
+
match,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public predicate(args: {
|
|
55
|
+
event: MessageEvent<WebSocketConnectionData>
|
|
56
|
+
parsedResult: WebSocketHandlerParsedResult
|
|
57
|
+
}): boolean {
|
|
58
|
+
return args.parsedResult.match.matches
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async [kDispatchEvent](
|
|
62
|
+
event: MessageEvent<WebSocketConnectionData>,
|
|
63
|
+
): Promise<void> {
|
|
64
|
+
const parsedResult = this.parse({ event })
|
|
65
|
+
const connection = event.data
|
|
66
|
+
|
|
67
|
+
const resolvedConnection: WebSocketHandlerConnection = {
|
|
68
|
+
...connection,
|
|
69
|
+
params: parsedResult.match.params || {},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Support `event.stopPropagation()` for various client/server events.
|
|
73
|
+
connection.client.addEventListener(
|
|
74
|
+
'message',
|
|
75
|
+
createStopPropagationListener(this),
|
|
76
|
+
)
|
|
77
|
+
connection.client.addEventListener(
|
|
78
|
+
'close',
|
|
79
|
+
createStopPropagationListener(this),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
connection.server.addEventListener(
|
|
83
|
+
'open',
|
|
84
|
+
createStopPropagationListener(this),
|
|
85
|
+
)
|
|
86
|
+
connection.server.addEventListener(
|
|
87
|
+
'message',
|
|
88
|
+
createStopPropagationListener(this),
|
|
89
|
+
)
|
|
90
|
+
connection.server.addEventListener(
|
|
91
|
+
'error',
|
|
92
|
+
createStopPropagationListener(this),
|
|
93
|
+
)
|
|
94
|
+
connection.server.addEventListener(
|
|
95
|
+
'close',
|
|
96
|
+
createStopPropagationListener(this),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
// Emit the connection event on the handler.
|
|
100
|
+
// This is what the developer adds listeners for.
|
|
101
|
+
this[kEmitter].emit('connection', resolvedConnection)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function createStopPropagationListener(handler: WebSocketHandler) {
|
|
106
|
+
return function stopPropagationListener(event: Event) {
|
|
107
|
+
const propagationStoppedAt = Reflect.get(event, 'kPropagationStoppedAt') as
|
|
108
|
+
| string
|
|
109
|
+
| undefined
|
|
110
|
+
|
|
111
|
+
if (propagationStoppedAt && handler.id !== propagationStoppedAt) {
|
|
112
|
+
event.stopImmediatePropagation()
|
|
113
|
+
return
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
Object.defineProperty(event, KOnStopPropagation, {
|
|
117
|
+
value(this: WebSocketHandler) {
|
|
118
|
+
Object.defineProperty(event, 'kPropagationStoppedAt', {
|
|
119
|
+
value: handler.id,
|
|
120
|
+
})
|
|
121
|
+
},
|
|
122
|
+
configurable: true,
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
// Since the same event instance is shared between all client/server objects,
|
|
126
|
+
// make sure to patch its `stopPropagation` method only once.
|
|
127
|
+
if (!Reflect.get(event, kStopPropagationPatched)) {
|
|
128
|
+
event.stopPropagation = new Proxy(event.stopPropagation, {
|
|
129
|
+
apply: (target, thisArg, args) => {
|
|
130
|
+
Reflect.get(event, KOnStopPropagation)?.call(handler)
|
|
131
|
+
return Reflect.apply(target, thisArg, args)
|
|
132
|
+
},
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
Object.defineProperty(event, kStopPropagationPatched, {
|
|
136
|
+
value: true,
|
|
137
|
+
// If something else attempts to redefine this, throw.
|
|
138
|
+
configurable: false,
|
|
139
|
+
})
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
package/src/core/index.ts
CHANGED
|
@@ -2,13 +2,21 @@ import { checkGlobals } from './utils/internal/checkGlobals'
|
|
|
2
2
|
|
|
3
3
|
export { SetupApi } from './SetupApi'
|
|
4
4
|
|
|
5
|
-
/*
|
|
5
|
+
/* HTTP handlers */
|
|
6
6
|
export { RequestHandler } from './handlers/RequestHandler'
|
|
7
7
|
export { http } from './http'
|
|
8
8
|
export { HttpHandler, HttpMethods } from './handlers/HttpHandler'
|
|
9
9
|
export { graphql } from './graphql'
|
|
10
10
|
export { GraphQLHandler } from './handlers/GraphQLHandler'
|
|
11
11
|
|
|
12
|
+
/* WebSocket handler */
|
|
13
|
+
export { ws, type WebSocketLink } from './ws'
|
|
14
|
+
export {
|
|
15
|
+
WebSocketHandler,
|
|
16
|
+
type WebSocketHandlerEventMap,
|
|
17
|
+
type WebSocketHandlerConnection,
|
|
18
|
+
} from './handlers/WebSocketHandler'
|
|
19
|
+
|
|
12
20
|
/* Utils */
|
|
13
21
|
export { matchRequestUrl } from './utils/matching/matchRequestUrl'
|
|
14
22
|
export * from './utils/handleRequest'
|
|
@@ -45,6 +53,8 @@ export type {
|
|
|
45
53
|
} from './handlers/GraphQLHandler'
|
|
46
54
|
export type { GraphQLRequestHandler, GraphQLResponseResolver } from './graphql'
|
|
47
55
|
|
|
56
|
+
export type { WebSocketData, WebSocketEventListener } from './ws'
|
|
57
|
+
|
|
48
58
|
export type { Path, PathParams, Match } from './utils/matching/matchRequestUrl'
|
|
49
59
|
export type { ParsedGraphQLRequest } from './utils/internal/parseGraphQLRequest'
|
|
50
60
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
RequestHandler,
|
|
3
|
-
RequestHandlerExecutionResult,
|
|
3
|
+
type RequestHandlerExecutionResult,
|
|
4
4
|
} from '../handlers/RequestHandler'
|
|
5
5
|
|
|
6
6
|
export interface HandlersExecutionResult {
|
|
@@ -18,7 +18,7 @@ export interface ResponseResolutionContext {
|
|
|
18
18
|
* Returns the execution result object containing any matching request
|
|
19
19
|
* handler and any mocked response it returned.
|
|
20
20
|
*/
|
|
21
|
-
export const executeHandlers = async <Handlers extends Array<
|
|
21
|
+
export const executeHandlers = async <Handlers extends Array<unknown>>({
|
|
22
22
|
request,
|
|
23
23
|
requestId,
|
|
24
24
|
handlers,
|
|
@@ -33,6 +33,10 @@ export const executeHandlers = async <Handlers extends Array<RequestHandler>>({
|
|
|
33
33
|
let result: RequestHandlerExecutionResult<any> | null = null
|
|
34
34
|
|
|
35
35
|
for (const handler of handlers) {
|
|
36
|
+
if (!(handler instanceof RequestHandler)) {
|
|
37
|
+
continue
|
|
38
|
+
}
|
|
39
|
+
|
|
36
40
|
result = await handler.run({ request, requestId, resolutionContext })
|
|
37
41
|
|
|
38
42
|
// If the handler produces some result for this request,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { until } from '@open-draft/until'
|
|
2
2
|
import { Emitter } from 'strict-event-emitter'
|
|
3
|
-
import { RequestHandler } from '../handlers/RequestHandler'
|
|
4
3
|
import { LifeCycleEventsMap, SharedOptions } from '../sharedOptions'
|
|
5
4
|
import { RequiredDeep } from '../typeUtils'
|
|
5
|
+
import type { RequestHandler } from '../handlers/RequestHandler'
|
|
6
6
|
import { HandlersExecutionResult, executeHandlers } from './executeHandlers'
|
|
7
7
|
import { onUnhandledRequest } from './request/onUnhandledRequest'
|
|
8
8
|
import { storeResponseCookies } from './request/storeResponseCookies'
|
|
@@ -1,18 +1,32 @@
|
|
|
1
1
|
import { getTimestamp } from './getTimestamp'
|
|
2
2
|
|
|
3
3
|
beforeAll(() => {
|
|
4
|
-
|
|
5
|
-
// to always produce a predictable value for testing purposes.
|
|
6
|
-
vi.spyOn(global.Date.prototype, 'getHours').mockImplementation(() => 12)
|
|
7
|
-
vi.spyOn(global.Date.prototype, 'getMinutes').mockImplementation(() => 4)
|
|
8
|
-
vi.spyOn(global.Date.prototype, 'getSeconds').mockImplementation(() => 8)
|
|
4
|
+
vi.useFakeTimers()
|
|
9
5
|
})
|
|
10
6
|
|
|
11
7
|
afterAll(() => {
|
|
12
|
-
vi.
|
|
8
|
+
vi.useRealTimers()
|
|
13
9
|
})
|
|
14
10
|
|
|
15
11
|
test('returns a timestamp string of the invocation time', () => {
|
|
12
|
+
vi.setSystemTime(new Date('2024-01-01 12:4:8'))
|
|
16
13
|
const timestamp = getTimestamp()
|
|
17
14
|
expect(timestamp).toBe('12:04:08')
|
|
18
15
|
})
|
|
16
|
+
|
|
17
|
+
test('returns a timestamp with milliseconds', () => {
|
|
18
|
+
vi.setSystemTime(new Date('2024-01-01 12:4:8'))
|
|
19
|
+
expect(getTimestamp({ milliseconds: true })).toBe('12:04:08.000')
|
|
20
|
+
|
|
21
|
+
vi.setSystemTime(new Date('2024-01-01 12:4:8.000'))
|
|
22
|
+
expect(getTimestamp({ milliseconds: true })).toBe('12:04:08.000')
|
|
23
|
+
|
|
24
|
+
vi.setSystemTime(new Date('2024-01-01 12:4:8.4'))
|
|
25
|
+
expect(getTimestamp({ milliseconds: true })).toBe('12:04:08.400')
|
|
26
|
+
|
|
27
|
+
vi.setSystemTime(new Date('2024-01-01 12:4:8.123'))
|
|
28
|
+
expect(getTimestamp({ milliseconds: true })).toBe('12:04:08.123')
|
|
29
|
+
|
|
30
|
+
vi.setSystemTime(new Date('2024-01-01 12:00:00'))
|
|
31
|
+
expect(getTimestamp({ milliseconds: true })).toBe('12:00:00.000')
|
|
32
|
+
})
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
interface GetTimestampOptions {
|
|
2
|
+
milliseconds?: boolean
|
|
3
|
+
}
|
|
4
|
+
|
|
1
5
|
/**
|
|
2
6
|
* Returns a timestamp string in a "HH:MM:SS" format.
|
|
3
7
|
*/
|
|
4
|
-
export function getTimestamp(): string {
|
|
8
|
+
export function getTimestamp(options?: GetTimestampOptions): string {
|
|
5
9
|
const now = new Date()
|
|
10
|
+
const timestamp = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`
|
|
11
|
+
|
|
12
|
+
if (options?.milliseconds) {
|
|
13
|
+
return `${timestamp}.${now.getMilliseconds().toString().padStart(3, '0')}`
|
|
14
|
+
}
|
|
6
15
|
|
|
7
|
-
return
|
|
8
|
-
.map(String)
|
|
9
|
-
.map((chunk) => chunk.slice(0, 2))
|
|
10
|
-
.map((chunk) => chunk.padStart(2, '0'))
|
|
11
|
-
.join(':')
|
|
16
|
+
return timestamp
|
|
12
17
|
}
|
|
@@ -72,6 +72,50 @@ describe('matchRequestUrl', () => {
|
|
|
72
72
|
userId: undefined,
|
|
73
73
|
})
|
|
74
74
|
})
|
|
75
|
+
|
|
76
|
+
test('returns true for matching WebSocket URL', () => {
|
|
77
|
+
expect(
|
|
78
|
+
matchRequestUrl(new URL('ws://test.mswjs.io'), 'ws://test.mswjs.io'),
|
|
79
|
+
).toEqual({
|
|
80
|
+
matches: true,
|
|
81
|
+
params: {},
|
|
82
|
+
})
|
|
83
|
+
expect(
|
|
84
|
+
matchRequestUrl(new URL('wss://test.mswjs.io'), 'wss://test.mswjs.io'),
|
|
85
|
+
).toEqual({
|
|
86
|
+
matches: true,
|
|
87
|
+
params: {},
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('returns false for non-matching WebSocket URL', () => {
|
|
92
|
+
expect(
|
|
93
|
+
matchRequestUrl(new URL('ws://test.mswjs.io'), 'ws://foo.mswjs.io'),
|
|
94
|
+
).toEqual({
|
|
95
|
+
matches: false,
|
|
96
|
+
params: {},
|
|
97
|
+
})
|
|
98
|
+
expect(
|
|
99
|
+
matchRequestUrl(new URL('wss://test.mswjs.io'), 'wss://completely.diff'),
|
|
100
|
+
).toEqual({
|
|
101
|
+
matches: false,
|
|
102
|
+
params: {},
|
|
103
|
+
})
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
test('returns path parameters when matched a WebSocket URL', () => {
|
|
107
|
+
expect(
|
|
108
|
+
matchRequestUrl(
|
|
109
|
+
new URL('wss://test.mswjs.io'),
|
|
110
|
+
'wss://:service.mswjs.io',
|
|
111
|
+
),
|
|
112
|
+
).toEqual({
|
|
113
|
+
matches: true,
|
|
114
|
+
params: {
|
|
115
|
+
service: 'test',
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
})
|
|
75
119
|
})
|
|
76
120
|
|
|
77
121
|
describe('coercePath', () => {
|