mockaton 8.2.20 → 8.2.22
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 +31 -32
- package/package.json +1 -1
- package/src/Dashboard.css +0 -1
- package/src/Dashboard.js +1 -10
- package/src/MockDispatcher.js +2 -2
- package/src/MockDispatcherPlugins.js +1 -1
- package/src/Mockaton.js +2 -5
- package/src/Mockaton.test.js +1 -0
- package/src/utils/fs.js +2 -1
- package/src/utils/openInBrowser.js +1 -1
package/README.md
CHANGED
|
@@ -17,20 +17,21 @@ By the way, [this browser
|
|
|
17
17
|
extension](https://github.com/ericfortis/devtools-ext-tar-http-requests)
|
|
18
18
|
can create a TAR of your requests following that convention.
|
|
19
19
|
|
|
20
|
-
Nonetheless, you don’t need to mock all your APIs. Mockaton
|
|
21
|
-
the routes you don’t have mocks for.
|
|
20
|
+
Nonetheless, you don’t need to mock all your APIs. Mockaton
|
|
21
|
+
can request from your backend the routes you don’t have mocks for.
|
|
22
|
+
That’s done with `config.proxyFallback = 'http://mybackend'`
|
|
22
23
|
|
|
23
24
|
## Multiple Mock Variants
|
|
24
25
|
Each route can have many mocks, which could either be:
|
|
25
26
|
- Different response __status code__. For example, for triggering errors.
|
|
26
27
|
- __Comment__ on the filename, which is anything within parentheses.
|
|
27
|
-
|
|
28
|
+
- e.g. `api/login(locked out user).POST.423.json`
|
|
28
29
|
|
|
29
30
|
|
|
30
31
|
## Dashboard UI
|
|
31
32
|
|
|
32
33
|
In the dashboard, you can select a mock variant for a particular
|
|
33
|
-
route, among other options.
|
|
34
|
+
route, among other options. In addition, there’s a programmatic API,
|
|
34
35
|
which is handy for setting up tests (see **Commander API** below).
|
|
35
36
|
|
|
36
37
|
<picture>
|
|
@@ -64,7 +65,7 @@ node --import=tsx my-mockaton.js
|
|
|
64
65
|
|
|
65
66
|
|
|
66
67
|
## Running the Demo Example
|
|
67
|
-
This demo uses the [sample-mocks/](./sample-mocks)
|
|
68
|
+
This demo uses the [sample-mocks/](./sample-mocks) of this repository.
|
|
68
69
|
|
|
69
70
|
```sh
|
|
70
71
|
git clone https://github.com/ericfortis/mockaton.git
|
|
@@ -95,9 +96,9 @@ The _Reset_ button is for registering newly added, removed, or renamed mocks.
|
|
|
95
96
|
- slow to build assets
|
|
96
97
|
|
|
97
98
|
### Time Travel
|
|
98
|
-
If you commit the mocks
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
If you commit the mocks to your repo, it’s straightforward to bisect bugs and
|
|
100
|
+
checking out long-lived branches. In other words, you don’t have to downgrade
|
|
101
|
+
backends to old API contracts or databases.
|
|
101
102
|
|
|
102
103
|
### Deterministic Standalone Demo Server
|
|
103
104
|
Perhaps you need to demo your app, but the ideal flow is too complex to
|
|
@@ -122,15 +123,6 @@ filename, such as `(demo-part1)`, `(demo-part2)`.
|
|
|
122
123
|
- Reverse Proxies such as [Burp](https://portswigger.net/burp) are also handy for overriding responses.
|
|
123
124
|
- [Mock Server Worker](https://mswjs.io)
|
|
124
125
|
|
|
125
|
-
---
|
|
126
|
-
## Default Mock for a Route
|
|
127
|
-
You can add the comment: `(default)` to a filename.
|
|
128
|
-
Otherwise, the first file in **alphabetical order** wins.
|
|
129
|
-
|
|
130
|
-
```
|
|
131
|
-
api/user(default).GET.200.json
|
|
132
|
-
```
|
|
133
|
-
|
|
134
126
|
---
|
|
135
127
|
|
|
136
128
|
## You can write JSON mocks in JavaScript or TypeScript
|
|
@@ -226,6 +218,15 @@ api/foo<b>(my comment)</b>.GET.200.json
|
|
|
226
218
|
api/foo.GET.200.json
|
|
227
219
|
</pre>
|
|
228
220
|
|
|
221
|
+
### Default Mock for a Route
|
|
222
|
+
You can add the comment: `(default)`.
|
|
223
|
+
Otherwise, the first file in **alphabetical order** wins.
|
|
224
|
+
|
|
225
|
+
<pre>
|
|
226
|
+
api/user<b>(default)</b>.GET.200.json
|
|
227
|
+
</pre>
|
|
228
|
+
|
|
229
|
+
|
|
229
230
|
### Query String Params
|
|
230
231
|
The query string is ignored when routing to it. In other words, it’s only used for
|
|
231
232
|
documenting the URL contract.
|
|
@@ -233,13 +234,12 @@ documenting the URL contract.
|
|
|
233
234
|
api/video<b>?limit=[limit]</b>.GET.200.json
|
|
234
235
|
</pre>
|
|
235
236
|
|
|
236
|
-
Speaking of which,
|
|
237
|
+
Speaking of which, on Windows filenames containing "?" are [not
|
|
237
238
|
permitted](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file), but since that’s part of the query string, it’s ignored anyway.
|
|
238
239
|
|
|
239
240
|
|
|
240
|
-
### Index-like
|
|
241
|
-
|
|
242
|
-
`api/foo`, you have two options:
|
|
241
|
+
### Index-like routes
|
|
242
|
+
If you have `api/foo` and `api/foo/bar`, you have two options:
|
|
243
243
|
|
|
244
244
|
**Option A:**
|
|
245
245
|
```
|
|
@@ -272,8 +272,7 @@ Defaults to `/(\.DS_Store|~)$/`
|
|
|
272
272
|
|
|
273
273
|
|
|
274
274
|
### `delay?: number` 🕓
|
|
275
|
-
The
|
|
276
|
-
response. The delay is globally configurable via `config.delay = 1200` (milliseconds).
|
|
275
|
+
The delay is globally configurable, it defaults to `1200` (milliseconds).
|
|
277
276
|
|
|
278
277
|
|
|
279
278
|
### `proxyFallback?: string`
|
|
@@ -309,15 +308,13 @@ config.cookies = {
|
|
|
309
308
|
})
|
|
310
309
|
}
|
|
311
310
|
```
|
|
312
|
-
The selected cookie is
|
|
311
|
+
The selected cookie, which is the first one by default, is sent in every
|
|
312
|
+
response in a `Set-Cookie` header. If you need to send more
|
|
313
|
+
cookies, inject them globally in `config.extraHeaders`.
|
|
313
314
|
|
|
314
315
|
By the way, the `jwtCookie` helper has a hardcoded header and signature.
|
|
315
316
|
In other words, it’s useful only if you care about the payload.
|
|
316
317
|
|
|
317
|
-
If you need to send more than one cookie,
|
|
318
|
-
inject them globally in `config.extraHeaders`.
|
|
319
|
-
|
|
320
|
-
|
|
321
318
|
|
|
322
319
|
### `extraHeaders?: string[]`
|
|
323
320
|
Note it’s a unidimensional array. The header name goes at even indices.
|
|
@@ -337,6 +334,8 @@ config.extraMimes = {
|
|
|
337
334
|
jpe: 'application/jpeg'
|
|
338
335
|
}
|
|
339
336
|
```
|
|
337
|
+
These media types take precedence over the built-in
|
|
338
|
+
[utils/mime.js](src/utils/mime.js), so you can override them.
|
|
340
339
|
|
|
341
340
|
|
|
342
341
|
### `plugins?: [filenameTester: RegExp, plugin: Plugin][]`
|
|
@@ -352,7 +351,7 @@ type Plugin = (
|
|
|
352
351
|
```
|
|
353
352
|
Plugins are for processing mocks before sending them.
|
|
354
353
|
|
|
355
|
-
Note: don’t call `response.end()`
|
|
354
|
+
Note: don’t call `response.end()` on them.
|
|
356
355
|
|
|
357
356
|
<details>
|
|
358
357
|
<summary><b> See Plugin Examples </b></summary>
|
|
@@ -366,7 +365,7 @@ import { readFileSync } from 'node:js'
|
|
|
366
365
|
import { jsToJsonPlugin } from 'mockaton'
|
|
367
366
|
|
|
368
367
|
config.plugins = [
|
|
369
|
-
[/\.(js|ts)$/, jsToJsonPlugin], // Default
|
|
368
|
+
[/\.(js|ts)$/, jsToJsonPlugin], // Default but you need to add it to your list if you need it
|
|
370
369
|
[/\.yml$/, yamlToJsonPlugin],
|
|
371
370
|
[/foo\.GET\.200\.txt$/, capitalizePlugin], // e.g. GET /api/foo would be capitalized
|
|
372
371
|
]
|
|
@@ -401,7 +400,7 @@ config.corsExposedHeaders = [] // headers you need to access in client-side JS
|
|
|
401
400
|
|
|
402
401
|
|
|
403
402
|
### `onReady?: (dashboardUrl: string) => void`
|
|
404
|
-
This defaults to trying to open the dashboard in your default browser
|
|
403
|
+
This defaults to trying to open the dashboard in your default browser on macOS and
|
|
405
404
|
Windows. For a more cross-platform utility, you could `npm install open` and pass it.
|
|
406
405
|
```js
|
|
407
406
|
import open from 'open'
|
|
@@ -434,7 +433,7 @@ await mockaton.select('api/foo.200.GET.json')
|
|
|
434
433
|
```js
|
|
435
434
|
await mockaton.bulkSelectByComment('(demo-a)')
|
|
436
435
|
```
|
|
437
|
-
Parentheses are optional
|
|
436
|
+
Parentheses are optional, so you can pass a partial match.
|
|
438
437
|
For example, passing `'demo-'` (without the final `a`), selects the
|
|
439
438
|
first mock in alphabetical order that matches.
|
|
440
439
|
|
package/package.json
CHANGED
package/src/Dashboard.css
CHANGED
package/src/Dashboard.js
CHANGED
|
@@ -60,8 +60,7 @@ init()
|
|
|
60
60
|
|
|
61
61
|
function App(apiResponses) {
|
|
62
62
|
empty(document.body)
|
|
63
|
-
|
|
64
|
-
.render(DevPanel(apiResponses))
|
|
63
|
+
document.body.appendChild(DevPanel(apiResponses))
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
function DevPanel([brokersByMethod, cookies, comments, corsAllowed, fallbackAddress, staticFiles]) {
|
|
@@ -404,14 +403,6 @@ function empty(node) {
|
|
|
404
403
|
// These are simplified React-compatible implementations.
|
|
405
404
|
// IOW, for switching to React, remove the `createRoot`, `createElement`, `useRef`
|
|
406
405
|
|
|
407
|
-
function createRoot(root) {
|
|
408
|
-
return {
|
|
409
|
-
render(app) {
|
|
410
|
-
root.appendChild(app)
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
406
|
function createElement(elem, props = null, ...children) {
|
|
416
407
|
if (typeof elem === 'function')
|
|
417
408
|
return elem(props)
|
package/src/MockDispatcher.js
CHANGED
|
@@ -3,7 +3,7 @@ import { join } from 'node:path'
|
|
|
3
3
|
import { proxy } from './ProxyRelay.js'
|
|
4
4
|
import { cookie } from './cookie.js'
|
|
5
5
|
import { Config } from './Config.js'
|
|
6
|
-
import {
|
|
6
|
+
import { applyPlugins } from './MockDispatcherPlugins.js'
|
|
7
7
|
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
8
8
|
import { JsonBodyParserError } from './utils/http-request.js'
|
|
9
9
|
import { sendInternalServerError, sendNotFound, sendBadRequest } from './utils/http-response.js'
|
|
@@ -31,7 +31,7 @@ export async function dispatchMock(req, response) {
|
|
|
31
31
|
|
|
32
32
|
const { mime, body } = broker.isTemp500
|
|
33
33
|
? { mime: '', body: '' }
|
|
34
|
-
: await
|
|
34
|
+
: await applyPlugins(join(Config.mocksDir, broker.file), req, response)
|
|
35
35
|
|
|
36
36
|
response.setHeader('Content-Type', mime)
|
|
37
37
|
setTimeout(() => response.end(body), broker.delay)
|
|
@@ -3,7 +3,7 @@ import { mimeFor } from './utils/mime.js'
|
|
|
3
3
|
import { Config } from './Config.js'
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
export async function
|
|
6
|
+
export async function applyPlugins(filePath, req, response) {
|
|
7
7
|
for (const [regex, plugin] of Config.plugins) // TESTME capitalizePlugin
|
|
8
8
|
if (regex.test(filePath))
|
|
9
9
|
return await plugin(filePath, req, response)
|
package/src/Mockaton.js
CHANGED
|
@@ -13,10 +13,8 @@ import { apiPatchRequests, apiGetRequests } from './Api.js'
|
|
|
13
13
|
export function Mockaton(options) {
|
|
14
14
|
setup(options)
|
|
15
15
|
mockBrokerCollection.init()
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
server.listen(Config.port, Config.host, (error) => {
|
|
19
|
-
const { address, port } = server.address()
|
|
16
|
+
return createServer(onRequest).listen(Config.port, Config.host, function (error) {
|
|
17
|
+
const { address, port } = this.address()
|
|
20
18
|
const url = `http://${address}:${port}`
|
|
21
19
|
console.log('Listening', url)
|
|
22
20
|
console.log('Dashboard', url + API.dashboard)
|
|
@@ -25,7 +23,6 @@ export function Mockaton(options) {
|
|
|
25
23
|
else
|
|
26
24
|
Config.onReady(url + API.dashboard)
|
|
27
25
|
})
|
|
28
|
-
return server
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
async function onRequest(req, response) {
|
package/src/Mockaton.test.js
CHANGED
package/src/utils/fs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join } from 'node:path'
|
|
1
|
+
import path, { join } from 'node:path'
|
|
2
2
|
import { lstatSync, readFileSync, readdirSync } from 'node:fs'
|
|
3
3
|
|
|
4
4
|
|
|
@@ -9,4 +9,5 @@ export const read = path => readFileSync(path)
|
|
|
9
9
|
|
|
10
10
|
/** @returns {Array<string>} paths relative to `dir` */
|
|
11
11
|
export const listFilesRecursively = dir => readdirSync(dir, { recursive: true })
|
|
12
|
+
.map(f => f.replaceAll(path.sep, path.posix.sep)) // TESTME
|
|
12
13
|
.filter(f => isFile(join(dir, f)))
|