mockaton 10.4.1 → 10.5.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 +5 -1
- package/package.json +1 -1
- package/src/Dashboard.css +2 -2
- package/src/Dashboard.js +124 -139
- package/src/Mockaton.js +7 -6
package/README.md
CHANGED
|
@@ -431,8 +431,12 @@ config.plugins = [
|
|
|
431
431
|
// IOW, your plugins array overwrites the default list. This way you can remove it.
|
|
432
432
|
[/\.(js|ts)$/, jsToJsonPlugin],
|
|
433
433
|
|
|
434
|
+
|
|
434
435
|
[/\.yml$/, yamlToJsonPlugin],
|
|
435
|
-
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
// e.g. GET /api/foo would be capitalized
|
|
439
|
+
[/foo\.GET\.200\.txt$/, capitalizePlugin]
|
|
436
440
|
]
|
|
437
441
|
|
|
438
442
|
function yamlToJsonPlugin(filePath) {
|
package/package.json
CHANGED
package/src/Dashboard.css
CHANGED
|
@@ -385,7 +385,7 @@ table {
|
|
|
385
385
|
border-top: 20px solid transparent;
|
|
386
386
|
text-align: left;
|
|
387
387
|
}
|
|
388
|
-
|
|
388
|
+
|
|
389
389
|
> tr:first-child > th {
|
|
390
390
|
border-top: 0;
|
|
391
391
|
}
|
|
@@ -633,7 +633,7 @@ table {
|
|
|
633
633
|
height: 2px;
|
|
634
634
|
background: var(--colorComboBoxHeaderBackground);
|
|
635
635
|
|
|
636
|
-
div {
|
|
636
|
+
> div {
|
|
637
637
|
position: absolute;
|
|
638
638
|
top: 0;
|
|
639
639
|
left: 0;
|
package/src/Dashboard.js
CHANGED
|
@@ -3,38 +3,6 @@ import { parseFilename, extractComments } from './Filename.js'
|
|
|
3
3
|
import { Commander } from './ApiCommander.js'
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
const Strings = {
|
|
7
|
-
auto500: 'Auto500',
|
|
8
|
-
bulk_select: 'Bulk Select',
|
|
9
|
-
bulk_select_disabled_title: 'No mock files have comments, which are anything within parentheses on the filename.',
|
|
10
|
-
click_link_to_preview: 'Click a link to preview it',
|
|
11
|
-
cookie: 'Cookie',
|
|
12
|
-
cookie_disabled_title: 'No cookies specified in config.cookies',
|
|
13
|
-
delay: 'Delay',
|
|
14
|
-
delay_ms: 'Delay (ms)',
|
|
15
|
-
empty_response_body: '/* Empty Response Body */',
|
|
16
|
-
fallback_server: 'Fallback',
|
|
17
|
-
fallback_server_error: '⛔ Fallback Backend Error',
|
|
18
|
-
fallback_server_placeholder: 'Type backend address',
|
|
19
|
-
fetching: 'Fetching…',
|
|
20
|
-
got: 'Got',
|
|
21
|
-
group_by_method: 'Group by Method',
|
|
22
|
-
internal_server_error: 'Internal Server Error',
|
|
23
|
-
mock_selector: 'Mock Selector',
|
|
24
|
-
no_mocks_found: 'No mocks found',
|
|
25
|
-
none: 'None',
|
|
26
|
-
not_found: 'Not Found',
|
|
27
|
-
pick_comment: 'Pick Comment…',
|
|
28
|
-
preview: 'Preview',
|
|
29
|
-
proxied: 'Proxied',
|
|
30
|
-
proxy_toggler: 'Proxy Toggler',
|
|
31
|
-
reset: 'Reset',
|
|
32
|
-
save_proxied: 'Save Mocks',
|
|
33
|
-
settings: 'Settings',
|
|
34
|
-
static_get: 'Static GET',
|
|
35
|
-
title: 'Mockaton'
|
|
36
|
-
}
|
|
37
|
-
|
|
38
6
|
const CSS = {
|
|
39
7
|
BulkSelector: null,
|
|
40
8
|
CookieSelector: null,
|
|
@@ -70,26 +38,19 @@ const CSS = {
|
|
|
70
38
|
status4xx: null,
|
|
71
39
|
|
|
72
40
|
json: null,
|
|
73
|
-
syntaxKey: null,
|
|
74
|
-
syntaxStr: null,
|
|
75
|
-
syntaxVal: null,
|
|
76
|
-
|
|
77
41
|
syntaxAttr: null,
|
|
78
42
|
syntaxAttrVal: null,
|
|
43
|
+
syntaxKey: null,
|
|
44
|
+
syntaxPunc: null,
|
|
45
|
+
syntaxStr: null,
|
|
79
46
|
syntaxTag: null,
|
|
80
|
-
|
|
47
|
+
syntaxVal: null
|
|
81
48
|
}
|
|
82
49
|
for (const k of Object.keys(CSS))
|
|
83
50
|
CSS[k] = k
|
|
84
51
|
|
|
85
52
|
|
|
86
|
-
/** @type {State
|
|
87
|
-
* canProxy: boolean
|
|
88
|
-
* groupByMethod: boolean
|
|
89
|
-
* toggleGroupByMethod: () => void
|
|
90
|
-
* leftSideWidth?: number
|
|
91
|
-
* }} */
|
|
92
|
-
const state = {
|
|
53
|
+
const state = /** @type {State} */ {
|
|
93
54
|
brokersByMethod: {},
|
|
94
55
|
staticBrokers: {},
|
|
95
56
|
cookies: [],
|
|
@@ -99,19 +60,21 @@ const state = {
|
|
|
99
60
|
collectProxied: false,
|
|
100
61
|
proxyFallback: '',
|
|
101
62
|
get canProxy() {
|
|
102
|
-
return Boolean(
|
|
63
|
+
return Boolean(state.proxyFallback)
|
|
103
64
|
},
|
|
104
65
|
|
|
105
|
-
groupByMethod:
|
|
66
|
+
groupByMethod: initPreference('groupByMethod'),
|
|
106
67
|
toggleGroupByMethod() {
|
|
107
|
-
|
|
108
|
-
|
|
68
|
+
state.groupByMethod = !state.groupByMethod
|
|
69
|
+
togglePreference('groupByMethod', state.groupByMethod)
|
|
70
|
+
updateState()
|
|
109
71
|
},
|
|
110
72
|
|
|
111
73
|
leftSideWidth: undefined
|
|
112
74
|
}
|
|
113
75
|
|
|
114
|
-
|
|
76
|
+
|
|
77
|
+
const mockaton = new Commander(location.origin)
|
|
115
78
|
updateState()
|
|
116
79
|
initLongPoll()
|
|
117
80
|
|
|
@@ -124,13 +87,15 @@ async function updateState() {
|
|
|
124
87
|
document.body.replaceChildren(...App())
|
|
125
88
|
}
|
|
126
89
|
catch (error) {
|
|
127
|
-
onError(
|
|
90
|
+
onError(error)
|
|
128
91
|
}
|
|
129
92
|
}
|
|
130
93
|
|
|
131
94
|
const r = createElement
|
|
132
95
|
const s = createSvgElement
|
|
133
96
|
|
|
97
|
+
const t = translation => translation[0]
|
|
98
|
+
|
|
134
99
|
const leftSideRef = useRef()
|
|
135
100
|
|
|
136
101
|
function App() {
|
|
@@ -157,7 +122,7 @@ function Header() {
|
|
|
157
122
|
return (
|
|
158
123
|
r('header', null,
|
|
159
124
|
r('img', {
|
|
160
|
-
alt:
|
|
125
|
+
alt: t`Mockaton`,
|
|
161
126
|
src: 'mockaton/Logo.svg',
|
|
162
127
|
width: 160
|
|
163
128
|
}),
|
|
@@ -171,47 +136,38 @@ function Header() {
|
|
|
171
136
|
}
|
|
172
137
|
|
|
173
138
|
function SettingsMenu() {
|
|
174
|
-
const
|
|
139
|
+
const { groupByMethod, toggleGroupByMethod } = state
|
|
140
|
+
|
|
141
|
+
const id = '_settings_menu_'
|
|
142
|
+
return (
|
|
143
|
+
r('button', {
|
|
144
|
+
title: t`Settings`,
|
|
145
|
+
popovertarget: id,
|
|
146
|
+
className: CSS.MenuTrigger
|
|
147
|
+
},
|
|
148
|
+
SettingsIcon(),
|
|
175
149
|
|
|
176
|
-
function MenuContent() {
|
|
177
|
-
return (
|
|
178
150
|
r('menu', {
|
|
179
151
|
id,
|
|
152
|
+
deferred: true,
|
|
180
153
|
popover: '',
|
|
181
|
-
className: CSS.SettingsMenu
|
|
182
|
-
onToggle(event) {
|
|
183
|
-
if (event.newState === 'closed')
|
|
184
|
-
this.parentNode.removeChild(this)
|
|
185
|
-
}
|
|
154
|
+
className: CSS.SettingsMenu
|
|
186
155
|
},
|
|
156
|
+
|
|
187
157
|
r('label', className(CSS.GroupByMethod),
|
|
188
158
|
r('input', {
|
|
189
159
|
type: 'checkbox',
|
|
190
|
-
checked:
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
updateState()
|
|
194
|
-
}
|
|
160
|
+
checked: groupByMethod,
|
|
161
|
+
autofocus: true,
|
|
162
|
+
onChange: toggleGroupByMethod
|
|
195
163
|
}),
|
|
196
|
-
r('span', null,
|
|
164
|
+
r('span', null, t`Group by Method`)),
|
|
197
165
|
|
|
198
166
|
r('a', {
|
|
199
167
|
href: 'https://github.com/ericfortis/mockaton',
|
|
200
168
|
target: '_blank',
|
|
201
169
|
rel: 'noopener noreferrer'
|
|
202
|
-
},
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
return (
|
|
206
|
-
r('button', {
|
|
207
|
-
title: Strings.settings,
|
|
208
|
-
onClick() {
|
|
209
|
-
if (!this.querySelector('menu'))
|
|
210
|
-
this.appendChild(MenuContent())
|
|
211
|
-
},
|
|
212
|
-
className: CSS.MenuTrigger,
|
|
213
|
-
popovertarget: id
|
|
214
|
-
}, SettingsIcon()))
|
|
170
|
+
}, t`Documentation`))))
|
|
215
171
|
}
|
|
216
172
|
|
|
217
173
|
function CookieSelector() {
|
|
@@ -222,14 +178,14 @@ function CookieSelector() {
|
|
|
222
178
|
.catch(onError)
|
|
223
179
|
}
|
|
224
180
|
const disabled = cookies.length <= 1
|
|
225
|
-
const list = cookies.length ? cookies : [[
|
|
181
|
+
const list = cookies.length ? cookies : [[t`None`, true]]
|
|
226
182
|
return (
|
|
227
183
|
r('label', className(CSS.Field, CSS.CookieSelector),
|
|
228
|
-
r('span', null,
|
|
184
|
+
r('span', null, t`Cookie`),
|
|
229
185
|
r('select', {
|
|
230
186
|
autocomplete: 'off',
|
|
231
187
|
disabled,
|
|
232
|
-
title: disabled ?
|
|
188
|
+
title: disabled ? t`No cookies specified in config.cookies` : '',
|
|
233
189
|
onChange
|
|
234
190
|
}, list.map(([value, selected]) =>
|
|
235
191
|
r('option', { value, selected }, value)))))
|
|
@@ -239,7 +195,7 @@ function BulkSelector() {
|
|
|
239
195
|
const { comments } = state
|
|
240
196
|
// UX wise this should be a menu instead of this `select`.
|
|
241
197
|
// But this way is easier to implement, with a few hacks.
|
|
242
|
-
const firstOption =
|
|
198
|
+
const firstOption = t`Pick Comment…`
|
|
243
199
|
function onChange() {
|
|
244
200
|
const value = this.value
|
|
245
201
|
this.value = firstOption // Hack
|
|
@@ -252,12 +208,12 @@ function BulkSelector() {
|
|
|
252
208
|
const disabled = !comments.length
|
|
253
209
|
return (
|
|
254
210
|
r('label', className(CSS.Field),
|
|
255
|
-
r('span', null,
|
|
211
|
+
r('span', null, t`Bulk Select`),
|
|
256
212
|
r('select', {
|
|
257
213
|
className: CSS.BulkSelector,
|
|
258
214
|
autocomplete: 'off',
|
|
259
215
|
disabled,
|
|
260
|
-
title: disabled ?
|
|
216
|
+
title: disabled ? t`No mock files have comments which are anything within parentheses on the filename.` : '',
|
|
261
217
|
onChange
|
|
262
218
|
},
|
|
263
219
|
r('option', { value: firstOption }, firstOption),
|
|
@@ -265,7 +221,7 @@ function BulkSelector() {
|
|
|
265
221
|
comments.map(value =>
|
|
266
222
|
r('option', { value }, value)),
|
|
267
223
|
r('hr'),
|
|
268
|
-
r('option', { value: AUTOGENERATED_500_COMMENT },
|
|
224
|
+
r('option', { value: AUTOGENERATED_500_COMMENT }, t`Auto500`)
|
|
269
225
|
)))
|
|
270
226
|
}
|
|
271
227
|
|
|
@@ -285,7 +241,7 @@ function GlobalDelayField() {
|
|
|
285
241
|
}
|
|
286
242
|
return (
|
|
287
243
|
r('label', className(CSS.Field, CSS.GlobalDelayField),
|
|
288
|
-
r('span', null,
|
|
244
|
+
r('span', null, t`Delay (ms)`),
|
|
289
245
|
r('input', {
|
|
290
246
|
type: 'number',
|
|
291
247
|
min: 0,
|
|
@@ -315,11 +271,11 @@ function ProxyFallbackField() {
|
|
|
315
271
|
return (
|
|
316
272
|
r('div', className(CSS.Field, CSS.FallbackBackend),
|
|
317
273
|
r('label', null,
|
|
318
|
-
r('span', null,
|
|
274
|
+
r('span', null, t`Fallback`),
|
|
319
275
|
r('input', {
|
|
320
276
|
type: 'url',
|
|
321
277
|
autocomplete: 'none',
|
|
322
|
-
placeholder:
|
|
278
|
+
placeholder: t`Type backend address`,
|
|
323
279
|
value: proxyFallback,
|
|
324
280
|
onChange
|
|
325
281
|
})),
|
|
@@ -341,7 +297,7 @@ function SaveProxiedCheckbox() {
|
|
|
341
297
|
checked: collectProxied,
|
|
342
298
|
onChange
|
|
343
299
|
}),
|
|
344
|
-
r('span', null,
|
|
300
|
+
r('span', null, t`Save Mocks`)))
|
|
345
301
|
}
|
|
346
302
|
|
|
347
303
|
function ResetButton() {
|
|
@@ -356,7 +312,7 @@ function ResetButton() {
|
|
|
356
312
|
r('button', {
|
|
357
313
|
className: CSS.ResetButton,
|
|
358
314
|
onClick
|
|
359
|
-
},
|
|
315
|
+
}, t`Reset`))
|
|
360
316
|
}
|
|
361
317
|
|
|
362
318
|
|
|
@@ -369,7 +325,7 @@ function MockList() {
|
|
|
369
325
|
if (!Object.keys(brokersByMethod).length)
|
|
370
326
|
return (
|
|
371
327
|
r('div', className(CSS.empty),
|
|
372
|
-
|
|
328
|
+
t`No mocks found`))
|
|
373
329
|
|
|
374
330
|
if (groupByMethod)
|
|
375
331
|
return Object.keys(brokersByMethod).map((method) => Fragment(
|
|
@@ -437,6 +393,8 @@ function PreviewLink(method, urlMask, urlMaskDittoed) {
|
|
|
437
393
|
: tail))
|
|
438
394
|
}
|
|
439
395
|
|
|
396
|
+
const STR_PROXIED = t`Proxied`
|
|
397
|
+
|
|
440
398
|
/** @param {ClientMockBroker} broker */
|
|
441
399
|
function MockSelector(broker) {
|
|
442
400
|
function onChange() {
|
|
@@ -449,25 +407,25 @@ function MockSelector(broker) {
|
|
|
449
407
|
}
|
|
450
408
|
|
|
451
409
|
let selected = broker.currentMock.file
|
|
452
|
-
const { status
|
|
410
|
+
const { status } = parseFilename(selected)
|
|
453
411
|
const files = broker.mocks.filter(item =>
|
|
454
412
|
status === 500 ||
|
|
455
413
|
!item.includes(AUTOGENERATED_500_COMMENT))
|
|
456
414
|
if (!selected) {
|
|
457
|
-
selected =
|
|
415
|
+
selected = STR_PROXIED
|
|
458
416
|
files.push(selected)
|
|
459
417
|
}
|
|
460
418
|
|
|
461
419
|
function nameFor(file) {
|
|
462
|
-
if (file ===
|
|
463
|
-
return
|
|
464
|
-
const { status, ext
|
|
420
|
+
if (file === STR_PROXIED)
|
|
421
|
+
return STR_PROXIED
|
|
422
|
+
const { status, ext } = parseFilename(file)
|
|
465
423
|
const comments = extractComments(file)
|
|
466
424
|
const isAutogen500 = comments.includes(AUTOGENERATED_500_COMMENT)
|
|
467
425
|
return [
|
|
468
426
|
isAutogen500 ? '' : status,
|
|
469
427
|
ext === 'empty' || ext === 'unknown' ? '' : ext,
|
|
470
|
-
isAutogen500 ?
|
|
428
|
+
isAutogen500 ? t`Auto500` : comments.join(' ')
|
|
471
429
|
].filter(Boolean).join(' ')
|
|
472
430
|
}
|
|
473
431
|
|
|
@@ -475,7 +433,7 @@ function MockSelector(broker) {
|
|
|
475
433
|
r('select', {
|
|
476
434
|
onChange,
|
|
477
435
|
autocomplete: 'off',
|
|
478
|
-
'aria-label':
|
|
436
|
+
'aria-label': t`Mock Selector`,
|
|
479
437
|
disabled: files.length <= 1,
|
|
480
438
|
...className(
|
|
481
439
|
CSS.MockSelector,
|
|
@@ -516,7 +474,7 @@ function InternalServerErrorToggler(broker) {
|
|
|
516
474
|
return (
|
|
517
475
|
r('label', {
|
|
518
476
|
className: CSS.InternalServerErrorToggler,
|
|
519
|
-
title:
|
|
477
|
+
title: t`Internal Server Error`
|
|
520
478
|
},
|
|
521
479
|
r('input', {
|
|
522
480
|
type: 'checkbox',
|
|
@@ -524,7 +482,7 @@ function InternalServerErrorToggler(broker) {
|
|
|
524
482
|
checked: parseFilename(broker.currentMock.file).status === 500,
|
|
525
483
|
onChange
|
|
526
484
|
}),
|
|
527
|
-
r('span', null,
|
|
485
|
+
r('span', null, t`500`)))
|
|
528
486
|
}
|
|
529
487
|
|
|
530
488
|
/** @param {ClientMockBroker} broker */
|
|
@@ -540,7 +498,7 @@ function ProxyToggler(broker) {
|
|
|
540
498
|
return (
|
|
541
499
|
r('label', {
|
|
542
500
|
className: CSS.ProxyToggler,
|
|
543
|
-
title:
|
|
501
|
+
title: t`Proxy Toggler`
|
|
544
502
|
},
|
|
545
503
|
r('input', {
|
|
546
504
|
type: 'checkbox',
|
|
@@ -565,10 +523,10 @@ function StaticFilesList() {
|
|
|
565
523
|
Fragment(
|
|
566
524
|
r('tr', null,
|
|
567
525
|
r('th', { colspan: (2 + Number(!groupByMethod)) + Number(canProxy) }),
|
|
568
|
-
r('th', null,
|
|
526
|
+
r('th', null, t`Static GET`)),
|
|
569
527
|
Object.values(staticBrokers).map((broker, i) =>
|
|
570
528
|
r('tr', null,
|
|
571
|
-
canProxy && r('td'
|
|
529
|
+
canProxy && r('td'),
|
|
572
530
|
r('td', null, DelayStaticRouteToggler(broker)),
|
|
573
531
|
r('td', null, NotFoundToggler(broker)),
|
|
574
532
|
!groupByMethod && r('td', className(CSS.Method), 'GET'),
|
|
@@ -603,31 +561,14 @@ function NotFoundToggler(broker) {
|
|
|
603
561
|
return (
|
|
604
562
|
r('label', {
|
|
605
563
|
className: CSS.NotFoundToggler,
|
|
606
|
-
title:
|
|
564
|
+
title: t`Not Found`
|
|
607
565
|
},
|
|
608
566
|
r('input', {
|
|
609
567
|
type: 'checkbox',
|
|
610
568
|
checked: broker.status === 404,
|
|
611
569
|
onChange
|
|
612
570
|
}),
|
|
613
|
-
r('span', null,
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
function ProxyStaticToggler() { // TODO
|
|
617
|
-
function onChange() {
|
|
618
|
-
}
|
|
619
|
-
return (
|
|
620
|
-
r('label', {
|
|
621
|
-
style: { visibility: 'hidden' },
|
|
622
|
-
className: CSS.ProxyToggler,
|
|
623
|
-
title: Strings.proxy_toggler
|
|
624
|
-
},
|
|
625
|
-
r('input', {
|
|
626
|
-
type: 'checkbox',
|
|
627
|
-
disabled: true,
|
|
628
|
-
onChange
|
|
629
|
-
}),
|
|
630
|
-
CloudIcon()))
|
|
571
|
+
r('span', null, t`404`)))
|
|
631
572
|
}
|
|
632
573
|
|
|
633
574
|
|
|
@@ -650,7 +591,7 @@ function ClickDragToggler({ checked, commit }) {
|
|
|
650
591
|
return (
|
|
651
592
|
r('label', {
|
|
652
593
|
className: CSS.DelayToggler,
|
|
653
|
-
title:
|
|
594
|
+
title: t`Delay`
|
|
654
595
|
},
|
|
655
596
|
r('input', {
|
|
656
597
|
type: 'checkbox',
|
|
@@ -708,20 +649,15 @@ const payloadViewerRef = useRef()
|
|
|
708
649
|
function PayloadViewer() {
|
|
709
650
|
return (
|
|
710
651
|
r('div', className(CSS.PayloadViewer),
|
|
711
|
-
r('h2', { ref: payloadViewerTitleRef },
|
|
652
|
+
r('h2', { ref: payloadViewerTitleRef }, t`Preview`),
|
|
712
653
|
r('pre', null,
|
|
713
|
-
r('code', { ref: payloadViewerRef },
|
|
654
|
+
r('code', { ref: payloadViewerRef }, t`Click a link to preview it`))))
|
|
714
655
|
}
|
|
715
656
|
|
|
716
657
|
function PayloadViewerProgressBar() {
|
|
717
658
|
return (
|
|
718
659
|
r('div', className(CSS.ProgressBar),
|
|
719
|
-
r('div', {
|
|
720
|
-
style: {
|
|
721
|
-
animationDelay: 80 + 'ms',
|
|
722
|
-
animationDuration: -80 + state.delay + 'ms'
|
|
723
|
-
}
|
|
724
|
-
})))
|
|
660
|
+
r('div', { style: { animationDuration: state.delay + 'ms' } })))
|
|
725
661
|
}
|
|
726
662
|
|
|
727
663
|
function PayloadViewerTitle({ file, statusText }) {
|
|
@@ -740,8 +676,8 @@ function PayloadViewerTitleWhenProxied({ mime, status, statusText, gatewayIsBad
|
|
|
740
676
|
return (
|
|
741
677
|
r('span', null,
|
|
742
678
|
gatewayIsBad
|
|
743
|
-
? r('span', className(CSS.red),
|
|
744
|
-
: r('span', null,
|
|
679
|
+
? r('span', className(CSS.red), t`⛔ Fallback Backend Error` + ' ')
|
|
680
|
+
: r('span', null, t`Got` + ' '),
|
|
745
681
|
r('abbr', { title: statusText }, status),
|
|
746
682
|
' ' + mime))
|
|
747
683
|
}
|
|
@@ -750,14 +686,17 @@ async function previewMock(method, urlMask, href) {
|
|
|
750
686
|
previewMock.controller?.abort()
|
|
751
687
|
previewMock.controller = new AbortController
|
|
752
688
|
|
|
753
|
-
|
|
754
|
-
|
|
689
|
+
const spinnerTimer = setTimeout(() => {
|
|
690
|
+
payloadViewerTitleRef.current.replaceChildren(t`Fetching…`)
|
|
691
|
+
payloadViewerRef.current.replaceChildren(PayloadViewerProgressBar())
|
|
692
|
+
}, 80)
|
|
755
693
|
|
|
756
694
|
try {
|
|
757
695
|
const response = await fetch(href, {
|
|
758
696
|
method,
|
|
759
697
|
signal: previewMock.controller.signal
|
|
760
698
|
})
|
|
699
|
+
clearTimeout(spinnerTimer)
|
|
761
700
|
await updatePayloadViewer(method, urlMask, response)
|
|
762
701
|
}
|
|
763
702
|
catch (err) {
|
|
@@ -766,11 +705,12 @@ async function previewMock(method, urlMask, href) {
|
|
|
766
705
|
}
|
|
767
706
|
}
|
|
768
707
|
|
|
708
|
+
|
|
769
709
|
async function updatePayloadViewer(method, urlMask, response) {
|
|
770
710
|
const mime = response.headers.get('content-type') || ''
|
|
771
711
|
|
|
772
712
|
const file = mockSelectorFor(method, urlMask).value
|
|
773
|
-
if (file ===
|
|
713
|
+
if (file === STR_PROXIED)
|
|
774
714
|
payloadViewerTitleRef.current.replaceChildren(PayloadViewerTitleWhenProxied({
|
|
775
715
|
status: response.status,
|
|
776
716
|
statusText: response.statusText,
|
|
@@ -790,7 +730,7 @@ async function updatePayloadViewer(method, urlMask, response) {
|
|
|
790
730
|
}))
|
|
791
731
|
}
|
|
792
732
|
else {
|
|
793
|
-
const body = await response.text() ||
|
|
733
|
+
const body = await response.text() || t`/* Empty Response Body */`
|
|
794
734
|
if (mime === 'application/json')
|
|
795
735
|
payloadViewerRef.current.replaceChildren(r('span', className(CSS.json), syntaxJSON(body)))
|
|
796
736
|
else if (isXML(mime))
|
|
@@ -834,9 +774,9 @@ function onError(error) {
|
|
|
834
774
|
if (error?.name === 'AbortError')
|
|
835
775
|
return
|
|
836
776
|
if (error?.message === 'Failed to fetch')
|
|
837
|
-
showErrorToast(
|
|
777
|
+
showErrorToast(t`Looks like the Mockaton server is not running`)
|
|
838
778
|
else
|
|
839
|
-
showErrorToast(error ||
|
|
779
|
+
showErrorToast(error || t`Unexpected Error`)
|
|
840
780
|
console.error(error)
|
|
841
781
|
}
|
|
842
782
|
|
|
@@ -921,6 +861,14 @@ function className(...args) {
|
|
|
921
861
|
|
|
922
862
|
|
|
923
863
|
function createElement(tag, props, ...children) {
|
|
864
|
+
if (props?.deferred) {
|
|
865
|
+
delete props.deferred
|
|
866
|
+
const placeholder = document.createComment('')
|
|
867
|
+
deferred(() =>
|
|
868
|
+
placeholder.replaceWith(createElement(tag, props, ...children)))
|
|
869
|
+
return placeholder
|
|
870
|
+
}
|
|
871
|
+
|
|
924
872
|
const node = document.createElement(tag)
|
|
925
873
|
for (const [k, v] of Object.entries(props || {}))
|
|
926
874
|
if (k === 'ref') v.current = node
|
|
@@ -954,6 +902,43 @@ function Fragment(...args) {
|
|
|
954
902
|
return frag
|
|
955
903
|
}
|
|
956
904
|
|
|
905
|
+
function deferred(cb) {
|
|
906
|
+
return window.requestIdleCallback
|
|
907
|
+
? requestIdleCallback(cb)
|
|
908
|
+
: setTimeout(cb, 100) // Safari
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
|
|
912
|
+
// When false, the URL will be updated with param=false
|
|
913
|
+
function initPreference(param) {
|
|
914
|
+
const qs = new URLSearchParams(location.search)
|
|
915
|
+
if (!qs.has(param)) {
|
|
916
|
+
const group = localStorage.getItem(param) !== 'false'
|
|
917
|
+
if (!group) {
|
|
918
|
+
const url = new URL(location.href)
|
|
919
|
+
url.searchParams.set(param, false)
|
|
920
|
+
history.replaceState(null, '', url)
|
|
921
|
+
}
|
|
922
|
+
return group
|
|
923
|
+
}
|
|
924
|
+
return qs.get(param) !== 'false'
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
// When false, the URL and localStorage will have param=false
|
|
928
|
+
function togglePreference(param, nextVal) {
|
|
929
|
+
if (nextVal)
|
|
930
|
+
localStorage.removeItem(param)
|
|
931
|
+
else
|
|
932
|
+
localStorage.setItem(param, nextVal)
|
|
933
|
+
|
|
934
|
+
const url = new URL(location.href)
|
|
935
|
+
if (nextVal)
|
|
936
|
+
url.searchParams.delete(param)
|
|
937
|
+
else
|
|
938
|
+
url.searchParams.set(param, false)
|
|
939
|
+
history.replaceState(null, '', url)
|
|
940
|
+
}
|
|
941
|
+
|
|
957
942
|
|
|
958
943
|
/**
|
|
959
944
|
* Think of this as a way of printing a directory tree in which
|
package/src/Mockaton.js
CHANGED
|
@@ -52,8 +52,9 @@ async function onRequest(req, response) {
|
|
|
52
52
|
sendBadRequest(response)
|
|
53
53
|
return
|
|
54
54
|
}
|
|
55
|
-
|
|
55
|
+
|
|
56
56
|
try {
|
|
57
|
+
const route = new URL(url, 'http://_').pathname
|
|
57
58
|
const { method } = req
|
|
58
59
|
|
|
59
60
|
if (config.corsAllowed)
|
|
@@ -61,11 +62,11 @@ async function onRequest(req, response) {
|
|
|
61
62
|
|
|
62
63
|
if (isPreflight(req))
|
|
63
64
|
sendNoContent(response)
|
|
64
|
-
else if (method === 'PATCH' && apiPatchRequests.has(
|
|
65
|
-
await apiPatchRequests.get(
|
|
66
|
-
else if (method === 'GET' && apiGetRequests.has(
|
|
67
|
-
apiGetRequests.get(
|
|
68
|
-
else if (method === 'GET' && staticCollection.brokerByRoute(
|
|
65
|
+
else if (method === 'PATCH' && apiPatchRequests.has(route))
|
|
66
|
+
await apiPatchRequests.get(route)(req, response)
|
|
67
|
+
else if (method === 'GET' && apiGetRequests.has(route))
|
|
68
|
+
apiGetRequests.get(route)(req, response)
|
|
69
|
+
else if (method === 'GET' && staticCollection.brokerByRoute(route))
|
|
69
70
|
await dispatchStatic(req, response)
|
|
70
71
|
else
|
|
71
72
|
await dispatchMock(req, response)
|