mockaton 6.0.0 → 6.3.0
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 -6
- package/Tests.js +21 -5
- package/index.d.ts +3 -2
- package/package.json +1 -1
- package/src/Config.js +6 -4
- package/src/MockBroker.js +0 -1
- package/src/MockDispatcher.js +2 -2
- package/src/mime.js +93 -0
- package/src/mockBrokersCollection.js +1 -1
- package/src/utils/http-response.js +1 -1
- package/src/utils/mime.js +0 -47
package/README.md
CHANGED
|
@@ -48,10 +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
|
|
54
|
-
|
|
55
|
+
extraMimes?: object
|
|
55
56
|
extraHeaders?: []
|
|
56
57
|
}
|
|
57
58
|
```
|
|
@@ -116,10 +117,6 @@ The delay is globally configurable via `Config.delay = 1200` (milliseconds).
|
|
|
116
117
|
### Extension
|
|
117
118
|
`.Method.ResponseStatusCode.FileExt`
|
|
118
119
|
|
|
119
|
-
The **file extension** can be anything, but `.md` is reserved for documentation.
|
|
120
|
-
|
|
121
|
-
The `Config.allowedExt` regex defaults to: `/\.(md|json|txt|js)$/`
|
|
122
|
-
|
|
123
120
|
|
|
124
121
|
### Dynamic Parameters
|
|
125
122
|
Anything within square brackets. For example:
|
|
@@ -132,7 +129,7 @@ Comments are anything within parentheses, including them.
|
|
|
132
129
|
They are ignored for URL purposes, so they have no effect
|
|
133
130
|
on the URL mask. For example, these two are for `/api/foo`
|
|
134
131
|
<pre>
|
|
135
|
-
api/foo<b>(my comment)</b>.GET.200.json
|
|
132
|
+
api/foo<b>(my comment)</b>.GET.200.json
|
|
136
133
|
api/foo.GET.200.json
|
|
137
134
|
</pre>
|
|
138
135
|
|
|
@@ -150,6 +147,9 @@ but since that’s part of the query string, it’s ignored anyway.
|
|
|
150
147
|
|
|
151
148
|
|
|
152
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
|
+
|
|
153
153
|
**Option A.** Place it outside the directory:
|
|
154
154
|
```
|
|
155
155
|
api/foo/
|
|
@@ -199,6 +199,13 @@ Config.extraHeaders = [
|
|
|
199
199
|
]
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
+
## `Config.extraMimes`
|
|
203
|
+
```js
|
|
204
|
+
Config.extraMimes = {
|
|
205
|
+
jpg: 'application/jpeg'
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
202
209
|
---
|
|
203
210
|
|
|
204
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) {
|
|
@@ -354,7 +371,6 @@ async function testInvalidFilenamesAreIgnored() {
|
|
|
354
371
|
const consoleErrorSpy = t.mock.method(console, 'error')
|
|
355
372
|
consoleErrorSpy.mock.mockImplementation(() => {}) // so they don’t render in the test report
|
|
356
373
|
|
|
357
|
-
// An extension is needed for testing because of `Config.allowedExt`
|
|
358
374
|
write('api/_INVALID_FILENAME_CONVENTION_.json', '')
|
|
359
375
|
write('api/bad-filename.GET._INVALID_STATUS_.json', '')
|
|
360
376
|
write('api/bad-filename._INVALID_METHOD_.200.json', '')
|
package/index.d.ts
CHANGED
|
@@ -5,12 +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
|
-
|
|
13
|
-
|
|
13
|
+
extraHeaders?: [string, string][]
|
|
14
|
+
extraMimes?: object
|
|
14
15
|
}
|
|
15
16
|
|
|
16
17
|
export function Mockaton(options: Config): Server
|
package/package.json
CHANGED
package/src/Config.js
CHANGED
|
@@ -8,12 +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
|
-
|
|
16
|
-
|
|
16
|
+
extraHeaders: [],
|
|
17
|
+
extraMimes: {}
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export function setup(options) {
|
|
@@ -23,12 +24,13 @@ export function setup(options) {
|
|
|
23
24
|
staticDir: optional(isDirectory),
|
|
24
25
|
host: is(String),
|
|
25
26
|
port: port => Number.isInteger(port) && port >= 0 && port < 2 ** 16,
|
|
27
|
+
ignore: is(RegExp),
|
|
26
28
|
delay: ms => Number.isInteger(ms) && ms > 0,
|
|
27
29
|
cookies: is(Object),
|
|
28
30
|
onReady: is(Function),
|
|
29
31
|
proxyFallback: optional(URL.canParse),
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
extraHeaders: Array.isArray,
|
|
33
|
+
extraMimes: is(Object)
|
|
32
34
|
})
|
|
33
35
|
}
|
|
34
36
|
|
package/src/MockBroker.js
CHANGED
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'
|
|
@@ -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/mime.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { Config } from './Config.js'
|
|
2
|
+
|
|
3
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
|
4
|
+
// m = {};
|
|
5
|
+
// for (const row of tbody.children)
|
|
6
|
+
// m[row.children[0].querySelector('code').innerText] = row.children[2].querySelector('code').innerText
|
|
7
|
+
const mimes = {
|
|
8
|
+
'3g2': 'video/3gpp2',
|
|
9
|
+
'3gp': 'video/3gpp',
|
|
10
|
+
'7z': 'application/x-7z-compressed',
|
|
11
|
+
aac: 'audio/aac',
|
|
12
|
+
abw: 'application/x-abiword',
|
|
13
|
+
apng: 'image/apng',
|
|
14
|
+
arc: 'application/x-freearc',
|
|
15
|
+
avi: 'video/x-msvideo',
|
|
16
|
+
avif: 'image/avif',
|
|
17
|
+
azw: 'application/vnd.amazon.ebook',
|
|
18
|
+
bin: 'application/octet-stream',
|
|
19
|
+
bmp: 'image/bmp',
|
|
20
|
+
bz2: 'application/x-bzip2',
|
|
21
|
+
bz: 'application/x-bzip',
|
|
22
|
+
cda: 'application/x-cdf',
|
|
23
|
+
cjs: 'text/javascript',
|
|
24
|
+
csh: 'application/x-csh',
|
|
25
|
+
css: 'text/css',
|
|
26
|
+
csv: 'text/csv',
|
|
27
|
+
doc: 'application/msword',
|
|
28
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
29
|
+
eot: 'application/vnd.ms-fontobject',
|
|
30
|
+
epub: 'application/epub+zip',
|
|
31
|
+
gif: 'image/gif',
|
|
32
|
+
gz: 'application/gzip',
|
|
33
|
+
htm: 'text/html',
|
|
34
|
+
html: 'text/html',
|
|
35
|
+
ico: 'image/vnd.microsoft.icon',
|
|
36
|
+
ics: 'text/calendar',
|
|
37
|
+
jar: 'application/java-archive',
|
|
38
|
+
jpeg: 'image/jpeg',
|
|
39
|
+
jpg: 'image/jpeg',
|
|
40
|
+
js: 'text/javascript',
|
|
41
|
+
json: 'application/json',
|
|
42
|
+
jsonld: 'application/ld+json',
|
|
43
|
+
mid: 'audio/midi',
|
|
44
|
+
midi: 'audio/midi',
|
|
45
|
+
mjs: 'text/javascript',
|
|
46
|
+
mp3: 'audio/mpeg',
|
|
47
|
+
mp4: 'video/mp4',
|
|
48
|
+
mpeg: 'video/mpeg',
|
|
49
|
+
mpkg: 'application/vnd.apple.installer+xml',
|
|
50
|
+
odp: 'application/vnd.oasis.opendocument.presentation',
|
|
51
|
+
ods: 'application/vnd.oasis.opendocument.spreadsheet',
|
|
52
|
+
odt: 'application/vnd.oasis.opendocument.text',
|
|
53
|
+
oga: 'audio/ogg',
|
|
54
|
+
ogv: 'video/ogg',
|
|
55
|
+
ogx: 'application/ogg',
|
|
56
|
+
opus: 'audio/ogg',
|
|
57
|
+
otf: 'font/otf',
|
|
58
|
+
pdf: 'application/pdf',
|
|
59
|
+
php: 'application/x-httpd-php',
|
|
60
|
+
png: 'image/png',
|
|
61
|
+
ppt: 'application/vnd.ms-powerpoint',
|
|
62
|
+
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
|
63
|
+
rar: 'application/vnd.rar',
|
|
64
|
+
rtf: 'application/rtf',
|
|
65
|
+
sh: 'application/x-sh',
|
|
66
|
+
svg: 'image/svg+xml',
|
|
67
|
+
tar: 'application/x-tar',
|
|
68
|
+
tif: 'image/tiff',
|
|
69
|
+
ts: 'video/mp2t',
|
|
70
|
+
ttf: 'font/ttf',
|
|
71
|
+
txt: 'text/plain',
|
|
72
|
+
vsd: 'application/vnd.visio',
|
|
73
|
+
wav: 'audio/wav',
|
|
74
|
+
weba: 'audio/webm',
|
|
75
|
+
webm: 'video/webm',
|
|
76
|
+
webp: 'image/webp',
|
|
77
|
+
woff2: 'font/woff2',
|
|
78
|
+
woff: 'font/woff',
|
|
79
|
+
xhtml: 'application/xhtml+xml',
|
|
80
|
+
xls: 'application/vnd.ms-excel',
|
|
81
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
82
|
+
xml: 'application/xml',
|
|
83
|
+
xul: 'application/vnd.mozilla.xul+xml',
|
|
84
|
+
zip: 'application/zip'
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function mimeFor(filename) {
|
|
88
|
+
const ext = filename.replace(/.*\./, '').toLowerCase()
|
|
89
|
+
const mime = Config.extraMimes[ext] || mimes[ext] || ''
|
|
90
|
+
if (!mime)
|
|
91
|
+
console.info(`Missing MIME for ${filename}`)
|
|
92
|
+
return mime
|
|
93
|
+
}
|
|
@@ -25,7 +25,7 @@ export function init() {
|
|
|
25
25
|
cookie.init(Config.cookies)
|
|
26
26
|
|
|
27
27
|
const files = readDir(Config.mocksDir, { recursive: true })
|
|
28
|
-
.filter(f => Config.
|
|
28
|
+
.filter(f => !Config.ignore.test(f) && isFile(join(Config.mocksDir, f)))
|
|
29
29
|
.sort()
|
|
30
30
|
|
|
31
31
|
for (const file of files) {
|
package/src/utils/mime.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
|
2
|
-
const mimes = {
|
|
3
|
-
aac: 'audio/acc',
|
|
4
|
-
apng: 'image/apng',
|
|
5
|
-
avif: 'image/avif',
|
|
6
|
-
css: 'text/css',
|
|
7
|
-
doc: 'application/msword',
|
|
8
|
-
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
9
|
-
eot: 'application/vnd.ms-fontobject',
|
|
10
|
-
epub: 'application/epub+zip',
|
|
11
|
-
gif: 'image/gif',
|
|
12
|
-
gz: 'application/gzip',
|
|
13
|
-
htm: 'text/html',
|
|
14
|
-
html: 'text/html',
|
|
15
|
-
ico: 'image/vnd.microsoft.icon',
|
|
16
|
-
ics: 'text/calendar',
|
|
17
|
-
jpeg: 'image/jpeg',
|
|
18
|
-
jpg: 'image/jpeg',
|
|
19
|
-
js: 'application/javascript',
|
|
20
|
-
json: 'application/json',
|
|
21
|
-
jsonld: 'application/ld+json',
|
|
22
|
-
md: 'text/markdown',
|
|
23
|
-
mjs: 'text/javascript',
|
|
24
|
-
mp3: 'audio/mpeg',
|
|
25
|
-
mp4: 'video/mp4',
|
|
26
|
-
oft: 'font/otf',
|
|
27
|
-
pdf: 'application/pdf',
|
|
28
|
-
png: 'image/png',
|
|
29
|
-
ttf: 'font/ttf',
|
|
30
|
-
txt: 'plain/text',
|
|
31
|
-
wav: 'audio/wav',
|
|
32
|
-
weba: 'audio/webm',
|
|
33
|
-
webm: 'video/webm',
|
|
34
|
-
webp: 'image/webp',
|
|
35
|
-
woff2: 'font/woff2',
|
|
36
|
-
woff: 'font/woff',
|
|
37
|
-
xml: 'application/xml',
|
|
38
|
-
zip: 'application/zip'
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function mimeFor(filename) {
|
|
42
|
-
const ext = filename.replace(/.*\./, '')
|
|
43
|
-
const mime = mimes[ext] || ''
|
|
44
|
-
if (!mime)
|
|
45
|
-
console.error(`Missing MIME for ${filename}`)
|
|
46
|
-
return mime
|
|
47
|
-
}
|