mockaton 6.4.5 → 6.4.7
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-dashboard.png +0 -0
- package/README.md +16 -10
- package/Tests.js +6 -4
- package/package.json +1 -1
- package/src/Api.js +4 -5
- package/src/MockDispatcher.js +21 -31
- package/src/utils/http-response.js +1 -7
package/README-dashboard.png
CHANGED
|
Binary file
|
package/README.md
CHANGED
|
@@ -12,35 +12,41 @@ my-mocks-dir/api/user/[user-id].GET.200.json
|
|
|
12
12
|
[This browser extension](https://github.com/ericfortis/devtools-ext-tar-http-requests)
|
|
13
13
|
can be used for downloading a TAR of your XHR requests following that convention.
|
|
14
14
|
|
|
15
|
-
## What do I use
|
|
15
|
+
## What do I use it for?
|
|
16
16
|
- I’m a frontend dev, so I don’t have to spin up and maintain hefty or complex backends.
|
|
17
|
-
- For a deterministic and comprehensive state.
|
|
18
|
-
state variants
|
|
17
|
+
- For a deterministic and comprehensive backend state. For example, having all the possible
|
|
18
|
+
state variants of a particular collection helps for spotting inadvertent bugs. And having those
|
|
19
|
+
assorted responses are not easy to trigger from the backend.
|
|
19
20
|
- Testing empty responses.
|
|
20
21
|
- Testing spinners by delaying responses.
|
|
21
|
-
-
|
|
22
|
+
- Testing errors such as _Bad Request_ and _Internal Server Error_.
|
|
22
23
|
- Triggering notifications and alerts.
|
|
23
|
-
-
|
|
24
|
+
- Prototyping before the backend API is developed.
|
|
25
|
+
- Setting up tests.
|
|
26
|
+
- If you commit the mocks in the repo, when bisecting a bug, you don’t
|
|
24
27
|
have to sync the frontend with many backend repos.
|
|
25
28
|
- Similarly, I can check out long-lived branches that have old API contracts.
|
|
26
|
-
- Prototyping before the backend API is developed.
|
|
27
29
|
- As API documentation.
|
|
28
|
-
- Setting up tests.
|
|
29
30
|
|
|
30
31
|
## Alternatives
|
|
31
32
|
- Chrome DevTools allows for [overriding responses](https://developer.chrome.com/docs/devtools/overrides)
|
|
32
33
|
- Reverse Proxies such as [Burp](https://portswigger.net/burp) are also handy for overriding responses.
|
|
33
|
-
- [
|
|
34
|
+
- Storybook’s [MSW](https://storybook.js.org/addons/msw-storybook-addon)
|
|
34
35
|
|
|
35
36
|
### Caveats
|
|
36
|
-
- Syncing the mocks
|
|
37
|
+
- Syncing the mocks, but the browser extension mentioned above helps.
|
|
37
38
|
|
|
38
39
|
|
|
39
40
|
## Getting Started
|
|
40
41
|
The best way to learn _Mockaton_ is by checking out this repo and
|
|
41
42
|
exploring its [sample-mocks/](./sample-mocks) directory. Then, run
|
|
42
|
-
[`./_usage_example.js`](./_usage_example.js) and you’ll see
|
|
43
|
+
[`./_usage_example.js`](./_usage_example.js) and you’ll see the dashboard.
|
|
44
|
+
|
|
45
|
+
You can edit mock files without resetting Mockaton. The _Reset_
|
|
46
|
+
button is for when you add, remove, or rename a mock file.
|
|
43
47
|
|
|
48
|
+
The dropdown lets you pick a mock variant, details in the next section. Next to it is a
|
|
49
|
+
_Delay_ toggler, and a button for sending _500 - Internal Server Error_ on that endpoint.
|
|
44
50
|
|
|
45
51
|
<img src="./README-dashboard.png" style="max-width:820px"/>
|
|
46
52
|
|
package/Tests.js
CHANGED
|
@@ -165,7 +165,7 @@ async function runTests() {
|
|
|
165
165
|
'api/alternative(comment-2).GET.200.json',
|
|
166
166
|
JSON.stringify({ comment: 2 }))
|
|
167
167
|
|
|
168
|
-
await
|
|
168
|
+
await testBadRequestWhenUpdatingNonExistingMockAlternative()
|
|
169
169
|
|
|
170
170
|
await testAutogenerates500(
|
|
171
171
|
'/api/alternative',
|
|
@@ -288,13 +288,15 @@ async function testItUpdatesDelayAndFile(url, file, expectedBody) {
|
|
|
288
288
|
})
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
async function
|
|
291
|
+
async function testBadRequestWhenUpdatingNonExistingMockAlternative() {
|
|
292
292
|
await it('There are mocks for /api/the-route but not this one', async () => {
|
|
293
|
+
const missingFile = 'api/the-route(non-existing-variant).GET.200.json'
|
|
293
294
|
const res = await request(API.edit, {
|
|
294
295
|
method: 'PATCH',
|
|
295
|
-
body: JSON.stringify({ [DF.file]:
|
|
296
|
+
body: JSON.stringify({ [DF.file]: missingFile })
|
|
296
297
|
})
|
|
297
|
-
equal(res.status,
|
|
298
|
+
equal(res.status, 400)
|
|
299
|
+
equal(await res.text(), `Missing Mock: ${missingFile}`)
|
|
298
300
|
})
|
|
299
301
|
}
|
|
300
302
|
|
package/package.json
CHANGED
package/src/Api.js
CHANGED
|
@@ -9,7 +9,7 @@ import { Config } from './Config.js'
|
|
|
9
9
|
import { DF, API } from './ApiConstants.js'
|
|
10
10
|
import { parseJSON } from './utils/http-request.js'
|
|
11
11
|
import * as mockBrokersCollection from './mockBrokersCollection.js'
|
|
12
|
-
import { sendOK, sendBadRequest, sendJSON, sendFile
|
|
12
|
+
import { sendOK, sendBadRequest, sendJSON, sendFile } from './utils/http-response.js'
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
export const apiGetRequests = new Map([
|
|
@@ -60,10 +60,9 @@ async function updateBroker(req, response) {
|
|
|
60
60
|
const body = await parseJSON(req)
|
|
61
61
|
const file = body[DF.file]
|
|
62
62
|
const broker = mockBrokersCollection.getBrokerByFilename(file)
|
|
63
|
-
if (!broker || !broker.mockExists(file))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
63
|
+
if (!broker || !broker.mockExists(file))
|
|
64
|
+
throw `Missing Mock: ${file}`
|
|
65
|
+
|
|
67
66
|
if (DF.delayed in body)
|
|
68
67
|
broker.updateDelay(body[DF.delayed])
|
|
69
68
|
broker.updateFile(file)
|
package/src/MockDispatcher.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { readFileSync as read } from 'node:fs'
|
|
3
3
|
|
|
4
4
|
import { proxy } from './ProxyRelay.js'
|
|
5
5
|
import { cookie } from './cookie.js'
|
|
@@ -11,34 +11,40 @@ import { sendInternalServerError, sendNotFound, sendBadRequest } from './utils/h
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
export async function dispatchMock(req, response) {
|
|
14
|
-
const broker = mockBrokerCollection.getBrokerForUrl(req.method, req.url)
|
|
15
|
-
if (!broker) {
|
|
16
|
-
if (Config.proxyFallback)
|
|
17
|
-
await proxy(req, response)
|
|
18
|
-
else
|
|
19
|
-
sendNotFound(response)
|
|
20
|
-
return
|
|
21
|
-
}
|
|
22
|
-
|
|
23
14
|
try {
|
|
15
|
+
const broker = mockBrokerCollection.getBrokerForUrl(req.method, req.url)
|
|
16
|
+
if (!broker) {
|
|
17
|
+
if (Config.proxyFallback)
|
|
18
|
+
await proxy(req, response)
|
|
19
|
+
else
|
|
20
|
+
sendNotFound(response)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
24
|
const { file, status, delay } = broker
|
|
25
25
|
console.log(decodeURIComponent(req.url), ' → ', file)
|
|
26
|
+
const filePath = join(Config.mocksDir, file)
|
|
26
27
|
|
|
27
|
-
let
|
|
28
|
+
let mockBody
|
|
28
29
|
if (file.endsWith('.js')) {
|
|
29
30
|
response.setHeader('Content-Type', mimeFor('.json'))
|
|
30
|
-
|
|
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)
|
|
31
35
|
}
|
|
32
36
|
else {
|
|
33
37
|
response.setHeader('Content-Type', mimeFor(file))
|
|
34
|
-
|
|
38
|
+
mockBody = broker.isTemp500
|
|
39
|
+
? ''
|
|
40
|
+
: read(filePath)
|
|
35
41
|
}
|
|
36
|
-
|
|
42
|
+
|
|
37
43
|
if (cookie.getCurrent())
|
|
38
44
|
response.setHeader('Set-Cookie', cookie.getCurrent())
|
|
39
45
|
|
|
40
46
|
response.writeHead(status, Config.extraHeaders)
|
|
41
|
-
setTimeout(() => response.end(
|
|
47
|
+
setTimeout(() => response.end(mockBody), delay)
|
|
42
48
|
}
|
|
43
49
|
catch (error) {
|
|
44
50
|
if (error instanceof JsonBodyParserError)
|
|
@@ -49,19 +55,3 @@ export async function dispatchMock(req, response) {
|
|
|
49
55
|
sendInternalServerError(response, error)
|
|
50
56
|
}
|
|
51
57
|
}
|
|
52
|
-
|
|
53
|
-
async function jsMockText(file, req, response) {
|
|
54
|
-
const jsExport = await importDefault(file)
|
|
55
|
-
return typeof jsExport === 'function'
|
|
56
|
-
? await jsExport(req, response)
|
|
57
|
-
: JSON.stringify(jsExport, null, 2)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function readMock(file) {
|
|
61
|
-
return readFileSync(join(Config.mocksDir, file))
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function importDefault(file) {
|
|
65
|
-
// The date param is just for cache busting
|
|
66
|
-
return (await import(join(Config.mocksDir, file) + '?' + Date.now())).default
|
|
67
|
-
}
|
|
@@ -51,7 +51,7 @@ export async function sendPartialContent(response, range, file) {
|
|
|
51
51
|
export function sendBadRequest(response, error) {
|
|
52
52
|
console.error(error)
|
|
53
53
|
response.statusCode = 400
|
|
54
|
-
response.end()
|
|
54
|
+
response.end(error)
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
export function sendNotFound(response) {
|
|
@@ -59,12 +59,6 @@ export function sendNotFound(response) {
|
|
|
59
59
|
response.end()
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
export function sendUnprocessableContent(response, error) {
|
|
63
|
-
console.error(error)
|
|
64
|
-
response.statusCode = 422
|
|
65
|
-
response.end()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
62
|
export function sendInternalServerError(response, error) {
|
|
69
63
|
console.error(error)
|
|
70
64
|
response.statusCode = 500
|