@schmock/core 2.2.3 → 2.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/LICENSE +21 -0
- package/README.md +35 -0
- package/dist/abort.d.ts +3 -0
- package/dist/abort.d.ts.map +1 -0
- package/dist/abort.js +30 -0
- package/dist/binary.d.ts +8 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +9 -0
- package/dist/builder.d.ts +31 -10
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +804 -203
- package/dist/constants.d.ts +38 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +123 -2
- package/dist/errors.d.ts +14 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +41 -6
- package/dist/helpers.d.ts +9 -0
- package/dist/helpers.d.ts.map +1 -1
- package/dist/helpers.js +18 -2
- package/dist/http-helpers.d.ts +52 -5
- package/dist/http-helpers.d.ts.map +1 -1
- package/dist/http-helpers.js +153 -37
- package/dist/index.d.ts +381 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -3
- package/dist/interceptor.d.ts +11 -1
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +354 -156
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +12 -3
- package/dist/plugin-pipeline.d.ts +11 -4
- package/dist/plugin-pipeline.d.ts.map +1 -1
- package/dist/plugin-pipeline.js +117 -39
- package/dist/response-normalizer.d.ts +16 -0
- package/dist/response-normalizer.d.ts.map +1 -0
- package/dist/response-normalizer.js +316 -0
- package/dist/response-parser.d.ts.map +1 -1
- package/dist/response-parser.js +85 -18
- package/dist/route-matcher.d.ts +3 -0
- package/dist/route-matcher.d.ts.map +1 -1
- package/dist/route-matcher.js +12 -6
- package/dist/types.d.ts +7 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +18 -8
- package/src/audit-core-builder.test.ts +0 -218
- package/src/audit-response-guard.test.ts +0 -38
- package/src/builder.test.ts +0 -289
- package/src/builder.ts +0 -701
- package/src/constants.test.ts +0 -99
- package/src/constants.ts +0 -73
- package/src/debug.test.ts +0 -241
- package/src/delay.test.ts +0 -319
- package/src/dist-shape.test.ts +0 -35
- package/src/errors.test.ts +0 -223
- package/src/errors.ts +0 -130
- package/src/factory.test.ts +0 -133
- package/src/helpers.test.ts +0 -147
- package/src/helpers.ts +0 -58
- package/src/http-helpers.ts +0 -113
- package/src/index.ts +0 -151
- package/src/interceptor.test.ts +0 -291
- package/src/interceptor.ts +0 -320
- package/src/namespace.test.ts +0 -274
- package/src/parser.property.test.ts +0 -594
- package/src/parser.test.ts +0 -148
- package/src/parser.ts +0 -64
- package/src/plugin-pipeline.ts +0 -103
- package/src/plugin-system.test.ts +0 -602
- package/src/response-parser.ts +0 -75
- package/src/response-parsing.test.ts +0 -333
- package/src/route-matcher.ts +0 -69
- package/src/route-matching.test.ts +0 -394
- package/src/server.test.ts +0 -165
- package/src/smart-defaults.test.ts +0 -361
- package/src/steps/async-support.steps.ts +0 -400
- package/src/steps/audit-core-builder.steps.ts +0 -188
- package/src/steps/audit-onerror-tuple.steps.ts +0 -99
- package/src/steps/basic-usage.steps.ts +0 -245
- package/src/steps/developer-experience.steps.ts +0 -204
- package/src/steps/error-handling.steps.ts +0 -345
- package/src/steps/fluent-api.steps.ts +0 -255
- package/src/steps/http-methods.steps.ts +0 -331
- package/src/steps/interceptor.steps.ts +0 -260
- package/src/steps/lifecycle-events.steps.ts +0 -142
- package/src/steps/performance-reliability.steps.ts +0 -423
- package/src/steps/plugin-integration.steps.ts +0 -280
- package/src/steps/request-history.steps.ts +0 -276
- package/src/steps/response-delay.steps.ts +0 -88
- package/src/steps/route-key-format.steps.ts +0 -99
- package/src/steps/standalone-server.steps.ts +0 -233
- package/src/steps/state-concurrency.steps.ts +0 -739
- package/src/steps/stateful-workflows.steps.ts +0 -353
- package/src/types.ts +0 -36
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
|
|
2
|
-
import { expect } from "vitest";
|
|
3
|
-
import { schmock } from "../index";
|
|
4
|
-
import type { CallableMockInstance, Plugin } from "../types";
|
|
5
|
-
|
|
6
|
-
const feature = await loadFeature("../../features/http-methods.feature");
|
|
7
|
-
|
|
8
|
-
describeFeature(feature, ({ Scenario }) => {
|
|
9
|
-
let mock: CallableMockInstance;
|
|
10
|
-
let response: any;
|
|
11
|
-
let responses: any[] = [];
|
|
12
|
-
|
|
13
|
-
Scenario("GET method with query parameters", ({ Given, When, Then }) => {
|
|
14
|
-
Given("I create a mock with a GET search endpoint", () => {
|
|
15
|
-
mock = schmock();
|
|
16
|
-
mock('GET /search', ({ query }) => ({
|
|
17
|
-
results: [],
|
|
18
|
-
query: query.q,
|
|
19
|
-
page: parseInt(query.page || '1'),
|
|
20
|
-
limit: parseInt(query.limit || '10'),
|
|
21
|
-
}));
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
When("I make a GET request to {string}", async (_, path: string) => {
|
|
25
|
-
const [pathname, queryString] = path.split("?");
|
|
26
|
-
const query: Record<string, string> = {};
|
|
27
|
-
if (queryString) {
|
|
28
|
-
queryString.split("&").forEach((param) => {
|
|
29
|
-
const [key, value] = param.split("=");
|
|
30
|
-
query[key] = value;
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
response = await mock.handle('GET', pathname, { query });
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
Then("I should receive GET method response:", (_, docString: string) => {
|
|
37
|
-
const expected = JSON.parse(docString);
|
|
38
|
-
expect(response.body).toEqual(expected);
|
|
39
|
-
});
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
Scenario("POST method with JSON body", ({ Given, When, Then, And }) => {
|
|
43
|
-
Given("I create a mock with a POST users endpoint", () => {
|
|
44
|
-
mock = schmock();
|
|
45
|
-
mock('POST /users', ({ body }) => [201, {
|
|
46
|
-
id: 123,
|
|
47
|
-
...(body as Record<string, unknown>),
|
|
48
|
-
createdAt: '2023-01-01T00:00:00Z',
|
|
49
|
-
}]);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
When("I make a POST request to {string} with JSON body:", async (_, path: string, docString: string) => {
|
|
53
|
-
const body = JSON.parse(docString);
|
|
54
|
-
response = await mock.handle('POST', path, { body });
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
58
|
-
expect(response.status).toBe(status);
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
And("I should receive POST method response:", (_, docString: string) => {
|
|
62
|
-
const expected = JSON.parse(docString);
|
|
63
|
-
expect(response.body).toEqual(expected);
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
Scenario("PUT method for resource updates", ({ Given, When, Then }) => {
|
|
68
|
-
Given("I create a mock with a PUT users endpoint", () => {
|
|
69
|
-
mock = schmock();
|
|
70
|
-
mock('PUT /users/:id', ({ params, body }) => ({
|
|
71
|
-
id: parseInt(params.id),
|
|
72
|
-
...(body as Record<string, unknown>),
|
|
73
|
-
updatedAt: '2023-01-01T00:00:00Z',
|
|
74
|
-
}));
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
When("I make a PUT request to {string} with JSON body:", async (_, path: string, docString: string) => {
|
|
78
|
-
const body = JSON.parse(docString);
|
|
79
|
-
response = await mock.handle('PUT', path, { body });
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
Then("I should receive PUT method response:", (_, docString: string) => {
|
|
83
|
-
const expected = JSON.parse(docString);
|
|
84
|
-
expect(response.body).toEqual(expected);
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
Scenario("DELETE method with confirmation", ({ Given, When, Then, And }) => {
|
|
89
|
-
Given("I create a mock with a DELETE users endpoint", () => {
|
|
90
|
-
mock = schmock();
|
|
91
|
-
mock('DELETE /users/:id', ({ params }) => [204, null]);
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
When("I make a DELETE request to {string}", async (_, path: string) => {
|
|
95
|
-
response = await mock.handle('DELETE', path);
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
99
|
-
expect(response.status).toBe(status);
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
And("the DELETE response body should be empty", () => {
|
|
103
|
-
expect(response.body).toBeUndefined();
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
Scenario("PATCH method for partial updates", ({ Given, When, Then }) => {
|
|
108
|
-
Given("I create a mock with a PATCH users endpoint", () => {
|
|
109
|
-
mock = schmock();
|
|
110
|
-
mock('PATCH /users/:id', ({ params, body }) => ({
|
|
111
|
-
id: parseInt(params.id),
|
|
112
|
-
email: 'existing@example.com',
|
|
113
|
-
...(body as Record<string, unknown>),
|
|
114
|
-
updatedAt: '2023-01-01T00:00:00Z',
|
|
115
|
-
}));
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
When("I make a PATCH request to {string} with JSON body:", async (_, path: string, docString: string) => {
|
|
119
|
-
const body = JSON.parse(docString);
|
|
120
|
-
response = await mock.handle('PATCH', path, { body });
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
Then("I should receive PATCH method response:", (_, docString: string) => {
|
|
124
|
-
const expected = JSON.parse(docString);
|
|
125
|
-
expect(response.body).toEqual(expected);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
Scenario("HEAD method returns headers only", ({ Given, When, Then, And }) => {
|
|
130
|
-
Given("I create a mock with a HEAD users endpoint", () => {
|
|
131
|
-
mock = schmock();
|
|
132
|
-
mock('HEAD /users/:id', ({ params }) => [200, null, {
|
|
133
|
-
'Content-Type': 'application/json',
|
|
134
|
-
'Last-Modified': 'Wed, 01 Jan 2023 00:00:00 GMT',
|
|
135
|
-
'Content-Length': '156',
|
|
136
|
-
}]);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
When("I make a HEAD request to {string}", async (_, path: string) => {
|
|
140
|
-
response = await mock.handle('HEAD', path);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
144
|
-
expect(response.status).toBe(status);
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
And("the HEAD response body should be empty", () => {
|
|
148
|
-
expect(response.body).toBeUndefined();
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
And("the HEAD response should have proper headers set", () => {
|
|
152
|
-
expect(response.headers?.['Content-Type']).toBe('application/json');
|
|
153
|
-
expect(response.headers?.['Last-Modified']).toBe('Wed, 01 Jan 2023 00:00:00 GMT');
|
|
154
|
-
expect(response.headers?.['Content-Length']).toBe('156');
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
Scenario("OPTIONS method for CORS preflight", ({ Given, When, Then, And }) => {
|
|
159
|
-
Given("I create a mock with an OPTIONS users endpoint", () => {
|
|
160
|
-
mock = schmock();
|
|
161
|
-
mock('OPTIONS /api/users', () => [200, null, {
|
|
162
|
-
'Access-Control-Allow-Origin': '*',
|
|
163
|
-
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
|
|
164
|
-
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
|
|
165
|
-
}]);
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
When("I make an OPTIONS request to {string}", async (_, path: string) => {
|
|
169
|
-
response = await mock.handle('OPTIONS', path);
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
173
|
-
expect(response.status).toBe(status);
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
And("the OPTIONS response body should be empty", () => {
|
|
177
|
-
expect(response.body).toBeUndefined();
|
|
178
|
-
});
|
|
179
|
-
|
|
180
|
-
And("the OPTIONS response should have header {string} with value {string}", (_, headerName: string, headerValue: string) => {
|
|
181
|
-
expect(response.headers?.[headerName]).toBe(headerValue);
|
|
182
|
-
});
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
Scenario("Multiple methods on same path", ({ Given, When, Then, And }) => {
|
|
186
|
-
Given("I create a mock with GET, POST, PUT, and DELETE on the same path", () => {
|
|
187
|
-
mock = schmock();
|
|
188
|
-
mock('GET /resource', { action: 'read' });
|
|
189
|
-
mock('POST /resource', { action: 'create' });
|
|
190
|
-
mock('PUT /resource', { action: 'update' });
|
|
191
|
-
mock('DELETE /resource', { action: 'delete' });
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
When("I test all methods on {string}", async (_, path: string) => {
|
|
195
|
-
responses = [];
|
|
196
|
-
responses.push(await mock.handle('GET', path));
|
|
197
|
-
responses.push(await mock.handle('POST', path));
|
|
198
|
-
responses.push(await mock.handle('PUT', path));
|
|
199
|
-
responses.push(await mock.handle('DELETE', path));
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
Then("the GET method should return:", (_, docString: string) => {
|
|
203
|
-
const expected = JSON.parse(docString);
|
|
204
|
-
expect(responses[0].body).toEqual(expected);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
And("the POST method should return:", (_, docString: string) => {
|
|
208
|
-
const expected = JSON.parse(docString);
|
|
209
|
-
expect(responses[1].body).toEqual(expected);
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
And("the PUT method should return:", (_, docString: string) => {
|
|
213
|
-
const expected = JSON.parse(docString);
|
|
214
|
-
expect(responses[2].body).toEqual(expected);
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
And("the DELETE method should return:", (_, docString: string) => {
|
|
218
|
-
const expected = JSON.parse(docString);
|
|
219
|
-
expect(responses[3].body).toEqual(expected);
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
Scenario("Method-specific content types", ({ Given, When, Then, And }) => {
|
|
224
|
-
Given("I create a mock with JSON, XML, text, and upload endpoints", () => {
|
|
225
|
-
mock = schmock();
|
|
226
|
-
mock('GET /data.json', { data: 'json' });
|
|
227
|
-
mock('GET /data.xml', '<data>xml</data>', { contentType: 'application/xml' });
|
|
228
|
-
mock('GET /data.txt', 'plain text data');
|
|
229
|
-
mock('POST /upload', 'File uploaded successfully', { contentType: 'text/plain' });
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
When("I test method-specific content types", async () => {
|
|
233
|
-
responses = [];
|
|
234
|
-
responses.push(await mock.handle('GET', '/data.json'));
|
|
235
|
-
responses.push(await mock.handle('GET', '/data.xml'));
|
|
236
|
-
responses.push(await mock.handle('GET', '/data.txt'));
|
|
237
|
-
responses.push(await mock.handle('POST', '/upload'));
|
|
238
|
-
});
|
|
239
|
-
|
|
240
|
-
Then("the JSON endpoint should have content-type {string}", (_, contentType: string) => {
|
|
241
|
-
expect(responses[0].headers?.["content-type"]).toBe(contentType);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
And("the XML endpoint should have content-type {string}", (_, contentType: string) => {
|
|
245
|
-
expect(responses[1].headers?.["content-type"]).toBe(contentType);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
And("the text endpoint should have content-type {string}", (_, contentType: string) => {
|
|
249
|
-
expect(responses[2].headers?.["content-type"]).toBe(contentType);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
And("the upload endpoint should have content-type {string}", (_, contentType: string) => {
|
|
253
|
-
expect(responses[3].headers?.["content-type"]).toBe(contentType);
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
|
|
257
|
-
Scenario("Method with request headers validation", ({ Given, When, Then, And }) => {
|
|
258
|
-
Given("I create a mock with authorization header checking", () => {
|
|
259
|
-
mock = schmock();
|
|
260
|
-
mock('POST /secure', ({ headers, body }) => {
|
|
261
|
-
if (headers.authorization !== 'Bearer valid-token') {
|
|
262
|
-
return [401, { error: 'Unauthorized' }];
|
|
263
|
-
}
|
|
264
|
-
return [200, { message: 'Success', data: body }];
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
When("I make a POST request with valid headers", async () => {
|
|
269
|
-
response = await mock.handle('POST', '/secure', {
|
|
270
|
-
headers: { authorization: 'Bearer valid-token' },
|
|
271
|
-
body: { test: true }
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
Then("I should receive authorized response:", (_, docString: string) => {
|
|
276
|
-
const expected = JSON.parse(docString);
|
|
277
|
-
expect(response.body).toEqual(expected);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
When("I make a POST request with invalid headers", async () => {
|
|
281
|
-
response = await mock.handle('POST', '/secure', {
|
|
282
|
-
headers: { authorization: 'Bearer invalid-token' },
|
|
283
|
-
body: { test: true }
|
|
284
|
-
});
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
Then("I should receive status {int}", (_, status: number) => {
|
|
288
|
-
expect(response.status).toBe(status);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
And("I should receive unauthorized response:", (_, docString: string) => {
|
|
292
|
-
const expected = JSON.parse(docString);
|
|
293
|
-
expect(response.body).toEqual(expected);
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
Scenario("Method chaining with plugins", ({ Given, When, Then, And }) => {
|
|
298
|
-
Given("I create a mock with a logger plugin on GET and POST", () => {
|
|
299
|
-
mock = schmock();
|
|
300
|
-
const loggerPlugin: Plugin = {
|
|
301
|
-
name: 'method-logger',
|
|
302
|
-
process: (ctx, response) => ({
|
|
303
|
-
context: ctx,
|
|
304
|
-
response: {
|
|
305
|
-
...response,
|
|
306
|
-
method: ctx.method,
|
|
307
|
-
logged: true,
|
|
308
|
-
},
|
|
309
|
-
}),
|
|
310
|
-
};
|
|
311
|
-
mock('GET /logged', { data: 'get' }).pipe(loggerPlugin);
|
|
312
|
-
mock('POST /logged', { data: 'post' }).pipe(loggerPlugin);
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
When("I test method chaining with plugins", async () => {
|
|
316
|
-
responses = [];
|
|
317
|
-
responses.push(await mock.handle('GET', '/logged'));
|
|
318
|
-
responses.push(await mock.handle('POST', '/logged'));
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
Then("the GET with plugin should return:", (_, docString: string) => {
|
|
322
|
-
const expected = JSON.parse(docString);
|
|
323
|
-
expect(responses[0].body).toEqual(expected);
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
And("the POST with plugin should return:", (_, docString: string) => {
|
|
327
|
-
const expected = JSON.parse(docString);
|
|
328
|
-
expect(responses[1].body).toEqual(expected);
|
|
329
|
-
});
|
|
330
|
-
});
|
|
331
|
-
});
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
/// <reference path="../../schmock.d.ts" />
|
|
2
|
-
|
|
3
|
-
import { describeFeature, loadFeature } from "@amiceli/vitest-cucumber";
|
|
4
|
-
import { expect, vi } from "vitest";
|
|
5
|
-
import { schmock } from "../index.js";
|
|
6
|
-
|
|
7
|
-
const feature = await loadFeature("../../features/fetch-interceptor.feature");
|
|
8
|
-
|
|
9
|
-
describeFeature(feature, ({ Scenario, AfterEachScenario }) => {
|
|
10
|
-
let mock: Schmock.CallableMockInstance;
|
|
11
|
-
let handle: Schmock.InterceptHandle | undefined;
|
|
12
|
-
let originalFetch: typeof globalThis.fetch;
|
|
13
|
-
let savedFetch: ReturnType<typeof vi.fn>;
|
|
14
|
-
let fetchResponse: Response | undefined;
|
|
15
|
-
|
|
16
|
-
function setup() {
|
|
17
|
-
originalFetch = globalThis.fetch;
|
|
18
|
-
savedFetch = vi.fn().mockResolvedValue(new Response("real backend"));
|
|
19
|
-
globalThis.fetch = savedFetch;
|
|
20
|
-
mock = schmock();
|
|
21
|
-
fetchResponse = undefined;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
AfterEachScenario(() => {
|
|
25
|
-
handle?.restore();
|
|
26
|
-
handle = undefined;
|
|
27
|
-
globalThis.fetch = originalFetch;
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
Scenario("Intercept a matched fetch request", ({ Given, When, Then, And }) => {
|
|
31
|
-
Given("a Schmock instance with route \"GET /api/users\" returning users", () => {
|
|
32
|
-
setup();
|
|
33
|
-
mock("GET /api/users", [{ id: 1, name: "Alice" }]);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
And("fetch is intercepted", () => {
|
|
37
|
-
handle = mock.intercept();
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
When("I fetch \"/api/users\"", async () => {
|
|
41
|
-
fetchResponse = await fetch("http://localhost/api/users");
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
Then("the fetch response status should be 200", () => {
|
|
45
|
-
expect(fetchResponse?.status).toBe(200);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
And("the fetch response body should be the mocked users", async () => {
|
|
49
|
-
const body = await fetchResponse?.json();
|
|
50
|
-
expect(body).toEqual([{ id: 1, name: "Alice" }]);
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
Scenario("Passthrough for unmatched routes", ({ Given, When, Then, And }) => {
|
|
55
|
-
Given("a Schmock instance with route \"GET /api/users\" returning users", () => {
|
|
56
|
-
setup();
|
|
57
|
-
mock("GET /api/users", [{ id: 1 }]);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
And("fetch is intercepted with passthrough enabled", () => {
|
|
61
|
-
handle = mock.intercept({ passthrough: true });
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
When("I fetch \"/api/other\"", async () => {
|
|
65
|
-
fetchResponse = await fetch("http://localhost/api/other");
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
Then("the original fetch should have been called", () => {
|
|
69
|
-
expect(savedFetch).toHaveBeenCalled();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
Scenario("Passthrough disabled returns 404", ({ Given, When, Then, And }) => {
|
|
74
|
-
Given("a Schmock instance with route \"GET /api/users\" returning users", () => {
|
|
75
|
-
setup();
|
|
76
|
-
mock("GET /api/users", [{ id: 1 }]);
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
And("fetch is intercepted with passthrough disabled", () => {
|
|
80
|
-
handle = mock.intercept({ passthrough: false });
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
When("I fetch \"/api/other\"", async () => {
|
|
84
|
-
fetchResponse = await fetch("http://localhost/api/other");
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
Then("the fetch response status should be 404", () => {
|
|
88
|
-
expect(fetchResponse?.status).toBe(404);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
Scenario("Restore puts original fetch back", ({ Given, When, Then, And }) => {
|
|
93
|
-
let savedRef: typeof globalThis.fetch;
|
|
94
|
-
|
|
95
|
-
Given("a Schmock instance with route \"GET /api/users\" returning users", () => {
|
|
96
|
-
setup();
|
|
97
|
-
mock("GET /api/users", [{ id: 1 }]);
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
And("fetch is intercepted", () => {
|
|
101
|
-
savedRef = globalThis.fetch;
|
|
102
|
-
handle = mock.intercept();
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
When("I restore the interceptor", () => {
|
|
106
|
-
handle?.restore();
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
Then("globalThis.fetch should be the original function", () => {
|
|
110
|
-
expect(globalThis.fetch).toBe(savedRef);
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
Scenario("BaseUrl filters which requests are intercepted", ({ Given, When, Then, And }) => {
|
|
115
|
-
Given("a Schmock instance with route \"GET /api/users\" returning users", () => {
|
|
116
|
-
setup();
|
|
117
|
-
mock("GET /api/users", [{ id: 1 }]);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
And("fetch is intercepted with baseUrl \"/api\"", () => {
|
|
121
|
-
handle = mock.intercept({ baseUrl: "/api" });
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
When("I fetch \"/other/endpoint\"", async () => {
|
|
125
|
-
await fetch("http://localhost/other/endpoint");
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
Then("the original fetch should have been called", () => {
|
|
129
|
-
expect(savedFetch).toHaveBeenCalled();
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
Scenario("Origin-form baseUrl intercepts matching origin only", ({ Given, When, Then, And }) => {
|
|
134
|
-
Given("a Schmock instance with route \"GET /api/users\" returning users", () => {
|
|
135
|
-
setup();
|
|
136
|
-
mock("GET /api/users", [{ id: 1 }]);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
And("fetch is intercepted with baseUrl \"https://api.example.com\"", () => {
|
|
140
|
-
handle = mock.intercept({ baseUrl: "https://api.example.com" });
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
When("I fetch \"https://api.example.com/api/users\"", async () => {
|
|
144
|
-
fetchResponse = await fetch("https://api.example.com/api/users");
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
Then("the response status should be {int}", (_, status: number) => {
|
|
148
|
-
expect(fetchResponse?.status).toBe(status);
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
When("I fetch \"https://other.example.com/api/users\"", async () => {
|
|
152
|
-
await fetch("https://other.example.com/api/users");
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
Then("the original fetch should have been called", () => {
|
|
156
|
-
expect(savedFetch).toHaveBeenCalled();
|
|
157
|
-
});
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
Scenario("Origin-form baseUrl with a path prefix requires both to match", ({ Given, When, Then, And }) => {
|
|
161
|
-
Given("a Schmock instance with route \"GET /v1/users\" returning users", () => {
|
|
162
|
-
setup();
|
|
163
|
-
mock("GET /v1/users", [{ id: 1 }]);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
And("fetch is intercepted with baseUrl \"https://api.example.com/v1\"", () => {
|
|
167
|
-
handle = mock.intercept({ baseUrl: "https://api.example.com/v1" });
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
When("I fetch \"https://api.example.com/v1/users\"", async () => {
|
|
171
|
-
fetchResponse = await fetch("https://api.example.com/v1/users");
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
Then("the response status should be {int}", (_, status: number) => {
|
|
175
|
-
expect(fetchResponse?.status).toBe(status);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
When("I fetch \"https://api.example.com/users\"", async () => {
|
|
179
|
-
await fetch("https://api.example.com/users");
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
Then("the original fetch should have been called", () => {
|
|
183
|
-
expect(savedFetch).toHaveBeenCalled();
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
Scenario("beforeRequest hook modifies the request", ({ Given, When, Then, And }) => {
|
|
188
|
-
Given("a Schmock instance with route \"GET /api/users\" that reads headers", () => {
|
|
189
|
-
setup();
|
|
190
|
-
mock("GET /api/users", ({ headers }) => [200, { token: headers["x-token"] }]);
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
And("fetch is intercepted with a beforeRequest hook that adds a header", () => {
|
|
194
|
-
handle = mock.intercept({
|
|
195
|
-
beforeRequest: (req) => ({
|
|
196
|
-
...req,
|
|
197
|
-
headers: { ...req.headers, "x-token": "injected" },
|
|
198
|
-
}),
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
When("I fetch \"/api/users\"", async () => {
|
|
203
|
-
fetchResponse = await fetch("http://localhost/api/users");
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
Then("the response should contain the injected header value", async () => {
|
|
207
|
-
const body = await fetchResponse?.json();
|
|
208
|
-
expect(body).toEqual({ token: "injected" });
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
Scenario("beforeResponse hook modifies the response", ({ Given, When, Then, And }) => {
|
|
213
|
-
Given("a Schmock instance with route \"GET /api/users\" returning users", () => {
|
|
214
|
-
setup();
|
|
215
|
-
mock("GET /api/users", [{ id: 1 }]);
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
And("fetch is intercepted with a beforeResponse hook that adds a header", () => {
|
|
219
|
-
handle = mock.intercept({
|
|
220
|
-
beforeResponse: (res) => ({
|
|
221
|
-
...res,
|
|
222
|
-
headers: { ...res.headers, "x-mock": "true" },
|
|
223
|
-
}),
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
When("I fetch \"/api/users\"", async () => {
|
|
228
|
-
fetchResponse = await fetch("http://localhost/api/users");
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
Then("the fetch response should have the injected header", () => {
|
|
232
|
-
expect(fetchResponse?.headers.get("x-mock")).toBe("true");
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
Scenario("Double intercept throws an error", ({ Given, When, Then, And }) => {
|
|
237
|
-
let error: Error | undefined;
|
|
238
|
-
|
|
239
|
-
Given("a Schmock instance with route \"GET /api/users\" returning users", () => {
|
|
240
|
-
setup();
|
|
241
|
-
mock("GET /api/users", [{ id: 1 }]);
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
And("fetch is intercepted", () => {
|
|
245
|
-
handle = mock.intercept();
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
When("I try to intercept again", () => {
|
|
249
|
-
try {
|
|
250
|
-
mock.intercept();
|
|
251
|
-
} catch (e) {
|
|
252
|
-
error = e as Error;
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
Then("it should throw an error about already intercepting", () => {
|
|
257
|
-
expect(error?.message).toMatch(/already intercepting/i);
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
});
|