mockaton 8.12.0 → 8.12.2
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/.coverage/lcov.info +2267 -0
- package/README.md +61 -53
- package/package.json +2 -1
- package/src/Mockaton.js +5 -4
- package/src/ProxyRelay.js +1 -1
- package/src/StaticDispatcher.js +1 -5
package/README.md
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
## Convention over Code
|
|
6
|
+
An HTTP mock server for simulating APIs with minimal setup
|
|
7
|
+
— ideal for testing edge cases and prototyping UIs.
|
|
9
8
|
|
|
9
|
+
## Convention Over Code
|
|
10
10
|
With Mockaton you don’t need to write code for wiring mocks. Instead, it scans a
|
|
11
11
|
given directory for filenames following a convention similar to the URLs.
|
|
12
12
|
|
|
@@ -31,12 +31,14 @@ for setting up tests. See **Commander API** section.
|
|
|
31
31
|
</picture>
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
<br/>
|
|
35
|
+
<br/>
|
|
36
|
+
|
|
34
37
|
## Multiple Mock Variants
|
|
35
|
-
Each route can have different mocks. There
|
|
38
|
+
Each route can have different mocks. There are two options for doing that:
|
|
36
39
|
|
|
37
40
|
### Adding comments to the filename
|
|
38
|
-
Comments are anything within parentheses,
|
|
39
|
-
including them. A filename can have many comments.
|
|
41
|
+
Comments are anything within parentheses, including them.
|
|
40
42
|
|
|
41
43
|
<pre>
|
|
42
44
|
api/login<b>(locked out user)</b>.POST.423.json
|
|
@@ -44,8 +46,8 @@ api/login<b>(invalid login attempt)</b>.POST.401.json
|
|
|
44
46
|
</pre>
|
|
45
47
|
|
|
46
48
|
### Different response status code
|
|
47
|
-
For instance,
|
|
48
|
-
responses
|
|
49
|
+
For instance, you can use a `4xx` or `5xx` status code for triggering error
|
|
50
|
+
responses, or a `2xx` such as `204` (No Content) for testing empty collections.
|
|
49
51
|
|
|
50
52
|
<pre>
|
|
51
53
|
api/videos(empty list).GET.<b>204</b>.json
|
|
@@ -54,16 +56,19 @@ api/videos.GET.<b>500</b>.txt
|
|
|
54
56
|
</pre>
|
|
55
57
|
|
|
56
58
|
|
|
59
|
+
<br/>
|
|
57
60
|
|
|
58
|
-
## Fallback to
|
|
59
|
-
No need to mock everything. Mockaton can
|
|
61
|
+
## Fallback to Your Backend
|
|
62
|
+
No need to mock everything. Mockaton can forward requests to your backend for routes
|
|
60
63
|
you don’t have mocks for, or routes that have the ☁️ **Cloud Checkbox** checked.
|
|
61
64
|
|
|
62
65
|
|
|
63
|
-
###
|
|
66
|
+
### Scraping mocks from your backend
|
|
64
67
|
If you check **Save Mocks**, Mockaton will collect the responses that hit your backend.
|
|
65
|
-
They will be saved
|
|
68
|
+
They will be saved in your `config.mocksDir` following the filename convention.
|
|
69
|
+
|
|
66
70
|
|
|
71
|
+
<br/>
|
|
67
72
|
|
|
68
73
|
|
|
69
74
|
## Basic Usage
|
|
@@ -91,11 +96,12 @@ Mockaton({
|
|
|
91
96
|
node --import=tsx my-mockaton.js
|
|
92
97
|
```
|
|
93
98
|
|
|
99
|
+
<br/>
|
|
94
100
|
|
|
95
101
|
## Demo App (Vite)
|
|
96
102
|
|
|
97
103
|
This is a minimal React + Vite + Mockaton app. It’s a list of
|
|
98
|
-
colors
|
|
104
|
+
colors containing all of their possible states. For example,
|
|
99
105
|
permutations for out-of-stock, new-arrival, and discontinued.
|
|
100
106
|
|
|
101
107
|
```sh
|
|
@@ -114,6 +120,7 @@ The app looks like this:
|
|
|
114
120
|
|
|
115
121
|
<img src="./demo-app-vite/pixaton-tests/pic-for-readme.vp500x800.light.gold.png" alt="Mockaton Demo App Screenshot" width="500" />
|
|
116
122
|
|
|
123
|
+
<br/>
|
|
117
124
|
|
|
118
125
|
## Use Cases
|
|
119
126
|
### Testing
|
|
@@ -121,34 +128,35 @@ The app looks like this:
|
|
|
121
128
|
- Spinners by delaying responses
|
|
122
129
|
- Errors such as _Bad Request_ and _Internal Server Error_
|
|
123
130
|
- Setting up UI tests
|
|
131
|
+
- Mocking third-party APIs
|
|
124
132
|
- Polled resources (for triggering their different states)
|
|
125
133
|
- alerts
|
|
126
134
|
- notifications
|
|
127
135
|
- slow to build resources
|
|
128
|
-
- Mocking third-party APIs
|
|
129
136
|
|
|
130
|
-
### Time
|
|
131
|
-
If you commit the mocks to your repo, it’s straightforward to
|
|
132
|
-
|
|
133
|
-
backends to old API contracts or databases.
|
|
137
|
+
### Time travel
|
|
138
|
+
If you commit the mocks to your repo, it’s straightforward to
|
|
139
|
+
bisect bugs and check out long-lived branches, so you don’t
|
|
140
|
+
have to downgrade backends to old API contracts or databases.
|
|
141
|
+
|
|
142
|
+
### Simulating complex backend states
|
|
143
|
+
Sometimes, the ideal flow you need is too difficult to reproduce from your actual backend.
|
|
144
|
+
For this, you can **Bulk Select** mocks by comments to simulate the complete states
|
|
145
|
+
you want. For example, by adding `(demo-part1)`, `(demo-part2)` to the filenames.
|
|
134
146
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
simulate from the actual backend. In this case, compile your frontend app and
|
|
138
|
-
put its built assets in `config.staticDir`. Then, on the dashboard
|
|
139
|
-
**Bulk Select** mocks to simulate the complete states you want to demo.
|
|
140
|
-
For bulk-selecting, you just need to add a comment to the mock
|
|
141
|
-
filename, such as `(demo-part1)`, `(demo-part2)`.
|
|
147
|
+
Similarly, you can deploy a **Standalone Demo Server** by compiling the frontend app and
|
|
148
|
+
putting its built assets in `config.staticDir`. And simulate the flow by Bulk Selecting mocks.
|
|
142
149
|
|
|
143
150
|
|
|
151
|
+
<br/>
|
|
152
|
+
|
|
144
153
|
## Motivation
|
|
145
|
-
- Avoids spinning up and
|
|
146
|
-
-
|
|
154
|
+
- Avoids spinning up and updating hefty backends when developing UIs.
|
|
155
|
+
- Allows for a deterministic, comprehensive, and consistent backend state. For example, having
|
|
147
156
|
a collection with all the possible state variants helps for spotting inadvertent bugs.
|
|
148
|
-
- Sometimes frontend progress is blocked waiting for
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
then showing those contracts to the backend team.
|
|
157
|
+
- Sometimes frontend progress is blocked by waiting for backend APIs.
|
|
158
|
+
|
|
159
|
+
<br/>
|
|
152
160
|
|
|
153
161
|
## Alternatives
|
|
154
162
|
- Chrome DevTools allows for [overriding responses](https://developer.chrome.com/docs/devtools/overrides)
|
|
@@ -158,7 +166,8 @@ filename, such as `(demo-part1)`, `(demo-part2)`.
|
|
|
158
166
|
- [Fetch Mock](https://github.com/wheresrhys/fetch-mock)
|
|
159
167
|
- [Mentoss](https://github.com/humanwhocodes/mentoss)
|
|
160
168
|
|
|
161
|
-
|
|
169
|
+
|
|
170
|
+
<br/>
|
|
162
171
|
|
|
163
172
|
## You can write JSON mocks in JavaScript or TypeScript
|
|
164
173
|
For example, `api/foo.GET.200.js`
|
|
@@ -179,7 +188,7 @@ export default (request, response) =>
|
|
|
179
188
|
JSON.stringify({ foo: 'bar' })
|
|
180
189
|
```
|
|
181
190
|
|
|
182
|
-
Think of these functions as HTTP handlers
|
|
191
|
+
Think of these functions as HTTP handlers that allow you to
|
|
183
192
|
intercept requests. For example, for writing to a database.
|
|
184
193
|
|
|
185
194
|
<details>
|
|
@@ -221,7 +230,7 @@ export default function listColors() {
|
|
|
221
230
|
**What if I need to serve a static .js?**
|
|
222
231
|
Put it in your `config.staticDir` without the mock filename convention.
|
|
223
232
|
|
|
224
|
-
|
|
233
|
+
<br/>
|
|
225
234
|
|
|
226
235
|
## Mock Filename Convention
|
|
227
236
|
|
|
@@ -256,7 +265,7 @@ want a `Content-Type` header in the response.
|
|
|
256
265
|
</p>
|
|
257
266
|
</details>
|
|
258
267
|
|
|
259
|
-
### Dynamic
|
|
268
|
+
### Dynamic parameters
|
|
260
269
|
Anything within square brackets is always matched. For example, for this route
|
|
261
270
|
`/api/company/1234/user/5678`
|
|
262
271
|
<pre>
|
|
@@ -265,14 +274,16 @@ api/company/<b>[id]</b>/user/<b>[uid]</b>.GET.200.json
|
|
|
265
274
|
|
|
266
275
|
### Comments
|
|
267
276
|
Comments are anything within parentheses, including them.
|
|
268
|
-
They are ignored for
|
|
277
|
+
They are ignored for routing purposes, so they have no effect
|
|
269
278
|
on the URL mask. For example, these two are for `/api/foo`
|
|
270
279
|
<pre>
|
|
271
280
|
api/foo<b>(my comment)</b>.GET.200.json
|
|
272
281
|
api/foo.GET.200.json
|
|
273
282
|
</pre>
|
|
274
283
|
|
|
275
|
-
|
|
284
|
+
A filename can have many comments.
|
|
285
|
+
|
|
286
|
+
### Default mock for a route
|
|
276
287
|
You can add the comment: `(default)`.
|
|
277
288
|
Otherwise, the first file in **alphabetical order** wins.
|
|
278
289
|
|
|
@@ -281,7 +292,7 @@ api/user<b>(default)</b>.GET.200.json
|
|
|
281
292
|
</pre>
|
|
282
293
|
|
|
283
294
|
|
|
284
|
-
### Query
|
|
295
|
+
### Query string params
|
|
285
296
|
The query string is ignored when routing to it. In other words, it’s only used for
|
|
286
297
|
documenting the URL contract.
|
|
287
298
|
<pre>
|
|
@@ -307,7 +318,8 @@ api/foo/.GET.200.json
|
|
|
307
318
|
api/foo/bar.GET.200.json
|
|
308
319
|
```
|
|
309
320
|
|
|
310
|
-
|
|
321
|
+
<br/>
|
|
322
|
+
|
|
311
323
|
## Config
|
|
312
324
|
### `mocksDir: string`
|
|
313
325
|
This is the only required field. The directory must exist.
|
|
@@ -354,7 +366,7 @@ a unique filename comment.
|
|
|
354
366
|
|
|
355
367
|
|
|
356
368
|
<details>
|
|
357
|
-
<summary>Extension
|
|
369
|
+
<summary>Extension details</summary>
|
|
358
370
|
<p>
|
|
359
371
|
An <code>.empty</code> extension means the <code>Content-Type</code>
|
|
360
372
|
header was not sent by your backend.
|
|
@@ -378,12 +390,12 @@ of `JSON.stringify(data, null, ' ')` (two spaces indentation).
|
|
|
378
390
|
build your frontend bundle, and serve it from Mockaton.
|
|
379
391
|
|
|
380
392
|
Files under `config.staticDir` don’t use the filename convention, and
|
|
381
|
-
they take precedence over `GET` mocks in `config.mocksDir`.
|
|
393
|
+
they take precedence over corresponding `GET` mocks in `config.mocksDir`.
|
|
382
394
|
For example, if you have two files for `GET /foo/bar.jpg`
|
|
383
395
|
|
|
384
396
|
<pre>
|
|
385
397
|
my-static-dir<b>/foo/bar.jpg</b>
|
|
386
|
-
my-mocks-dir<b>/foo/bar.jpg</b>.GET.200.jpg //
|
|
398
|
+
my-mocks-dir<b>/foo/bar.jpg</b>.GET.200.jpg // Unreachable
|
|
387
399
|
</pre>
|
|
388
400
|
|
|
389
401
|
|
|
@@ -412,7 +424,7 @@ In other words, it’s useful only if you care about its payload.
|
|
|
412
424
|
|
|
413
425
|
|
|
414
426
|
### `extraHeaders?: string[]`
|
|
415
|
-
Note it’s a
|
|
427
|
+
Note: it’s a one-dimensional array. The header name goes at even indices.
|
|
416
428
|
|
|
417
429
|
```js
|
|
418
430
|
config.extraHeaders = [
|
|
@@ -451,7 +463,7 @@ the file from disk and compute the MIME from the extension.
|
|
|
451
463
|
Note: don’t call `response.end()` on any plugin.
|
|
452
464
|
|
|
453
465
|
<details>
|
|
454
|
-
<summary><b> See
|
|
466
|
+
<summary><b> See plugin examples </b></summary>
|
|
455
467
|
|
|
456
468
|
```shell
|
|
457
469
|
npm install yaml
|
|
@@ -512,7 +524,7 @@ config.onReady = () => {}
|
|
|
512
524
|
|
|
513
525
|
At any rate, you can trigger any command besides opening a browser.
|
|
514
526
|
|
|
515
|
-
|
|
527
|
+
<br/>
|
|
516
528
|
|
|
517
529
|
## Commander API
|
|
518
530
|
`Commander` is a client for Mockaton’s HTTP API.
|
|
@@ -536,12 +548,12 @@ Parentheses are optional, so you can pass a partial match.
|
|
|
536
548
|
For example, passing `'demo-'` (without the final `a`), selects the
|
|
537
549
|
first mock in alphabetical order that matches.
|
|
538
550
|
|
|
539
|
-
### Set
|
|
551
|
+
### Set route is delayed flag
|
|
540
552
|
```js
|
|
541
553
|
await mockaton.setRouteIsDelayed('GET', '/api/foo', true)
|
|
542
554
|
```
|
|
543
555
|
|
|
544
|
-
### Set
|
|
556
|
+
### Set route is proxied
|
|
545
557
|
```js
|
|
546
558
|
await mockaton.setRouteIsProxied('GET', '/api/foo', true)
|
|
547
559
|
```
|
|
@@ -552,13 +564,13 @@ In `config.cookies`, each key is the label used for selecting it.
|
|
|
552
564
|
await mockaton.selectCookie('My Normal User')
|
|
553
565
|
```
|
|
554
566
|
|
|
555
|
-
### Set
|
|
567
|
+
### Set fallback proxy
|
|
556
568
|
```js
|
|
557
569
|
await mockaton.setProxyFallback('http://example.com')
|
|
558
570
|
```
|
|
559
571
|
Pass an empty string to disable it.
|
|
560
572
|
|
|
561
|
-
### Set
|
|
573
|
+
### Set save proxied mocks
|
|
562
574
|
```js
|
|
563
575
|
await mockaton.setCollectProxied(true)
|
|
564
576
|
```
|
|
@@ -569,7 +581,3 @@ default, but the `proxyFallback`, `colledProxied`, and `corsAllowed` are not aff
|
|
|
569
581
|
```js
|
|
570
582
|
await mockaton.reset()
|
|
571
583
|
```
|
|
572
|
-
|
|
573
|
-
<div style="display: flex; align-items: center; gap: 20px">
|
|
574
|
-
<img src="fixtures-mocks/api/user/avatar.GET.200.png" width="170"/>
|
|
575
|
-
</div>
|
package/package.json
CHANGED
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
"name": "mockaton",
|
|
3
3
|
"description": "A deterministic server-side for developing and testing APIs",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "8.12.
|
|
5
|
+
"version": "8.12.2",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"repository": "https://github.com/ericfortis/mockaton",
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "node --test \"src/**/*.test.js\"",
|
|
12
|
+
"coverage": "node --test --test-reporter=lcov --test-reporter-destination=.coverage/lcov.info --experimental-test-coverage \"src/**/*.test.js\"",
|
|
12
13
|
"start": "node dev-mockaton.js",
|
|
13
14
|
"start:ts": "node --import=tsx dev-mockaton.js",
|
|
14
15
|
"pixaton": "node --test --import=./pixaton-tests/_setup.js --experimental-test-isolation=none \"pixaton-tests/**/*.test.js\"",
|
package/src/Mockaton.js
CHANGED
|
@@ -20,14 +20,15 @@ export function Mockaton(options) {
|
|
|
20
20
|
watchMocksDir()
|
|
21
21
|
|
|
22
22
|
return createServer(onRequest).listen(config.port, config.host, function (error) {
|
|
23
|
+
if (error) {
|
|
24
|
+
console.error(error)
|
|
25
|
+
return
|
|
26
|
+
}
|
|
23
27
|
const { address, port } = this.address()
|
|
24
28
|
const url = `http://${address}:${port}`
|
|
25
29
|
console.log('Listening', url)
|
|
26
30
|
console.log('Dashboard', url + API.dashboard)
|
|
27
|
-
|
|
28
|
-
console.error(error)
|
|
29
|
-
else
|
|
30
|
-
config.onReady(url + API.dashboard)
|
|
31
|
+
config.onReady(url + API.dashboard)
|
|
31
32
|
})
|
|
32
33
|
}
|
|
33
34
|
|
package/src/ProxyRelay.js
CHANGED
|
@@ -43,7 +43,7 @@ export async function proxy(req, response, delay) {
|
|
|
43
43
|
let data = body
|
|
44
44
|
if (config.formatCollectedJSON && ext === 'json') // TESTME
|
|
45
45
|
try {
|
|
46
|
-
data = JSON.
|
|
46
|
+
data = JSON.stringify(JSON.parse(body), null, ' ')
|
|
47
47
|
}
|
|
48
48
|
catch {}
|
|
49
49
|
write(join(config.mocksDir, filename), data)
|
package/src/StaticDispatcher.js
CHANGED
|
@@ -20,7 +20,7 @@ export async function dispatchStatic(req, response) {
|
|
|
20
20
|
await sendPartialContent(response, req.headers.range, file)
|
|
21
21
|
else {
|
|
22
22
|
response.setHeader('Content-Type', mimeFor(file))
|
|
23
|
-
response.end(readFileSync(file
|
|
23
|
+
response.end(readFileSync(file))
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -57,7 +57,3 @@ async function sendPartialContent(response, range, file) {
|
|
|
57
57
|
})
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|