msw-fetch-mock 0.2.1 → 0.3.1
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/README.md +112 -6
- package/dist/browser.cjs +43 -0
- package/dist/browser.d.cts +15 -0
- package/dist/browser.d.ts +15 -0
- package/dist/browser.js +43 -0
- package/dist/chunk-43CWIVXO.cjs +540 -0
- package/dist/chunk-A56K5DPD.cjs +58 -0
- package/dist/chunk-ATXIIOZA.js +540 -0
- package/dist/chunk-D26SJTTI.js +27 -0
- package/dist/chunk-HYRDB2FH.cjs +27 -0
- package/dist/chunk-KSDUQENI.js +58 -0
- package/dist/fetch-mock-BNke4Oik.d.cts +199 -0
- package/dist/fetch-mock-BNke4Oik.d.ts +199 -0
- package/dist/index.cjs +19 -0
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -8
- package/dist/index.js +19 -5
- package/dist/legacy.cjs +71 -0
- package/dist/legacy.d.cts +38 -0
- package/dist/legacy.d.ts +38 -0
- package/dist/legacy.js +71 -0
- package/dist/node.cjs +19 -0
- package/dist/node.d.cts +18 -0
- package/dist/node.d.ts +18 -0
- package/dist/node.js +19 -0
- package/docs/api.md +175 -27
- package/docs/cloudflare-migration.md +4 -2
- package/docs/msw-v1-legacy.md +94 -0
- package/package.json +43 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/mock-call-history.d.ts +0 -65
- package/dist/mock-call-history.d.ts.map +0 -1
- package/dist/mock-call-history.js +0 -140
- package/dist/mock-server.d.ts +0 -85
- package/dist/mock-server.d.ts.map +0 -1
- package/dist/mock-server.js +0 -304
package/dist/node.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
var _chunkA56K5DPDcjs = require('./chunk-A56K5DPD.cjs');
|
|
6
|
+
require('./chunk-HYRDB2FH.cjs');
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
var _chunk43CWIVXOcjs = require('./chunk-43CWIVXO.cjs');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
exports.FetchMock = _chunk43CWIVXOcjs.FetchMock; exports.MockCallHistory = _chunk43CWIVXOcjs.MockCallHistory; exports.MockCallHistoryLog = _chunk43CWIVXOcjs.MockCallHistoryLog; exports.NodeMswAdapter = _chunkA56K5DPDcjs.NodeMswAdapter; exports.createFetchMock = _chunkA56K5DPDcjs.createFetchMock; exports.fetchMock = _chunkA56K5DPDcjs.fetchMock;
|
package/dist/node.d.cts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { M as MswAdapter, i as SetupServerLike, R as ResolvedActivateOptions, F as FetchMock } from './fetch-mock-BNke4Oik.cjs';
|
|
2
|
+
export { A as ActivateOptions, C as CallHistoryFilterCriteria, H as HandlerFactory, I as InterceptOptions, a as MockCallHistory, b as MockCallHistoryLog, c as MockCallHistoryLogData, d as MockInterceptor, e as MockPool, f as MockReplyChain, O as OnUnhandledRequest, P as PendingInterceptor, j as ReplyCallback, g as ReplyOptions, k as SingleReplyCallback, l as SingleReplyResult } from './fetch-mock-BNke4Oik.cjs';
|
|
3
|
+
|
|
4
|
+
declare class NodeMswAdapter implements MswAdapter {
|
|
5
|
+
private server;
|
|
6
|
+
private readonly ownsServer;
|
|
7
|
+
constructor(externalServer?: SetupServerLike);
|
|
8
|
+
use(...handlers: Array<unknown>): void;
|
|
9
|
+
resetHandlers(...handlers: Array<unknown>): void;
|
|
10
|
+
activate(options: ResolvedActivateOptions): void;
|
|
11
|
+
deactivate(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare function createFetchMock(server?: SetupServerLike): FetchMock;
|
|
15
|
+
/** Pre-built singleton for quick standalone use (Cloudflare migration compatible). */
|
|
16
|
+
declare const fetchMock: FetchMock;
|
|
17
|
+
|
|
18
|
+
export { FetchMock, MswAdapter, NodeMswAdapter, SetupServerLike, createFetchMock, fetchMock };
|
package/dist/node.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { M as MswAdapter, i as SetupServerLike, R as ResolvedActivateOptions, F as FetchMock } from './fetch-mock-BNke4Oik.js';
|
|
2
|
+
export { A as ActivateOptions, C as CallHistoryFilterCriteria, H as HandlerFactory, I as InterceptOptions, a as MockCallHistory, b as MockCallHistoryLog, c as MockCallHistoryLogData, d as MockInterceptor, e as MockPool, f as MockReplyChain, O as OnUnhandledRequest, P as PendingInterceptor, j as ReplyCallback, g as ReplyOptions, k as SingleReplyCallback, l as SingleReplyResult } from './fetch-mock-BNke4Oik.js';
|
|
3
|
+
|
|
4
|
+
declare class NodeMswAdapter implements MswAdapter {
|
|
5
|
+
private server;
|
|
6
|
+
private readonly ownsServer;
|
|
7
|
+
constructor(externalServer?: SetupServerLike);
|
|
8
|
+
use(...handlers: Array<unknown>): void;
|
|
9
|
+
resetHandlers(...handlers: Array<unknown>): void;
|
|
10
|
+
activate(options: ResolvedActivateOptions): void;
|
|
11
|
+
deactivate(): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare function createFetchMock(server?: SetupServerLike): FetchMock;
|
|
15
|
+
/** Pre-built singleton for quick standalone use (Cloudflare migration compatible). */
|
|
16
|
+
declare const fetchMock: FetchMock;
|
|
17
|
+
|
|
18
|
+
export { FetchMock, MswAdapter, NodeMswAdapter, SetupServerLike, createFetchMock, fetchMock };
|
package/dist/node.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NodeMswAdapter,
|
|
3
|
+
createFetchMock,
|
|
4
|
+
fetchMock
|
|
5
|
+
} from "./chunk-KSDUQENI.js";
|
|
6
|
+
import "./chunk-D26SJTTI.js";
|
|
7
|
+
import {
|
|
8
|
+
FetchMock,
|
|
9
|
+
MockCallHistory,
|
|
10
|
+
MockCallHistoryLog
|
|
11
|
+
} from "./chunk-ATXIIOZA.js";
|
|
12
|
+
export {
|
|
13
|
+
FetchMock,
|
|
14
|
+
MockCallHistory,
|
|
15
|
+
MockCallHistoryLog,
|
|
16
|
+
NodeMswAdapter,
|
|
17
|
+
createFetchMock,
|
|
18
|
+
fetchMock
|
|
19
|
+
};
|
package/docs/api.md
CHANGED
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
# API Reference
|
|
2
2
|
|
|
3
|
+
## Import Paths
|
|
4
|
+
|
|
5
|
+
| Path | Environment | MSW version |
|
|
6
|
+
| ------------------------ | ---------------------------- | ----------- |
|
|
7
|
+
| `msw-fetch-mock` | Node.js (re-exports `/node`) | v2 |
|
|
8
|
+
| `msw-fetch-mock/node` | Node.js | v2 |
|
|
9
|
+
| `msw-fetch-mock/browser` | Browser | v2 |
|
|
10
|
+
| `msw-fetch-mock/legacy` | Node.js (MSW v1) | v1 |
|
|
11
|
+
|
|
3
12
|
## `fetchMock` (singleton)
|
|
4
13
|
|
|
5
|
-
A pre-built `FetchMock` instance for standalone use. No setup required — just import and call `activate()`.
|
|
14
|
+
A pre-built `FetchMock` instance for standalone Node.js use. No setup required — just import and call `activate()`.
|
|
6
15
|
|
|
7
16
|
```typescript
|
|
8
17
|
import { fetchMock } from 'msw-fetch-mock';
|
|
9
18
|
|
|
10
|
-
beforeAll(() =>
|
|
19
|
+
beforeAll(async () => {
|
|
20
|
+
await fetchMock.activate({ onUnhandledRequest: 'error' });
|
|
21
|
+
});
|
|
11
22
|
afterAll(() => fetchMock.deactivate());
|
|
12
23
|
afterEach(() => {
|
|
13
24
|
fetchMock.assertNoPendingInterceptors();
|
|
@@ -15,27 +26,70 @@ afterEach(() => {
|
|
|
15
26
|
});
|
|
16
27
|
```
|
|
17
28
|
|
|
18
|
-
## `
|
|
29
|
+
## `createFetchMock(server?)` (Node)
|
|
19
30
|
|
|
20
|
-
Creates a `FetchMock`
|
|
31
|
+
Creates a `FetchMock` with `NodeMswAdapter`. Optionally pass an existing MSW server.
|
|
21
32
|
|
|
22
33
|
```typescript
|
|
23
|
-
import {
|
|
34
|
+
import { createFetchMock } from 'msw-fetch-mock/node';
|
|
35
|
+
import { setupServer } from 'msw/node';
|
|
24
36
|
|
|
25
|
-
// Standalone
|
|
26
|
-
const fetchMock =
|
|
37
|
+
// Standalone
|
|
38
|
+
const fetchMock = createFetchMock();
|
|
27
39
|
|
|
28
40
|
// With external MSW server
|
|
41
|
+
const server = setupServer();
|
|
42
|
+
const fetchMock = createFetchMock(server);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## `createFetchMock(worker)` (Browser)
|
|
46
|
+
|
|
47
|
+
Creates a `FetchMock` with `BrowserMswAdapter`. Requires an MSW worker.
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import { setupWorker } from 'msw/browser';
|
|
51
|
+
import { createFetchMock } from 'msw-fetch-mock/browser';
|
|
52
|
+
|
|
53
|
+
const worker = setupWorker();
|
|
54
|
+
const fetchMock = createFetchMock(worker);
|
|
55
|
+
|
|
56
|
+
beforeAll(async () => {
|
|
57
|
+
await fetchMock.activate({ onUnhandledRequest: 'error' });
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## `createFetchMock(rest, server?)` (Legacy)
|
|
62
|
+
|
|
63
|
+
Creates a `FetchMock` for MSW v1 environments. See [MSW v1 Legacy Guide](msw-v1-legacy.md).
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { rest } from 'msw';
|
|
29
67
|
import { setupServer } from 'msw/node';
|
|
68
|
+
import { createFetchMock } from 'msw-fetch-mock/legacy';
|
|
69
|
+
|
|
30
70
|
const server = setupServer();
|
|
31
|
-
const fetchMock =
|
|
71
|
+
const fetchMock = createFetchMock(rest, server);
|
|
32
72
|
```
|
|
33
73
|
|
|
34
|
-
|
|
35
|
-
| --------- | ------------- | -------- | ------------------------------------------------------- |
|
|
36
|
-
| `server` | `SetupServer` | No | Existing MSW server. Creates one internally if omitted. |
|
|
74
|
+
## `new FetchMock(adapter?)`
|
|
37
75
|
|
|
38
|
-
|
|
76
|
+
Creates a `FetchMock` instance with an explicit `MswAdapter`.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { FetchMock } from 'msw-fetch-mock';
|
|
80
|
+
import { NodeMswAdapter } from 'msw-fetch-mock/node';
|
|
81
|
+
import { BrowserMswAdapter } from 'msw-fetch-mock/browser';
|
|
82
|
+
|
|
83
|
+
// Node with external server
|
|
84
|
+
const fetchMock = new FetchMock(new NodeMswAdapter(server));
|
|
85
|
+
|
|
86
|
+
// Browser with worker
|
|
87
|
+
const fetchMock = new FetchMock(new BrowserMswAdapter(worker));
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
| Parameter | Type | Required | Description |
|
|
91
|
+
| --------- | ------------ | -------- | ----------------------------------------------------- |
|
|
92
|
+
| `adapter` | `MswAdapter` | No | Environment adapter. Use `createFetchMock()` instead. |
|
|
39
93
|
|
|
40
94
|
---
|
|
41
95
|
|
|
@@ -44,13 +98,13 @@ const fetchMock = new FetchMock(server);
|
|
|
44
98
|
### Lifecycle
|
|
45
99
|
|
|
46
100
|
```typescript
|
|
47
|
-
fetchMock.activate(options?); // start intercepting (
|
|
48
|
-
fetchMock.deactivate();
|
|
101
|
+
await fetchMock.activate(options?); // start intercepting (async — browser needs worker.start())
|
|
102
|
+
fetchMock.deactivate(); // stop intercepting
|
|
49
103
|
```
|
|
50
104
|
|
|
51
|
-
>
|
|
105
|
+
> `activate()` returns `Promise<void>`. In Node.js, the promise resolves synchronously. In the browser, it waits for the Service Worker to start. Vitest and Jest handle async `beforeAll` natively.
|
|
52
106
|
>
|
|
53
|
-
> **Conflict detection:** In standalone mode, `activate()` checks whether `globalThis.fetch` is already patched by MSW. If so, it throws an error guiding you to
|
|
107
|
+
> **Conflict detection (Node only):** In standalone mode, `activate()` checks whether `globalThis.fetch` is already patched by MSW. If so, it throws an error guiding you to use `new FetchMock(new NodeMswAdapter(server))` instead.
|
|
54
108
|
|
|
55
109
|
#### `ActivateOptions`
|
|
56
110
|
|
|
@@ -69,11 +123,11 @@ fetchMock.deactivate(); // stop intercepting (calls server.close())
|
|
|
69
123
|
|
|
70
124
|
```typescript
|
|
71
125
|
// Default — reject unmatched requests
|
|
72
|
-
fetchMock.activate();
|
|
73
|
-
fetchMock.activate({ onUnhandledRequest: 'error' });
|
|
126
|
+
await fetchMock.activate();
|
|
127
|
+
await fetchMock.activate({ onUnhandledRequest: 'error' });
|
|
74
128
|
|
|
75
129
|
// Custom callback
|
|
76
|
-
fetchMock.activate({
|
|
130
|
+
await fetchMock.activate({
|
|
77
131
|
onUnhandledRequest: (request, print) => {
|
|
78
132
|
if (new URL(request.url).pathname === '/health') return;
|
|
79
133
|
print.error();
|
|
@@ -102,12 +156,23 @@ fetchMock.calls.clear();
|
|
|
102
156
|
|
|
103
157
|
### `fetchMock.get(origin)`
|
|
104
158
|
|
|
105
|
-
Returns a `MockPool` scoped to the given origin.
|
|
159
|
+
Returns a `MockPool` scoped to the given origin. The `origin` parameter accepts three forms:
|
|
106
160
|
|
|
107
161
|
```typescript
|
|
162
|
+
// String — exact origin match
|
|
108
163
|
const pool = fetchMock.get('https://api.example.com');
|
|
164
|
+
|
|
165
|
+
// RegExp — match against URL origin
|
|
166
|
+
const pool = fetchMock.get(/\.example\.com$/);
|
|
167
|
+
|
|
168
|
+
// Function — custom origin predicate
|
|
169
|
+
const pool = fetchMock.get((origin) => origin.startsWith('https://'));
|
|
109
170
|
```
|
|
110
171
|
|
|
172
|
+
| Parameter | Type | Required | Description |
|
|
173
|
+
| --------- | ------------------------------------------------- | -------- | -------------- |
|
|
174
|
+
| `origin` | `string \| RegExp \| (origin: string) => boolean` | Yes | Origin matcher |
|
|
175
|
+
|
|
111
176
|
### `fetchMock.disableNetConnect()`
|
|
112
177
|
|
|
113
178
|
Prevents any real network requests. Unmatched requests will throw.
|
|
@@ -134,6 +199,44 @@ fetchMock.enableNetConnect((host) => host.endsWith('.test'));
|
|
|
134
199
|
| --------- | ----------------------------------------------- | -------- | ------------------------------------ |
|
|
135
200
|
| `matcher` | `string \| RegExp \| (host: string) => boolean` | No | Host matcher. Allows all if omitted. |
|
|
136
201
|
|
|
202
|
+
### `fetchMock.defaultReplyHeaders(headers)`
|
|
203
|
+
|
|
204
|
+
Sets default headers that are included in every response. Per-reply headers merge with and override defaults.
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
fetchMock.defaultReplyHeaders({
|
|
208
|
+
'x-request-id': 'test-123',
|
|
209
|
+
'cache-control': 'no-store',
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// This reply will have both x-request-id and content-type
|
|
213
|
+
fetchMock
|
|
214
|
+
.get('https://api.example.com')
|
|
215
|
+
.intercept({ path: '/data' })
|
|
216
|
+
.reply(200, { ok: true }, { headers: { 'content-type': 'application/json' } });
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
> `reset()` clears default reply headers.
|
|
220
|
+
|
|
221
|
+
| Parameter | Type | Required | Description |
|
|
222
|
+
| --------- | ------------------------ | -------- | ------------------------------------ |
|
|
223
|
+
| `headers` | `Record<string, string>` | Yes | Headers to include in every response |
|
|
224
|
+
|
|
225
|
+
### `fetchMock.enableCallHistory()`
|
|
226
|
+
|
|
227
|
+
Enables call history recording. This is the default state.
|
|
228
|
+
|
|
229
|
+
### `fetchMock.disableCallHistory()`
|
|
230
|
+
|
|
231
|
+
Disables call history recording. Requests are still intercepted and replied to, but not recorded. Useful when you want to reduce overhead in performance-sensitive tests.
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
fetchMock.disableCallHistory();
|
|
235
|
+
// ... requests are intercepted but not recorded
|
|
236
|
+
fetchMock.enableCallHistory();
|
|
237
|
+
// ... requests are now recorded again
|
|
238
|
+
```
|
|
239
|
+
|
|
137
240
|
### `fetchMock.getCallHistory()`
|
|
138
241
|
|
|
139
242
|
Returns the `MockCallHistory` instance. Cloudflare-compatible alias for `fetchMock.calls`.
|
|
@@ -142,6 +245,10 @@ Returns the `MockCallHistory` instance. Cloudflare-compatible alias for `fetchMo
|
|
|
142
245
|
|
|
143
246
|
Clears all recorded calls. Cloudflare-compatible alias for `fetchMock.calls.clear()`.
|
|
144
247
|
|
|
248
|
+
### `fetchMock.clearAllCallHistory()`
|
|
249
|
+
|
|
250
|
+
Alias for `clearCallHistory()`.
|
|
251
|
+
|
|
145
252
|
### `fetchMock.assertNoPendingInterceptors()`
|
|
146
253
|
|
|
147
254
|
Throws an error if any registered interceptor has not been consumed. This is a **pure assertion** — it does not clear call history, interceptors, or handlers. Use `reset()` to clean up state.
|
|
@@ -155,7 +262,7 @@ afterEach(() => {
|
|
|
155
262
|
|
|
156
263
|
### `fetchMock.reset()`
|
|
157
264
|
|
|
158
|
-
Clears all interceptors, call history, and MSW handlers. Resets the instance to a clean state without stopping the server. Use in `afterEach` after asserting no pending interceptors.
|
|
265
|
+
Clears all interceptors, call history, default reply headers, and MSW handlers. Resets the instance to a clean state without stopping the server. Use in `afterEach` after asserting no pending interceptors.
|
|
159
266
|
|
|
160
267
|
```typescript
|
|
161
268
|
afterEach(() => {
|
|
@@ -262,7 +369,7 @@ Returns: `MockInterceptor`
|
|
|
262
369
|
|
|
263
370
|
### `interceptor.reply(status, body?, options?)`
|
|
264
371
|
|
|
265
|
-
Defines the mock response.
|
|
372
|
+
Defines the mock response with a static body.
|
|
266
373
|
|
|
267
374
|
```typescript
|
|
268
375
|
// Static body
|
|
@@ -270,7 +377,13 @@ Defines the mock response.
|
|
|
270
377
|
|
|
271
378
|
// With response headers
|
|
272
379
|
.reply(200, { users: [] }, { headers: { 'x-request-id': '123' } })
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
### `interceptor.reply(status, callback)`
|
|
383
|
+
|
|
384
|
+
Defines the mock response with a dynamic body callback.
|
|
273
385
|
|
|
386
|
+
```typescript
|
|
274
387
|
// Callback (receives request info)
|
|
275
388
|
.reply(200, (req) => {
|
|
276
389
|
const input = JSON.parse(req.body!);
|
|
@@ -278,13 +391,39 @@ Defines the mock response.
|
|
|
278
391
|
})
|
|
279
392
|
```
|
|
280
393
|
|
|
394
|
+
### `interceptor.reply(callback)`
|
|
395
|
+
|
|
396
|
+
Single callback form — full control over status code, body, and response options.
|
|
397
|
+
|
|
398
|
+
```typescript
|
|
399
|
+
.reply((req) => {
|
|
400
|
+
const data = JSON.parse(req.body!);
|
|
401
|
+
return {
|
|
402
|
+
statusCode: 201,
|
|
403
|
+
data: { id: '1', ...data },
|
|
404
|
+
responseOptions: { headers: { 'x-created': 'true' } },
|
|
405
|
+
};
|
|
406
|
+
})
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
The callback receives `{ body: string | null }` and must return (or resolve to):
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
interface SingleReplyResult {
|
|
413
|
+
statusCode: number;
|
|
414
|
+
data: unknown;
|
|
415
|
+
responseOptions?: { headers?: Record<string, string> };
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
281
419
|
Returns: `MockReplyChain`
|
|
282
420
|
|
|
283
|
-
| Parameter
|
|
284
|
-
|
|
|
285
|
-
| `status`
|
|
286
|
-
| `body`
|
|
287
|
-
| `
|
|
421
|
+
| Parameter | Type | Description |
|
|
422
|
+
| ---------- | -------------------------------------- | ------------------------- |
|
|
423
|
+
| `status` | `number` | HTTP status code |
|
|
424
|
+
| `body` | `unknown \| (req) => unknown` | Response body or callback |
|
|
425
|
+
| `callback` | `SingleReplyCallback` | Full-control callback |
|
|
426
|
+
| `options` | `{ headers?: Record<string, string> }` | Response headers |
|
|
288
427
|
|
|
289
428
|
### `interceptor.replyWithError(error)`
|
|
290
429
|
|
|
@@ -328,6 +467,15 @@ Adds a delay before the response is sent.
|
|
|
328
467
|
.reply(200, { ok: true }).delay(500)
|
|
329
468
|
```
|
|
330
469
|
|
|
470
|
+
### `chain.replyContentLength()`
|
|
471
|
+
|
|
472
|
+
Automatically adds a `Content-Length` header to the response based on the JSON-serialized body size.
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
.reply(200, { ok: true }).replyContentLength()
|
|
476
|
+
// Response will include Content-Length: 13
|
|
477
|
+
```
|
|
478
|
+
|
|
331
479
|
---
|
|
332
480
|
|
|
333
481
|
## `MockCallHistory`
|
|
@@ -7,7 +7,7 @@ If you're migrating tests from Cloudflare Workers' `cloudflare:test` to a standa
|
|
|
7
7
|
| cloudflare:test | msw-fetch-mock |
|
|
8
8
|
| -------------------------------------------------- | ----------------------------------------------------------- |
|
|
9
9
|
| `import { fetchMock } from 'cloudflare:test'` | `import { fetchMock } from 'msw-fetch-mock'` |
|
|
10
|
-
| `fetchMock.activate()` | `fetchMock.activate()`
|
|
10
|
+
| `fetchMock.activate()` | `await fetchMock.activate()` |
|
|
11
11
|
| `fetchMock.disableNetConnect()` | `fetchMock.disableNetConnect()` |
|
|
12
12
|
| `fetchMock.enableNetConnect(matcher?)` | `fetchMock.enableNetConnect(matcher?)` |
|
|
13
13
|
| `fetchMock.deactivate()` | `fetchMock.deactivate()` |
|
|
@@ -46,7 +46,9 @@ it('calls API', async () => {
|
|
|
46
46
|
```typescript
|
|
47
47
|
import { fetchMock } from 'msw-fetch-mock';
|
|
48
48
|
|
|
49
|
-
beforeAll(() =>
|
|
49
|
+
beforeAll(async () => {
|
|
50
|
+
await fetchMock.activate();
|
|
51
|
+
});
|
|
50
52
|
afterAll(() => fetchMock.deactivate());
|
|
51
53
|
afterEach(() => {
|
|
52
54
|
fetchMock.assertNoPendingInterceptors();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# MSW v1 Legacy Guide
|
|
2
|
+
|
|
3
|
+
If you're using MSW v1 (`msw@^1.x`), use the `msw-fetch-mock/legacy` subpath export. The API is the same as the v2 version — only the setup differs.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -D msw-fetch-mock msw@^1
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { rest } from 'msw';
|
|
15
|
+
import { setupServer } from 'msw/node';
|
|
16
|
+
import { createFetchMock } from 'msw-fetch-mock/legacy';
|
|
17
|
+
|
|
18
|
+
const server = setupServer();
|
|
19
|
+
const fetchMock = createFetchMock(rest, server);
|
|
20
|
+
|
|
21
|
+
beforeAll(() => fetchMock.activate());
|
|
22
|
+
afterAll(() => fetchMock.deactivate());
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
fetchMock.assertNoPendingInterceptors();
|
|
25
|
+
fetchMock.reset();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('mocks a GET request', async () => {
|
|
29
|
+
fetchMock
|
|
30
|
+
.get('https://api.example.com')
|
|
31
|
+
.intercept({ path: '/users', method: 'GET' })
|
|
32
|
+
.reply(200, { users: [{ id: '1', name: 'Alice' }] });
|
|
33
|
+
|
|
34
|
+
const res = await fetch('https://api.example.com/users');
|
|
35
|
+
const data = await res.json();
|
|
36
|
+
|
|
37
|
+
expect(data.users).toHaveLength(1);
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## API Comparison
|
|
42
|
+
|
|
43
|
+
| MSW v2 (`msw-fetch-mock`) | MSW v1 (`msw-fetch-mock/legacy`) |
|
|
44
|
+
| -------------------------------------------- | ----------------------------------------------- |
|
|
45
|
+
| `import { fetchMock } from 'msw-fetch-mock'` | N/A (no singleton — must use `createFetchMock`) |
|
|
46
|
+
| `import { createFetchMock } from '.../node'` | `import { createFetchMock } from '.../legacy'` |
|
|
47
|
+
| `createFetchMock(server?)` | `createFetchMock(rest, server?)` |
|
|
48
|
+
| `import { http } from 'msw'` | `import { rest } from 'msw'` |
|
|
49
|
+
|
|
50
|
+
## Key Differences
|
|
51
|
+
|
|
52
|
+
| Aspect | MSW v2 | MSW v1 (Legacy) |
|
|
53
|
+
| ----------------- | ----------------------------------------- | ------------------------------------- |
|
|
54
|
+
| Import | `msw-fetch-mock` or `msw-fetch-mock/node` | `msw-fetch-mock/legacy` |
|
|
55
|
+
| Factory | `createFetchMock(server?)` | `createFetchMock(rest, server?)` |
|
|
56
|
+
| Singleton | `fetchMock` pre-built instance | Not available — use `createFetchMock` |
|
|
57
|
+
| Handler internals | Uses `http.*` + `HttpResponse` | Uses `rest.*` + `(req, res, ctx)` |
|
|
58
|
+
| MSW version | `msw@^2.12.7` | `msw@^1.0.0` |
|
|
59
|
+
|
|
60
|
+
## Exports
|
|
61
|
+
|
|
62
|
+
The `msw-fetch-mock/legacy` subpath exports:
|
|
63
|
+
|
|
64
|
+
| Export | Type | Description |
|
|
65
|
+
| -------------------------------- | -------- | ------------------------------------------------- |
|
|
66
|
+
| `createFetchMock(rest, server?)` | Function | Create a FetchMock instance for MSW v1 |
|
|
67
|
+
| `FetchMock` | Class | Core mock class (same as v2) |
|
|
68
|
+
| `createLegacyHandlerFactory` | Function | Low-level: create a v1-compatible handler factory |
|
|
69
|
+
| `LegacyRestApi` | Type | Type for MSW v1's `rest` object |
|
|
70
|
+
|
|
71
|
+
## Migrating from MSW v1 to v2
|
|
72
|
+
|
|
73
|
+
When you upgrade MSW from v1 to v2, update your imports:
|
|
74
|
+
|
|
75
|
+
```diff
|
|
76
|
+
- import { rest } from 'msw';
|
|
77
|
+
- import { createFetchMock } from 'msw-fetch-mock/legacy';
|
|
78
|
+
- const fetchMock = createFetchMock(rest, server);
|
|
79
|
+
+ import { createFetchMock } from 'msw-fetch-mock/node';
|
|
80
|
+
+ const fetchMock = createFetchMock(server);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Or use the singleton for standalone mode:
|
|
84
|
+
|
|
85
|
+
```diff
|
|
86
|
+
- import { rest } from 'msw';
|
|
87
|
+
- import { setupServer } from 'msw/node';
|
|
88
|
+
- import { createFetchMock } from 'msw-fetch-mock/legacy';
|
|
89
|
+
- const server = setupServer();
|
|
90
|
+
- const fetchMock = createFetchMock(rest, server);
|
|
91
|
+
+ import { fetchMock } from 'msw-fetch-mock';
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The rest of the API (`get()`, `intercept()`, `reply()`, `calls`, etc.) stays the same.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "msw-fetch-mock",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Undici-style fetch mock API built on MSW (Mock Service Worker)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"fetch",
|
|
10
10
|
"mock",
|
|
11
11
|
"testing",
|
|
12
|
-
"undici"
|
|
12
|
+
"undici",
|
|
13
|
+
"browser",
|
|
14
|
+
"service-worker"
|
|
13
15
|
],
|
|
14
16
|
"repository": {
|
|
15
17
|
"type": "git",
|
|
@@ -22,8 +24,39 @@
|
|
|
22
24
|
"exports": {
|
|
23
25
|
".": {
|
|
24
26
|
"source": "./src/index.ts",
|
|
25
|
-
"types":
|
|
26
|
-
|
|
27
|
+
"types": {
|
|
28
|
+
"import": "./dist/index.d.ts",
|
|
29
|
+
"require": "./dist/index.d.cts"
|
|
30
|
+
},
|
|
31
|
+
"import": "./dist/index.js",
|
|
32
|
+
"require": "./dist/index.cjs"
|
|
33
|
+
},
|
|
34
|
+
"./node": {
|
|
35
|
+
"source": "./src/node.ts",
|
|
36
|
+
"types": {
|
|
37
|
+
"import": "./dist/node.d.ts",
|
|
38
|
+
"require": "./dist/node.d.cts"
|
|
39
|
+
},
|
|
40
|
+
"import": "./dist/node.js",
|
|
41
|
+
"require": "./dist/node.cjs"
|
|
42
|
+
},
|
|
43
|
+
"./browser": {
|
|
44
|
+
"source": "./src/browser.ts",
|
|
45
|
+
"types": {
|
|
46
|
+
"import": "./dist/browser.d.ts",
|
|
47
|
+
"require": "./dist/browser.d.cts"
|
|
48
|
+
},
|
|
49
|
+
"import": "./dist/browser.js",
|
|
50
|
+
"require": "./dist/browser.cjs"
|
|
51
|
+
},
|
|
52
|
+
"./legacy": {
|
|
53
|
+
"source": "./src/legacy.ts",
|
|
54
|
+
"types": {
|
|
55
|
+
"import": "./dist/legacy.d.ts",
|
|
56
|
+
"require": "./dist/legacy.d.cts"
|
|
57
|
+
},
|
|
58
|
+
"import": "./dist/legacy.js",
|
|
59
|
+
"require": "./dist/legacy.cjs"
|
|
27
60
|
}
|
|
28
61
|
},
|
|
29
62
|
"files": [
|
|
@@ -34,17 +67,19 @@
|
|
|
34
67
|
],
|
|
35
68
|
"scripts": {
|
|
36
69
|
"prepare": "lefthook install",
|
|
37
|
-
"build": "
|
|
70
|
+
"build": "tsup",
|
|
71
|
+
"typecheck": "tsc --noEmit",
|
|
38
72
|
"test": "vitest",
|
|
39
73
|
"test:run": "vitest run",
|
|
40
74
|
"lint": "eslint src",
|
|
41
75
|
"lint:fix": "eslint src --fix",
|
|
42
76
|
"format": "prettier --write .",
|
|
43
77
|
"format:check": "prettier --check .",
|
|
44
|
-
"prepublishOnly": "
|
|
78
|
+
"prepublishOnly": "tsup",
|
|
79
|
+
"test:e2e": "bash scripts/test-e2e.sh"
|
|
45
80
|
},
|
|
46
81
|
"peerDependencies": {
|
|
47
|
-
"msw": "^2.12.7"
|
|
82
|
+
"msw": "^1.0.0 || ^2.12.7"
|
|
48
83
|
},
|
|
49
84
|
"devDependencies": {
|
|
50
85
|
"@eslint/js": "^9.39.2",
|
|
@@ -52,6 +87,7 @@
|
|
|
52
87
|
"lefthook": "^2.0.15",
|
|
53
88
|
"msw": "^2.12.7",
|
|
54
89
|
"prettier": "^3.8.1",
|
|
90
|
+
"tsup": "^8.5.1",
|
|
55
91
|
"typescript": "^5.9.3",
|
|
56
92
|
"typescript-eslint": "^8.53.1",
|
|
57
93
|
"vitest": "^4.0.17"
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE3D,sFAAsF;AACtF,eAAO,MAAM,SAAS,WAAkB,CAAC;AACzC,YAAY,EACV,eAAe,EACf,kBAAkB,EAClB,gBAAgB,EAChB,QAAQ,EACR,eAAe,EACf,cAAc,EACd,YAAY,EACZ,kBAAkB,GACnB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,YAAY,EAAE,sBAAsB,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
export interface MockCallHistoryLogData {
|
|
2
|
-
body: string | null;
|
|
3
|
-
method: string;
|
|
4
|
-
headers: Record<string, string>;
|
|
5
|
-
fullUrl: string;
|
|
6
|
-
origin: string;
|
|
7
|
-
path: string;
|
|
8
|
-
searchParams: Record<string, string>;
|
|
9
|
-
protocol: string;
|
|
10
|
-
host: string;
|
|
11
|
-
port: string;
|
|
12
|
-
hash: string;
|
|
13
|
-
}
|
|
14
|
-
export declare class MockCallHistoryLog {
|
|
15
|
-
readonly body: string | null;
|
|
16
|
-
readonly method: string;
|
|
17
|
-
readonly headers: Record<string, string>;
|
|
18
|
-
readonly fullUrl: string;
|
|
19
|
-
readonly origin: string;
|
|
20
|
-
readonly path: string;
|
|
21
|
-
readonly searchParams: Record<string, string>;
|
|
22
|
-
readonly protocol: string;
|
|
23
|
-
readonly host: string;
|
|
24
|
-
readonly port: string;
|
|
25
|
-
readonly hash: string;
|
|
26
|
-
constructor(data: MockCallHistoryLogData);
|
|
27
|
-
json(): unknown;
|
|
28
|
-
toMap(): Map<string, string | null | Record<string, string>>;
|
|
29
|
-
toString(): string;
|
|
30
|
-
}
|
|
31
|
-
export interface CallHistoryFilterCriteria {
|
|
32
|
-
method?: string;
|
|
33
|
-
path?: string;
|
|
34
|
-
origin?: string;
|
|
35
|
-
protocol?: string;
|
|
36
|
-
host?: string;
|
|
37
|
-
port?: string;
|
|
38
|
-
hash?: string;
|
|
39
|
-
fullUrl?: string;
|
|
40
|
-
}
|
|
41
|
-
export declare class MockCallHistory {
|
|
42
|
-
private logs;
|
|
43
|
-
get length(): number;
|
|
44
|
-
record(data: MockCallHistoryLogData): void;
|
|
45
|
-
called(criteria?: ((log: MockCallHistoryLog) => boolean) | CallHistoryFilterCriteria | RegExp): boolean;
|
|
46
|
-
calls(): MockCallHistoryLog[];
|
|
47
|
-
firstCall(criteria?: ((log: MockCallHistoryLog) => boolean) | CallHistoryFilterCriteria | RegExp): MockCallHistoryLog | undefined;
|
|
48
|
-
lastCall(criteria?: ((log: MockCallHistoryLog) => boolean) | CallHistoryFilterCriteria | RegExp): MockCallHistoryLog | undefined;
|
|
49
|
-
nthCall(n: number, criteria?: ((log: MockCallHistoryLog) => boolean) | CallHistoryFilterCriteria | RegExp): MockCallHistoryLog | undefined;
|
|
50
|
-
clear(): void;
|
|
51
|
-
[Symbol.iterator](): Iterator<MockCallHistoryLog>;
|
|
52
|
-
filterCalls(criteria: ((log: MockCallHistoryLog) => boolean) | CallHistoryFilterCriteria | RegExp, options?: {
|
|
53
|
-
operator?: 'AND' | 'OR';
|
|
54
|
-
}): MockCallHistoryLog[];
|
|
55
|
-
private filterBy;
|
|
56
|
-
filterCallsByMethod(filter: string | RegExp): MockCallHistoryLog[];
|
|
57
|
-
filterCallsByPath(filter: string | RegExp): MockCallHistoryLog[];
|
|
58
|
-
filterCallsByOrigin(filter: string | RegExp): MockCallHistoryLog[];
|
|
59
|
-
filterCallsByProtocol(filter: string | RegExp): MockCallHistoryLog[];
|
|
60
|
-
filterCallsByHost(filter: string | RegExp): MockCallHistoryLog[];
|
|
61
|
-
filterCallsByPort(filter: string | RegExp): MockCallHistoryLog[];
|
|
62
|
-
filterCallsByHash(filter: string | RegExp): MockCallHistoryLog[];
|
|
63
|
-
filterCallsByFullUrl(filter: string | RegExp): MockCallHistoryLog[];
|
|
64
|
-
}
|
|
65
|
-
//# sourceMappingURL=mock-call-history.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mock-call-history.d.ts","sourceRoot":"","sources":["../src/mock-call-history.ts"],"names":[],"mappings":"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"}
|