mockaton 8.13.0 → 8.13.1
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 +9 -3
- package/package.json +1 -1
- package/src/Dashboard.css +19 -13
- package/src/Dashboard.js +71 -43
package/README.md
CHANGED
|
@@ -75,9 +75,7 @@ They will be saved in your `config.mocksDir` following the filename convention.
|
|
|
75
75
|
<br/>
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
## Basic Usage
|
|
79
|
-
Mockaton is a Node.js program.
|
|
80
|
-
|
|
78
|
+
## Basic Usage (See below for Node < 23.6)
|
|
81
79
|
```sh
|
|
82
80
|
npm install mockaton --save-dev
|
|
83
81
|
```
|
|
@@ -98,6 +96,14 @@ Mockaton({
|
|
|
98
96
|
node my-mockaton.js
|
|
99
97
|
```
|
|
100
98
|
|
|
99
|
+
### Node < 23.6 + TypeScript
|
|
100
|
+
If you want to write mocks in TypeScript in a version older than Node 23.6:
|
|
101
|
+
```shell
|
|
102
|
+
npm install tsx
|
|
103
|
+
node --import=tsx my-mockaton.js
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
|
|
101
107
|
<br/>
|
|
102
108
|
|
|
103
109
|
## Demo App (Vite)
|
package/package.json
CHANGED
package/src/Dashboard.css
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
:root {
|
|
2
2
|
--boxShadow1: 0 2px 1px -1px rgba(0, 0, 0, 0.15), 0 1px 1px 0 rgba(0, 0, 0, 0.15), 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
3
|
-
--radius:
|
|
3
|
+
--radius: 4px;
|
|
4
4
|
--radiusSmall: 4px;
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
@media (prefers-color-scheme: light) {
|
|
8
8
|
:root {
|
|
9
9
|
--color4xxBackground: #ffedd1;
|
|
10
|
-
--colorAccent: #
|
|
10
|
+
--colorAccent: #0170cc;
|
|
11
11
|
--colorAccentAlt: #068185;
|
|
12
12
|
--colorBackground: #fff;
|
|
13
13
|
--colorComboBoxHeaderBackground: #fff;
|
|
14
|
-
--colorComboBoxBackground: #
|
|
14
|
+
--colorComboBoxBackground: #eee;
|
|
15
15
|
--colorHeaderBackground: #eee;
|
|
16
|
-
--colorSecondaryButtonBackground: #
|
|
16
|
+
--colorSecondaryButtonBackground: #eee;
|
|
17
17
|
--colorSecondaryAction: #555;
|
|
18
18
|
--colorDisabledMockSelector: #444;
|
|
19
19
|
--colorHover: #dfefff;
|
|
@@ -27,11 +27,10 @@
|
|
|
27
27
|
:root {
|
|
28
28
|
--color4xxBackground: #403630;
|
|
29
29
|
--colorAccent: #2495ff;
|
|
30
|
-
--colorAccentAlt: #00bf64;
|
|
31
30
|
--colorBackground: #161616;
|
|
32
31
|
--colorHeaderBackground: #090909;
|
|
33
|
-
--colorComboBoxBackground: #
|
|
34
|
-
--colorSecondaryButtonBackground: #
|
|
32
|
+
--colorComboBoxBackground: #2a2a2a;
|
|
33
|
+
--colorSecondaryButtonBackground: #2a2a2a;
|
|
35
34
|
--colorSecondaryAction: #999;
|
|
36
35
|
--colorComboBoxHeaderBackground: #222;
|
|
37
36
|
--colorDisabledMockSelector: #b9b9b9;
|
|
@@ -227,6 +226,11 @@ select {
|
|
|
227
226
|
}
|
|
228
227
|
|
|
229
228
|
|
|
229
|
+
.Main {
|
|
230
|
+
display: flex;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
230
234
|
.MockList {
|
|
231
235
|
display: flex;
|
|
232
236
|
align-items: flex-start;
|
|
@@ -254,6 +258,7 @@ select {
|
|
|
254
258
|
|
|
255
259
|
.PayloadViewer {
|
|
256
260
|
position: sticky;
|
|
261
|
+
margin-top: 62px;
|
|
257
262
|
top: 62px;
|
|
258
263
|
width: 50%;
|
|
259
264
|
margin-left: 20px;
|
|
@@ -292,7 +297,6 @@ select {
|
|
|
292
297
|
|
|
293
298
|
span {
|
|
294
299
|
opacity: 0.5;
|
|
295
|
-
filter: saturate(0.5);
|
|
296
300
|
}
|
|
297
301
|
|
|
298
302
|
&:hover {
|
|
@@ -359,7 +363,6 @@ select {
|
|
|
359
363
|
.DelayToggler {
|
|
360
364
|
> input {
|
|
361
365
|
&:checked ~ svg {
|
|
362
|
-
border: 1px solid var(--colorBackground);
|
|
363
366
|
fill: var(--colorAccent);
|
|
364
367
|
background: var(--colorAccent);
|
|
365
368
|
stroke: var(--colorBackground);
|
|
@@ -373,11 +376,12 @@ select {
|
|
|
373
376
|
}
|
|
374
377
|
|
|
375
378
|
> svg {
|
|
376
|
-
width:
|
|
377
|
-
height:
|
|
379
|
+
width: 19px;
|
|
380
|
+
height: 19px;
|
|
378
381
|
stroke-width: 2.5px;
|
|
379
382
|
border-radius: 50%;
|
|
380
383
|
background: var(--colorSecondaryButtonBackground);
|
|
384
|
+
box-shadow: var(--boxShadow1);
|
|
381
385
|
}
|
|
382
386
|
}
|
|
383
387
|
|
|
@@ -385,10 +389,12 @@ select {
|
|
|
385
389
|
padding: 1px 3px;
|
|
386
390
|
background: var(--colorSecondaryButtonBackground);
|
|
387
391
|
border-radius: var(--radiusSmall);
|
|
392
|
+
box-shadow: var(--boxShadow1);
|
|
388
393
|
|
|
389
394
|
&:has(input:checked),
|
|
390
395
|
&:has(input:disabled) {
|
|
391
396
|
background: transparent;
|
|
397
|
+
box-shadow: none;
|
|
392
398
|
}
|
|
393
399
|
|
|
394
400
|
> input {
|
|
@@ -456,6 +462,7 @@ select {
|
|
|
456
462
|
color: var(--colorSecondaryAction);
|
|
457
463
|
border-radius: var(--radiusSmall);
|
|
458
464
|
background: var(--colorSecondaryButtonBackground);
|
|
465
|
+
box-shadow: var(--boxShadow1);
|
|
459
466
|
|
|
460
467
|
&:hover {
|
|
461
468
|
background: var(--colorLightRed);
|
|
@@ -510,7 +517,7 @@ select {
|
|
|
510
517
|
display: inline-block;
|
|
511
518
|
padding: 6px;
|
|
512
519
|
border-radius: var(--radius);
|
|
513
|
-
color: var(--
|
|
520
|
+
color: var(--colorAccent);
|
|
514
521
|
text-decoration: none;
|
|
515
522
|
|
|
516
523
|
&:hover {
|
|
@@ -519,7 +526,6 @@ select {
|
|
|
519
526
|
|
|
520
527
|
span {
|
|
521
528
|
opacity: 0.5;
|
|
522
|
-
filter: saturate(0.5);
|
|
523
529
|
}
|
|
524
530
|
}
|
|
525
531
|
}
|
package/src/Dashboard.js
CHANGED
|
@@ -51,6 +51,7 @@ const CSS = {
|
|
|
51
51
|
GlobalDelayField: 'GlobalDelayField',
|
|
52
52
|
SaveProxiedCheckbox: 'SaveProxiedCheckbox',
|
|
53
53
|
StaticFilesList: 'StaticFilesList',
|
|
54
|
+
Main: 'Main',
|
|
54
55
|
|
|
55
56
|
red: 'red',
|
|
56
57
|
empty: 'empty',
|
|
@@ -89,8 +90,11 @@ function App([brokersByMethod, cookies, comments, delay, collectProxied, fallbac
|
|
|
89
90
|
return (
|
|
90
91
|
r('div', null,
|
|
91
92
|
r(Header, { cookies, comments, delay, fallbackAddress, collectProxied }),
|
|
92
|
-
r(
|
|
93
|
-
|
|
93
|
+
r('main', { className: CSS.Main },
|
|
94
|
+
r('div', null,
|
|
95
|
+
r(MockList, { brokersByMethod, canProxy: Boolean(fallbackAddress) }),
|
|
96
|
+
r(StaticFilesList, { staticFiles })),
|
|
97
|
+
r(PayloadViewer))))
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
|
|
@@ -243,13 +247,12 @@ function MockList({ brokersByMethod, canProxy }) {
|
|
|
243
247
|
const hasMocks = Object.keys(brokersByMethod).length
|
|
244
248
|
if (!hasMocks)
|
|
245
249
|
return (
|
|
246
|
-
r('
|
|
250
|
+
r('div', { className: cssClass(CSS.MockList, CSS.empty) },
|
|
247
251
|
Strings.no_mocks_found))
|
|
248
252
|
return (
|
|
249
|
-
r('
|
|
253
|
+
r('div', { className: CSS.MockList },
|
|
250
254
|
r('table', null, Object.entries(brokersByMethod).map(([method, brokers]) =>
|
|
251
|
-
r(SectionByMethod, { method, brokers, canProxy })))
|
|
252
|
-
r(PayloadViewer)))
|
|
255
|
+
r(SectionByMethod, { method, brokers, canProxy })))))
|
|
253
256
|
}
|
|
254
257
|
|
|
255
258
|
function SectionByMethod({ method, brokers, canProxy }) {
|
|
@@ -258,20 +261,20 @@ function SectionByMethod({ method, brokers, canProxy }) {
|
|
|
258
261
|
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
259
262
|
|
|
260
263
|
const urlMasks = brokersSorted.map(([urlMask]) => urlMask)
|
|
261
|
-
const
|
|
264
|
+
const urlMasksDittoed = dittoSplitPaths(urlMasks)
|
|
262
265
|
return (
|
|
263
266
|
r('tbody', null,
|
|
264
267
|
r('th', null, method),
|
|
265
268
|
brokersSorted.map(([urlMask, broker], i) =>
|
|
266
269
|
r('tr', { 'data-method': method, 'data-urlMask': urlMask },
|
|
267
|
-
r('td', null, r(PreviewLink, { method, urlMask,
|
|
270
|
+
r('td', null, r(PreviewLink, { method, urlMask, urlMaskDittoed: urlMasksDittoed[i] })),
|
|
268
271
|
r('td', null, r(MockSelector, { broker })),
|
|
269
272
|
r('td', null, r(InternalServerErrorToggler, { broker })),
|
|
270
273
|
r('td', null, r(DelayRouteToggler, { broker })),
|
|
271
274
|
r('td', null, r(ProxyToggler, { broker, disabled: !canProxy }))))))
|
|
272
275
|
}
|
|
273
276
|
|
|
274
|
-
function PreviewLink({ method, urlMask,
|
|
277
|
+
function PreviewLink({ method, urlMask, urlMaskDittoed }) {
|
|
275
278
|
async function onClick(event) {
|
|
276
279
|
event.preventDefault()
|
|
277
280
|
try {
|
|
@@ -283,13 +286,15 @@ function PreviewLink({ method, urlMask, urlMaskHighlighted }) {
|
|
|
283
286
|
onError(error)
|
|
284
287
|
}
|
|
285
288
|
}
|
|
289
|
+
const [ditto, tail] = urlMaskDittoed
|
|
286
290
|
return (
|
|
287
291
|
r('a', {
|
|
288
292
|
className: CSS.PreviewLink,
|
|
289
293
|
href: urlMask,
|
|
290
|
-
onClick
|
|
291
|
-
|
|
292
|
-
|
|
294
|
+
onClick
|
|
295
|
+
}, ditto
|
|
296
|
+
? [r('span', null, ditto), tail]
|
|
297
|
+
: tail))
|
|
293
298
|
}
|
|
294
299
|
|
|
295
300
|
function MockSelector({ broker }) {
|
|
@@ -496,7 +501,9 @@ function mockSelectorFor(method, urlMask) {
|
|
|
496
501
|
function StaticFilesList({ staticFiles }) {
|
|
497
502
|
if (!staticFiles.length)
|
|
498
503
|
return null
|
|
499
|
-
const
|
|
504
|
+
const paths = dittoSplitPaths(staticFiles).map(([ditto, tail]) => ditto
|
|
505
|
+
? [r('span', null, ditto), tail]
|
|
506
|
+
: tail)
|
|
500
507
|
return (
|
|
501
508
|
r('section', {
|
|
502
509
|
open: true,
|
|
@@ -507,9 +514,8 @@ function StaticFilesList({ staticFiles }) {
|
|
|
507
514
|
r('li', null,
|
|
508
515
|
r('a', {
|
|
509
516
|
href: f,
|
|
510
|
-
target: '_blank'
|
|
511
|
-
|
|
512
|
-
}))))))
|
|
517
|
+
target: '_blank'
|
|
518
|
+
}, paths[i]))))))
|
|
513
519
|
}
|
|
514
520
|
|
|
515
521
|
|
|
@@ -619,37 +625,59 @@ function useRef() {
|
|
|
619
625
|
|
|
620
626
|
|
|
621
627
|
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
628
|
+
/**
|
|
629
|
+
* This is for styling the repeated paths with a faint style.
|
|
630
|
+
* It splits each path into [dittoPrefix, tail], where dittoPrefix is
|
|
631
|
+
* the longest previously-seen common directory prefix.
|
|
632
|
+
*/
|
|
633
|
+
function dittoSplitPaths(paths) {
|
|
626
634
|
const result = []
|
|
627
|
-
for (
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
635
|
+
for (let i = 0; i < paths.length; i++) {
|
|
636
|
+
const path = paths[i]
|
|
637
|
+
const currParts = path.split('/')
|
|
638
|
+
|
|
639
|
+
let dittoParts = []
|
|
640
|
+
for (let j = 0; j < i; j++) {
|
|
641
|
+
const prevParts = paths[j].split('/')
|
|
642
|
+
|
|
643
|
+
let k = 0
|
|
644
|
+
while (
|
|
645
|
+
k < currParts.length &&
|
|
646
|
+
k < prevParts.length &&
|
|
647
|
+
currParts[k] === prevParts[k])
|
|
648
|
+
k++
|
|
649
|
+
|
|
650
|
+
if (k > dittoParts.length)
|
|
651
|
+
dittoParts = currParts.slice(0, k)
|
|
640
652
|
}
|
|
641
653
|
|
|
642
|
-
if (
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
result.push(`<span>${prefix}</span>${suffix}`)
|
|
654
|
+
if (!dittoParts.length)
|
|
655
|
+
result.push(['', path])
|
|
656
|
+
else {
|
|
657
|
+
const ditto = dittoParts.join('/') + '/'
|
|
658
|
+
result.push([ditto, path.slice(ditto.length)])
|
|
648
659
|
}
|
|
649
|
-
else
|
|
650
|
-
result.push(path)
|
|
651
|
-
|
|
652
|
-
seen.push(path)
|
|
653
660
|
}
|
|
661
|
+
|
|
654
662
|
return result
|
|
655
663
|
}
|
|
664
|
+
|
|
665
|
+
(function testDittoSplitPaths() {
|
|
666
|
+
const input = [
|
|
667
|
+
'/api/user',
|
|
668
|
+
'/api/user/avatar',
|
|
669
|
+
'/api/user/friends',
|
|
670
|
+
'/api/vid',
|
|
671
|
+
'/api/video/id',
|
|
672
|
+
'/api/video/stats'
|
|
673
|
+
]
|
|
674
|
+
const expected = [
|
|
675
|
+
['', '/api/user'],
|
|
676
|
+
['/api/user/', 'avatar'],
|
|
677
|
+
['/api/user/', 'friends'],
|
|
678
|
+
['/api/', 'vid'],
|
|
679
|
+
['/api/', 'video/id'],
|
|
680
|
+
['/api/video/', 'stats']
|
|
681
|
+
]
|
|
682
|
+
console.assert(JSON.stringify(dittoSplitPaths(input)) === JSON.stringify(expected))
|
|
683
|
+
}())
|