mockaton 8.16.5 → 8.17.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 +37 -19
- package/package.json +1 -1
- package/src/Dashboard.css +41 -6
- package/src/Dashboard.js +28 -10
package/README.md
CHANGED
|
@@ -65,17 +65,20 @@ api/videos.GET.<b>500</b>.txt # Internal Server Error
|
|
|
65
65
|
## Scraping mocks from your Backend
|
|
66
66
|
|
|
67
67
|
### Option 1: Browser Extension
|
|
68
|
-
This [
|
|
69
|
-
lets you download
|
|
68
|
+
This [browser extension](https://github.com/ericfortis/download-http-requests-browser-ext)
|
|
69
|
+
lets you download all the HTTP responses following the filename convention.
|
|
70
70
|
|
|
71
71
|
### Option 2: Fallback to Your Backend
|
|
72
72
|
This option could be a bit elaborate if your backend uses third-party auth,
|
|
73
|
-
because
|
|
73
|
+
because you’d have to inject the cookies or tokens in `sessionStorage` manually.
|
|
74
74
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
On the other hand, if you backend handles the session cookie, or if you can
|
|
76
|
+
develop without auth, proxying to your backend is straightforward to set up.
|
|
77
|
+
|
|
78
|
+
In a nutshell, you can forward requests to your backend for routes you don’t have
|
|
79
|
+
mocks for, or routes that have the ☁️ **Cloud Checkbox** checked. In addition, by
|
|
80
|
+
checking ✅ **Save Mocks**, you can collect the responses that hit your backend.
|
|
81
|
+
They will be saved in your `config.mocksDir` following the filename convention.
|
|
79
82
|
|
|
80
83
|
|
|
81
84
|
<br/>
|
|
@@ -161,6 +164,7 @@ you want. For example, by adding `(demo-part1)`, `(demo-part2)` to the filenames
|
|
|
161
164
|
|
|
162
165
|
Similarly, you can deploy a **Standalone Demo Server** by compiling the frontend app and
|
|
163
166
|
putting its built assets in `config.staticDir`. And simulate the flow by Bulk Selecting mocks.
|
|
167
|
+
The [aot-fetch-demo repo](https://github.com/ericfortis/aot-fetch-demo) has a working example.
|
|
164
168
|
|
|
165
169
|
|
|
166
170
|
<br/>
|
|
@@ -232,8 +236,19 @@ export default function listColors() {
|
|
|
232
236
|
```
|
|
233
237
|
</details>
|
|
234
238
|
|
|
235
|
-
|
|
236
|
-
|
|
239
|
+
<br/>
|
|
240
|
+
|
|
241
|
+
**What if I need to serve a static .js or .ts?**
|
|
242
|
+
|
|
243
|
+
**Option A:** Put it in your `config.staticDir` without the `.GET.200.js` extension.
|
|
244
|
+
|
|
245
|
+
**Option B:** Read it and return it. For example:
|
|
246
|
+
```js
|
|
247
|
+
export default function (_, response) {
|
|
248
|
+
response.setHeader('Content-Type', 'application/javascript')
|
|
249
|
+
return readFileSync('./some-dir/foo.js', 'utf8')
|
|
250
|
+
}
|
|
251
|
+
```
|
|
237
252
|
|
|
238
253
|
<br/>
|
|
239
254
|
|
|
@@ -337,19 +352,22 @@ api/foo/bar.GET.200.json
|
|
|
337
352
|
This is the only required field. The directory must exist.
|
|
338
353
|
|
|
339
354
|
### `staticDir?: string`
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
355
|
+
**This option is not needed** besides serving partial content (e.g., videos). But
|
|
356
|
+
it’s convenient for serving 200 GET requests without having to add the filename
|
|
357
|
+
extension convention. For example, for using Mockaton as a standalone demo server,
|
|
358
|
+
as explained above in the _Use Cases_ section.
|
|
359
|
+
|
|
360
|
+
Files under `config.staticDir` don’t use the filename convention, and they take
|
|
361
|
+
precedence over corresponding `GET` mocks in `config.mocksDir` (regardless
|
|
362
|
+
of status code). For example, if you have two files for `GET /foo/bar.jpg`:
|
|
348
363
|
<pre>
|
|
349
|
-
my-static-dir<b>/foo/bar.jpg</b>
|
|
350
|
-
my-mocks-dir<b>/foo/bar.jpg</b>.GET.200.jpg // Unreachable
|
|
364
|
+
my-static-dir<b>/foo/bar.jpg</b> <span style="color:green"> // Wins</span>
|
|
365
|
+
my-mocks-dir<b>/foo/bar.jpg</b>.GET.200.jpg <span style="color:red"> // Unreachable</span>
|
|
351
366
|
</pre>
|
|
352
367
|
|
|
368
|
+
|
|
369
|
+
<br/>
|
|
370
|
+
|
|
353
371
|
### `ignore?: RegExp`
|
|
354
372
|
Defaults to `/(\.DS_Store|~)$/`
|
|
355
373
|
|
package/package.json
CHANGED
package/src/Dashboard.css
CHANGED
|
@@ -272,7 +272,7 @@ table {
|
|
|
272
272
|
|
|
273
273
|
th {
|
|
274
274
|
padding-bottom: 2px;
|
|
275
|
-
padding-left:
|
|
275
|
+
padding-left: 4px;
|
|
276
276
|
text-align: left;
|
|
277
277
|
}
|
|
278
278
|
|
|
@@ -359,7 +359,6 @@ table {
|
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
.DelayToggler {
|
|
362
|
-
margin-left: 8px;
|
|
363
362
|
> input {
|
|
364
363
|
&:checked ~ svg {
|
|
365
364
|
border-color: var(--colorAccent);
|
|
@@ -387,6 +386,7 @@ table {
|
|
|
387
386
|
.ProxyToggler {
|
|
388
387
|
padding: 1px 3px;
|
|
389
388
|
border: 1px solid var(--colorSecondaryActionBorder);
|
|
389
|
+
margin-right: 8px;
|
|
390
390
|
border-radius: var(--radius);
|
|
391
391
|
|
|
392
392
|
&:has(input:checked),
|
|
@@ -506,10 +506,8 @@ table {
|
|
|
506
506
|
|
|
507
507
|
.SpinnerClock {
|
|
508
508
|
display: flex;
|
|
509
|
-
width:
|
|
510
|
-
|
|
511
|
-
margin-top: 92px;
|
|
512
|
-
justify-self: center;
|
|
509
|
+
width: 48px;
|
|
510
|
+
margin-top: 6px;
|
|
513
511
|
fill: none;
|
|
514
512
|
stroke: var(--colorSecondaryAction);
|
|
515
513
|
stroke-width: 2px;
|
|
@@ -539,6 +537,43 @@ table {
|
|
|
539
537
|
filter: saturate(0);
|
|
540
538
|
}
|
|
541
539
|
|
|
540
|
+
.ErrorToast {
|
|
541
|
+
position: fixed;
|
|
542
|
+
z-index: 9999;
|
|
543
|
+
bottom: 12px;
|
|
544
|
+
left: 12px;
|
|
545
|
+
padding: 12px 16px;
|
|
546
|
+
cursor: pointer;
|
|
547
|
+
background: var(--colorRed);
|
|
548
|
+
color: white;
|
|
549
|
+
border-radius: var(--radius);
|
|
550
|
+
box-shadow: var(--boxShadow1);
|
|
551
|
+
opacity: 0;
|
|
552
|
+
transform: translateY(20px);
|
|
553
|
+
animation: _kfToastIn 240ms forwards;
|
|
554
|
+
|
|
555
|
+
&:hover::after {
|
|
556
|
+
position: absolute;
|
|
557
|
+
top: 0;
|
|
558
|
+
left: 0;
|
|
559
|
+
width: 100%;
|
|
560
|
+
height: 100%;
|
|
561
|
+
text-align: center;
|
|
562
|
+
content: '×';
|
|
563
|
+
font-size: 28px;
|
|
564
|
+
line-height: 34px;
|
|
565
|
+
border-radius: var(--radius);
|
|
566
|
+
background: rgba(0, 0, 0, 0.5);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
@keyframes _kfToastIn {
|
|
571
|
+
to {
|
|
572
|
+
opacity: 1;
|
|
573
|
+
transform: translateY(0);
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
542
577
|
|
|
543
578
|
/*
|
|
544
579
|
* Prism
|
package/src/Dashboard.js
CHANGED
|
@@ -39,6 +39,7 @@ const Strings = {
|
|
|
39
39
|
const CSS = {
|
|
40
40
|
BulkSelector: 'BulkSelector',
|
|
41
41
|
DelayToggler: 'DelayToggler',
|
|
42
|
+
ErrorToast: 'ErrorToast',
|
|
42
43
|
FallbackBackend: 'FallbackBackend',
|
|
43
44
|
Field: 'Field',
|
|
44
45
|
GlobalDelayField: 'GlobalDelayField',
|
|
@@ -91,12 +92,13 @@ function init() {
|
|
|
91
92
|
|
|
92
93
|
function App([brokersByMethod, cookies, comments, delay, collectProxied, fallbackAddress, staticBrokers]) {
|
|
93
94
|
globalDelay = delay
|
|
95
|
+
const canProxy = Boolean(fallbackAddress)
|
|
94
96
|
return [
|
|
95
97
|
r(Header, { cookies, comments, delay, fallbackAddress, collectProxied }),
|
|
96
98
|
r('main', null,
|
|
97
99
|
r('div', { className: CSS.MainLeftSide },
|
|
98
|
-
r(MockList, { brokersByMethod, canProxy
|
|
99
|
-
r(StaticFilesList, { brokers: staticBrokers })),
|
|
100
|
+
r(MockList, { brokersByMethod, canProxy }),
|
|
101
|
+
r(StaticFilesList, { brokers: staticBrokers, canProxy })),
|
|
100
102
|
r('div', { className: CSS.MainRightSide },
|
|
101
103
|
r(PayloadViewer)))]
|
|
102
104
|
}
|
|
@@ -269,10 +271,12 @@ function SectionByMethod({ method, brokers, canProxy }) {
|
|
|
269
271
|
const urlMasksDittoed = dittoSplitPaths(urlMasks)
|
|
270
272
|
return (
|
|
271
273
|
r('tbody', null,
|
|
272
|
-
r('
|
|
274
|
+
r('tr', null,
|
|
275
|
+
r('th', { colspan: 2 + Number(canProxy) }),
|
|
276
|
+
r('th', null, method)),
|
|
273
277
|
brokersSorted.map(([urlMask, broker], i) =>
|
|
274
278
|
r('tr', { 'data-method': method, 'data-urlMask': urlMask },
|
|
275
|
-
r('td', null, r(ProxyToggler, { broker
|
|
279
|
+
canProxy && r('td', null, r(ProxyToggler, { broker })),
|
|
276
280
|
r('td', null, r(DelayRouteToggler, { broker })),
|
|
277
281
|
r('td', null, r(InternalServerErrorToggler, { broker })),
|
|
278
282
|
r('td', null, r(PreviewLink, { method, urlMask, urlMaskDittoed: urlMasksDittoed[i] })),
|
|
@@ -386,7 +390,7 @@ function InternalServerErrorToggler({ broker }) {
|
|
|
386
390
|
}
|
|
387
391
|
|
|
388
392
|
/** @param {{ broker: MockBroker, disabled: boolean }} props */
|
|
389
|
-
function ProxyToggler({ broker
|
|
393
|
+
function ProxyToggler({ broker }) {
|
|
390
394
|
function onChange() {
|
|
391
395
|
const { urlMask, method } = parseFilename(broker.mocks[0])
|
|
392
396
|
mockaton.setRouteIsProxied(method, urlMask, this.checked)
|
|
@@ -401,7 +405,6 @@ function ProxyToggler({ broker, disabled }) {
|
|
|
401
405
|
},
|
|
402
406
|
r('input', {
|
|
403
407
|
type: 'checkbox',
|
|
404
|
-
disabled,
|
|
405
408
|
checked: !broker.currentMock.file,
|
|
406
409
|
onChange
|
|
407
410
|
}),
|
|
@@ -413,7 +416,7 @@ function ProxyToggler({ broker, disabled }) {
|
|
|
413
416
|
* # StaticFilesList
|
|
414
417
|
* @param {{ brokers: StaticBroker[] }} props
|
|
415
418
|
*/
|
|
416
|
-
function StaticFilesList({ brokers }) {
|
|
419
|
+
function StaticFilesList({ brokers, canProxy }) {
|
|
417
420
|
if (!Object.keys(brokers).length)
|
|
418
421
|
return null
|
|
419
422
|
const dp = dittoSplitPaths(Object.keys(brokers)).map(([ditto, tail]) => ditto
|
|
@@ -421,11 +424,14 @@ function StaticFilesList({ brokers }) {
|
|
|
421
424
|
: tail)
|
|
422
425
|
return (
|
|
423
426
|
r('table', { className: CSS.StaticFilesList },
|
|
427
|
+
r('thead', null,
|
|
428
|
+
r('tr', null,
|
|
429
|
+
r('th', { colspan: 2 + Number(canProxy) }),
|
|
430
|
+
r('th', null, Strings.static_get))),
|
|
424
431
|
r('tbody', null,
|
|
425
|
-
r('th', { colspan: 4 }, Strings.static_get),
|
|
426
432
|
Object.values(brokers).map((broker, i) =>
|
|
427
433
|
r('tr', null,
|
|
428
|
-
r('td', null, r(ProxyStaticToggler, {})),
|
|
434
|
+
canProxy && r('td', null, r(ProxyStaticToggler, {})),
|
|
429
435
|
r('td', null, r(DelayStaticRouteToggler, { broker })),
|
|
430
436
|
r('td', null, r(NotFoundToggler, { broker })),
|
|
431
437
|
r('td', null, r('a', { href: broker.route, target: '_blank' }, dp[i]))
|
|
@@ -592,10 +598,22 @@ function mockSelectorFor(method, urlMask) {
|
|
|
592
598
|
|
|
593
599
|
function onError(error) {
|
|
594
600
|
if (error?.message === 'Failed to fetch')
|
|
595
|
-
|
|
601
|
+
showErrorToast('Looks like the Mockaton server is not running')
|
|
596
602
|
console.error(error)
|
|
597
603
|
}
|
|
598
604
|
|
|
605
|
+
function showErrorToast(msg) {
|
|
606
|
+
document.getElementsByClassName(CSS.ErrorToast)[0]?.remove()
|
|
607
|
+
document.body.appendChild(
|
|
608
|
+
r('div', {
|
|
609
|
+
className: CSS.ErrorToast,
|
|
610
|
+
onClick() {
|
|
611
|
+
const toast = this
|
|
612
|
+
document.startViewTransition(() => toast.remove())
|
|
613
|
+
}
|
|
614
|
+
}, msg))
|
|
615
|
+
}
|
|
616
|
+
|
|
599
617
|
function TimerIcon() {
|
|
600
618
|
return (
|
|
601
619
|
s('svg', { viewBox: '0 0 24 24' },
|