mockaton 3.0.0 → 4.0.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 +10 -11
- package/Tests.js +6 -6
- package/index.d.ts +1 -2
- package/package.json +1 -1
- package/src/ApiConstants.js +2 -0
- package/src/Config.js +2 -4
- package/src/Dashboard.css +46 -13
- package/src/Dashboard.js +62 -19
- package/src/MockBroker.js +9 -7
- package/src/MockDispatcher.js +14 -11
- package/src/Mockaton.js +1 -2
- package/src/mockBrokersCollection.js +1 -4
- package/src/utils/http-response.js +7 -3
package/README.md
CHANGED
|
@@ -44,17 +44,16 @@ node my-mockaton.js
|
|
|
44
44
|
## Config Options
|
|
45
45
|
```ts
|
|
46
46
|
interface Config {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
extraHeaders?: []
|
|
47
|
+
mocksDir: string
|
|
48
|
+
staticDir?: string
|
|
49
|
+
host?: string, // defaults to 'localhost'
|
|
50
|
+
port?: number // defaults to 0, which means auto-assigned
|
|
51
|
+
delay?: number // defaults to 1200 (ms)
|
|
52
|
+
onReady?: (dashboardUrl: string) => void // defaults to openInBrowser. pass a noop to prevent opening the dashboard
|
|
53
|
+
cookies?: object
|
|
54
|
+
proxyFallback?: string // e.g. http://localhost:9999 Target for relaying routes without mocks
|
|
55
|
+
allowedExt?: RegExp // /\.(json|txt|md|js)$/ Just for excluding temporary editor files (e.g. JetBrains appends a ~)
|
|
56
|
+
extraHeaders?: []
|
|
58
57
|
}
|
|
59
58
|
```
|
|
60
59
|
|
package/Tests.js
CHANGED
|
@@ -11,7 +11,7 @@ import { writeFileSync, mkdtempSync, mkdirSync } from 'node:fs'
|
|
|
11
11
|
import { Route } from './src/Route.js'
|
|
12
12
|
import { mimeFor } from './src/utils/mime.js'
|
|
13
13
|
import { Mockaton } from './src/Mockaton.js'
|
|
14
|
-
import { API, DF } from './src/ApiConstants.js'
|
|
14
|
+
import { API, DF, DEFAULT_500_COMMENT } from './src/ApiConstants.js'
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
const tmpDir = mkdtempSync(tmpdir()) + '/'
|
|
@@ -106,12 +106,11 @@ writeStatic('another-entry/index.html', '<h1>Another</h1>')
|
|
|
106
106
|
const server = Mockaton({
|
|
107
107
|
mocksDir: tmpDir,
|
|
108
108
|
staticDir: staticTmpDir,
|
|
109
|
-
|
|
109
|
+
onReady: () => {},
|
|
110
110
|
cookies: {
|
|
111
111
|
userA: 'CookieA',
|
|
112
112
|
userB: 'CookieB'
|
|
113
113
|
},
|
|
114
|
-
generate500: true,
|
|
115
114
|
extraHeaders: ['Server', 'MockatonTester']
|
|
116
115
|
})
|
|
117
116
|
server.on('listening', runTests)
|
|
@@ -129,8 +128,8 @@ async function runTests() {
|
|
|
129
128
|
JSON.stringify({ comment: 2 }))
|
|
130
129
|
|
|
131
130
|
await testAutogenerates500(
|
|
132
|
-
'/api/
|
|
133
|
-
|
|
131
|
+
'/api/alternative',
|
|
132
|
+
`api/alternative${DEFAULT_500_COMMENT}.GET.500.txt`)
|
|
134
133
|
|
|
135
134
|
await testPreservesExiting500(
|
|
136
135
|
'/api',
|
|
@@ -148,6 +147,7 @@ async function runTests() {
|
|
|
148
147
|
await testExtractsAllComments([
|
|
149
148
|
'(comment-1)',
|
|
150
149
|
'(comment-2)',
|
|
150
|
+
DEFAULT_500_COMMENT,
|
|
151
151
|
'(this is the actual comment)',
|
|
152
152
|
'(another comment)'
|
|
153
153
|
])
|
|
@@ -248,7 +248,7 @@ async function testAutogenerates500(url, file) {
|
|
|
248
248
|
})
|
|
249
249
|
const res = await request(url)
|
|
250
250
|
const body = await res.text()
|
|
251
|
-
await describe('autogenerated 500', () => {
|
|
251
|
+
await describe('autogenerated in-memory 500', () => {
|
|
252
252
|
it('body is empty', () => equal(body, ''))
|
|
253
253
|
it('status is: 500', () => equal(res.status, 500))
|
|
254
254
|
})
|
package/index.d.ts
CHANGED
|
@@ -6,11 +6,10 @@ interface Config {
|
|
|
6
6
|
host?: string,
|
|
7
7
|
port?: number
|
|
8
8
|
delay?: number
|
|
9
|
-
|
|
9
|
+
onReady?: (address: string) => void
|
|
10
10
|
cookies?: object
|
|
11
11
|
proxyFallback?: string
|
|
12
12
|
allowedExt?: RegExp
|
|
13
|
-
generate500?: boolean,
|
|
14
13
|
extraHeaders?: [string, string][]
|
|
15
14
|
}
|
|
16
15
|
|
package/package.json
CHANGED
package/src/ApiConstants.js
CHANGED
package/src/Config.js
CHANGED
|
@@ -8,11 +8,10 @@ export const Config = {
|
|
|
8
8
|
host: '127.0.0.1',
|
|
9
9
|
port: 0, // auto-assigned
|
|
10
10
|
delay: 1200, // milliseconds
|
|
11
|
-
open: openInBrowser,
|
|
12
11
|
cookies: {}, // defaults to the first kv
|
|
12
|
+
onReady: openInBrowser,
|
|
13
13
|
proxyFallback: '', // e.g. http://localhost:9999
|
|
14
14
|
allowedExt: /\.(json|txt|md|js)$/, // Just for excluding temporary editor files (e.g. JetBrains appends a ~)
|
|
15
|
-
generate500: false,
|
|
16
15
|
extraHeaders: []
|
|
17
16
|
}
|
|
18
17
|
|
|
@@ -24,11 +23,10 @@ export function setup(options) {
|
|
|
24
23
|
host: is(String),
|
|
25
24
|
port: port => Number.isInteger(port) && port >= 0 && port < 2 ** 16,
|
|
26
25
|
delay: ms => Number.isInteger(ms) && ms > 0,
|
|
27
|
-
open: is(Function),
|
|
28
26
|
cookies: is(Object),
|
|
27
|
+
onReady: is(Function),
|
|
29
28
|
proxyFallback: optional(URL.canParse),
|
|
30
29
|
allowedExt: is(RegExp),
|
|
31
|
-
generate500: is(Boolean),
|
|
32
30
|
extraHeaders: Array.isArray
|
|
33
31
|
})
|
|
34
32
|
}
|
package/src/Dashboard.css
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
}
|
|
9
9
|
html, body {
|
|
10
10
|
margin: 0;
|
|
11
|
-
font-size:
|
|
11
|
+
font-size: 12px;
|
|
12
12
|
}
|
|
13
13
|
body {
|
|
14
14
|
padding: 16px;
|
|
@@ -50,36 +50,40 @@ main {
|
|
|
50
50
|
padding-bottom: 2px;
|
|
51
51
|
text-align: left;
|
|
52
52
|
}
|
|
53
|
+
|
|
54
|
+
tr {
|
|
55
|
+
border-bottom: 2px solid transparent;
|
|
56
|
+
}
|
|
53
57
|
}
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
menu {
|
|
57
61
|
display: flex;
|
|
62
|
+
align-items: flex-end;
|
|
58
63
|
margin-bottom: 12px;
|
|
59
64
|
gap: 14px;
|
|
60
|
-
align-items: flex-end;
|
|
61
65
|
|
|
62
66
|
h1 {
|
|
63
67
|
margin: 0;
|
|
64
|
-
margin-right:
|
|
65
|
-
font-size:
|
|
68
|
+
margin-right: 12px;
|
|
69
|
+
font-size: 26px;
|
|
66
70
|
}
|
|
67
71
|
|
|
68
72
|
label {
|
|
69
73
|
span {
|
|
70
74
|
display: block;
|
|
71
75
|
color: #555;
|
|
72
|
-
font-size:
|
|
76
|
+
font-size: 11px;
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
select {
|
|
76
|
-
width:
|
|
80
|
+
width: 144px;
|
|
77
81
|
padding: 3px 0;
|
|
78
82
|
border: 1px solid #bbb;
|
|
79
83
|
margin-top: 1px;
|
|
80
84
|
cursor: pointer;
|
|
81
85
|
border-radius: 4px;
|
|
82
|
-
font-size:
|
|
86
|
+
font-size: 11px;
|
|
83
87
|
}
|
|
84
88
|
}
|
|
85
89
|
|
|
@@ -105,6 +109,8 @@ menu {
|
|
|
105
109
|
margin-left: 16px;
|
|
106
110
|
|
|
107
111
|
pre {
|
|
112
|
+
tab-size: 2;
|
|
113
|
+
|
|
108
114
|
&:not(:empty) {
|
|
109
115
|
overflow: auto;
|
|
110
116
|
max-height: calc(100vh - 160px);
|
|
@@ -147,6 +153,7 @@ menu {
|
|
|
147
153
|
text-align: right;
|
|
148
154
|
direction: rtl;
|
|
149
155
|
text-overflow: ellipsis;
|
|
156
|
+
font-size: 12px;
|
|
150
157
|
|
|
151
158
|
&:disabled {
|
|
152
159
|
background: transparent;
|
|
@@ -160,14 +167,11 @@ menu {
|
|
|
160
167
|
&.status4xx {
|
|
161
168
|
background: var(--colorLightOrange);
|
|
162
169
|
}
|
|
163
|
-
&.status5xx {
|
|
164
|
-
background: var(--colorLightRed);
|
|
165
|
-
}
|
|
166
170
|
}
|
|
167
171
|
|
|
168
|
-
.
|
|
172
|
+
.DelayToggler {
|
|
169
173
|
display: flex;
|
|
170
|
-
margin-left:
|
|
174
|
+
margin-left: 8px;
|
|
171
175
|
cursor: pointer;
|
|
172
176
|
|
|
173
177
|
> input {
|
|
@@ -189,7 +193,36 @@ menu {
|
|
|
189
193
|
background: white;
|
|
190
194
|
|
|
191
195
|
&:hover {
|
|
192
|
-
background: #
|
|
196
|
+
background: #bce5ff;
|
|
197
|
+
fill: #00318b;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.InternalServerErrorToggler {
|
|
203
|
+
display: flex;
|
|
204
|
+
margin-left: 6px;
|
|
205
|
+
cursor: pointer;
|
|
206
|
+
|
|
207
|
+
> input {
|
|
208
|
+
display: none;
|
|
209
|
+
|
|
210
|
+
&:checked ~ span {
|
|
211
|
+
color: white;
|
|
212
|
+
background: var(--colorRed);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
> span {
|
|
217
|
+
padding: 4px;
|
|
218
|
+
font-size: 10px;
|
|
219
|
+
color: #333;
|
|
220
|
+
border-radius: 2px;
|
|
221
|
+
background: white;
|
|
222
|
+
|
|
223
|
+
&:hover {
|
|
224
|
+
background: var(--colorLightRed);
|
|
225
|
+
color: var(--colorRed);
|
|
193
226
|
}
|
|
194
227
|
}
|
|
195
228
|
}
|
package/src/Dashboard.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Route } from '../Route.js'
|
|
2
|
-
import { API, DF } from '../ApiConstants.js'
|
|
2
|
+
import { API, DF, DEFAULT_500_COMMENT } from '../ApiConstants.js'
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
const Strings = {
|
|
@@ -9,6 +9,7 @@ const Strings = {
|
|
|
9
9
|
delay: 'Delay',
|
|
10
10
|
empty_response_body: '/* Empty Response Body */',
|
|
11
11
|
fetching: '⌚ Fetching…',
|
|
12
|
+
internal_server_error: 'Internal Server Error',
|
|
12
13
|
mock: 'Mock',
|
|
13
14
|
reset: 'Reset',
|
|
14
15
|
select_one: 'Select One',
|
|
@@ -16,7 +17,8 @@ const Strings = {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
const CSS = {
|
|
19
|
-
|
|
20
|
+
DelayToggler: 'DelayToggler',
|
|
21
|
+
InternalServerErrorToggler: 'InternalServerErrorToggler',
|
|
20
22
|
Documentation: 'Documentation',
|
|
21
23
|
MockSelector: 'MockSelector',
|
|
22
24
|
PayloadViewer: 'PayloadViewer',
|
|
@@ -25,7 +27,6 @@ const CSS = {
|
|
|
25
27
|
bold: 'bold',
|
|
26
28
|
chosen: 'chosen',
|
|
27
29
|
status4xx: 'status4xx',
|
|
28
|
-
status5xx: 'status5xx'
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
const r = createElement
|
|
@@ -136,8 +137,10 @@ function SectionByMethod({ method, brokers }) {
|
|
|
136
137
|
.map(([urlMask, broker]) =>
|
|
137
138
|
r('tr', null,
|
|
138
139
|
r('td', null, r(PreviewLink, { method, urlMask, documentation: broker.documentation })),
|
|
139
|
-
r('td', null, r(MockSelector, {
|
|
140
|
-
r('td', null, r(DelayToggler, {
|
|
140
|
+
r('td', null, r(MockSelector, { broker })),
|
|
141
|
+
r('td', null, r(DelayToggler, { broker })),
|
|
142
|
+
r('td', null, r(InternalServerErrorToggler, { broker }))
|
|
143
|
+
))))
|
|
141
144
|
}
|
|
142
145
|
|
|
143
146
|
function PreviewLink({ method, urlMask, documentation }) {
|
|
@@ -173,19 +176,27 @@ function PreviewLink({ method, urlMask, documentation }) {
|
|
|
173
176
|
}, urlMask))
|
|
174
177
|
}
|
|
175
178
|
|
|
176
|
-
function MockSelector({
|
|
179
|
+
function MockSelector({ broker }) {
|
|
177
180
|
const className = (defaultIsSelected, status) => cssClass(
|
|
178
181
|
CSS.MockSelector,
|
|
179
182
|
!defaultIsSelected && CSS.bold,
|
|
180
|
-
status >= 400 && status < 500 && CSS.status4xx
|
|
181
|
-
|
|
183
|
+
status >= 400 && status < 500 && CSS.status4xx)
|
|
184
|
+
|
|
185
|
+
const items = broker.mocks
|
|
186
|
+
const selected = broker.currentMock.file
|
|
187
|
+
|
|
188
|
+
const { status } = Route.parseFilename(selected)
|
|
189
|
+
const files = items.filter(item =>
|
|
190
|
+
status === 500 ||
|
|
191
|
+
!item.includes(DEFAULT_500_COMMENT))
|
|
192
|
+
|
|
182
193
|
return (
|
|
183
194
|
r('select', {
|
|
184
|
-
className: className(selected ===
|
|
195
|
+
className: className(selected === files[0], status),
|
|
185
196
|
autocomplete: 'off',
|
|
186
|
-
disabled:
|
|
197
|
+
disabled: files.length <= 1,
|
|
187
198
|
onChange() {
|
|
188
|
-
const status = Route.parseFilename(this.value)
|
|
199
|
+
const { status } = Route.parseFilename(this.value)
|
|
189
200
|
this.style.fontWeight = this.value === this.options[0].value // default is selected
|
|
190
201
|
? 'normal'
|
|
191
202
|
: 'bold'
|
|
@@ -194,20 +205,22 @@ function MockSelector({ items, selected }) {
|
|
|
194
205
|
body: JSON.stringify({ [DF.file]: this.value })
|
|
195
206
|
}).then(() => {
|
|
196
207
|
this.closest('tr').querySelector('a').click()
|
|
197
|
-
this.
|
|
208
|
+
this.closest('tr').querySelector(`.${CSS.InternalServerErrorToggler}>[type=checkbox]`).checked = status === 500
|
|
209
|
+
this.className = className(this.value === this.options[0].value, status)
|
|
198
210
|
})
|
|
199
211
|
}
|
|
200
|
-
},
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}, item))))
|
|
212
|
+
}, files.map(file => r('option', {
|
|
213
|
+
value: file,
|
|
214
|
+
selected: file === selected
|
|
215
|
+
}, file))))
|
|
205
216
|
}
|
|
206
217
|
|
|
207
|
-
function DelayToggler({
|
|
218
|
+
function DelayToggler({ broker }) {
|
|
219
|
+
const name = broker.currentMock.file
|
|
220
|
+
const checked = Boolean(broker.currentMock.delay)
|
|
208
221
|
return (
|
|
209
222
|
r('label', {
|
|
210
|
-
className: CSS.
|
|
223
|
+
className: CSS.DelayToggler,
|
|
211
224
|
title: Strings.delay
|
|
212
225
|
},
|
|
213
226
|
r('input', {
|
|
@@ -234,6 +247,36 @@ function TimerIcon() {
|
|
|
234
247
|
r('path', { d: 'M12 7H11v6l5 3.2.75-1.23-4.5-3z' })))
|
|
235
248
|
}
|
|
236
249
|
|
|
250
|
+
function InternalServerErrorToggler({ broker }) {
|
|
251
|
+
const items = broker.mocks
|
|
252
|
+
const name = broker.currentMock.file
|
|
253
|
+
const checked = Route.parseFilename(broker.currentMock.file).status === 500
|
|
254
|
+
return (
|
|
255
|
+
r('label', {
|
|
256
|
+
className: CSS.InternalServerErrorToggler,
|
|
257
|
+
title: Strings.internal_server_error
|
|
258
|
+
},
|
|
259
|
+
r('input', {
|
|
260
|
+
type: 'checkbox',
|
|
261
|
+
autocomplete: 'off',
|
|
262
|
+
name,
|
|
263
|
+
checked,
|
|
264
|
+
onChange(event) {
|
|
265
|
+
fetch(API.edit, {
|
|
266
|
+
method: 'PATCH',
|
|
267
|
+
body: JSON.stringify({
|
|
268
|
+
[DF.file]: event.currentTarget.checked
|
|
269
|
+
? items.find(f => f.includes(DEFAULT_500_COMMENT))
|
|
270
|
+
: items[0]
|
|
271
|
+
})
|
|
272
|
+
}).then(init)
|
|
273
|
+
}
|
|
274
|
+
}),
|
|
275
|
+
r('span', null, '500')
|
|
276
|
+
)
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
|
|
237
280
|
|
|
238
281
|
|
|
239
282
|
/* === Utils === */
|
package/src/MockBroker.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
|
-
import { existsSync, lstatSync
|
|
2
|
+
import { existsSync, lstatSync } from 'node:fs'
|
|
3
3
|
|
|
4
4
|
import { Route } from './Route.js'
|
|
5
5
|
import { Config } from './Config.js'
|
|
6
|
+
import { DEFAULT_500_COMMENT } from './ApiConstants.js'
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
// MockBroker is a state for a particular route. It knows the available
|
|
9
10
|
// mock files that can be served for the route, the currently selected
|
|
10
|
-
// file, and its delay. Also, knows if the route has documentation (md)
|
|
11
|
+
// file, and its delay. Also, knows if the route has documentation (md)
|
|
11
12
|
export class MockBroker {
|
|
12
13
|
#route
|
|
13
14
|
|
|
@@ -40,7 +41,8 @@ export class MockBroker {
|
|
|
40
41
|
|
|
41
42
|
get file() { return this.currentMock.file }
|
|
42
43
|
get delay() { return this.currentMock.delay }
|
|
43
|
-
get status() { return Route.parseFilename(this.
|
|
44
|
+
get status() { return Route.parseFilename(this.file).status }
|
|
45
|
+
get isTemp500() { return Route.hasInParentheses(this.file, DEFAULT_500_COMMENT) }
|
|
44
46
|
|
|
45
47
|
updateFile(filename) {
|
|
46
48
|
this.currentMock.file = filename
|
|
@@ -67,7 +69,7 @@ export class MockBroker {
|
|
|
67
69
|
|
|
68
70
|
ensureItHas500() {
|
|
69
71
|
if (!this.#has500())
|
|
70
|
-
this.#
|
|
72
|
+
this.#registerTemp500()
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
#has500() {
|
|
@@ -75,14 +77,14 @@ export class MockBroker {
|
|
|
75
77
|
Route.parseFilename(mock).status === 500)
|
|
76
78
|
}
|
|
77
79
|
|
|
78
|
-
#
|
|
80
|
+
#registerTemp500() {
|
|
79
81
|
const { urlMask, method } = Route.parseFilename(this.mocks[0])
|
|
80
82
|
let mask = urlMask
|
|
81
83
|
const t = join(Config.mocksDir, urlMask)
|
|
82
84
|
if (existsSync(t) && lstatSync(t).isDirectory())
|
|
83
85
|
mask = urlMask + '/'
|
|
84
|
-
|
|
85
|
-
|
|
86
|
+
mask = mask.replace(/^\//, '') // remove initial slash
|
|
87
|
+
const file = `${mask}${DEFAULT_500_COMMENT}.${method}.500.txt`
|
|
86
88
|
this.register(file)
|
|
87
89
|
}
|
|
88
90
|
}
|
package/src/MockDispatcher.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { join } from 'node:path'
|
|
2
2
|
import { readFileSync } from 'node:fs'
|
|
3
3
|
|
|
4
|
-
import { DF } from './ApiConstants.js'
|
|
5
4
|
import { proxy } from './ProxyRelay.js'
|
|
6
5
|
import { cookie } from './cookie.js'
|
|
7
6
|
import { Config } from './Config.js'
|
|
8
7
|
import { mimeFor } from './utils/mime.js'
|
|
9
8
|
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
10
|
-
import {
|
|
9
|
+
import { JsonBodyParserError } from './utils/http-request.js'
|
|
11
10
|
import { sendInternalServerError, sendNotFound, sendFile, sendBadRequest } from './utils/http-response.js'
|
|
12
11
|
|
|
13
12
|
|
|
@@ -33,20 +32,17 @@ export async function dispatchMock(req, response) {
|
|
|
33
32
|
|
|
34
33
|
let mockText
|
|
35
34
|
if (file.endsWith('.js')) {
|
|
36
|
-
response.setHeader('
|
|
37
|
-
|
|
38
|
-
mockText = typeof jsExport === 'function'
|
|
39
|
-
? await jsExport(req, response)
|
|
40
|
-
: JSON.stringify(jsExport, null, 2)
|
|
35
|
+
response.setHeader('Content-Type', mimeFor('.json'))
|
|
36
|
+
mockText = await jsMockText(file, req, response)
|
|
41
37
|
}
|
|
42
38
|
else {
|
|
43
|
-
response.setHeader('
|
|
44
|
-
mockText = readMock(file)
|
|
39
|
+
response.setHeader('Content-Type', mimeFor(file))
|
|
40
|
+
mockText = broker.isTemp500 ? '' : readMock(file)
|
|
45
41
|
}
|
|
46
42
|
|
|
47
43
|
if (cookie.getCurrent())
|
|
48
|
-
response.setHeader('
|
|
49
|
-
|
|
44
|
+
response.setHeader('Set-Cookie', cookie.getCurrent())
|
|
45
|
+
|
|
50
46
|
response.writeHead(status, Config.extraHeaders)
|
|
51
47
|
setTimeout(() => response.end(mockText), delay)
|
|
52
48
|
}
|
|
@@ -61,6 +57,13 @@ export async function dispatchMock(req, response) {
|
|
|
61
57
|
}
|
|
62
58
|
}
|
|
63
59
|
|
|
60
|
+
async function jsMockText(file, req, response) {
|
|
61
|
+
const jsExport = await importDefault(file)
|
|
62
|
+
return typeof jsExport === 'function'
|
|
63
|
+
? await jsExport(req, response)
|
|
64
|
+
: JSON.stringify(jsExport, null, 2)
|
|
65
|
+
}
|
|
66
|
+
|
|
64
67
|
function readMock(file) {
|
|
65
68
|
return readFileSync(join(Config.mocksDir, file), 'utf8')
|
|
66
69
|
}
|
package/src/Mockaton.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { exec } from 'node:child_process'
|
|
2
1
|
import { createServer } from 'node:http'
|
|
3
2
|
|
|
4
3
|
import { API } from './ApiConstants.js'
|
|
@@ -37,6 +36,6 @@ export function Mockaton(options) {
|
|
|
37
36
|
if (error)
|
|
38
37
|
console.error(error)
|
|
39
38
|
else
|
|
40
|
-
Config.
|
|
39
|
+
Config.onReady(url + API.dashboard)
|
|
41
40
|
})
|
|
42
41
|
}
|
|
@@ -40,8 +40,7 @@ export function init() {
|
|
|
40
40
|
collection[method][urlMask].register(file)
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
forEachBroker(broker => broker.ensureItHas500())
|
|
43
|
+
forEachBroker(broker => broker.ensureItHas500())
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
function forEachBroker(fn) {
|
|
@@ -49,7 +48,6 @@ function forEachBroker(fn) {
|
|
|
49
48
|
Object.values(brokers).forEach(fn)
|
|
50
49
|
}
|
|
51
50
|
|
|
52
|
-
|
|
53
51
|
export const getAll = () => collection
|
|
54
52
|
|
|
55
53
|
export const getBrokerByFilename = file => {
|
|
@@ -72,7 +70,6 @@ export function getBrokerForUrl(method, url) {
|
|
|
72
70
|
return brokers[i]
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
|
|
76
73
|
export function extractAllComments() {
|
|
77
74
|
const comments = new Set()
|
|
78
75
|
forEachBroker(broker => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import fs, { readFileSync } from 'node:fs'
|
|
1
|
+
import fs, { existsSync, readFileSync } from 'node:fs'
|
|
2
2
|
import { mimeFor } from './mime.js'
|
|
3
3
|
|
|
4
4
|
|
|
@@ -12,8 +12,12 @@ export function sendJSON(response, payload) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function sendFile(response, file) {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
if (!existsSync(file))
|
|
16
|
+
sendNotFound(response)
|
|
17
|
+
else {
|
|
18
|
+
response.setHeader('content-type', mimeFor(file))
|
|
19
|
+
response.end(readFileSync(file))
|
|
20
|
+
}
|
|
17
21
|
}
|
|
18
22
|
|
|
19
23
|
export async function sendPartialContent(response, range, file) {
|