mockaton 6.1.0 → 6.3.2
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 +13 -3
- package/Tests.js +21 -4
- package/index.d.ts +3 -1
- package/package.json +1 -1
- package/src/Config.js +6 -2
- package/src/MockBroker.js +2 -14
- package/src/MockDispatcher.js +3 -3
- package/src/Route.js +1 -2
- package/src/{utils/mime.js → mime.js} +4 -2
- package/src/mockBrokersCollection.js +1 -2
- package/src/utils/http-response.js +1 -1
package/README.md
CHANGED
|
@@ -48,9 +48,11 @@ interface Config {
|
|
|
48
48
|
host?: string, // defaults to 'localhost'
|
|
49
49
|
port?: number // defaults to 0, which means auto-assigned
|
|
50
50
|
delay?: number // defaults to 1200 (ms)
|
|
51
|
+
ignore?: RegExp // defaults to /(.DS_Store|~)$/
|
|
51
52
|
onReady?: (dashboardUrl: string) => void // defaults to trying to open macOS default browser. pass a noop to prevent opening the dashboard
|
|
52
53
|
cookies?: object
|
|
53
54
|
proxyFallback?: string // e.g. http://localhost:9999 Target for relaying routes without mocks
|
|
55
|
+
extraMimes?: object
|
|
54
56
|
extraHeaders?: []
|
|
55
57
|
}
|
|
56
58
|
```
|
|
@@ -115,8 +117,6 @@ The delay is globally configurable via `Config.delay = 1200` (milliseconds).
|
|
|
115
117
|
### Extension
|
|
116
118
|
`.Method.ResponseStatusCode.FileExt`
|
|
117
119
|
|
|
118
|
-
The **file extension** can be anything, but `.md` is reserved for documentation.
|
|
119
|
-
|
|
120
120
|
|
|
121
121
|
### Dynamic Parameters
|
|
122
122
|
Anything within square brackets. For example:
|
|
@@ -129,7 +129,7 @@ Comments are anything within parentheses, including them.
|
|
|
129
129
|
They are ignored for URL purposes, so they have no effect
|
|
130
130
|
on the URL mask. For example, these two are for `/api/foo`
|
|
131
131
|
<pre>
|
|
132
|
-
api/foo<b>(my comment)</b>.GET.200.json
|
|
132
|
+
api/foo<b>(my comment)</b>.GET.200.json
|
|
133
133
|
api/foo.GET.200.json
|
|
134
134
|
</pre>
|
|
135
135
|
|
|
@@ -147,6 +147,9 @@ but since that’s part of the query string, it’s ignored anyway.
|
|
|
147
147
|
|
|
148
148
|
|
|
149
149
|
### Default (index-like) route
|
|
150
|
+
For instance, let's say you have `api/foo/bar`, and
|
|
151
|
+
`api/foo`. For the latter you have two options:
|
|
152
|
+
|
|
150
153
|
**Option A.** Place it outside the directory:
|
|
151
154
|
```
|
|
152
155
|
api/foo/
|
|
@@ -196,6 +199,13 @@ Config.extraHeaders = [
|
|
|
196
199
|
]
|
|
197
200
|
```
|
|
198
201
|
|
|
202
|
+
## `Config.extraMimes`
|
|
203
|
+
```js
|
|
204
|
+
Config.extraMimes = {
|
|
205
|
+
jpg: 'application/jpeg'
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
199
209
|
---
|
|
200
210
|
|
|
201
211
|
## API
|
package/Tests.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env -S node --experimental-default-type=module
|
|
2
2
|
|
|
3
3
|
import { tmpdir } from 'node:os'
|
|
4
4
|
import { dirname } from 'node:path'
|
|
@@ -9,13 +9,20 @@ import { equal, deepEqual, match } from 'node:assert/strict'
|
|
|
9
9
|
import { writeFileSync, mkdtempSync, mkdirSync } from 'node:fs'
|
|
10
10
|
|
|
11
11
|
import { Route } from './src/Route.js'
|
|
12
|
-
import { mimeFor } from './src/
|
|
12
|
+
import { mimeFor } from './src/mime.js'
|
|
13
13
|
import { Mockaton } from './src/Mockaton.js'
|
|
14
14
|
import { API, DF, DEFAULT_500_COMMENT } from './src/ApiConstants.js'
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
const tmpDir = mkdtempSync(tmpdir()) + '/'
|
|
18
18
|
const staticTmpDir = mkdtempSync(tmpdir()) + '/'
|
|
19
|
+
|
|
20
|
+
const fixtureCustomMime = [
|
|
21
|
+
'/api/custom-mime',
|
|
22
|
+
'api/custom-mime.GET.200.my_custom_extension',
|
|
23
|
+
'Custom Extension and MIME'
|
|
24
|
+
]
|
|
25
|
+
|
|
19
26
|
const fixtures = [
|
|
20
27
|
[
|
|
21
28
|
'/api',
|
|
@@ -87,7 +94,8 @@ const fixtures = [
|
|
|
87
94
|
'/api/company-e/1234/?limit=4',
|
|
88
95
|
'api/company-e/[id]?limit=[limit].GET.200.json',
|
|
89
96
|
'with pretty-param and query-params'
|
|
90
|
-
]
|
|
97
|
+
],
|
|
98
|
+
fixtureCustomMime
|
|
91
99
|
]
|
|
92
100
|
for (const [, file, body] of fixtures)
|
|
93
101
|
write(file, file.endsWith('.json') ? JSON.stringify(body) : body)
|
|
@@ -95,6 +103,7 @@ for (const [, file, body] of fixtures)
|
|
|
95
103
|
write('api/.GET.500.txt', 'keeps non-autogenerated 500')
|
|
96
104
|
write('api/alternative(comment-2).GET.200.json', JSON.stringify({ comment: 2 }))
|
|
97
105
|
write('api/my-route(comment-2).GET.200.json', JSON.stringify({ comment: 2 }))
|
|
106
|
+
write('api/ignored.GET.200.json~', '')
|
|
98
107
|
|
|
99
108
|
// JavaScript to JSON
|
|
100
109
|
write('/api/object.GET.200.js', 'export default { JSON_FROM_JS: true }')
|
|
@@ -111,7 +120,10 @@ const server = Mockaton({
|
|
|
111
120
|
userA: 'CookieA',
|
|
112
121
|
userB: 'CookieB'
|
|
113
122
|
},
|
|
114
|
-
extraHeaders: ['Server', 'MockatonTester']
|
|
123
|
+
extraHeaders: ['Server', 'MockatonTester'],
|
|
124
|
+
extraMimes: {
|
|
125
|
+
my_custom_extension: 'my_custom_mime'
|
|
126
|
+
}
|
|
115
127
|
})
|
|
116
128
|
server.on('listening', runTests)
|
|
117
129
|
|
|
@@ -161,6 +173,7 @@ async function runTests() {
|
|
|
161
173
|
await testMockDispatching(url, file, body)
|
|
162
174
|
|
|
163
175
|
await testMockDispatching('/api/object', 'api/object.GET.200.js', { JSON_FROM_JS: true }, mimeFor('.json'))
|
|
176
|
+
await testMockDispatching(...fixtureCustomMime, 'my_custom_mime')
|
|
164
177
|
await testJsFunctionMocks()
|
|
165
178
|
|
|
166
179
|
await testItUpdatesUserRole()
|
|
@@ -190,6 +203,10 @@ async function test404() {
|
|
|
190
203
|
const res = await request('/api/non-existing-too', { method: 'DELETE' })
|
|
191
204
|
equal(res.status, 404)
|
|
192
205
|
})
|
|
206
|
+
await it('Ignores files ending in ~ by default, e.g. JetBrains temp files', async () => {
|
|
207
|
+
const res = await request('/api/ignored')
|
|
208
|
+
equal(res.status, 404)
|
|
209
|
+
})
|
|
193
210
|
}
|
|
194
211
|
|
|
195
212
|
async function testMockDispatching(url, file, expectedBody, forcedMime = void 0) {
|
package/index.d.ts
CHANGED
|
@@ -5,11 +5,13 @@ interface Config {
|
|
|
5
5
|
staticDir?: string
|
|
6
6
|
host?: string,
|
|
7
7
|
port?: number
|
|
8
|
+
ignore?: RegExp
|
|
8
9
|
delay?: number
|
|
9
10
|
onReady?: (address: string) => void
|
|
10
11
|
cookies?: object
|
|
11
12
|
proxyFallback?: string
|
|
12
|
-
extraHeaders?: [string, string][]
|
|
13
|
+
extraHeaders?: [string, string][]
|
|
14
|
+
extraMimes?: object
|
|
13
15
|
}
|
|
14
16
|
|
|
15
17
|
export function Mockaton(options: Config): Server
|
package/package.json
CHANGED
package/src/Config.js
CHANGED
|
@@ -8,11 +8,13 @@ export const Config = {
|
|
|
8
8
|
staticDir: '',
|
|
9
9
|
host: '127.0.0.1',
|
|
10
10
|
port: 0, // auto-assigned
|
|
11
|
+
ignore: /(\.DS_Store|~)$/,
|
|
11
12
|
delay: 1200, // milliseconds
|
|
12
13
|
cookies: {}, // defaults to the first kv
|
|
13
14
|
onReady: openInBrowser,
|
|
14
15
|
proxyFallback: '', // e.g. http://localhost:9999
|
|
15
|
-
extraHeaders: []
|
|
16
|
+
extraHeaders: [],
|
|
17
|
+
extraMimes: {}
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export function setup(options) {
|
|
@@ -22,11 +24,13 @@ export function setup(options) {
|
|
|
22
24
|
staticDir: optional(isDirectory),
|
|
23
25
|
host: is(String),
|
|
24
26
|
port: port => Number.isInteger(port) && port >= 0 && port < 2 ** 16,
|
|
27
|
+
ignore: is(RegExp),
|
|
25
28
|
delay: ms => Number.isInteger(ms) && ms > 0,
|
|
26
29
|
cookies: is(Object),
|
|
27
30
|
onReady: is(Function),
|
|
28
31
|
proxyFallback: optional(URL.canParse),
|
|
29
|
-
extraHeaders: Array.isArray
|
|
32
|
+
extraHeaders: Array.isArray,
|
|
33
|
+
extraMimes: is(Object)
|
|
30
34
|
})
|
|
31
35
|
}
|
|
32
36
|
|
package/src/MockBroker.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import { join } from 'node:path'
|
|
2
1
|
import { Route } from './Route.js'
|
|
3
2
|
import { Config } from './Config.js'
|
|
4
|
-
import { isDirectory } from './utils/fs.js'
|
|
5
3
|
import { DEFAULT_500_COMMENT } from './ApiConstants.js'
|
|
6
4
|
|
|
7
5
|
|
|
@@ -12,13 +10,11 @@ export class MockBroker {
|
|
|
12
10
|
|
|
13
11
|
constructor(file) {
|
|
14
12
|
this.#route = new Route(file)
|
|
15
|
-
this.method = this.#route.method
|
|
16
13
|
this.mocks = []
|
|
17
14
|
this.currentMock = {
|
|
18
15
|
file: '',
|
|
19
16
|
delay: 0
|
|
20
17
|
}
|
|
21
|
-
|
|
22
18
|
this.register(file)
|
|
23
19
|
}
|
|
24
20
|
|
|
@@ -62,19 +58,11 @@ export class MockBroker {
|
|
|
62
58
|
if (!this.#has500())
|
|
63
59
|
this.#registerTemp500()
|
|
64
60
|
}
|
|
65
|
-
|
|
66
61
|
#has500() {
|
|
67
|
-
return this.mocks.some(mock =>
|
|
68
|
-
Route.parseFilename(mock).status === 500)
|
|
62
|
+
return this.mocks.some(mock => Route.parseFilename(mock).status === 500)
|
|
69
63
|
}
|
|
70
|
-
|
|
71
64
|
#registerTemp500() {
|
|
72
65
|
const { urlMask, method } = Route.parseFilename(this.mocks[0])
|
|
73
|
-
|
|
74
|
-
if (isDirectory(join(Config.mocksDir, urlMask)))
|
|
75
|
-
mask = urlMask + '/'
|
|
76
|
-
mask = mask.replace(/^\//, '') // remove initial slash
|
|
77
|
-
const file = `${mask}${DEFAULT_500_COMMENT}.${method}.500.txt`
|
|
78
|
-
this.register(file)
|
|
66
|
+
this.register(`${urlMask}${DEFAULT_500_COMMENT}.${method}.500.txt`)
|
|
79
67
|
}
|
|
80
68
|
}
|
package/src/MockDispatcher.js
CHANGED
|
@@ -4,7 +4,7 @@ import { readFileSync } from 'node:fs'
|
|
|
4
4
|
import { proxy } from './ProxyRelay.js'
|
|
5
5
|
import { cookie } from './cookie.js'
|
|
6
6
|
import { Config } from './Config.js'
|
|
7
|
-
import { mimeFor } from './
|
|
7
|
+
import { mimeFor } from './mime.js'
|
|
8
8
|
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
9
9
|
import { JsonBodyParserError } from './utils/http-request.js'
|
|
10
10
|
import { sendInternalServerError, sendNotFound, sendBadRequest } from './utils/http-response.js'
|
|
@@ -22,7 +22,7 @@ export async function dispatchMock(req, response) {
|
|
|
22
22
|
|
|
23
23
|
try {
|
|
24
24
|
const { file, status, delay } = broker
|
|
25
|
-
console.log(
|
|
25
|
+
console.log(req.url, ' → ', file)
|
|
26
26
|
|
|
27
27
|
let mockText
|
|
28
28
|
if (file.endsWith('.js')) {
|
|
@@ -59,7 +59,7 @@ async function jsMockText(file, req, response) {
|
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
function readMock(file) {
|
|
62
|
-
return readFileSync(join(Config.mocksDir, file)
|
|
62
|
+
return readFileSync(join(Config.mocksDir, file))
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
async function importDefault(file) {
|
package/src/Route.js
CHANGED
|
@@ -14,8 +14,7 @@ export class Route {
|
|
|
14
14
|
#urlRegex
|
|
15
15
|
|
|
16
16
|
constructor(file) {
|
|
17
|
-
const { urlMask
|
|
18
|
-
this.method = method
|
|
17
|
+
const { urlMask } = Route.parseFilename(file)
|
|
19
18
|
this.#urlRegex = new RegExp('^' + disregardVariables(removeQueryStringAndFragment(urlMask)) + '/*$')
|
|
20
19
|
}
|
|
21
20
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { Config } from './Config.js'
|
|
2
|
+
|
|
1
3
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
|
2
4
|
// m = {};
|
|
3
5
|
// for (const row of tbody.children)
|
|
4
6
|
// m[row.children[0].querySelector('code').innerText] = row.children[2].querySelector('code').innerText
|
|
5
|
-
|
|
6
7
|
const mimes = {
|
|
7
8
|
'3g2': 'video/3gpp2',
|
|
8
9
|
'3gp': 'video/3gpp',
|
|
@@ -40,6 +41,7 @@ const mimes = {
|
|
|
40
41
|
json: 'application/json',
|
|
41
42
|
jsonld: 'application/ld+json',
|
|
42
43
|
mid: 'audio/midi',
|
|
44
|
+
midi: 'audio/midi',
|
|
43
45
|
mjs: 'text/javascript',
|
|
44
46
|
mp3: 'audio/mpeg',
|
|
45
47
|
mp4: 'video/mp4',
|
|
@@ -84,7 +86,7 @@ const mimes = {
|
|
|
84
86
|
|
|
85
87
|
export function mimeFor(filename) {
|
|
86
88
|
const ext = filename.replace(/.*\./, '').toLowerCase()
|
|
87
|
-
const mime = mimes[ext] || ''
|
|
89
|
+
const mime = Config.extraMimes[ext] || mimes[ext] || ''
|
|
88
90
|
if (!mime)
|
|
89
91
|
console.info(`Missing MIME for ${filename}`)
|
|
90
92
|
return mime
|
|
@@ -5,7 +5,6 @@ import { Route } from './Route.js'
|
|
|
5
5
|
import { Config } from './Config.js'
|
|
6
6
|
import { cookie } from './cookie.js'
|
|
7
7
|
import { isFile } from './utils/fs.js'
|
|
8
|
-
import { mimeFor } from './utils/mime.js'
|
|
9
8
|
import { MockBroker } from './MockBroker.js'
|
|
10
9
|
|
|
11
10
|
|
|
@@ -26,7 +25,7 @@ export function init() {
|
|
|
26
25
|
cookie.init(Config.cookies)
|
|
27
26
|
|
|
28
27
|
const files = readDir(Config.mocksDir, { recursive: true })
|
|
29
|
-
.filter(f => isFile(join(Config.mocksDir, f))
|
|
28
|
+
.filter(f => !Config.ignore.test(f) && isFile(join(Config.mocksDir, f)))
|
|
30
29
|
.sort()
|
|
31
30
|
|
|
32
31
|
for (const file of files) {
|