@juit/lib-fetch-mock 1.0.3 → 1.1.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 +28 -0
- package/dist/index.cjs +32 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +29 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +52 -10
package/README.md
CHANGED
|
@@ -5,6 +5,7 @@ This package allows to quicly mock the global `fetch` distributed with Node 18
|
|
|
5
5
|
(and greater).
|
|
6
6
|
|
|
7
7
|
* [Request Handlers](#request-handlers)
|
|
8
|
+
* [Wrapping Mocks](#wrapping-mocks)
|
|
8
9
|
* [Request Interception](#request-interception)
|
|
9
10
|
* [License](LICENSE.md)
|
|
10
11
|
* [Copyright](NOTICE.md)
|
|
@@ -55,6 +56,33 @@ The functions `sendStatus(...)`, `sendJson(...)`, `sendText(...)` and
|
|
|
55
56
|
`sendData(...)` can be used to generate simple responses for mocking.
|
|
56
57
|
|
|
57
58
|
|
|
59
|
+
### Wrapping Mocks
|
|
60
|
+
|
|
61
|
+
Mock instances can be wrapped to support in testing. For example:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const handler1 = new FetchMock()
|
|
65
|
+
handler1.on('GET', '/foo', (_) => new Response('FOO'))
|
|
66
|
+
handler1.on('GET', '/baz', (_) => new Response('FOO'))
|
|
67
|
+
|
|
68
|
+
const handler2 = new FetchMock()
|
|
69
|
+
handler2.on('GET', '/foo', (_) => new Response('Another FOO'))
|
|
70
|
+
handler2.on('GET', '/bar', (_) => new Response('BAR'))
|
|
71
|
+
|
|
72
|
+
handler1.install()
|
|
73
|
+
handler2.install() // this will be the _top most_ as it's installed later
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The last installed `FetchMock` instance will be the _top-most_ capturing all
|
|
77
|
+
requests and forwarding requests to sub-mocks only when not matched.
|
|
78
|
+
|
|
79
|
+
In the example above:
|
|
80
|
+
|
|
81
|
+
* requests for `/foo` will be intercepted by `handler2` and _not_ forwarded to `handler1`
|
|
82
|
+
* requests for `/bar` will be handled locally
|
|
83
|
+
* requests for `/baz` will simply be forwarded to `handler1`
|
|
84
|
+
|
|
85
|
+
|
|
58
86
|
### Request Interception
|
|
59
87
|
|
|
60
88
|
As in some case it's easier to _await_ in tests for a specific request (rather
|
package/dist/index.cjs
CHANGED
|
@@ -28,17 +28,17 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
29
|
|
|
30
30
|
// index.ts
|
|
31
|
-
var
|
|
32
|
-
__export(
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
33
|
FetchMock: () => FetchMock,
|
|
34
34
|
sendData: () => sendData,
|
|
35
35
|
sendJson: () => sendJson,
|
|
36
36
|
sendStatus: () => sendStatus,
|
|
37
37
|
sendText: () => sendText
|
|
38
38
|
});
|
|
39
|
-
module.exports = __toCommonJS(
|
|
40
|
-
var import_node_http = require("node:http");
|
|
39
|
+
module.exports = __toCommonJS(index_exports);
|
|
41
40
|
var import_node_assert = __toESM(require("node:assert"));
|
|
41
|
+
var import_node_http = require("node:http");
|
|
42
42
|
var mockSymbol = Symbol.for("juit.fetch.mock");
|
|
43
43
|
process.on("exit", () => process.exit(process.exitCode));
|
|
44
44
|
var FetchMockImpl = class {
|
|
@@ -61,7 +61,11 @@ var FetchMockImpl = class {
|
|
|
61
61
|
response = typeof result === "number" ? sendStatus(result) : result;
|
|
62
62
|
break;
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
if (response) return response;
|
|
65
|
+
if (this._fetch[mockSymbol]) {
|
|
66
|
+
return this._fetch[mockSymbol].$fetch(info, init);
|
|
67
|
+
}
|
|
68
|
+
return sendStatus(404);
|
|
65
69
|
}
|
|
66
70
|
/* === HANDLERS =========================================================== */
|
|
67
71
|
on(method, path, handler) {
|
|
@@ -107,7 +111,14 @@ var FetchMockImpl = class {
|
|
|
107
111
|
install() {
|
|
108
112
|
if (mockSymbol in globalThis.fetch) {
|
|
109
113
|
if (globalThis.fetch[mockSymbol] === this) return this;
|
|
110
|
-
|
|
114
|
+
}
|
|
115
|
+
let fn = globalThis.fetch;
|
|
116
|
+
while (fn && fn[mockSymbol]) {
|
|
117
|
+
if (fn[mockSymbol] === this) {
|
|
118
|
+
throw new Error("Global `fetch` already mocked by this instance");
|
|
119
|
+
} else {
|
|
120
|
+
fn = fn[mockSymbol]._fetch;
|
|
121
|
+
}
|
|
111
122
|
}
|
|
112
123
|
this._fetch = globalThis.fetch;
|
|
113
124
|
globalThis.fetch = this.$fetch.bind(this);
|
|
@@ -116,9 +127,20 @@ var FetchMockImpl = class {
|
|
|
116
127
|
}
|
|
117
128
|
destroy() {
|
|
118
129
|
if (!(mockSymbol in globalThis.fetch)) return;
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
130
|
+
if (!this._fetch) return;
|
|
131
|
+
let parent = void 0;
|
|
132
|
+
let fn = globalThis.fetch;
|
|
133
|
+
while (fn[mockSymbol]) {
|
|
134
|
+
if (fn[mockSymbol] === this) {
|
|
135
|
+
if (!parent) globalThis.fetch = this._fetch;
|
|
136
|
+
else parent._fetch = this._fetch;
|
|
137
|
+
break;
|
|
138
|
+
} else {
|
|
139
|
+
parent = fn[mockSymbol];
|
|
140
|
+
fn = fn[mockSymbol]._fetch;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
this._fetch = void 0;
|
|
122
144
|
}
|
|
123
145
|
};
|
|
124
146
|
var FetchMock = FetchMockImpl;
|
|
@@ -183,7 +205,7 @@ function sendJson(data, status = 200) {
|
|
|
183
205
|
});
|
|
184
206
|
}
|
|
185
207
|
function sendData(data, status = 200) {
|
|
186
|
-
return new Response(data, {
|
|
208
|
+
return new Response(Buffer.from(data), {
|
|
187
209
|
headers: { "content-type": "application/octet-stream" },
|
|
188
210
|
statusText: import_node_http.STATUS_CODES[status],
|
|
189
211
|
status
|
package/dist/index.cjs.map
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,
|
|
4
|
+
"mappings": "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",
|
|
5
5
|
"names": ["assert", "info", "init"]
|
|
6
6
|
}
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// index.ts
|
|
2
|
-
import { STATUS_CODES } from "node:http";
|
|
3
2
|
import assert from "node:assert";
|
|
3
|
+
import { STATUS_CODES } from "node:http";
|
|
4
4
|
var mockSymbol = Symbol.for("juit.fetch.mock");
|
|
5
5
|
process.on("exit", () => process.exit(process.exitCode));
|
|
6
6
|
var FetchMockImpl = class {
|
|
@@ -23,7 +23,11 @@ var FetchMockImpl = class {
|
|
|
23
23
|
response = typeof result === "number" ? sendStatus(result) : result;
|
|
24
24
|
break;
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
if (response) return response;
|
|
27
|
+
if (this._fetch[mockSymbol]) {
|
|
28
|
+
return this._fetch[mockSymbol].$fetch(info, init);
|
|
29
|
+
}
|
|
30
|
+
return sendStatus(404);
|
|
27
31
|
}
|
|
28
32
|
/* === HANDLERS =========================================================== */
|
|
29
33
|
on(method, path, handler) {
|
|
@@ -69,7 +73,14 @@ var FetchMockImpl = class {
|
|
|
69
73
|
install() {
|
|
70
74
|
if (mockSymbol in globalThis.fetch) {
|
|
71
75
|
if (globalThis.fetch[mockSymbol] === this) return this;
|
|
72
|
-
|
|
76
|
+
}
|
|
77
|
+
let fn = globalThis.fetch;
|
|
78
|
+
while (fn && fn[mockSymbol]) {
|
|
79
|
+
if (fn[mockSymbol] === this) {
|
|
80
|
+
throw new Error("Global `fetch` already mocked by this instance");
|
|
81
|
+
} else {
|
|
82
|
+
fn = fn[mockSymbol]._fetch;
|
|
83
|
+
}
|
|
73
84
|
}
|
|
74
85
|
this._fetch = globalThis.fetch;
|
|
75
86
|
globalThis.fetch = this.$fetch.bind(this);
|
|
@@ -78,9 +89,20 @@ var FetchMockImpl = class {
|
|
|
78
89
|
}
|
|
79
90
|
destroy() {
|
|
80
91
|
if (!(mockSymbol in globalThis.fetch)) return;
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
92
|
+
if (!this._fetch) return;
|
|
93
|
+
let parent = void 0;
|
|
94
|
+
let fn = globalThis.fetch;
|
|
95
|
+
while (fn[mockSymbol]) {
|
|
96
|
+
if (fn[mockSymbol] === this) {
|
|
97
|
+
if (!parent) globalThis.fetch = this._fetch;
|
|
98
|
+
else parent._fetch = this._fetch;
|
|
99
|
+
break;
|
|
100
|
+
} else {
|
|
101
|
+
parent = fn[mockSymbol];
|
|
102
|
+
fn = fn[mockSymbol]._fetch;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this._fetch = void 0;
|
|
84
106
|
}
|
|
85
107
|
};
|
|
86
108
|
var FetchMock = FetchMockImpl;
|
|
@@ -145,7 +167,7 @@ function sendJson(data, status = 200) {
|
|
|
145
167
|
});
|
|
146
168
|
}
|
|
147
169
|
function sendData(data, status = 200) {
|
|
148
|
-
return new Response(data, {
|
|
170
|
+
return new Response(Buffer.from(data), {
|
|
149
171
|
headers: { "content-type": "application/octet-stream" },
|
|
150
172
|
statusText: STATUS_CODES[status],
|
|
151
173
|
status
|
package/dist/index.mjs.map
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"mappings": ";AAAA,
|
|
4
|
+
"mappings": "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",
|
|
5
5
|
"names": ["info", "init"]
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juit/lib-fetch-mock",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Easy Mocking of Node.js' own `fetch`",
|
|
5
5
|
"main": "./dist/index.cjs",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"author": "Juit Developers <developers@juit.com>",
|
|
29
29
|
"license": "Apache-2.0",
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@plugjs/build": "^0.6.
|
|
32
|
-
"typescript": "^5.
|
|
31
|
+
"@plugjs/build": "^0.6.65",
|
|
32
|
+
"typescript": "^5.9.3"
|
|
33
33
|
},
|
|
34
34
|
"files": [
|
|
35
35
|
"*.md",
|
package/src/index.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { STATUS_CODES } from 'node:http'
|
|
2
1
|
import assert from 'node:assert'
|
|
2
|
+
import { STATUS_CODES } from 'node:http'
|
|
3
3
|
|
|
4
4
|
const mockSymbol = Symbol.for('juit.fetch.mock')
|
|
5
5
|
|
|
6
6
|
type FetchFunction = typeof globalThis.fetch
|
|
7
7
|
type FetchArguments = Parameters<FetchFunction>
|
|
8
|
+
type MaybeMockedFetchFunction = FetchFunction & { [mockSymbol]?: FetchMockImpl }
|
|
8
9
|
|
|
9
10
|
type FetchHandlerResult = Response | number | void | null | undefined
|
|
10
11
|
type FetchHandler = (request: Request, fetch: FetchFunction) => FetchHandlerResult | Promise<FetchHandlerResult>
|
|
@@ -54,7 +55,7 @@ export interface FetchMockConstructor {
|
|
|
54
55
|
|
|
55
56
|
class FetchMockImpl implements FetchMock {
|
|
56
57
|
private _handlers: FetchHandler[] = []
|
|
57
|
-
private _fetch?:
|
|
58
|
+
private _fetch?: MaybeMockedFetchFunction
|
|
58
59
|
private _baseurl: URL
|
|
59
60
|
|
|
60
61
|
constructor(baseurl?: string | URL | undefined) {
|
|
@@ -80,8 +81,8 @@ class FetchMockImpl implements FetchMock {
|
|
|
80
81
|
): Promise<Response> => realFetch.call(globalThis, info, init)
|
|
81
82
|
|
|
82
83
|
const request = (info instanceof URL) || (typeof info === 'string') ?
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
new Request(new URL(info, this._baseurl), init) :
|
|
85
|
+
new Request(info, init)
|
|
85
86
|
|
|
86
87
|
let response: Response | undefined = undefined
|
|
87
88
|
|
|
@@ -93,7 +94,16 @@ class FetchMockImpl implements FetchMock {
|
|
|
93
94
|
break
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
// If we have a response from this mock, return it
|
|
98
|
+
if (response) return response
|
|
99
|
+
|
|
100
|
+
// If we are wrapping another mock, call it
|
|
101
|
+
if (this._fetch[mockSymbol]) {
|
|
102
|
+
return this._fetch[mockSymbol].$fetch(info, init)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Otherwise, return not found (404)
|
|
106
|
+
return sendStatus(404)
|
|
97
107
|
}
|
|
98
108
|
|
|
99
109
|
/* === HANDLERS =========================================================== */
|
|
@@ -152,23 +162,55 @@ class FetchMockImpl implements FetchMock {
|
|
|
152
162
|
/* === INSTALL / DESTROY ================================================== */
|
|
153
163
|
|
|
154
164
|
install(): this {
|
|
165
|
+
// Ignore this if we are already installed at the root of the mock chain
|
|
155
166
|
if (mockSymbol in globalThis.fetch) {
|
|
156
167
|
if ((globalThis as any).fetch[mockSymbol] === this) return this
|
|
157
|
-
assert.fail('Global `fetch` already mocked')
|
|
158
168
|
}
|
|
159
169
|
|
|
170
|
+
// If we are already installed somewhere in the mock chain, fail
|
|
171
|
+
let fn: MaybeMockedFetchFunction | undefined = globalThis.fetch
|
|
172
|
+
while (fn && fn[mockSymbol]) {
|
|
173
|
+
if (fn[mockSymbol] === this) {
|
|
174
|
+
throw new Error('Global `fetch` already mocked by this instance')
|
|
175
|
+
} else {
|
|
176
|
+
fn = fn[mockSymbol]._fetch
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Save the current fetch (either the real one, or another mock)
|
|
160
181
|
this._fetch = globalThis.fetch
|
|
161
182
|
|
|
183
|
+
// Install our fetch handler
|
|
162
184
|
globalThis.fetch = this.$fetch.bind(this)
|
|
163
185
|
Object.defineProperty(globalThis.fetch, mockSymbol, { value: this })
|
|
164
186
|
return this
|
|
165
187
|
}
|
|
166
188
|
|
|
167
189
|
destroy(): void {
|
|
190
|
+
// If we're not mocked *globally*, just return
|
|
168
191
|
if (! (mockSymbol in globalThis.fetch)) return
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
192
|
+
|
|
193
|
+
// If we have never been installed, do nothing
|
|
194
|
+
if (! this._fetch) return
|
|
195
|
+
|
|
196
|
+
// Start looking into our chain of mocks
|
|
197
|
+
let parent: FetchMockImpl | undefined = undefined
|
|
198
|
+
let fn = globalThis.fetch as MaybeMockedFetchFunction
|
|
199
|
+
|
|
200
|
+
// Walk the chain of mocks until we find ourselves
|
|
201
|
+
while (fn[mockSymbol]) {
|
|
202
|
+
if (fn[mockSymbol] === this) {
|
|
203
|
+
if (! parent) globalThis.fetch = this._fetch
|
|
204
|
+
else parent._fetch = this._fetch
|
|
205
|
+
break
|
|
206
|
+
} else {
|
|
207
|
+
parent = fn[mockSymbol]
|
|
208
|
+
fn = fn[mockSymbol]._fetch as MaybeMockedFetchFunction
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Forget our saved fetch
|
|
213
|
+
this._fetch = undefined
|
|
172
214
|
}
|
|
173
215
|
}
|
|
174
216
|
|
|
@@ -292,7 +334,7 @@ export function sendJson(data: unknown, status = 200): Response {
|
|
|
292
334
|
|
|
293
335
|
/** Create a `Response` with the specified binary body and an optional status */
|
|
294
336
|
export function sendData(data: Uint8Array, status = 200): Response {
|
|
295
|
-
return new Response(data, {
|
|
337
|
+
return new Response(Buffer.from(data), {
|
|
296
338
|
headers: { 'content-type': 'application/octet-stream' },
|
|
297
339
|
statusText: STATUS_CODES[status],
|
|
298
340
|
status,
|