mockaton 0.9.8 → 0.9.10
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 +34 -28
- package/Tests.js +4 -4
- package/index.js +2 -2
- package/package.json +1 -1
- package/{MockDispatcher.js → src/MockDispatcher.js} +5 -5
- package/{Mockaton.js → src/Mockaton.js} +2 -2
- package/{mockBrokersCollection.js → src/mockBrokersCollection.js} +22 -21
- /package/{Api.js → src/Api.js} +0 -0
- /package/{ApiConstants.js → src/ApiConstants.js} +0 -0
- /package/{Config.js → src/Config.js} +0 -0
- /package/{Dashboard.css → src/Dashboard.css} +0 -0
- /package/{Dashboard.html → src/Dashboard.html} +0 -0
- /package/{Dashboard.js → src/Dashboard.js} +0 -0
- /package/{MockBroker.js → src/MockBroker.js} +0 -0
- /package/{Route.js → src/Route.js} +0 -0
- /package/{StaticDispatcher.js → src/StaticDispatcher.js} +0 -0
- /package/{cookie.js → src/cookie.js} +0 -0
- /package/{utils → src/utils}/http-request.js +0 -0
- /package/{utils → src/utils}/http-response.js +0 -0
- /package/{utils → src/utils}/jwt.js +0 -0
- /package/{utils → src/utils}/mime.js +0 -0
- /package/{utils → src/utils}/validate.js +0 -0
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
|
|
@@ -63,11 +63,12 @@ npm install mockaton
|
|
|
63
63
|
Create a `my-mockaton.js` file
|
|
64
64
|
```js
|
|
65
65
|
import { resolve } from 'node:path'
|
|
66
|
-
import { Mockaton } from '
|
|
66
|
+
import { Mockaton } from 'src/Mockaton'
|
|
67
|
+
|
|
67
68
|
|
|
68
69
|
Mockaton({ // Config options
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
port: 2345,
|
|
71
|
+
mocksDir: resolve('my-mocks-dir')
|
|
71
72
|
})
|
|
72
73
|
```
|
|
73
74
|
|
|
@@ -92,15 +93,16 @@ interface Config {
|
|
|
92
93
|
|
|
93
94
|
## Cookies
|
|
94
95
|
```js
|
|
95
|
-
import { jwtCookie } from '
|
|
96
|
+
import { jwtCookie } from 'src/Mockaton'
|
|
97
|
+
|
|
96
98
|
|
|
97
99
|
Config.cookies = {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
'My Admin User': 'my-cookie=1;Path=/;SameSite=strict',
|
|
101
|
+
'My Normal User': 'my-cookie=0;Path=/;SameSite=strict',
|
|
102
|
+
'My JWT': jwtCookie('my-cookie', {
|
|
103
|
+
email: 'john.doe@example.com',
|
|
104
|
+
picture: 'https://cdn.auth0.com/avatars/jd.png'
|
|
105
|
+
})
|
|
104
106
|
}
|
|
105
107
|
```
|
|
106
108
|
|
|
@@ -122,23 +124,27 @@ The `Config.allowedExt` regex defaults to: `/\.(json|txt|md|mjs)$/`
|
|
|
122
124
|
|
|
123
125
|
|
|
124
126
|
### Dynamic Parameters
|
|
125
|
-
Anything within square brackets. For example,
|
|
127
|
+
Anything within square brackets. For example,
|
|
128
|
+
<pre>
|
|
129
|
+
api/user/<b>[id]</b>/<b>[age]</b>.GET.200.json
|
|
130
|
+
</pre>
|
|
126
131
|
|
|
127
132
|
### 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)
|
|
133
|
+
Comments are anything within parentheses, including them.
|
|
134
|
+
They are ignored for URL purposes, so they have no effect
|
|
135
|
+
on the URL mask. For example, these two are for `/api/foo`
|
|
136
|
+
<pre>
|
|
137
|
+
api/foo<b>(my comment)</b>.GET.200.json<b>(foo)</b>
|
|
133
138
|
api/foo.GET.200.json
|
|
134
|
-
|
|
139
|
+
</pre>
|
|
135
140
|
|
|
136
141
|
### Query String Params
|
|
137
|
-
|
|
138
|
-
api/video
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
+
<pre>
|
|
143
|
+
api/video<b>?limit=[limit]</b>.GET.200.json
|
|
144
|
+
</pre>
|
|
145
|
+
|
|
146
|
+
The query string is ignored when routing to it. It’s
|
|
147
|
+
only used for documenting the URL API contract.
|
|
142
148
|
|
|
143
149
|
BTW, in Windows, filenames containing "?" are [not
|
|
144
150
|
permitted](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file),
|
|
@@ -199,7 +205,7 @@ PATCH /mockaton/edit
|
|
|
199
205
|
```
|
|
200
206
|
PATCH /mockaton/bulk-select
|
|
201
207
|
{
|
|
202
|
-
"comment": "demo-a"
|
|
208
|
+
"comment": "(demo-a)"
|
|
203
209
|
}
|
|
204
210
|
```
|
|
205
211
|
---
|
package/Tests.js
CHANGED
|
@@ -6,10 +6,10 @@ import { describe, it } from 'node:test'
|
|
|
6
6
|
import { equal, deepEqual, match } from 'node:assert/strict'
|
|
7
7
|
import { writeFileSync, mkdtempSync, mkdirSync } from 'node:fs'
|
|
8
8
|
|
|
9
|
-
import { Route } from './Route.js'
|
|
10
|
-
import { mimeFor } from './utils/mime.js'
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
9
|
+
import { Route } from './src/Route.js'
|
|
10
|
+
import { mimeFor } from './src/utils/mime.js'
|
|
11
|
+
import { Mockaton } from './src/Mockaton.js'
|
|
12
|
+
import { DP, DF } from './src/ApiConstants.js'
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
const tmpDir = mkdtempSync(tmpdir()) + '/'
|
package/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { Mockaton } from './Mockaton.js'
|
|
2
|
-
export { jwtCookie } from './utils/jwt.js'
|
|
1
|
+
export { Mockaton } from './src/Mockaton.js'
|
|
2
|
+
export { jwtCookie } from './src/utils/jwt.js'
|
package/package.json
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,14 +17,14 @@ 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 } =
|
|
27
|
+
const { file, status, delay, currentTransform } = broker
|
|
28
28
|
console.log('\n', req.url, '→\n ', file)
|
|
29
29
|
|
|
30
30
|
response.statusCode = status
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -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/{Api.js → src/Api.js}
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|