mockaton 8.16.6 → 8.18.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 +23 -18
- package/package.json +1 -1
- package/src/Dashboard.css +33 -19
- package/src/Dashboard.js +28 -13
- package/src/Filename.js +4 -4
- package/src/config.js +2 -2
package/README.md
CHANGED
|
@@ -70,12 +70,15 @@ 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,9 +236,11 @@ export default function listColors() {
|
|
|
232
236
|
```
|
|
233
237
|
</details>
|
|
234
238
|
|
|
239
|
+
<br/>
|
|
240
|
+
|
|
235
241
|
**What if I need to serve a static .js or .ts?**
|
|
236
242
|
|
|
237
|
-
**Option A:** Put it in your `config.staticDir` without the
|
|
243
|
+
**Option A:** Put it in your `config.staticDir` without the `.GET.200.js` extension.
|
|
238
244
|
|
|
239
245
|
**Option B:** Read it and return it. For example:
|
|
240
246
|
```js
|
|
@@ -346,20 +352,19 @@ api/foo/bar.GET.200.json
|
|
|
346
352
|
This is the only required field. The directory must exist.
|
|
347
353
|
|
|
348
354
|
### `staticDir?: string`
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
For example,
|
|
352
|
-
|
|
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`:
|
|
353
363
|
<pre>
|
|
354
|
-
my-static-dir<b>/foo/bar.jpg</b>
|
|
355
|
-
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>
|
|
356
366
|
</pre>
|
|
357
367
|
|
|
358
|
-
This `config.staticDir` is not actually needed besides serving partial content
|
|
359
|
-
(e.g., videos). At any rate, it’s convenient for serving 200 GET requests without
|
|
360
|
-
having to add the filename extension convention (i.e., no `.GET.200.ext`).
|
|
361
|
-
For example, for using Mockaton as a standalone demo server. For that, you
|
|
362
|
-
can build your frontend bundle and put its built assets in this folder.
|
|
363
368
|
|
|
364
369
|
<br/>
|
|
365
370
|
|
|
@@ -545,7 +550,7 @@ Defaults to `true`. When `true`, these are the default options:
|
|
|
545
550
|
```js
|
|
546
551
|
config.corsOrigins = ['*']
|
|
547
552
|
config.corsMethods = require('node:http').METHODS
|
|
548
|
-
config.corsHeaders = ['content-type']
|
|
553
|
+
config.corsHeaders = ['content-type', 'authorization']
|
|
549
554
|
config.corsCredentials = true
|
|
550
555
|
config.corsMaxAge = 0 // seconds to cache the preflight req
|
|
551
556
|
config.corsExposedHeaders = [] // headers you need to access in client-side JS
|
package/package.json
CHANGED
package/src/Dashboard.css
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
:root {
|
|
8
8
|
--color4xxBackground: #ffedd1;
|
|
9
9
|
--colorAccent: #0068c1;
|
|
10
|
-
--colorAccentAlt: #068185;
|
|
11
10
|
--colorBackground: #fff;
|
|
12
11
|
--colorComboBoxHeaderBackground: #fff;
|
|
13
12
|
--colorComboBoxBackground: #eee;
|
|
@@ -126,12 +125,11 @@ header {
|
|
|
126
125
|
align-items: flex-end;
|
|
127
126
|
gap: 10px;
|
|
128
127
|
|
|
129
|
-
@media (max-width:
|
|
130
|
-
max-width:
|
|
128
|
+
@media (max-width: 890px) {
|
|
129
|
+
max-width: 400px;
|
|
131
130
|
}
|
|
132
131
|
}
|
|
133
132
|
|
|
134
|
-
|
|
135
133
|
img {
|
|
136
134
|
width: 130px;
|
|
137
135
|
align-self: center;
|
|
@@ -233,6 +231,21 @@ header {
|
|
|
233
231
|
color: white;
|
|
234
232
|
}
|
|
235
233
|
}
|
|
234
|
+
|
|
235
|
+
.Help {
|
|
236
|
+
align-self: center;
|
|
237
|
+
margin-left: auto;
|
|
238
|
+
min-width: 24px;
|
|
239
|
+
fill: var(--colorSecondaryAction);
|
|
240
|
+
|
|
241
|
+
&:hover {
|
|
242
|
+
fill: var(--colorAccent);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@media (max-width: 590px) {
|
|
246
|
+
display: none;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
236
249
|
}
|
|
237
250
|
|
|
238
251
|
|
|
@@ -272,7 +285,7 @@ table {
|
|
|
272
285
|
|
|
273
286
|
th {
|
|
274
287
|
padding-bottom: 2px;
|
|
275
|
-
padding-left:
|
|
288
|
+
padding-left: 4px;
|
|
276
289
|
text-align: left;
|
|
277
290
|
}
|
|
278
291
|
|
|
@@ -359,7 +372,6 @@ table {
|
|
|
359
372
|
}
|
|
360
373
|
|
|
361
374
|
.DelayToggler {
|
|
362
|
-
margin-left: 8px;
|
|
363
375
|
> input {
|
|
364
376
|
&:checked ~ svg {
|
|
365
377
|
border-color: var(--colorAccent);
|
|
@@ -387,6 +399,7 @@ table {
|
|
|
387
399
|
.ProxyToggler {
|
|
388
400
|
padding: 1px 3px;
|
|
389
401
|
border: 1px solid var(--colorSecondaryActionBorder);
|
|
402
|
+
margin-right: 8px;
|
|
390
403
|
border-radius: var(--radius);
|
|
391
404
|
|
|
392
405
|
&:has(input:checked),
|
|
@@ -539,30 +552,31 @@ table {
|
|
|
539
552
|
|
|
540
553
|
.ErrorToast {
|
|
541
554
|
position: fixed;
|
|
555
|
+
z-index: 9999;
|
|
542
556
|
bottom: 12px;
|
|
543
557
|
left: 12px;
|
|
544
|
-
|
|
558
|
+
padding: 12px 16px;
|
|
545
559
|
cursor: pointer;
|
|
546
560
|
background: var(--colorRed);
|
|
547
561
|
color: white;
|
|
548
|
-
padding: 12px 16px;
|
|
549
562
|
border-radius: var(--radius);
|
|
550
563
|
box-shadow: var(--boxShadow1);
|
|
551
564
|
opacity: 0;
|
|
552
565
|
transform: translateY(20px);
|
|
553
566
|
animation: _kfToastIn 240ms forwards;
|
|
554
567
|
|
|
555
|
-
&:hover {
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
568
|
+
&:hover::after {
|
|
569
|
+
position: absolute;
|
|
570
|
+
top: 0;
|
|
571
|
+
left: 0;
|
|
572
|
+
width: 100%;
|
|
573
|
+
height: 100%;
|
|
574
|
+
text-align: center;
|
|
575
|
+
content: '×';
|
|
576
|
+
font-size: 28px;
|
|
577
|
+
line-height: 34px;
|
|
578
|
+
border-radius: var(--radius);
|
|
579
|
+
background: rgba(0, 0, 0, 0.5);
|
|
566
580
|
}
|
|
567
581
|
}
|
|
568
582
|
|
package/src/Dashboard.js
CHANGED
|
@@ -43,6 +43,7 @@ const CSS = {
|
|
|
43
43
|
FallbackBackend: 'FallbackBackend',
|
|
44
44
|
Field: 'Field',
|
|
45
45
|
GlobalDelayField: 'GlobalDelayField',
|
|
46
|
+
Help: 'Help',
|
|
46
47
|
InternalServerErrorToggler: 'InternalServerErrorToggler',
|
|
47
48
|
MainLeftSide: 'leftSide',
|
|
48
49
|
MainRightSide: 'rightSide',
|
|
@@ -92,12 +93,13 @@ function init() {
|
|
|
92
93
|
|
|
93
94
|
function App([brokersByMethod, cookies, comments, delay, collectProxied, fallbackAddress, staticBrokers]) {
|
|
94
95
|
globalDelay = delay
|
|
96
|
+
const canProxy = Boolean(fallbackAddress)
|
|
95
97
|
return [
|
|
96
98
|
r(Header, { cookies, comments, delay, fallbackAddress, collectProxied }),
|
|
97
99
|
r('main', null,
|
|
98
100
|
r('div', { className: CSS.MainLeftSide },
|
|
99
|
-
r(MockList, { brokersByMethod, canProxy
|
|
100
|
-
r(StaticFilesList, { brokers: staticBrokers })),
|
|
101
|
+
r(MockList, { brokersByMethod, canProxy }),
|
|
102
|
+
r(StaticFilesList, { brokers: staticBrokers, canProxy })),
|
|
101
103
|
r('div', { className: CSS.MainRightSide },
|
|
102
104
|
r(PayloadViewer)))]
|
|
103
105
|
}
|
|
@@ -114,7 +116,13 @@ function Header({ cookies, comments, delay, fallbackAddress, collectProxied }) {
|
|
|
114
116
|
r(BulkSelector, { comments }),
|
|
115
117
|
r(GlobalDelayField, { delay }),
|
|
116
118
|
r(ProxyFallbackField, { fallbackAddress, collectProxied }),
|
|
117
|
-
r(ResetButton))
|
|
119
|
+
r(ResetButton)),
|
|
120
|
+
r('a', {
|
|
121
|
+
className: CSS.Help,
|
|
122
|
+
href: 'https://github.com/ericfortis/mockaton',
|
|
123
|
+
target: '_blank',
|
|
124
|
+
rel: 'noopener noreferrer'
|
|
125
|
+
}, r(HelpIcon))))
|
|
118
126
|
}
|
|
119
127
|
|
|
120
128
|
function Logo() {
|
|
@@ -270,10 +278,12 @@ function SectionByMethod({ method, brokers, canProxy }) {
|
|
|
270
278
|
const urlMasksDittoed = dittoSplitPaths(urlMasks)
|
|
271
279
|
return (
|
|
272
280
|
r('tbody', null,
|
|
273
|
-
r('
|
|
281
|
+
r('tr', null,
|
|
282
|
+
r('th', { colspan: 2 + Number(canProxy) }),
|
|
283
|
+
r('th', null, method)),
|
|
274
284
|
brokersSorted.map(([urlMask, broker], i) =>
|
|
275
285
|
r('tr', { 'data-method': method, 'data-urlMask': urlMask },
|
|
276
|
-
r('td', null, r(ProxyToggler, { broker
|
|
286
|
+
canProxy && r('td', null, r(ProxyToggler, { broker })),
|
|
277
287
|
r('td', null, r(DelayRouteToggler, { broker })),
|
|
278
288
|
r('td', null, r(InternalServerErrorToggler, { broker })),
|
|
279
289
|
r('td', null, r(PreviewLink, { method, urlMask, urlMaskDittoed: urlMasksDittoed[i] })),
|
|
@@ -387,7 +397,7 @@ function InternalServerErrorToggler({ broker }) {
|
|
|
387
397
|
}
|
|
388
398
|
|
|
389
399
|
/** @param {{ broker: MockBroker, disabled: boolean }} props */
|
|
390
|
-
function ProxyToggler({ broker
|
|
400
|
+
function ProxyToggler({ broker }) {
|
|
391
401
|
function onChange() {
|
|
392
402
|
const { urlMask, method } = parseFilename(broker.mocks[0])
|
|
393
403
|
mockaton.setRouteIsProxied(method, urlMask, this.checked)
|
|
@@ -402,7 +412,6 @@ function ProxyToggler({ broker, disabled }) {
|
|
|
402
412
|
},
|
|
403
413
|
r('input', {
|
|
404
414
|
type: 'checkbox',
|
|
405
|
-
disabled,
|
|
406
415
|
checked: !broker.currentMock.file,
|
|
407
416
|
onChange
|
|
408
417
|
}),
|
|
@@ -414,7 +423,7 @@ function ProxyToggler({ broker, disabled }) {
|
|
|
414
423
|
* # StaticFilesList
|
|
415
424
|
* @param {{ brokers: StaticBroker[] }} props
|
|
416
425
|
*/
|
|
417
|
-
function StaticFilesList({ brokers }) {
|
|
426
|
+
function StaticFilesList({ brokers, canProxy }) {
|
|
418
427
|
if (!Object.keys(brokers).length)
|
|
419
428
|
return null
|
|
420
429
|
const dp = dittoSplitPaths(Object.keys(brokers)).map(([ditto, tail]) => ditto
|
|
@@ -422,11 +431,14 @@ function StaticFilesList({ brokers }) {
|
|
|
422
431
|
: tail)
|
|
423
432
|
return (
|
|
424
433
|
r('table', { className: CSS.StaticFilesList },
|
|
434
|
+
r('thead', null,
|
|
435
|
+
r('tr', null,
|
|
436
|
+
r('th', { colspan: 2 + Number(canProxy) }),
|
|
437
|
+
r('th', null, Strings.static_get))),
|
|
425
438
|
r('tbody', null,
|
|
426
|
-
r('th', { colspan: 4 }, Strings.static_get),
|
|
427
439
|
Object.values(brokers).map((broker, i) =>
|
|
428
440
|
r('tr', null,
|
|
429
|
-
r('td', null, r(ProxyStaticToggler, {})),
|
|
441
|
+
canProxy && r('td', null, r(ProxyStaticToggler, {})),
|
|
430
442
|
r('td', null, r(DelayStaticRouteToggler, { broker })),
|
|
431
443
|
r('td', null, r(NotFoundToggler, { broker })),
|
|
432
444
|
r('td', null, r('a', { href: broker.route, target: '_blank' }, dp[i]))
|
|
@@ -604,9 +616,7 @@ function showErrorToast(msg) {
|
|
|
604
616
|
className: CSS.ErrorToast,
|
|
605
617
|
onClick() {
|
|
606
618
|
const toast = this
|
|
607
|
-
document.startViewTransition(() =>
|
|
608
|
-
toast.remove()
|
|
609
|
-
})
|
|
619
|
+
document.startViewTransition(() => toast.remove())
|
|
610
620
|
}
|
|
611
621
|
}, msg))
|
|
612
622
|
}
|
|
@@ -624,6 +634,11 @@ function CloudIcon() {
|
|
|
624
634
|
s('path', { d: 'm6.1 9.1c2.8 0 5 2.3 5 5' })))
|
|
625
635
|
}
|
|
626
636
|
|
|
637
|
+
function HelpIcon() {
|
|
638
|
+
return (
|
|
639
|
+
s('svg', { viewBox: '0 0 24 24' },
|
|
640
|
+
s('path', { d: 'M11 18h2v-2h-2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2m0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8m0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4' })))
|
|
641
|
+
}
|
|
627
642
|
|
|
628
643
|
/**
|
|
629
644
|
* # Poll UI Sync Version
|
package/src/Filename.js
CHANGED
|
@@ -47,10 +47,10 @@ function validateFilename(file) {
|
|
|
47
47
|
export function parseFilename(file) {
|
|
48
48
|
const tokens = file.replace(reComments, '').split('.')
|
|
49
49
|
return {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
ext: tokens.pop(),
|
|
51
|
+
status: Number(tokens.pop()),
|
|
52
|
+
method: tokens.pop(),
|
|
53
|
+
urlMask: '/' + removeTrailingSlash(tokens.join('.'))
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
56
|
|
package/src/config.js
CHANGED
|
@@ -42,7 +42,7 @@ const schema = {
|
|
|
42
42
|
corsAllowed: [true, is(Boolean)],
|
|
43
43
|
corsOrigins: [['*'], validateCorsAllowedOrigins],
|
|
44
44
|
corsMethods: [SUPPORTED_METHODS, validateCorsAllowedMethods],
|
|
45
|
-
corsHeaders: [['content-type'], Array.isArray],
|
|
45
|
+
corsHeaders: [['content-type', 'authorization'], Array.isArray],
|
|
46
46
|
corsExposedHeaders: [[], Array.isArray],
|
|
47
47
|
corsCredentials: [true, is(Boolean)],
|
|
48
48
|
corsMaxAge: [0, is(Number)],
|
|
@@ -61,7 +61,7 @@ for (const [k, [defaultVal, validator]] of Object.entries(schema)) {
|
|
|
61
61
|
/** @type {Config} */
|
|
62
62
|
export const config = Object.seal(defaults)
|
|
63
63
|
|
|
64
|
-
/** @type {Record<keyof Config, (val: unknown) =>
|
|
64
|
+
/** @type {Record<keyof Config, (val: unknown) => val is Config[keyof Config]>} */
|
|
65
65
|
export const ConfigValidator = Object.freeze(validators)
|
|
66
66
|
|
|
67
67
|
|