mockaton 8.6.1 → 8.7.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 +8 -13
- package/index.d.ts +6 -6
- package/package.json +4 -1
- package/src/Api.js +1 -1
- package/src/Commander.js +1 -0
- package/src/Dashboard.js +49 -44
- package/src/MockBroker.js +2 -5
- package/src/Mockaton.js +5 -5
- package/src/config.js +1 -0
- package/src/mockBrokersCollection.js +9 -3
package/README.md
CHANGED
|
@@ -17,20 +17,15 @@ For example, for this route `/api/user/1234`, the mock filename would be:
|
|
|
17
17
|
my-mocks-dir/api/user/[user-id].GET.200.json
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
And hey, no need to mock everything.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
And there’s one more cool thing—you can collect those responses
|
|
25
|
-
by clicking the **Save Mocks** checkbox. Those mocks will
|
|
20
|
+
And hey, no need to mock everything. Mockaton can fallback to your real
|
|
21
|
+
backend on routes you don’t have mocks for. Just type your backend
|
|
22
|
+
address in the **Fallback Backend** field. Check **Save Mocks** so you
|
|
23
|
+
can collect those responses that hit your fallback server. The mocks will
|
|
26
24
|
be saved to your `config.mocksDir` following the filename convention.
|
|
27
25
|
|
|
28
26
|
|
|
29
27
|
## Multiple Mock Variants
|
|
30
|
-
|
|
31
|
-
You can have many mocks for any route. For example, you might
|
|
32
|
-
want different mocks with different response status codes
|
|
33
|
-
(like triggering errors). Here are a couple of ways to do it:
|
|
28
|
+
Here’s how you can create multiple mocks for a particular route:
|
|
34
29
|
|
|
35
30
|
### Adding comments in filenames
|
|
36
31
|
Want to mock a locked-out user or an invalid login attempt? You
|
|
@@ -101,7 +96,7 @@ By the way, that directory has scripts for opening Mockaton and Vite in one comm
|
|
|
101
96
|
|
|
102
97
|
The app looks like this:
|
|
103
98
|
|
|
104
|
-
<img src="./demo-app-vite/
|
|
99
|
+
<img src="./demo-app-vite/pixaton-tests/pic-for-readme.vp500x800.light.gold.png" alt="Mockaton Demo App Screenshot" width="500" />
|
|
105
100
|
|
|
106
101
|
|
|
107
102
|
## Use Cases
|
|
@@ -124,7 +119,7 @@ backends to old API contracts or databases.
|
|
|
124
119
|
Perhaps you need to demo your app, but the ideal flow is too complex to
|
|
125
120
|
simulate from the actual backend. In this case, compile your frontend app and
|
|
126
121
|
put its built assets in `config.staticDir`. Then, on the dashboard
|
|
127
|
-
|
|
122
|
+
**Bulk Select** mocks to simulate the complete states you want to demo.
|
|
128
123
|
For bulk-selecting, you just need to add a comment to the mock
|
|
129
124
|
filename, such as `(demo-part1)`, `(demo-part2)`.
|
|
130
125
|
|
|
@@ -470,7 +465,7 @@ Nonetheless, you can trigger any command besides opening a browser.
|
|
|
470
465
|
---
|
|
471
466
|
|
|
472
467
|
## Commander API
|
|
473
|
-
`Commander` is a
|
|
468
|
+
`Commander` is a client for Mockaton’s HTTP API.
|
|
474
469
|
All of its methods return their `fetch` response promise.
|
|
475
470
|
```js
|
|
476
471
|
import { Commander } from 'mockaton'
|
package/index.d.ts
CHANGED
|
@@ -27,12 +27,12 @@ interface Config {
|
|
|
27
27
|
plugins?: [filenameTester: RegExp, plugin: Plugin][]
|
|
28
28
|
|
|
29
29
|
corsAllowed?: boolean,
|
|
30
|
-
corsOrigins
|
|
31
|
-
corsMethods
|
|
32
|
-
corsHeaders
|
|
33
|
-
corsExposedHeaders
|
|
34
|
-
corsCredentials
|
|
35
|
-
corsMaxAge
|
|
30
|
+
corsOrigins?: string[]
|
|
31
|
+
corsMethods?: string[]
|
|
32
|
+
corsHeaders?: string[]
|
|
33
|
+
corsExposedHeaders?: string[]
|
|
34
|
+
corsCredentials?: boolean
|
|
35
|
+
corsMaxAge?: number
|
|
36
36
|
|
|
37
37
|
onReady?: (address: string) => void
|
|
38
38
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "mockaton",
|
|
3
3
|
"description": "A deterministic server-side for developing and testing frontend clients",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "8.
|
|
5
|
+
"version": "8.7.0",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
8
|
"license": "MIT",
|
|
@@ -17,5 +17,8 @@
|
|
|
17
17
|
"optionalDependencies": {
|
|
18
18
|
"pixaton": ">=1.0.2",
|
|
19
19
|
"puppeteer": ">=24.1.1"
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"pixaton": "1.0.2"
|
|
20
23
|
}
|
|
21
24
|
}
|
package/src/Api.js
CHANGED
|
@@ -90,7 +90,7 @@ async function selectMock(req, response) {
|
|
|
90
90
|
try {
|
|
91
91
|
const file = await parseJSON(req)
|
|
92
92
|
const broker = mockBrokersCollection.getBrokerByFilename(file)
|
|
93
|
-
if (!broker || !broker.
|
|
93
|
+
if (!broker || !broker.hasMock(file))
|
|
94
94
|
throw `Missing Mock: ${file}`
|
|
95
95
|
broker.updateFile(file)
|
|
96
96
|
sendOK(response)
|
package/src/Commander.js
CHANGED
package/src/Dashboard.js
CHANGED
|
@@ -47,7 +47,7 @@ const refPayloadViewerFileTitle = useRef()
|
|
|
47
47
|
const mockaton = new Commander(window.location.origin)
|
|
48
48
|
|
|
49
49
|
function init() {
|
|
50
|
-
Promise.all([
|
|
50
|
+
return Promise.all([
|
|
51
51
|
mockaton.listMocks(),
|
|
52
52
|
mockaton.listCookies(),
|
|
53
53
|
mockaton.listComments(),
|
|
@@ -98,13 +98,12 @@ function CookieSelector({ list }) {
|
|
|
98
98
|
r('label', { className: CSS.Field },
|
|
99
99
|
r('span', null, Strings.cookie),
|
|
100
100
|
r('select', {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
r('option', { value, selected }, value)))))
|
|
101
|
+
autocomplete: 'off',
|
|
102
|
+
disabled,
|
|
103
|
+
title: disabled ? Strings.cookie_disabled_title : '',
|
|
104
|
+
onChange
|
|
105
|
+
}, list.map(([value, selected]) =>
|
|
106
|
+
r('option', { value, selected }, value)))))
|
|
108
107
|
}
|
|
109
108
|
|
|
110
109
|
|
|
@@ -122,14 +121,13 @@ function BulkSelector({ comments }) {
|
|
|
122
121
|
r('label', { className: CSS.Field },
|
|
123
122
|
r('span', null, Strings.bulk_select_by_comment),
|
|
124
123
|
r('select', {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
r('option', { value }, value)))))
|
|
124
|
+
'data-qaid': 'BulkSelector',
|
|
125
|
+
autocomplete: 'off',
|
|
126
|
+
disabled,
|
|
127
|
+
title: disabled ? Strings.bulk_select_by_comment_disabled_title : '',
|
|
128
|
+
onChange
|
|
129
|
+
}, list.map(value =>
|
|
130
|
+
r('option', { value }, value)))))
|
|
133
131
|
}
|
|
134
132
|
|
|
135
133
|
|
|
@@ -145,7 +143,7 @@ function ProxyFallbackField({ fallbackAddress = '', collectProxied }) {
|
|
|
145
143
|
.catch(onError)
|
|
146
144
|
}
|
|
147
145
|
return (
|
|
148
|
-
r('div', { className: CSS.Field
|
|
146
|
+
r('div', { className: cssClass(CSS.Field, CSS.FallbackBackend) },
|
|
149
147
|
r('label', null,
|
|
150
148
|
r('span', null, Strings.fallback_server),
|
|
151
149
|
r('input', {
|
|
@@ -203,13 +201,9 @@ function StaticFilesList({ staticFiles }) {
|
|
|
203
201
|
className: CSS.StaticFilesList
|
|
204
202
|
},
|
|
205
203
|
r('summary', null, Strings.static),
|
|
206
|
-
r('ul', null,
|
|
207
|
-
|
|
208
|
-
r('
|
|
209
|
-
r('a', {
|
|
210
|
-
href: f,
|
|
211
|
-
target: '_blank'
|
|
212
|
-
}, f))))))
|
|
204
|
+
r('ul', null, staticFiles.map(f =>
|
|
205
|
+
r('li', null,
|
|
206
|
+
r('a', { href: f, target: '_blank' }, f))))))
|
|
213
207
|
}
|
|
214
208
|
|
|
215
209
|
|
|
@@ -221,7 +215,7 @@ function SectionByMethod({ method, brokers }) {
|
|
|
221
215
|
.filter(([, broker]) => broker.mocks.length > 1) // >1 because of autogen500
|
|
222
216
|
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
223
217
|
.map(([urlMask, broker]) =>
|
|
224
|
-
r('tr',
|
|
218
|
+
r('tr', { 'data-method': method, 'data-urlMask': urlMask },
|
|
225
219
|
r('td', null, r(PreviewLink, { method, urlMask })),
|
|
226
220
|
r('td', null, r(MockSelector, { broker })),
|
|
227
221
|
r('td', null, r(DelayRouteToggler, { broker })),
|
|
@@ -237,9 +231,7 @@ function PreviewLink({ method, urlMask }) {
|
|
|
237
231
|
empty(refPayloadViewer.current)
|
|
238
232
|
refPayloadViewer.current.append(ProgressBar())
|
|
239
233
|
}, 180)
|
|
240
|
-
const res = await fetch(this.href, {
|
|
241
|
-
method: this.getAttribute('data-method')
|
|
242
|
-
})
|
|
234
|
+
const res = await fetch(this.href, { method })
|
|
243
235
|
document.querySelector(`.${CSS.PreviewLink}.${CSS.chosen}`)?.classList.remove(CSS.chosen)
|
|
244
236
|
this.classList.add(CSS.chosen)
|
|
245
237
|
clearTimeout(spinner)
|
|
@@ -252,7 +244,7 @@ function PreviewLink({ method, urlMask }) {
|
|
|
252
244
|
|
|
253
245
|
empty(refPayloadViewerFileTitle.current)
|
|
254
246
|
refPayloadViewerFileTitle.current.append(PayloadViewerTitle({
|
|
255
|
-
file:
|
|
247
|
+
file: mockSelectorFor(method, urlMask).value
|
|
256
248
|
}))
|
|
257
249
|
}
|
|
258
250
|
catch (error) {
|
|
@@ -263,7 +255,6 @@ function PreviewLink({ method, urlMask }) {
|
|
|
263
255
|
r('a', {
|
|
264
256
|
className: CSS.PreviewLink,
|
|
265
257
|
href: urlMask,
|
|
266
|
-
'data-method': method,
|
|
267
258
|
onClick
|
|
268
259
|
}, urlMask))
|
|
269
260
|
}
|
|
@@ -301,14 +292,14 @@ function updatePayloadViewer(body, mime) {
|
|
|
301
292
|
|
|
302
293
|
function MockSelector({ broker }) {
|
|
303
294
|
function onChange() {
|
|
304
|
-
const { status } = parseFilename(this.value)
|
|
295
|
+
const { status, urlMask, method } = parseFilename(this.value)
|
|
305
296
|
this.style.fontWeight = this.value === this.options[0].value // default is selected
|
|
306
297
|
? 'normal'
|
|
307
298
|
: 'bold'
|
|
308
299
|
mockaton.select(this.value)
|
|
309
300
|
.then(() => {
|
|
310
|
-
|
|
311
|
-
|
|
301
|
+
linkFor(method, urlMask)?.click()
|
|
302
|
+
checkbox500For(method, urlMask).checked = status === 500
|
|
312
303
|
this.className = className(this.value === this.options[0].value, status)
|
|
313
304
|
})
|
|
314
305
|
.catch(onError)
|
|
@@ -329,17 +320,16 @@ function MockSelector({ broker }) {
|
|
|
329
320
|
|
|
330
321
|
return (
|
|
331
322
|
r('select', {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}, file))))
|
|
323
|
+
'data-qaid': urlMask,
|
|
324
|
+
autocomplete: 'off',
|
|
325
|
+
className: className(selected === files[0], status),
|
|
326
|
+
disabled: files.length <= 1,
|
|
327
|
+
onChange
|
|
328
|
+
}, files.map(file =>
|
|
329
|
+
r('option', {
|
|
330
|
+
value: file,
|
|
331
|
+
selected: file === selected
|
|
332
|
+
}, file))))
|
|
343
333
|
}
|
|
344
334
|
|
|
345
335
|
|
|
@@ -373,10 +363,12 @@ function TimerIcon() {
|
|
|
373
363
|
|
|
374
364
|
function InternalServerErrorToggler({ broker }) {
|
|
375
365
|
function onChange(event) {
|
|
366
|
+
const { urlMask, method } = parseFilename(broker.mocks[0])
|
|
376
367
|
mockaton.select(event.currentTarget.checked
|
|
377
368
|
? broker.mocks.find(f => parseFilename(f).status === 500)
|
|
378
369
|
: broker.mocks[0])
|
|
379
370
|
.then(init)
|
|
371
|
+
.then(() => linkFor(method, urlMask)?.click())
|
|
380
372
|
.catch(onError)
|
|
381
373
|
}
|
|
382
374
|
return (
|
|
@@ -395,6 +387,19 @@ function InternalServerErrorToggler({ broker }) {
|
|
|
395
387
|
)
|
|
396
388
|
}
|
|
397
389
|
|
|
390
|
+
function trFor(method, urlMask) {
|
|
391
|
+
return document.querySelector(`tr[data-method="${method}"][data-urlMask="${urlMask}"]`)
|
|
392
|
+
}
|
|
393
|
+
function linkFor(method, urlMask) {
|
|
394
|
+
return trFor(method, urlMask)?.querySelector(`a.${CSS.PreviewLink}`)
|
|
395
|
+
}
|
|
396
|
+
function checkbox500For(method, urlMask) {
|
|
397
|
+
return trFor(method, urlMask)?.querySelector(`.${CSS.InternalServerErrorToggler} > input`)
|
|
398
|
+
}
|
|
399
|
+
function mockSelectorFor(method, urlMask) {
|
|
400
|
+
return trFor(method, urlMask)?.querySelector(`select.${CSS.MockSelector}`)
|
|
401
|
+
}
|
|
402
|
+
|
|
398
403
|
|
|
399
404
|
function onError(error) {
|
|
400
405
|
if (error?.message === 'Failed to fetch')
|
package/src/MockBroker.js
CHANGED
|
@@ -19,10 +19,7 @@ export class MockBroker {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
register(file) {
|
|
22
|
-
if (
|
|
23
|
-
return
|
|
24
|
-
const { status } = parseFilename(file)
|
|
25
|
-
if (status === 500) {
|
|
22
|
+
if (parseFilename(file).status === 500) {
|
|
26
23
|
this.#deleteTemp500()
|
|
27
24
|
if (this.temp500IsSelected)
|
|
28
25
|
this.updateFile(file)
|
|
@@ -84,7 +81,7 @@ export class MockBroker {
|
|
|
84
81
|
this.updateFile(this.mocks[0])
|
|
85
82
|
}
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
hasMock(file) { return this.mocks.includes(file) }
|
|
88
85
|
updateFile(filename) { this.currentMock.file = filename }
|
|
89
86
|
updateDelay(delayed) { this.currentMock.delay = Number(delayed) * config.delay }
|
|
90
87
|
|
package/src/Mockaton.js
CHANGED
|
@@ -17,13 +17,13 @@ export function Mockaton(options) {
|
|
|
17
17
|
mockBrokerCollection.init()
|
|
18
18
|
|
|
19
19
|
watch(config.mocksDir, { recursive: true, persistent: false },
|
|
20
|
-
function handleAddedOrDeletedMocks(_,
|
|
21
|
-
if (!
|
|
20
|
+
function handleAddedOrDeletedMocks(_, file) {
|
|
21
|
+
if (!file)
|
|
22
22
|
return
|
|
23
|
-
if (existsSync(join(config.mocksDir,
|
|
24
|
-
mockBrokerCollection.registerMock(
|
|
23
|
+
if (existsSync(join(config.mocksDir, file)))
|
|
24
|
+
mockBrokerCollection.registerMock(file, 'ensureItHas500')
|
|
25
25
|
else
|
|
26
|
-
mockBrokerCollection.unregisterMock(
|
|
26
|
+
mockBrokerCollection.unregisterMock(file)
|
|
27
27
|
})
|
|
28
28
|
|
|
29
29
|
return createServer(onRequest).listen(config.port, config.host, function (error) {
|
package/src/config.js
CHANGED
|
@@ -6,11 +6,15 @@ import { parseFilename, filenameIsValid } from './Filename.js'
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
+
* @type {{
|
|
10
|
+
* [method: string]:
|
|
11
|
+
* { [route: string]: MockBroker }
|
|
12
|
+
* }}
|
|
9
13
|
* @example
|
|
10
14
|
* {
|
|
11
15
|
* GET: {
|
|
12
|
-
* /api/route-a:
|
|
13
|
-
* /api/route-b:
|
|
16
|
+
* '/api/route-a': mockBrokerA,
|
|
17
|
+
* '/api/route-b': mockBrokerB
|
|
14
18
|
* },
|
|
15
19
|
* POST: {…}
|
|
16
20
|
* }
|
|
@@ -32,7 +36,9 @@ export function init() {
|
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
export function registerMock(file, shouldEnsure500) {
|
|
35
|
-
if (
|
|
39
|
+
if (getBrokerByFilename(file)?.hasMock(file)
|
|
40
|
+
|| config.ignore.test(file)
|
|
41
|
+
|| !filenameIsValid(file))
|
|
36
42
|
return
|
|
37
43
|
|
|
38
44
|
const { method, urlMask } = parseFilename(file)
|