msw-fetch-mock 0.3.3 → 0.4.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/README.md +46 -15
- package/README.zh-TW.md +274 -0
- package/dist/browser.cjs +5 -4
- package/dist/browser.d.cts +2 -2
- package/dist/browser.d.ts +2 -2
- package/dist/browser.js +2 -1
- package/dist/{chunk-OSKJXLRH.cjs → chunk-27BEAYUI.cjs} +4 -4
- package/dist/chunk-3RE2WWHX.cjs +1 -0
- package/dist/{chunk-3RAWYKAG.js → chunk-3XFP4NAO.js} +30 -0
- package/dist/chunk-GZFGTCZB.js +0 -0
- package/dist/{chunk-NFPFLI3N.js → chunk-IWHL7QPE.js} +1 -1
- package/dist/{chunk-N6B7UP6B.cjs → chunk-VUNESK75.cjs} +30 -0
- package/dist/{fetch-mock-DhiqmHdF.d.cts → fetch-mock-1oOS8WUJ.d.cts} +31 -1
- package/dist/{fetch-mock-DhiqmHdF.d.ts → fetch-mock-1oOS8WUJ.d.ts} +31 -1
- package/dist/index.cjs +4 -3
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -2
- package/dist/legacy.cjs +5 -5
- package/dist/legacy.d.cts +2 -2
- package/dist/legacy.d.ts +2 -2
- package/dist/legacy.js +1 -1
- package/dist/native.cjs +92 -0
- package/dist/native.d.cts +20 -0
- package/dist/native.d.ts +20 -0
- package/dist/native.js +92 -0
- package/dist/node.cjs +4 -3
- package/dist/node.d.cts +14 -2
- package/dist/node.d.ts +14 -2
- package/dist/node.js +3 -2
- package/docs/api.md +35 -10
- package/docs/api.zh-TW.md +707 -0
- package/docs/cloudflare-migration.zh-TW.md +80 -0
- package/docs/msw-v1-legacy.zh-TW.md +94 -0
- package/package.json +15 -1
package/README.md
CHANGED
|
@@ -4,16 +4,18 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/msw-fetch-mock)
|
|
5
5
|
[](https://github.com/recca0120/msw-fetch-mock/blob/main/LICENSE.md)
|
|
6
6
|
|
|
7
|
+
[English](README.md) | [繁體中文](README.zh-TW.md)
|
|
8
|
+
|
|
7
9
|
Undici-style fetch mock API built on [MSW](https://mswjs.io/) (Mock Service Worker).
|
|
8
10
|
|
|
9
11
|
If you're familiar with Cloudflare Workers' `fetchMock` (from `cloudflare:test`) or Node.js undici's `MockAgent`, you already know this API.
|
|
10
12
|
|
|
11
|
-
Supports
|
|
13
|
+
Supports **Node.js** (`msw/node`), **Browser** (`msw/browser`), and **Native** (no MSW dependency) environments via subpath exports.
|
|
12
14
|
|
|
13
15
|
## Requirements
|
|
14
16
|
|
|
15
17
|
- **Node.js** >= 18
|
|
16
|
-
- **MSW** ^1.0.0 (via `/legacy`) or ^2.12.7
|
|
18
|
+
- **MSW** ^1.0.0 (via `/legacy`) or ^2.12.7 — **optional** when using `/native`
|
|
17
19
|
|
|
18
20
|
## Install
|
|
19
21
|
|
|
@@ -23,12 +25,18 @@ npm install -D msw-fetch-mock msw
|
|
|
23
25
|
|
|
24
26
|
`msw` is a peer dependency — you provide your own version.
|
|
25
27
|
|
|
28
|
+
For MSW-free usage (patches `globalThis.fetch` directly):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -D msw-fetch-mock
|
|
32
|
+
```
|
|
33
|
+
|
|
26
34
|
## Quick Start
|
|
27
35
|
|
|
28
36
|
### Node.js (Vitest, Jest)
|
|
29
37
|
|
|
30
38
|
```typescript
|
|
31
|
-
// Works with
|
|
39
|
+
// Works with root import or explicit /node subpath
|
|
32
40
|
import { fetchMock } from 'msw-fetch-mock';
|
|
33
41
|
// import { fetchMock } from 'msw-fetch-mock/node';
|
|
34
42
|
|
|
@@ -78,11 +86,10 @@ If you already use MSW, pass your server to share a single interceptor:
|
|
|
78
86
|
```typescript
|
|
79
87
|
import { setupServer } from 'msw/node';
|
|
80
88
|
import { http, HttpResponse } from 'msw';
|
|
81
|
-
import {
|
|
82
|
-
import { NodeMswAdapter } from 'msw-fetch-mock/node';
|
|
89
|
+
import { createFetchMock } from 'msw-fetch-mock';
|
|
83
90
|
|
|
84
91
|
const server = setupServer(http.get('/api/users', () => HttpResponse.json([{ id: 1 }])));
|
|
85
|
-
const fetchMock =
|
|
92
|
+
const fetchMock = createFetchMock(server);
|
|
86
93
|
|
|
87
94
|
beforeAll(() => server.listen());
|
|
88
95
|
afterAll(() => server.close());
|
|
@@ -92,7 +99,26 @@ afterEach(() => {
|
|
|
92
99
|
});
|
|
93
100
|
```
|
|
94
101
|
|
|
95
|
-
> **Note:** Only one MSW server can be active at a time. If
|
|
102
|
+
> **Note:** Only one MSW server can be active at a time. If a server is already listening, standalone `activate()` will throw an error. Use `createFetchMock(server)` to share an existing server.
|
|
103
|
+
|
|
104
|
+
### Native (no MSW dependency)
|
|
105
|
+
|
|
106
|
+
For environments where you don't want to install MSW, the `/native` subpath patches `globalThis.fetch` directly:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { fetchMock } from 'msw-fetch-mock/native';
|
|
110
|
+
|
|
111
|
+
beforeAll(async () => {
|
|
112
|
+
await fetchMock.activate({ onUnhandledRequest: 'error' });
|
|
113
|
+
});
|
|
114
|
+
afterAll(() => fetchMock.deactivate());
|
|
115
|
+
afterEach(() => {
|
|
116
|
+
fetchMock.assertNoPendingInterceptors();
|
|
117
|
+
fetchMock.reset();
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The API is identical — only the underlying transport changes (no Service Worker, no MSW server).
|
|
96
122
|
|
|
97
123
|
### Legacy (MSW v1)
|
|
98
124
|
|
|
@@ -144,12 +170,13 @@ fetchMock.activate({
|
|
|
144
170
|
|
|
145
171
|
### Import Paths
|
|
146
172
|
|
|
147
|
-
| Path | Environment | MSW version
|
|
148
|
-
| ------------------------ | ---------------------------- |
|
|
149
|
-
| `msw-fetch-mock` | Node.js (re-exports `/node`) | v2
|
|
150
|
-
| `msw-fetch-mock/node` | Node.js | v2
|
|
151
|
-
| `msw-fetch-mock/browser` | Browser | v2
|
|
152
|
-
| `msw-fetch-mock/
|
|
173
|
+
| Path | Environment | MSW version |
|
|
174
|
+
| ------------------------ | ---------------------------- | ------------ |
|
|
175
|
+
| `msw-fetch-mock` | Node.js (re-exports `/node`) | v2 |
|
|
176
|
+
| `msw-fetch-mock/node` | Node.js | v2 |
|
|
177
|
+
| `msw-fetch-mock/browser` | Browser | v2 |
|
|
178
|
+
| `msw-fetch-mock/native` | Any (no MSW) | not required |
|
|
179
|
+
| `msw-fetch-mock/legacy` | Node.js (MSW v1) | v1 |
|
|
153
180
|
|
|
154
181
|
### `fetchMock` (singleton)
|
|
155
182
|
|
|
@@ -162,11 +189,12 @@ Factory function that creates a `FetchMock` with the appropriate adapter.
|
|
|
162
189
|
|
|
163
190
|
- Node: `createFetchMock(server?)` — optionally pass an existing MSW `SetupServer`
|
|
164
191
|
- Browser: `createFetchMock(worker)` — pass an MSW `SetupWorker` (required)
|
|
192
|
+
- Native: `createFetchMock()` — no arguments, no MSW dependency
|
|
165
193
|
- Legacy: `createFetchMock(rest, server?)` — pass MSW v1 `rest` object
|
|
166
194
|
|
|
167
195
|
### `new FetchMock(adapter?)`
|
|
168
196
|
|
|
169
|
-
Creates a `FetchMock` instance with an explicit `MswAdapter`. Use `NodeMswAdapter` or `
|
|
197
|
+
Creates a `FetchMock` instance with an explicit `MswAdapter`. Use `NodeMswAdapter`, `BrowserMswAdapter`, or `NativeFetchAdapter`.
|
|
170
198
|
|
|
171
199
|
### Intercepting & Replying
|
|
172
200
|
|
|
@@ -204,6 +232,9 @@ E2E tests run on every CI push across these environments:
|
|
|
204
232
|
| Jest CJS | CJS (require) | Jest |
|
|
205
233
|
| Node.js Test | ESM (import) | Node test runner |
|
|
206
234
|
| Node.js CJS | CJS (require) | Node test runner |
|
|
235
|
+
| Native ESM | ESM (import) | Node test runner |
|
|
236
|
+
| Native CJS | CJS (require) | Node test runner |
|
|
237
|
+
| Legacy CJS | CJS (require) | Jest (MSW v1) |
|
|
207
238
|
| Vitest Browser | ESM (import) | Vitest + Playwright |
|
|
208
239
|
|
|
209
240
|
## Documentation
|
|
@@ -236,7 +267,7 @@ pnpm test:e2e -- node-cjs
|
|
|
236
267
|
pnpm test:e2e -- --all
|
|
237
268
|
```
|
|
238
269
|
|
|
239
|
-
Available suites: `jest-esm`, `jest-cjs`, `node-test`, `node-cjs`, `vitest-browser`
|
|
270
|
+
Available suites: `jest-esm`, `jest-cjs`, `node-test`, `node-cjs`, `native-esm`, `native-cjs`, `legacy-cjs`, `vitest-browser`
|
|
240
271
|
|
|
241
272
|
## License
|
|
242
273
|
|
package/README.zh-TW.md
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
# msw-fetch-mock
|
|
2
|
+
|
|
3
|
+
[](https://github.com/recca0120/msw-fetch-mock/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/msw-fetch-mock)
|
|
5
|
+
[](https://github.com/recca0120/msw-fetch-mock/blob/main/LICENSE.md)
|
|
6
|
+
|
|
7
|
+
[English](README.md) | [繁體中文](README.zh-TW.md)
|
|
8
|
+
|
|
9
|
+
基於 [MSW](https://mswjs.io/)(Mock Service Worker)的 Undici 風格 fetch mock API。
|
|
10
|
+
|
|
11
|
+
如果你熟悉 Cloudflare Workers 的 `fetchMock`(來自 `cloudflare:test`)或 Node.js undici 的 `MockAgent`,你已經會使用這個 API 了。
|
|
12
|
+
|
|
13
|
+
透過 subpath exports 支援 **Node.js**(`msw/node`)、**瀏覽器**(`msw/browser`)和**原生**(無 MSW 依賴)環境。
|
|
14
|
+
|
|
15
|
+
## 系統需求
|
|
16
|
+
|
|
17
|
+
- **Node.js** >= 18
|
|
18
|
+
- **MSW** ^1.0.0(透過 `/legacy`)或 ^2.12.7 — 使用 `/native` 時**可選**
|
|
19
|
+
|
|
20
|
+
## 安裝
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -D msw-fetch-mock msw
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`msw` 是 peer dependency — 需要自行安裝你的版本。
|
|
27
|
+
|
|
28
|
+
若不需要 MSW(直接 patch `globalThis.fetch`):
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -D msw-fetch-mock
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## 快速開始
|
|
35
|
+
|
|
36
|
+
### Node.js(Vitest、Jest)
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// 可使用根路徑匯入或明確的 /node 子路徑
|
|
40
|
+
import { fetchMock } from 'msw-fetch-mock';
|
|
41
|
+
// import { fetchMock } from 'msw-fetch-mock/node';
|
|
42
|
+
|
|
43
|
+
beforeAll(() => fetchMock.activate({ onUnhandledRequest: 'error' }));
|
|
44
|
+
afterAll(() => fetchMock.deactivate());
|
|
45
|
+
afterEach(() => {
|
|
46
|
+
fetchMock.assertNoPendingInterceptors();
|
|
47
|
+
fetchMock.reset();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('模擬 GET 請求', async () => {
|
|
51
|
+
fetchMock
|
|
52
|
+
.get('https://api.example.com')
|
|
53
|
+
.intercept({ path: '/users', method: 'GET' })
|
|
54
|
+
.reply(200, { users: [{ id: '1', name: 'Alice' }] });
|
|
55
|
+
|
|
56
|
+
const res = await fetch('https://api.example.com/users');
|
|
57
|
+
const data = await res.json();
|
|
58
|
+
|
|
59
|
+
expect(data.users).toHaveLength(1);
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 瀏覽器(Storybook、Vitest Browser Mode)
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { setupWorker } from 'msw/browser';
|
|
67
|
+
import { createFetchMock } from 'msw-fetch-mock/browser';
|
|
68
|
+
|
|
69
|
+
const worker = setupWorker();
|
|
70
|
+
const fetchMock = createFetchMock(worker);
|
|
71
|
+
|
|
72
|
+
beforeAll(async () => {
|
|
73
|
+
await fetchMock.activate({ onUnhandledRequest: 'error' });
|
|
74
|
+
});
|
|
75
|
+
afterAll(() => fetchMock.deactivate());
|
|
76
|
+
afterEach(() => {
|
|
77
|
+
fetchMock.assertNoPendingInterceptors();
|
|
78
|
+
fetchMock.reset();
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### 搭配現有的 MSW server
|
|
83
|
+
|
|
84
|
+
如果你已經在使用 MSW,可以傳入你的 server 來共用同一個攔截器:
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { setupServer } from 'msw/node';
|
|
88
|
+
import { http, HttpResponse } from 'msw';
|
|
89
|
+
import { createFetchMock } from 'msw-fetch-mock';
|
|
90
|
+
|
|
91
|
+
const server = setupServer(http.get('/api/users', () => HttpResponse.json([{ id: 1 }])));
|
|
92
|
+
const fetchMock = createFetchMock(server);
|
|
93
|
+
|
|
94
|
+
beforeAll(() => server.listen());
|
|
95
|
+
afterAll(() => server.close());
|
|
96
|
+
afterEach(() => {
|
|
97
|
+
server.resetHandlers();
|
|
98
|
+
fetchMock.assertNoPendingInterceptors();
|
|
99
|
+
});
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
> **注意:** 同一時間只能有一個 MSW server 處於啟動狀態。如果已有 server 在監聽中,獨立模式的 `activate()` 會拋出錯誤。請使用 `createFetchMock(server)` 來共用現有的 server。
|
|
103
|
+
|
|
104
|
+
### 原生模式(無 MSW 依賴)
|
|
105
|
+
|
|
106
|
+
若不想安裝 MSW,`/native` 子路徑會直接 patch `globalThis.fetch`:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { fetchMock } from 'msw-fetch-mock/native';
|
|
110
|
+
|
|
111
|
+
beforeAll(async () => {
|
|
112
|
+
await fetchMock.activate({ onUnhandledRequest: 'error' });
|
|
113
|
+
});
|
|
114
|
+
afterAll(() => fetchMock.deactivate());
|
|
115
|
+
afterEach(() => {
|
|
116
|
+
fetchMock.assertNoPendingInterceptors();
|
|
117
|
+
fetchMock.reset();
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
API 完全相同 — 只有底層傳輸方式不同(無 Service Worker、無 MSW server)。
|
|
122
|
+
|
|
123
|
+
### Legacy(MSW v1)
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
import { rest } from 'msw';
|
|
127
|
+
import { setupServer } from 'msw/node';
|
|
128
|
+
import { createFetchMock } from 'msw-fetch-mock/legacy';
|
|
129
|
+
|
|
130
|
+
const server = setupServer();
|
|
131
|
+
const fetchMock = createFetchMock(rest, server);
|
|
132
|
+
|
|
133
|
+
beforeAll(() => fetchMock.activate());
|
|
134
|
+
afterAll(() => fetchMock.deactivate());
|
|
135
|
+
afterEach(() => {
|
|
136
|
+
fetchMock.assertNoPendingInterceptors();
|
|
137
|
+
fetchMock.reset();
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
詳見 [MSW v1 Legacy 指南](docs/msw-v1-legacy.zh-TW.md)。
|
|
142
|
+
|
|
143
|
+
## 未處理的請求
|
|
144
|
+
|
|
145
|
+
預設情況下 `activate()` 使用 `'error'` 模式 — 未匹配的請求會導致 `fetch()` 拒絕。這包含對**已消耗**攔截器的請求(一次性攔截器使用後,其 handler 會從 MSW 中移除)。
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// 拒絕未匹配的請求(預設行為)
|
|
149
|
+
fetchMock.activate();
|
|
150
|
+
fetchMock.activate({ onUnhandledRequest: 'error' });
|
|
151
|
+
|
|
152
|
+
// 記錄警告但允許通過
|
|
153
|
+
fetchMock.activate({ onUnhandledRequest: 'warn' });
|
|
154
|
+
|
|
155
|
+
// 靜默允許通過
|
|
156
|
+
fetchMock.activate({ onUnhandledRequest: 'bypass' });
|
|
157
|
+
|
|
158
|
+
// 自訂回呼
|
|
159
|
+
fetchMock.activate({
|
|
160
|
+
onUnhandledRequest: (request, print) => {
|
|
161
|
+
if (request.url.includes('/health')) return; // 忽略
|
|
162
|
+
print.error(); // 阻擋其他所有請求
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
> `enableNetConnect()` 的優先順序高於 `onUnhandledRequest` — 允許的主機一律直接通過。
|
|
168
|
+
|
|
169
|
+
## API 概覽
|
|
170
|
+
|
|
171
|
+
### 匯入路徑
|
|
172
|
+
|
|
173
|
+
| 路徑 | 環境 | MSW 版本 |
|
|
174
|
+
| ------------------------ | ----------------------------- | -------- |
|
|
175
|
+
| `msw-fetch-mock` | Node.js(re-exports `/node`) | v2 |
|
|
176
|
+
| `msw-fetch-mock/node` | Node.js | v2 |
|
|
177
|
+
| `msw-fetch-mock/browser` | 瀏覽器 | v2 |
|
|
178
|
+
| `msw-fetch-mock/native` | 任何環境(無 MSW) | 不需要 |
|
|
179
|
+
| `msw-fetch-mock/legacy` | Node.js(MSW v1) | v1 |
|
|
180
|
+
|
|
181
|
+
### `fetchMock`(單例)
|
|
182
|
+
|
|
183
|
+
預先建立的 `FetchMock` 實例,適用於獨立使用。匯入後直接呼叫 `activate()` 即可 — 無需額外設定。
|
|
184
|
+
可從 `msw-fetch-mock` 和 `msw-fetch-mock/node` 匯入。
|
|
185
|
+
|
|
186
|
+
### `createFetchMock(server?)` / `createFetchMock(worker)`
|
|
187
|
+
|
|
188
|
+
建立 `FetchMock` 的工廠函式,會自動搭配對應的 adapter。
|
|
189
|
+
|
|
190
|
+
- Node:`createFetchMock(server?)` — 可選擇性傳入現有的 MSW `SetupServer`
|
|
191
|
+
- 瀏覽器:`createFetchMock(worker)` — 傳入 MSW `SetupWorker`(必要)
|
|
192
|
+
- 原生:`createFetchMock()` — 無需參數,無 MSW 依賴
|
|
193
|
+
- Legacy:`createFetchMock(rest, server?)` — 傳入 MSW v1 的 `rest` 物件
|
|
194
|
+
|
|
195
|
+
### `new FetchMock(adapter?)`
|
|
196
|
+
|
|
197
|
+
使用明確的 `MswAdapter` 建立 `FetchMock` 實例。可使用 `NodeMswAdapter`、`BrowserMswAdapter` 或 `NativeFetchAdapter`。
|
|
198
|
+
|
|
199
|
+
### 攔截與回應
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
fetchMock
|
|
203
|
+
.get(origin) // string、RegExp 或 function
|
|
204
|
+
.intercept({ path, method, headers, body, query }) // 匹配條件
|
|
205
|
+
.reply(status, body, options) // 定義回應
|
|
206
|
+
.times(n) / .persist(); // 重複控制
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### 呼叫歷史
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
fetchMock.calls.lastCall(); // 最近一次
|
|
213
|
+
fetchMock.calls.firstCall(); // 最早一次
|
|
214
|
+
fetchMock.calls.nthCall(2); // 第 2 次呼叫(1-indexed)
|
|
215
|
+
fetchMock.calls.filterCalls({ method: 'POST', path: '/users' }, { operator: 'AND' });
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 斷言與清理
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
fetchMock.assertNoPendingInterceptors(); // 若有未消耗的攔截器則拋出錯誤
|
|
222
|
+
fetchMock.reset(); // 清除攔截器 + 呼叫歷史 + handlers
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## 測試環境
|
|
226
|
+
|
|
227
|
+
每次 CI 推送都會在以下環境執行 E2E 測試:
|
|
228
|
+
|
|
229
|
+
| 環境 | 模組系統 | 測試框架 |
|
|
230
|
+
| -------------- | ------------- | ------------------- |
|
|
231
|
+
| Jest ESM | ESM (import) | Jest |
|
|
232
|
+
| Jest CJS | CJS (require) | Jest |
|
|
233
|
+
| Node.js Test | ESM (import) | Node test runner |
|
|
234
|
+
| Node.js CJS | CJS (require) | Node test runner |
|
|
235
|
+
| Native ESM | ESM (import) | Node test runner |
|
|
236
|
+
| Native CJS | CJS (require) | Node test runner |
|
|
237
|
+
| Legacy CJS | CJS (require) | Jest (MSW v1) |
|
|
238
|
+
| Vitest Browser | ESM (import) | Vitest + Playwright |
|
|
239
|
+
|
|
240
|
+
## 文件
|
|
241
|
+
|
|
242
|
+
- [API 參考](docs/api.zh-TW.md) — 完整的 API 細節、匹配選項、回應回呼
|
|
243
|
+
- [Cloudflare Workers 遷移指南](docs/cloudflare-migration.zh-TW.md) — 從 `cloudflare:test` fetchMock 遷移
|
|
244
|
+
- [MSW v1 Legacy 指南](docs/msw-v1-legacy.zh-TW.md) — 搭配 MSW v1 使用 msw-fetch-mock
|
|
245
|
+
|
|
246
|
+
## 開發
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
pnpm install
|
|
250
|
+
pnpm build # 使用 tsup 建置
|
|
251
|
+
pnpm test # 單元測試(vitest)
|
|
252
|
+
pnpm test:e2e # E2E 測試(jest-esm、jest-cjs、node-test、node-cjs、legacy-cjs)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### E2E 測試
|
|
256
|
+
|
|
257
|
+
E2E 測試驗證已發佈的套件能在不同 runtime 和模組系統下正常運作。腳本會建置、透過 `npm pack` 打包 tarball,然後安裝到每個 `e2e/` 專案中 — 完全模擬 CI 流程。
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# 執行預設套組(跳過 vitest-browser)
|
|
261
|
+
pnpm test:e2e
|
|
262
|
+
|
|
263
|
+
# 執行單一套組
|
|
264
|
+
pnpm test:e2e -- node-cjs
|
|
265
|
+
|
|
266
|
+
# 執行所有套組,包含 vitest-browser(會自動安裝 Playwright)
|
|
267
|
+
pnpm test:e2e -- --all
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
可用套組:`jest-esm`、`jest-cjs`、`node-test`、`node-cjs`、`native-esm`、`native-cjs`、`legacy-cjs`、`vitest-browser`
|
|
271
|
+
|
|
272
|
+
## 授權
|
|
273
|
+
|
|
274
|
+
[MIT](LICENSE.md)
|
package/dist/browser.cjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
"use strict";Object.defineProperty(exports, "__esModule", {value: true});
|
|
2
2
|
|
|
3
3
|
var _chunkLVGXTY6Jcjs = require('./chunk-LVGXTY6J.cjs');
|
|
4
|
+
require('./chunk-3RE2WWHX.cjs');
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
|
|
8
|
-
var
|
|
9
|
+
var _chunkVUNESK75cjs = require('./chunk-VUNESK75.cjs');
|
|
9
10
|
|
|
10
11
|
// src/browser-adapter.ts
|
|
11
12
|
var BrowserMswAdapter = class {
|
|
@@ -30,9 +31,9 @@ var BrowserMswAdapter = class {
|
|
|
30
31
|
};
|
|
31
32
|
|
|
32
33
|
// src/browser.ts
|
|
33
|
-
|
|
34
|
+
_chunkVUNESK75cjs.FetchMock._handlerFactory = _chunkLVGXTY6Jcjs.HandlerFactory;
|
|
34
35
|
function createFetchMock(worker) {
|
|
35
|
-
return new (0,
|
|
36
|
+
return new (0, _chunkVUNESK75cjs.FetchMock)(new BrowserMswAdapter(worker));
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
|
|
@@ -40,4 +41,4 @@ function createFetchMock(worker) {
|
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
|
|
43
|
-
exports.BrowserMswAdapter = BrowserMswAdapter; exports.FetchMock =
|
|
44
|
+
exports.BrowserMswAdapter = BrowserMswAdapter; exports.FetchMock = _chunkVUNESK75cjs.FetchMock; exports.MockCallHistory = _chunkVUNESK75cjs.MockCallHistory; exports.MockCallHistoryLog = _chunkVUNESK75cjs.MockCallHistoryLog; exports.createFetchMock = createFetchMock;
|
package/dist/browser.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MswAdapter, S as SetupWorkerLike, R as ResolvedActivateOptions, F as FetchMock } from './fetch-mock-
|
|
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, g as ReplyOptions } from './fetch-mock-
|
|
1
|
+
import { M as MswAdapter, S as SetupWorkerLike, R as ResolvedActivateOptions, F as FetchMock } from './fetch-mock-1oOS8WUJ.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, g as ReplyCallback, h as ReplyOptions, i as SingleReplyCallback, j as SingleReplyResult } from './fetch-mock-1oOS8WUJ.cjs';
|
|
3
3
|
|
|
4
4
|
declare class BrowserMswAdapter implements MswAdapter {
|
|
5
5
|
private readonly worker;
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { M as MswAdapter, S as SetupWorkerLike, R as ResolvedActivateOptions, F as FetchMock } from './fetch-mock-
|
|
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, g as ReplyOptions } from './fetch-mock-
|
|
1
|
+
import { M as MswAdapter, S as SetupWorkerLike, R as ResolvedActivateOptions, F as FetchMock } from './fetch-mock-1oOS8WUJ.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, g as ReplyCallback, h as ReplyOptions, i as SingleReplyCallback, j as SingleReplyResult } from './fetch-mock-1oOS8WUJ.js';
|
|
3
3
|
|
|
4
4
|
declare class BrowserMswAdapter implements MswAdapter {
|
|
5
5
|
private readonly worker;
|
package/dist/browser.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
HandlerFactory
|
|
3
3
|
} from "./chunk-KGCQG4D2.js";
|
|
4
|
+
import "./chunk-GZFGTCZB.js";
|
|
4
5
|
import {
|
|
5
6
|
FetchMock,
|
|
6
7
|
MockCallHistory,
|
|
7
8
|
MockCallHistoryLog
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3XFP4NAO.js";
|
|
9
10
|
|
|
10
11
|
// src/browser-adapter.ts
|
|
11
12
|
var BrowserMswAdapter = class {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
var _chunkLVGXTY6Jcjs = require('./chunk-LVGXTY6J.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
var
|
|
6
|
+
var _chunkVUNESK75cjs = require('./chunk-VUNESK75.cjs');
|
|
7
7
|
|
|
8
8
|
// src/node-adapter.ts
|
|
9
9
|
var _node = require('msw/node');
|
|
@@ -44,10 +44,10 @@ var NodeMswAdapter = class {
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
// src/node.ts
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
_chunkVUNESK75cjs.FetchMock._defaultAdapterFactory = () => new NodeMswAdapter();
|
|
48
|
+
_chunkVUNESK75cjs.FetchMock._handlerFactory = _chunkLVGXTY6Jcjs.HandlerFactory;
|
|
49
49
|
function createFetchMock(server) {
|
|
50
|
-
return new (0,
|
|
50
|
+
return new (0, _chunkVUNESK75cjs.FetchMock)(new NodeMswAdapter(server));
|
|
51
51
|
}
|
|
52
52
|
var fetchMock = createFetchMock();
|
|
53
53
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";
|
|
@@ -28,6 +28,10 @@ var MockCallHistoryLog = class {
|
|
|
28
28
|
if (this.body === null) return null;
|
|
29
29
|
return JSON.parse(this.body);
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Returns a Map representation of this call log.
|
|
33
|
+
* Provided for compatibility with the `cloudflare:test` fetchMock API.
|
|
34
|
+
*/
|
|
31
35
|
toMap() {
|
|
32
36
|
return /* @__PURE__ */ new Map([
|
|
33
37
|
["body", this.body],
|
|
@@ -43,6 +47,10 @@ var MockCallHistoryLog = class {
|
|
|
43
47
|
["hash", this.hash]
|
|
44
48
|
]);
|
|
45
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Returns a pipe-separated string representation of this call log.
|
|
52
|
+
* Provided for compatibility with the `cloudflare:test` fetchMock API.
|
|
53
|
+
*/
|
|
46
54
|
toString() {
|
|
47
55
|
return [
|
|
48
56
|
`method->${this.method}`,
|
|
@@ -105,32 +113,47 @@ var MockCallHistory = class {
|
|
|
105
113
|
(log) => operator === "AND" ? predicates.every((p) => p(log)) : predicates.some((p) => p(log))
|
|
106
114
|
);
|
|
107
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Filter helper — matches a single field by exact string or RegExp.
|
|
118
|
+
*
|
|
119
|
+
* The `filterCallsByXxx` methods below mirror the `cloudflare:test` fetchMock
|
|
120
|
+
* API so that tests written for Cloudflare Workers can be reused with this library
|
|
121
|
+
* without modification.
|
|
122
|
+
*/
|
|
108
123
|
filterBy(field, filter) {
|
|
109
124
|
return this.logs.filter(
|
|
110
125
|
(log) => typeof filter === "string" ? log[field] === filter : filter.test(String(log[field]))
|
|
111
126
|
);
|
|
112
127
|
}
|
|
128
|
+
/** Filter calls by HTTP method. Mirrors `cloudflare:test` fetchMock API. */
|
|
113
129
|
filterCallsByMethod(filter) {
|
|
114
130
|
return this.filterBy("method", filter);
|
|
115
131
|
}
|
|
132
|
+
/** Filter calls by path. Mirrors `cloudflare:test` fetchMock API. */
|
|
116
133
|
filterCallsByPath(filter) {
|
|
117
134
|
return this.filterBy("path", filter);
|
|
118
135
|
}
|
|
136
|
+
/** Filter calls by origin. Mirrors `cloudflare:test` fetchMock API. */
|
|
119
137
|
filterCallsByOrigin(filter) {
|
|
120
138
|
return this.filterBy("origin", filter);
|
|
121
139
|
}
|
|
140
|
+
/** Filter calls by protocol. Mirrors `cloudflare:test` fetchMock API. */
|
|
122
141
|
filterCallsByProtocol(filter) {
|
|
123
142
|
return this.filterBy("protocol", filter);
|
|
124
143
|
}
|
|
144
|
+
/** Filter calls by host. Mirrors `cloudflare:test` fetchMock API. */
|
|
125
145
|
filterCallsByHost(filter) {
|
|
126
146
|
return this.filterBy("host", filter);
|
|
127
147
|
}
|
|
148
|
+
/** Filter calls by port. Mirrors `cloudflare:test` fetchMock API. */
|
|
128
149
|
filterCallsByPort(filter) {
|
|
129
150
|
return this.filterBy("port", filter);
|
|
130
151
|
}
|
|
152
|
+
/** Filter calls by URL hash. Mirrors `cloudflare:test` fetchMock API. */
|
|
131
153
|
filterCallsByHash(filter) {
|
|
132
154
|
return this.filterBy("hash", filter);
|
|
133
155
|
}
|
|
156
|
+
/** Filter calls by full URL. Mirrors `cloudflare:test` fetchMock API. */
|
|
134
157
|
filterCallsByFullUrl(filter) {
|
|
135
158
|
return this.filterBy("fullUrl", filter);
|
|
136
159
|
}
|
|
@@ -327,12 +350,19 @@ var FetchMock = class _FetchMock {
|
|
|
327
350
|
const activeHandlers = [...this.mswHandlers.entries()].filter(([p]) => !p.consumed || p.persist).map(([, handler]) => handler);
|
|
328
351
|
this.adapter.resetHandlers(...activeHandlers);
|
|
329
352
|
}
|
|
353
|
+
/**
|
|
354
|
+
* Returns the call history instance.
|
|
355
|
+
* Provided for compatibility with the `cloudflare:test` fetchMock API.
|
|
356
|
+
* Equivalent to the `calls` getter.
|
|
357
|
+
*/
|
|
330
358
|
getCallHistory() {
|
|
331
359
|
return this._calls;
|
|
332
360
|
}
|
|
361
|
+
/** Clears recorded call history. Mirrors `cloudflare:test` fetchMock API. */
|
|
333
362
|
clearCallHistory() {
|
|
334
363
|
this._calls.clear();
|
|
335
364
|
}
|
|
365
|
+
/** Alias for `clearCallHistory()`. Mirrors `cloudflare:test` fetchMock API. */
|
|
336
366
|
clearAllCallHistory() {
|
|
337
367
|
this.clearCallHistory();
|
|
338
368
|
}
|
|
File without changes
|