mockaton 6.3.10 → 6.3.12
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 +60 -59
- package/Tests.js +12 -1
- package/package.json +1 -1
- package/src/Api.js +5 -4
- package/src/MockBroker.js +4 -0
- package/src/mockBrokersCollection.js +5 -4
- package/src/utils/fs.js +1 -0
- package/src/utils/http-response.js +4 -3
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Mockaton
|
|
1
|
+
# Mockaton
|
|
2
2
|
_Mockaton_ is a mock server for developing and testing frontends.
|
|
3
3
|
|
|
4
4
|
It scans a given directory for files following a specific
|
|
@@ -30,9 +30,10 @@ Create a `my-mockaton.js` file
|
|
|
30
30
|
import { resolve } from 'node:path'
|
|
31
31
|
import { Mockaton } from 'mockaton'
|
|
32
32
|
|
|
33
|
+
|
|
33
34
|
Mockaton({
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
mocksDir: resolve('my-mocks-dir'),
|
|
36
|
+
port: 2345
|
|
36
37
|
})
|
|
37
38
|
```
|
|
38
39
|
|
|
@@ -43,21 +44,21 @@ node my-mockaton.js
|
|
|
43
44
|
## Config Options
|
|
44
45
|
```ts
|
|
45
46
|
interface Config {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
47
|
+
mocksDir: string
|
|
48
|
+
ignore?: RegExp // defaults to /(\.DS_Store|~)$/
|
|
49
|
+
|
|
50
|
+
staticDir?: string
|
|
51
|
+
|
|
52
|
+
host?: string, // defaults to 'localhost'
|
|
53
|
+
port?: number // defaults to 0, which means auto-assigned
|
|
54
|
+
proxyFallback?: string // e.g. http://localhost:9999 Target for relaying routes without mocks
|
|
55
|
+
|
|
56
|
+
delay?: number // defaults to 1200 (ms)
|
|
57
|
+
cookies?: { [label: string]: string }
|
|
58
|
+
extraMimes?: { [fileExt: string]: string }
|
|
59
|
+
extraHeaders?: []
|
|
60
|
+
|
|
61
|
+
onReady?: (dashboardUrl: string) => void // defaults to trying to open macOS default browser. pass a noop to prevent opening the dashboard
|
|
61
62
|
}
|
|
62
63
|
```
|
|
63
64
|
|
|
@@ -65,9 +66,9 @@ interface Config {
|
|
|
65
66
|
|
|
66
67
|
## Mock Variants
|
|
67
68
|
Each route can have many mocks, which could either be:
|
|
68
|
-
- Different response __status code__. For example, for testing error responses.
|
|
69
|
+
- Different response __status code__. For example, for testing error responses.
|
|
69
70
|
- __Comment__ on the filename, which is anything within parentheses.
|
|
70
|
-
|
|
71
|
+
- e.g. `api/user(my-comment).POST.201.json`
|
|
71
72
|
|
|
72
73
|
Those alternatives can be manually selected in the dashboard
|
|
73
74
|
UI, or programmatically, for instance, for setting up tests.
|
|
@@ -80,7 +81,7 @@ An Object, Array, or String is sent as JSON.
|
|
|
80
81
|
`api/foo.GET.200.js`
|
|
81
82
|
```js
|
|
82
83
|
export default [
|
|
83
|
-
|
|
84
|
+
{ id: 0 }
|
|
84
85
|
]
|
|
85
86
|
```
|
|
86
87
|
|
|
@@ -94,10 +95,10 @@ database, or pull data from a backend. The `request` is of type
|
|
|
94
95
|
`response` a [ServerResponse](https://nodejs.org/api/http.html#class-httpserverresponse).
|
|
95
96
|
```js
|
|
96
97
|
export default function optionalName(request, response) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
globalThis.myDatabase ??= { count: 0 }
|
|
99
|
+
globalThis.myDatabase.count++
|
|
100
|
+
|
|
101
|
+
return JSON.stringify({ a: 1 })
|
|
101
102
|
}
|
|
102
103
|
```
|
|
103
104
|
|
|
@@ -107,7 +108,6 @@ export default function optionalName(request, response) {
|
|
|
107
108
|
server for serving routes you don’t have mocks for.
|
|
108
109
|
|
|
109
110
|
|
|
110
|
-
|
|
111
111
|
## Delay 🕓
|
|
112
112
|
The clock icon next to the mock selector is a checkbox for delaying a
|
|
113
113
|
particular response. They are handy for testing spinners.
|
|
@@ -115,7 +115,6 @@ particular response. They are handy for testing spinners.
|
|
|
115
115
|
The delay is globally configurable via `Config.delay = 1200` (milliseconds).
|
|
116
116
|
|
|
117
117
|
|
|
118
|
-
|
|
119
118
|
## File Name Convention
|
|
120
119
|
|
|
121
120
|
|
|
@@ -167,7 +166,6 @@ api/foo/.GET.200.json
|
|
|
167
166
|
```
|
|
168
167
|
|
|
169
168
|
|
|
170
|
-
|
|
171
169
|
## `Config.cookies`
|
|
172
170
|
The selected cookie is sent in every response in the `Set-Cookie` header.
|
|
173
171
|
|
|
@@ -181,13 +179,14 @@ words, it’s useful if you only care about its payload.
|
|
|
181
179
|
```js
|
|
182
180
|
import { jwtCookie } from 'mockaton'
|
|
183
181
|
|
|
182
|
+
|
|
184
183
|
Config.cookies = {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
184
|
+
'My Admin User': 'my-cookie=1;Path=/;SameSite=strict',
|
|
185
|
+
'My Normal User': 'my-cookie=0;Path=/;SameSite=strict',
|
|
186
|
+
'My JWT': jwtCookie('my-cookie', {
|
|
187
|
+
email: 'john.doe@example.com',
|
|
188
|
+
picture: 'https://cdn.auth0.com/avatars/jd.png'
|
|
189
|
+
})
|
|
191
190
|
}
|
|
192
191
|
```
|
|
193
192
|
|
|
@@ -198,16 +197,16 @@ that it's an array and the header name goes in even indices.
|
|
|
198
197
|
|
|
199
198
|
```js
|
|
200
199
|
Config.extraHeaders = [
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
200
|
+
'Server', 'Mockaton',
|
|
201
|
+
'Set-Cookie', 'foo=FOO;Path=/;SameSite=strict',
|
|
202
|
+
'Set-Cookie', 'bar=BAR;Path=/;SameSite=strict'
|
|
204
203
|
]
|
|
205
204
|
```
|
|
206
205
|
|
|
207
206
|
## `Config.extraMimes`
|
|
208
207
|
```js
|
|
209
208
|
Config.extraMimes = {
|
|
210
|
-
|
|
209
|
+
jpg: 'application/jpeg'
|
|
211
210
|
}
|
|
212
211
|
```
|
|
213
212
|
|
|
@@ -218,49 +217,51 @@ Config.extraMimes = {
|
|
|
218
217
|
### Select a mock for a route
|
|
219
218
|
```js
|
|
220
219
|
fetch(addr + '/mockaton/edit', {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
220
|
+
method: 'PATCH',
|
|
221
|
+
body: JSON.stringify({
|
|
222
|
+
file: 'api/foo.200.GET.json',
|
|
223
|
+
delayed: true // optional
|
|
224
|
+
})
|
|
226
225
|
})
|
|
227
226
|
```
|
|
228
227
|
|
|
229
228
|
### Select all mocks that have a particular comment
|
|
230
229
|
```js
|
|
231
230
|
fetch(addr + '/mockaton/bulk-select-by-comment', {
|
|
232
|
-
|
|
233
|
-
|
|
231
|
+
method: 'PATCH',
|
|
232
|
+
body: JSON.stringify('(demo-a)')
|
|
234
233
|
})
|
|
235
234
|
```
|
|
236
235
|
|
|
237
|
-
###
|
|
238
|
-
|
|
236
|
+
### List Cookies
|
|
237
|
+
Sends a list of the available cookies along with an "is selected" boolean flag.
|
|
239
238
|
```js
|
|
240
|
-
fetch(addr + '/mockaton/
|
|
241
|
-
method: 'PATCH'
|
|
242
|
-
})
|
|
239
|
+
fetch(addr + '/mockaton/cookies')
|
|
243
240
|
```
|
|
244
241
|
|
|
245
242
|
### Select a cookie
|
|
246
243
|
In `Config.cookies`, each key is the label used for changing it.
|
|
247
244
|
```js
|
|
248
245
|
fetch(addr + '/mockaton/cookies', {
|
|
249
|
-
|
|
250
|
-
|
|
246
|
+
method: 'PATCH',
|
|
247
|
+
body: JSON.stringify('My Normal User')
|
|
251
248
|
})
|
|
252
249
|
```
|
|
253
250
|
|
|
254
|
-
###
|
|
255
|
-
Sends a list of the available cookies along with a flag indicated if it’s the selected.
|
|
251
|
+
### Update Fallback Proxy
|
|
256
252
|
```js
|
|
257
|
-
fetch(addr + '/mockaton/
|
|
253
|
+
fetch(addr + '/mockaton/fallback', {
|
|
254
|
+
method: 'PATCH',
|
|
255
|
+
body: JSON.stringify('http://example.com')
|
|
256
|
+
})
|
|
258
257
|
```
|
|
259
258
|
|
|
260
|
-
###
|
|
259
|
+
### Reset
|
|
260
|
+
Re-initialize the collection. So if you added or removed mocks they
|
|
261
|
+
will be considered. The selected mocks, cookies, and delays are
|
|
262
|
+
back to default. But the `Config.proxyFalllback` is not affected.
|
|
261
263
|
```js
|
|
262
|
-
fetch(addr + '/mockaton/
|
|
263
|
-
|
|
264
|
-
body: JSON.stringify('http://example.com')
|
|
264
|
+
fetch(addr + '/mockaton/reset', {
|
|
265
|
+
method: 'PATCH'
|
|
265
266
|
})
|
|
266
267
|
```
|
package/Tests.js
CHANGED
|
@@ -29,7 +29,7 @@ const fixtures = [
|
|
|
29
29
|
[
|
|
30
30
|
'/api',
|
|
31
31
|
'api/.GET.200.json',
|
|
32
|
-
'index-like route
|
|
32
|
+
'index-like route for /api, which could just be the extension convention'
|
|
33
33
|
],
|
|
34
34
|
|
|
35
35
|
// Exact route paths
|
|
@@ -150,6 +150,8 @@ async function runTests() {
|
|
|
150
150
|
'api/alternative(comment-2).GET.200.json',
|
|
151
151
|
JSON.stringify({ comment: 2 }))
|
|
152
152
|
|
|
153
|
+
await test422WhenUpdatingNonExistingMockAlternative()
|
|
154
|
+
|
|
153
155
|
await testAutogenerates500(
|
|
154
156
|
'/api/alternative',
|
|
155
157
|
`api/alternative${DEFAULT_500_COMMENT}.GET.500.txt`)
|
|
@@ -266,6 +268,15 @@ async function testItUpdatesDelayAndFile(url, file, expectedBody) {
|
|
|
266
268
|
})
|
|
267
269
|
}
|
|
268
270
|
|
|
271
|
+
async function test422WhenUpdatingNonExistingMockAlternative() {
|
|
272
|
+
await it('There are mocks for /api/the-route but not this one', async () => {
|
|
273
|
+
const res = await request(API.edit, {
|
|
274
|
+
method: 'PATCH',
|
|
275
|
+
body: JSON.stringify({ [DF.file]: 'api/the-route(non-existing-variant).GET.200.json' })
|
|
276
|
+
})
|
|
277
|
+
equal(res.status, 422)
|
|
278
|
+
})
|
|
279
|
+
}
|
|
269
280
|
|
|
270
281
|
async function testAutogenerates500(url, file) {
|
|
271
282
|
await request(API.edit, {
|
package/package.json
CHANGED
package/src/Api.js
CHANGED
|
@@ -57,14 +57,15 @@ async function selectCookie(req, response) {
|
|
|
57
57
|
async function updateBroker(req, response) {
|
|
58
58
|
try {
|
|
59
59
|
const body = await parseJSON(req)
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
60
|
+
const file = body[DF.file]
|
|
61
|
+
const broker = mockBrokersCollection.getBrokerByFilename(file)
|
|
62
|
+
if (!broker || !broker.mockExists(file)) {
|
|
63
|
+
sendUnprocessableContent(response, `Missing Mock: "${file}`)
|
|
63
64
|
return
|
|
64
65
|
}
|
|
65
66
|
if (DF.delayed in body)
|
|
66
67
|
broker.updateDelay(body[DF.delayed])
|
|
67
|
-
broker.updateFile(
|
|
68
|
+
broker.updateFile(file)
|
|
68
69
|
sendOK(response)
|
|
69
70
|
}
|
|
70
71
|
catch (error) {
|
package/src/MockBroker.js
CHANGED
|
@@ -42,6 +42,10 @@ export class MockBroker {
|
|
|
42
42
|
get delay() { return this.currentMock.delay }
|
|
43
43
|
get status() { return parseFilename(this.file).status }
|
|
44
44
|
|
|
45
|
+
mockExists(file) {
|
|
46
|
+
return this.mocks.includes(file)
|
|
47
|
+
}
|
|
48
|
+
|
|
45
49
|
updateFile(filename) {
|
|
46
50
|
this.currentMock.file = filename
|
|
47
51
|
}
|
|
@@ -37,10 +37,6 @@ export function init() {
|
|
|
37
37
|
forEachBroker(broker => broker.ensureItHas500())
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
function forEachBroker(fn) {
|
|
41
|
-
for (const brokers of Object.values(collection))
|
|
42
|
-
Object.values(brokers).forEach(fn)
|
|
43
|
-
}
|
|
44
40
|
|
|
45
41
|
export const getAll = () => collection
|
|
46
42
|
|
|
@@ -78,3 +74,8 @@ export function setMocksMatchingComment(comment) {
|
|
|
78
74
|
}
|
|
79
75
|
|
|
80
76
|
|
|
77
|
+
function forEachBroker(fn) {
|
|
78
|
+
for (const brokers of Object.values(collection))
|
|
79
|
+
Object.values(brokers).forEach(fn)
|
|
80
|
+
}
|
|
81
|
+
|
package/src/utils/fs.js
CHANGED
|
@@ -7,5 +7,6 @@ export const isDirectory = path => lstatSync(path, { throwIfNoEntry: false })?.i
|
|
|
7
7
|
|
|
8
8
|
export const read = path => readFileSync(path)
|
|
9
9
|
|
|
10
|
+
/** @returns {Array<string>} paths relative to `dir` */
|
|
10
11
|
export const listFilesRecursively = dir => readdirSync(dir, { recursive: true })
|
|
11
12
|
.filter(f => isFile(join(dir, f)))
|
|
@@ -49,7 +49,7 @@ export async function sendPartialContent(response, range, file) {
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
export function sendBadRequest(response, error) {
|
|
52
|
-
console.error(error)
|
|
52
|
+
console.error(error)
|
|
53
53
|
response.statusCode = 400
|
|
54
54
|
response.end()
|
|
55
55
|
}
|
|
@@ -59,13 +59,14 @@ export function sendNotFound(response) {
|
|
|
59
59
|
response.end()
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
export function sendUnprocessableContent(response) {
|
|
62
|
+
export function sendUnprocessableContent(response, error) {
|
|
63
|
+
console.error(error)
|
|
63
64
|
response.statusCode = 422
|
|
64
65
|
response.end()
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
export function sendInternalServerError(response, error) {
|
|
68
|
-
console.error(error)
|
|
69
|
+
console.error(error)
|
|
69
70
|
response.statusCode = 500
|
|
70
71
|
response.end()
|
|
71
72
|
}
|