mockaton 7.6.1 → 7.7.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 +45 -30
- package/_usage_example.js +0 -2
- package/package.json +6 -2
- package/sample-mocks/api/user/scores-full.GET.200.ts +9 -0
- package/sample-mocks/api/user/scores.GET.200.ts +6 -0
- package/src/Dashboard.js +0 -1
- package/src/Filename.js +3 -0
- package/src/MockBroker.js +2 -2
- package/src/MockDispatcher.js +43 -17
- package/src/Mockaton.test.js +0 -2
package/README.md
CHANGED
|
@@ -51,7 +51,7 @@ _Reset_ button is for registering newly added, removed, or renamed mocks.
|
|
|
51
51
|
## Alternatives
|
|
52
52
|
- Chrome DevTools allows for [overriding responses](https://developer.chrome.com/docs/devtools/overrides)
|
|
53
53
|
- Reverse Proxies such as [Burp](https://portswigger.net/burp) are also handy for overriding responses.
|
|
54
|
-
-
|
|
54
|
+
- [Mock Server Worker](https://mswjs.io)
|
|
55
55
|
|
|
56
56
|
### Caveats
|
|
57
57
|
- Syncing the mocks, but the browser extension mentioned above helps.
|
|
@@ -84,11 +84,11 @@ interface Config {
|
|
|
84
84
|
mocksDir: string
|
|
85
85
|
ignore?: RegExp // Defaults to /(\.DS_Store|~)$/
|
|
86
86
|
|
|
87
|
-
staticDir?: string
|
|
87
|
+
staticDir?: string
|
|
88
88
|
|
|
89
89
|
host?: string, // Defaults to 'localhost'
|
|
90
90
|
port?: number // Defaults to 0, which means auto-assigned
|
|
91
|
-
proxyFallback?: string //
|
|
91
|
+
proxyFallback?: string // Target for relaying routes without mocks
|
|
92
92
|
|
|
93
93
|
delay?: number // Defaults to 1200 (ms)
|
|
94
94
|
cookies?: { [label: string]: string }
|
|
@@ -123,7 +123,14 @@ api/user(default).GET.200.json
|
|
|
123
123
|
|
|
124
124
|
---
|
|
125
125
|
|
|
126
|
-
## You can write JSON mocks in JavaScript
|
|
126
|
+
## You can write JSON mocks in JavaScript or TypeScript
|
|
127
|
+
For TypeScript mocks, install [tsx](https://www.npmjs.com/package/tsx) and load it.
|
|
128
|
+
```shell
|
|
129
|
+
npm install --save-dev tsx
|
|
130
|
+
node --import=tsx my-mockaton.js
|
|
131
|
+
```
|
|
132
|
+
---
|
|
133
|
+
|
|
127
134
|
An Object, Array, or String is sent as JSON.
|
|
128
135
|
|
|
129
136
|
`api/foo.GET.200.js`
|
|
@@ -150,14 +157,20 @@ export default function optionalName(request, response) {
|
|
|
150
157
|
}
|
|
151
158
|
```
|
|
152
159
|
|
|
153
|
-
If you need to serve a static `.js` file, put it in `Config.staticDir`.
|
|
160
|
+
If you need to serve a static `.js` file, put it in your `Config.staticDir`.
|
|
154
161
|
|
|
155
162
|
|
|
156
163
|
## File Name Convention
|
|
157
|
-
|
|
164
|
+
This convention is only for files within your `Config.mocksDir`.
|
|
158
165
|
|
|
159
166
|
### Extension
|
|
160
|
-
|
|
167
|
+
|
|
168
|
+
The last 3 dots are reserved for the HTTP Method,
|
|
169
|
+
Response Status Code, and the File Extension.
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
api/user.GET.200.json
|
|
173
|
+
```
|
|
161
174
|
|
|
162
175
|
|
|
163
176
|
### Dynamic Parameters
|
|
@@ -204,32 +217,29 @@ api/foo/.GET.200.json
|
|
|
204
217
|
```
|
|
205
218
|
|
|
206
219
|
---
|
|
220
|
+
## Config
|
|
207
221
|
|
|
208
|
-
|
|
209
|
-
Files under `Config.staticDir` don’t use the filename convention.
|
|
210
|
-
Also, they take precedence over the `GET` mocks in `Config.mockDir`.
|
|
211
|
-
|
|
212
|
-
For example, if you have two files for `GET /foo/bar.jpg`
|
|
213
|
-
```
|
|
214
|
-
my-static-dir/foo/bar.jpg
|
|
215
|
-
my-mocks-dir/foo/bar.jpg.GET.200.jpg // Unreacheable
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
## `Config.proxyFallback`
|
|
222
|
+
### `proxyFallback`
|
|
220
223
|
Lets you specify a target server for serving routes you don’t have mocks for.
|
|
224
|
+
For example, `Config.proxyFallback = 'http://example.com:8080'`
|
|
221
225
|
|
|
222
226
|
|
|
223
|
-
|
|
227
|
+
### `delay` 🕓
|
|
224
228
|
The clock icon next to the mock selector is a checkbox for delaying a
|
|
225
229
|
particular response. They are handy for testing spinners.
|
|
226
230
|
|
|
227
231
|
The delay is globally configurable via `Config.delay = 1200` (milliseconds).
|
|
228
232
|
|
|
229
233
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
234
|
+
### `staticDir`
|
|
235
|
+
Files under `Config.staticDir` don’t use the filename convention.
|
|
236
|
+
Also, they take precedence over the `GET` mocks in `Config.mockDir`.
|
|
237
|
+
|
|
238
|
+
For example, if you have two files for `GET /foo/bar.jpg`
|
|
239
|
+
```
|
|
240
|
+
my-static-dir/foo/bar.jpg
|
|
241
|
+
my-mocks-dir/foo/bar.jpg.GET.200.jpg // Unreacheable
|
|
242
|
+
```
|
|
233
243
|
|
|
234
244
|
Use Case 1: If you have a bunch of static assets you don’t want to add `.GET.200.ext`
|
|
235
245
|
|
|
@@ -237,7 +247,7 @@ Use Case 2: For a standalone demo server. For example,
|
|
|
237
247
|
build your frontend bundle, and serve it from Mockaton.
|
|
238
248
|
|
|
239
249
|
|
|
240
|
-
|
|
250
|
+
### `cookies`
|
|
241
251
|
The selected cookie is sent in every response in the `Set-Cookie` header.
|
|
242
252
|
|
|
243
253
|
The key is just a label used for selecting a particular cookie in the
|
|
@@ -261,7 +271,7 @@ Config.cookies = {
|
|
|
261
271
|
}
|
|
262
272
|
```
|
|
263
273
|
|
|
264
|
-
|
|
274
|
+
### `extraHeaders`
|
|
265
275
|
They are applied last, right before ending the response.
|
|
266
276
|
In other words, they can overwrite the `Content-Type`. Note
|
|
267
277
|
that it's an array and the header name goes in even indices.
|
|
@@ -274,14 +284,14 @@ Config.extraHeaders = [
|
|
|
274
284
|
]
|
|
275
285
|
```
|
|
276
286
|
|
|
277
|
-
|
|
287
|
+
### `extraMimes`
|
|
278
288
|
```js
|
|
279
289
|
Config.extraMimes = {
|
|
280
290
|
jpg: 'application/jpeg'
|
|
281
291
|
}
|
|
282
292
|
```
|
|
283
293
|
|
|
284
|
-
|
|
294
|
+
### `corsAllowed`
|
|
285
295
|
```js
|
|
286
296
|
Config.corsAllowed = true
|
|
287
297
|
|
|
@@ -294,7 +304,7 @@ Config.corsMaxAge = 0 // seconds to cache the preflight req
|
|
|
294
304
|
Config.corsExposedHeaders = [] // headers you need to access in client-side JS
|
|
295
305
|
```
|
|
296
306
|
|
|
297
|
-
|
|
307
|
+
### `onReady`
|
|
298
308
|
This is a callback `(dashboardAddress: string) => void`, which defaults to
|
|
299
309
|
trying to open the dashboard in your default browser in macOS and Windows.
|
|
300
310
|
|
|
@@ -368,8 +378,13 @@ but `Config.proxyFallback` and `Config.corsAllowed` are not affected.
|
|
|
368
378
|
await mockaton.reset()
|
|
369
379
|
```
|
|
370
380
|
|
|
381
|
+
<div style="display: flex; align-items: center; gap: 20px">
|
|
382
|
+
<img src="./sample-mocks/api/user/avatar.GET.200.png" width="170"/>
|
|
383
|
+
<p style="font-size: 18px">“Use Mockaton” - Albert Einstein</p>
|
|
384
|
+
</div>
|
|
385
|
+
|
|
371
386
|
|
|
372
387
|
## TODO
|
|
373
388
|
- Refactor Tests
|
|
374
|
-
- Dashboard. Indicate if some
|
|
375
|
-
- jsonc, json5
|
|
389
|
+
- Dashboard. Indicate if some file on `staticDir` is overriding a mock.
|
|
390
|
+
- jsonc, json5?
|
package/_usage_example.js
CHANGED
package/package.json
CHANGED
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
"name": "mockaton",
|
|
3
3
|
"description": "A deterministic server-side for developing and testing frontend clients",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "7.
|
|
5
|
+
"version": "7.7.0",
|
|
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",
|
|
12
|
-
"demo": "
|
|
12
|
+
"demo": "node _usage_example.js",
|
|
13
|
+
"demo:ts": "node --import=tsx _usage_example.js"
|
|
14
|
+
},
|
|
15
|
+
"optionalDependencies": {
|
|
16
|
+
"tsx": "4.19.1"
|
|
13
17
|
}
|
|
14
18
|
}
|
package/src/Dashboard.js
CHANGED
package/src/Filename.js
CHANGED
|
@@ -19,13 +19,16 @@ export function filenameIsValid(file) {
|
|
|
19
19
|
console.error(error, file)
|
|
20
20
|
return !error
|
|
21
21
|
}
|
|
22
|
+
|
|
22
23
|
function validateFilename(file) {
|
|
23
24
|
const tokens = file.replace(reComments, '').split('.')
|
|
24
25
|
if (tokens.length < 4)
|
|
25
26
|
return 'Invalid Filename Convention'
|
|
27
|
+
|
|
26
28
|
const { status, method } = parseFilename(file)
|
|
27
29
|
if (!responseStatusIsValid(status))
|
|
28
30
|
return `Invalid HTTP Response Status: "${status}"`
|
|
31
|
+
|
|
29
32
|
if (!httpMethods.includes(method))
|
|
30
33
|
return `Unrecognized HTTP Method: "${method}"`
|
|
31
34
|
}
|
package/src/MockBroker.js
CHANGED
|
@@ -64,9 +64,9 @@ export class MockBroker {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
extractComments() {
|
|
67
|
-
|
|
67
|
+
const comments = []
|
|
68
68
|
for (const file of this.mocks)
|
|
69
|
-
comments
|
|
69
|
+
comments.push(...extractComments(file))
|
|
70
70
|
return comments
|
|
71
71
|
}
|
|
72
72
|
|
package/src/MockDispatcher.js
CHANGED
|
@@ -25,33 +25,59 @@ export async function dispatchMock(req, response) {
|
|
|
25
25
|
console.log(decodeURIComponent(req.url), ' → ', file)
|
|
26
26
|
const filePath = join(Config.mocksDir, file)
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
if (file.endsWith('.js')) {
|
|
30
|
-
response.setHeader('Content-Type', mimeFor('.json'))
|
|
31
|
-
const jsExport = (await import(filePath + '?' + Date.now())).default // date for cache busting
|
|
32
|
-
mockBody = typeof jsExport === 'function'
|
|
33
|
-
? await jsExport(req, response)
|
|
34
|
-
: JSON.stringify(jsExport, null, 2)
|
|
35
|
-
}
|
|
36
|
-
else {
|
|
37
|
-
response.setHeader('Content-Type', mimeFor(file))
|
|
38
|
-
mockBody = broker.isTemp500
|
|
39
|
-
? ''
|
|
40
|
-
: read(filePath)
|
|
41
|
-
}
|
|
28
|
+
response.statusCode = status
|
|
42
29
|
|
|
43
30
|
if (cookie.getCurrent())
|
|
44
31
|
response.setHeader('Set-Cookie', cookie.getCurrent())
|
|
45
32
|
|
|
46
|
-
|
|
33
|
+
for (let i = 0; i < Config.extraHeaders.length; i += 2)
|
|
34
|
+
response.setHeader(Config.extraHeaders[i], Config.extraHeaders[i + 1])
|
|
35
|
+
|
|
36
|
+
const [mime, mockBody] = broker.isTemp500
|
|
37
|
+
? temp500Plugin(filePath, req, response)
|
|
38
|
+
: await preprocessPlugins(filePath, req, response)
|
|
39
|
+
|
|
40
|
+
response.setHeader('Content-Type', mime)
|
|
47
41
|
setTimeout(() => response.end(mockBody), delay)
|
|
48
42
|
}
|
|
49
43
|
catch (error) {
|
|
50
44
|
if (error instanceof JsonBodyParserError)
|
|
51
45
|
sendBadRequest(response, error)
|
|
52
|
-
else if (error.code === 'ENOENT')
|
|
53
|
-
sendNotFound(response)
|
|
46
|
+
else if (error.code === 'ENOENT') // mock-file has been deleted
|
|
47
|
+
sendNotFound(response)
|
|
48
|
+
else if (error.code === 'ERR_UNKNOWN_FILE_EXTENSION') {
|
|
49
|
+
if (error.toString().includes('Unknown file extension ".ts"'))
|
|
50
|
+
console.log('Looks like you need a TypeScript compiler\n',
|
|
51
|
+
' npm install tsx\n',
|
|
52
|
+
' node --import=tsx my-mockaton.js\n')
|
|
53
|
+
sendInternalServerError(response, error)
|
|
54
|
+
}
|
|
54
55
|
else
|
|
55
56
|
sendInternalServerError(response, error)
|
|
56
57
|
}
|
|
57
58
|
}
|
|
59
|
+
|
|
60
|
+
// TODO expose to userland for custom plugins such yaml -> json
|
|
61
|
+
async function preprocessPlugins(filePath, req, response) {
|
|
62
|
+
if (filePath.endsWith('.js') || filePath.endsWith('.ts'))
|
|
63
|
+
return await jsPlugin(filePath, req, response)
|
|
64
|
+
return readPlugin(filePath, req, response)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function temp500Plugin(filePath) {
|
|
68
|
+
return [mimeFor(filePath), '']
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function jsPlugin(filePath, req, response) {
|
|
72
|
+
const jsExport = (await import(filePath + '?' + Date.now())).default // date for cache busting
|
|
73
|
+
const mockBody = typeof jsExport === 'function'
|
|
74
|
+
? await jsExport(req, response)
|
|
75
|
+
: JSON.stringify(jsExport, null, 2)
|
|
76
|
+
const mime = response.getHeader('Content-Type') // jsFunc are allowed to set it
|
|
77
|
+
|| mimeFor('.json')
|
|
78
|
+
return [mime, mockBody]
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function readPlugin(filePath) {
|
|
82
|
+
return [mimeFor(filePath), read(filePath)]
|
|
83
|
+
}
|
package/src/Mockaton.test.js
CHANGED
|
@@ -24,7 +24,6 @@ const fixtureCustomMime = [
|
|
|
24
24
|
'api/custom-mime.GET.200.my_custom_extension',
|
|
25
25
|
'Custom Extension and MIME'
|
|
26
26
|
]
|
|
27
|
-
|
|
28
27
|
const fixtureNonDefaultInName = [
|
|
29
28
|
'/api/the-route',
|
|
30
29
|
'api/the-route(default).GET.200.json',
|
|
@@ -35,7 +34,6 @@ const fixtureDefaultInName = [
|
|
|
35
34
|
'api/the-route(default).GET.200.json',
|
|
36
35
|
'default my route body content'
|
|
37
36
|
]
|
|
38
|
-
|
|
39
37
|
const fixtureDelayed = [
|
|
40
38
|
'/api/delayed',
|
|
41
39
|
'api/delayed.GET.200.json',
|