mockaton 0.9.7 → 0.9.9
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/Api.js +1 -2
- package/MockBroker.js +1 -2
- package/MockDispatcher.js +6 -6
- package/Mockaton.js +2 -2
- package/README.md +22 -18
- package/Tests.js +9 -5
- package/mockBrokersCollection.js +22 -21
- package/package.json +1 -1
package/Api.js
CHANGED
|
@@ -72,8 +72,7 @@ async function updateBroker(req, response) {
|
|
|
72
72
|
const broker = mockBrokersCollection.getBrokerByFilename(body[DF.file])
|
|
73
73
|
if (DF.delayed in body)
|
|
74
74
|
broker.updateDelay(body[DF.delayed])
|
|
75
|
-
|
|
76
|
-
broker.updateFile(body[DF.file])
|
|
75
|
+
broker.updateFile(body[DF.file])
|
|
77
76
|
sendOK(response)
|
|
78
77
|
}
|
|
79
78
|
catch (error) {
|
package/MockBroker.js
CHANGED
|
@@ -20,7 +20,6 @@ export class MockBroker {
|
|
|
20
20
|
this.mocks = [] // *.json,txt
|
|
21
21
|
this.currentMock = {
|
|
22
22
|
file: '',
|
|
23
|
-
get status() { return Route.parseFilename(this.file).status },
|
|
24
23
|
delay: 0
|
|
25
24
|
}
|
|
26
25
|
|
|
@@ -45,8 +44,8 @@ export class MockBroker {
|
|
|
45
44
|
urlMaskMatches(url) { return this.#route.urlMaskMatches(url) }
|
|
46
45
|
|
|
47
46
|
get file() { return this.currentMock.file }
|
|
48
|
-
get status() { return this.currentMock.status }
|
|
49
47
|
get delay() { return this.currentMock.delay }
|
|
48
|
+
get status() { return Route.parseFilename(this.currentMock.file).status }
|
|
50
49
|
|
|
51
50
|
updateFile(filename) {
|
|
52
51
|
this.currentMock.file = filename
|
package/MockDispatcher.js
CHANGED
|
@@ -5,7 +5,7 @@ import { DF } from './ApiConstants.js'
|
|
|
5
5
|
import { cookie } from './cookie.js'
|
|
6
6
|
import { Config } from './Config.js'
|
|
7
7
|
import { mimeFor } from './utils/mime.js'
|
|
8
|
-
import * as
|
|
8
|
+
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
9
9
|
import { parseJSON, JsonBodyParserError } from './utils/http-request.js'
|
|
10
10
|
import { sendInternalServerError, sendNotFound, sendFile, sendBadRequest } from './utils/http-response.js'
|
|
11
11
|
|
|
@@ -17,15 +17,15 @@ export async function dispatchMock(req, response) {
|
|
|
17
17
|
return
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const
|
|
21
|
-
if (!
|
|
20
|
+
const broker = mockBrokerCollection.getBrokerForUrl(req.method, req.url)
|
|
21
|
+
if (!broker) {
|
|
22
22
|
sendNotFound(response)
|
|
23
23
|
return
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
-
const { file, status, delay, currentTransform } =
|
|
28
|
-
console.log('\n', req.url, '
|
|
27
|
+
const { file, status, delay, currentTransform } = broker
|
|
28
|
+
console.log('\n', req.url, '→\n ', file)
|
|
29
29
|
|
|
30
30
|
response.statusCode = status
|
|
31
31
|
response.setHeader('content-type', mimeFor(file))
|
|
@@ -33,7 +33,7 @@ export async function dispatchMock(req, response) {
|
|
|
33
33
|
response.setHeader('set-cookie', cookie.getCurrent())
|
|
34
34
|
|
|
35
35
|
let mockAsText = readMock(file)
|
|
36
|
-
if (
|
|
36
|
+
if (broker.currentTransform) {
|
|
37
37
|
const body = await requestBodyForTransform(req, mockAsText)
|
|
38
38
|
const transformFunc = await importTransformFunc(currentTransform)
|
|
39
39
|
mockAsText = transformFunc(mockAsText, body, Config)
|
package/Mockaton.js
CHANGED
|
@@ -4,14 +4,14 @@ import { createServer } from 'node:http'
|
|
|
4
4
|
import { DP } from './ApiConstants.js'
|
|
5
5
|
import { Config, setup } from './Config.js'
|
|
6
6
|
import { dispatchMock } from './MockDispatcher.js'
|
|
7
|
-
import * as
|
|
7
|
+
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
8
8
|
import { dispatchStatic, isStatic } from './StaticDispatcher.js'
|
|
9
9
|
import { apiPatchRequests, apiGetRequests } from './Api.js'
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
export function Mockaton(options) {
|
|
13
13
|
setup(options)
|
|
14
|
-
|
|
14
|
+
mockBrokerCollection.init()
|
|
15
15
|
|
|
16
16
|
return createServer(async (req, response) => {
|
|
17
17
|
const { url, method } = req
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Mockaton
|
|
2
2
|
_Mockaton_ is a mock server for developing and testing frontends.
|
|
3
3
|
|
|
4
|
-
It scans `Config.mocksDir` for files following a specific
|
|
5
|
-
file name convention, which is similar to the URL paths. For
|
|
4
|
+
It scans `Config.mocksDir` for files following a specific directory
|
|
5
|
+
and file name convention, which is similar to the URL paths. For
|
|
6
6
|
example, the following file will be served for `/api/user/1234`
|
|
7
7
|
```
|
|
8
|
-
api/
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
-- api/
|
|
9
|
+
|-- user/
|
|
10
|
+
|-- [user-id].GET.200.json
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
By the way, [this browser
|
|
@@ -122,23 +122,27 @@ The `Config.allowedExt` regex defaults to: `/\.(json|txt|md|mjs)$/`
|
|
|
122
122
|
|
|
123
123
|
|
|
124
124
|
### Dynamic Parameters
|
|
125
|
-
Anything within square brackets. For example,
|
|
125
|
+
Anything within square brackets. For example,
|
|
126
|
+
<pre>
|
|
127
|
+
api/user/<b>[id]</b>/<b>[age]</b>.GET.200.json
|
|
128
|
+
</pre>
|
|
126
129
|
|
|
127
130
|
### Comments
|
|
128
|
-
Comments are anything within parentheses, including them
|
|
129
|
-
ignored for URL purposes
|
|
130
|
-
URL mask. For example, these two are for `/api/foo`
|
|
131
|
-
|
|
132
|
-
api/foo(my comment)
|
|
131
|
+
Comments are anything within parentheses, including them.
|
|
132
|
+
They are ignored for URL purposes, so they have no effect
|
|
133
|
+
on the URL mask. For example, these two are for `/api/foo`
|
|
134
|
+
<pre>
|
|
135
|
+
api/foo<b>(my comment)</b>.GET.200.json<b>(foo)</b>
|
|
133
136
|
api/foo.GET.200.json
|
|
134
|
-
|
|
137
|
+
</pre>
|
|
135
138
|
|
|
136
139
|
### Query String Params
|
|
137
|
-
|
|
138
|
-
api/video
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
<pre>
|
|
141
|
+
api/video<b>?limit=[limit]</b>.GET.200.json
|
|
142
|
+
</pre>
|
|
143
|
+
|
|
144
|
+
The query string is ignored when routing to it. It’s
|
|
145
|
+
only used for documenting the URL API contract.
|
|
142
146
|
|
|
143
147
|
BTW, in Windows, filenames containing "?" are [not
|
|
144
148
|
permitted](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file),
|
|
@@ -199,7 +203,7 @@ PATCH /mockaton/edit
|
|
|
199
203
|
```
|
|
200
204
|
PATCH /mockaton/bulk-select
|
|
201
205
|
{
|
|
202
|
-
"comment": "demo-a"
|
|
206
|
+
"comment": "(demo-a)"
|
|
203
207
|
}
|
|
204
208
|
```
|
|
205
209
|
---
|
package/Tests.js
CHANGED
|
@@ -118,7 +118,8 @@ async function runTests() {
|
|
|
118
118
|
|
|
119
119
|
await testItUpdatesDelayAndFile(
|
|
120
120
|
'/api/alternative',
|
|
121
|
-
'api/alternative(comment-
|
|
121
|
+
'api/alternative(comment-2).GET.200.json',
|
|
122
|
+
JSON.stringify({ comment: 2 }))
|
|
122
123
|
|
|
123
124
|
await testAutogenerates500(
|
|
124
125
|
'/api/company-e/123?limit=9',
|
|
@@ -200,7 +201,7 @@ async function testItUpdatesTheCurrentSelectedMock(url, file, expectedStatus, ex
|
|
|
200
201
|
})
|
|
201
202
|
}
|
|
202
203
|
|
|
203
|
-
async function testItUpdatesDelayAndFile(url, file) {
|
|
204
|
+
async function testItUpdatesDelayAndFile(url, file, expectedBody) {
|
|
204
205
|
await request(DP.edit, {
|
|
205
206
|
method: 'PATCH',
|
|
206
207
|
body: JSON.stringify({
|
|
@@ -209,9 +210,12 @@ async function testItUpdatesDelayAndFile(url, file) {
|
|
|
209
210
|
})
|
|
210
211
|
})
|
|
211
212
|
const now = new Date()
|
|
212
|
-
await request(url)
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
const res = await request(url)
|
|
214
|
+
const body = await res.text()
|
|
215
|
+
await describe('url: ' + url, () => {
|
|
216
|
+
it('body is: ' + expectedBody, () => equal(body, expectedBody))
|
|
217
|
+
it('delay is over 1 sec', () => equal((new Date()).getTime() - now.getTime() > 1000, true))
|
|
218
|
+
})
|
|
215
219
|
}
|
|
216
220
|
|
|
217
221
|
|
package/mockBrokersCollection.js
CHANGED
|
@@ -20,43 +20,54 @@ import { MockBroker } from './MockBroker.js'
|
|
|
20
20
|
let collection = {}
|
|
21
21
|
|
|
22
22
|
export function init() {
|
|
23
|
+
collection = {}
|
|
23
24
|
cookie.init(Config.cookies)
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
const files = readdirSync(Config.mocksDir, { recursive: true })
|
|
27
|
+
.filter(f => Config.allowedExt.test(f) && lstatSync(join(Config.mocksDir, f)).isFile())
|
|
28
|
+
.sort()
|
|
29
|
+
|
|
30
|
+
for (const file of files) {
|
|
27
31
|
const { error, method, urlMask } = Route.parseFilename(file)
|
|
28
|
-
if (error)
|
|
32
|
+
if (error) {
|
|
29
33
|
console.error(error, file)
|
|
30
|
-
|
|
31
|
-
collection[method] ??= {}
|
|
32
|
-
if (!(urlMask in collection[method]))
|
|
33
|
-
collection[method][urlMask] = new MockBroker(file)
|
|
34
|
-
else
|
|
35
|
-
collection[method][urlMask].register(file)
|
|
34
|
+
continue
|
|
36
35
|
}
|
|
36
|
+
collection[method] ??= {}
|
|
37
|
+
if (!(urlMask in collection[method]))
|
|
38
|
+
collection[method][urlMask] = new MockBroker(file)
|
|
39
|
+
else
|
|
40
|
+
collection[method][urlMask].register(file)
|
|
37
41
|
}
|
|
38
42
|
forEachBroker(broker => broker.ensureItHas500())
|
|
39
43
|
}
|
|
40
44
|
|
|
45
|
+
function forEachBroker(fn) {
|
|
46
|
+
for (const brokers of Object.values(collection))
|
|
47
|
+
Object.values(brokers).forEach(fn)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
41
51
|
export const getAll = () => collection
|
|
52
|
+
|
|
42
53
|
export const getBrokerByFilename = file => {
|
|
43
54
|
const { method, urlMask } = Route.parseFilename(file)
|
|
44
55
|
return collection[method][urlMask]
|
|
45
56
|
}
|
|
46
57
|
|
|
47
|
-
|
|
48
58
|
// Searching the routes in reverse order so dynamic params (e.g.
|
|
49
59
|
// /user/[id]) don’t take precedence over exact paths (e.g.
|
|
50
60
|
// /user/name). That’s because "[]" chars are lower than alphanumeric ones.
|
|
51
61
|
// BTW, `urlMasks` always start with "/", so there’s no need to
|
|
52
62
|
// worry about the primacy of array-like keys when iterating.
|
|
53
|
-
export function
|
|
63
|
+
export function getBrokerForUrl(method, url) {
|
|
54
64
|
const brokers = Object.values(collection[method])
|
|
55
65
|
for (let i = brokers.length - 1; i >= 0; i--)
|
|
56
66
|
if (brokers[i].urlMaskMatches(url))
|
|
57
67
|
return brokers[i]
|
|
58
68
|
}
|
|
59
69
|
|
|
70
|
+
|
|
60
71
|
export function extractAllComments() {
|
|
61
72
|
const comments = new Set()
|
|
62
73
|
forEachBroker(broker => {
|
|
@@ -70,14 +81,4 @@ export function setMocksMatchingComment(comment) {
|
|
|
70
81
|
forEachBroker(broker => broker.setByMatchingComment(comment))
|
|
71
82
|
}
|
|
72
83
|
|
|
73
|
-
function listMocksDirRecursively() {
|
|
74
|
-
return readdirSync(Config.mocksDir, { recursive: true })
|
|
75
|
-
.filter(f => Config.allowedExt.test(f) && lstatSync(join(Config.mocksDir, f)).isFile())
|
|
76
|
-
.sort()
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function forEachBroker(fn) {
|
|
80
|
-
for (const brokers of Object.values(collection))
|
|
81
|
-
Object.values(brokers).forEach(fn)
|
|
82
|
-
}
|
|
83
84
|
|
package/package.json
CHANGED