mockaton 10.5.4 → 10.6.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/package.json +2 -2
- package/src/Dashboard.css +7 -16
- package/src/Dashboard.js +162 -64
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "mockaton",
|
|
3
3
|
"description": "HTTP Mock Server",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "10.
|
|
5
|
+
"version": "10.6.1",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
8
|
"license": "MIT",
|
|
@@ -27,6 +27,6 @@
|
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"pixaton": "1.1.3",
|
|
30
|
-
"puppeteer": "24.
|
|
30
|
+
"puppeteer": "24.24.1"
|
|
31
31
|
}
|
|
32
32
|
}
|
package/src/Dashboard.css
CHANGED
|
@@ -95,7 +95,7 @@ body {
|
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
select, a, input, button {
|
|
98
|
-
&:focus {
|
|
98
|
+
&:focus-visible {
|
|
99
99
|
outline: 2px solid var(--colorAccent);
|
|
100
100
|
}
|
|
101
101
|
}
|
|
@@ -294,7 +294,6 @@ header {
|
|
|
294
294
|
fill: var(--colorAccent);
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
|
-
|
|
298
297
|
}
|
|
299
298
|
|
|
300
299
|
.SettingsMenu {
|
|
@@ -344,7 +343,7 @@ main {
|
|
|
344
343
|
}
|
|
345
344
|
|
|
346
345
|
.leftSide {
|
|
347
|
-
width
|
|
346
|
+
/* the width is set in js (it’s resizable) */
|
|
348
347
|
padding: 16px;
|
|
349
348
|
border-right: 1px solid var(--colorSecondaryActionBorder);
|
|
350
349
|
user-select: none;
|
|
@@ -379,9 +378,9 @@ table {
|
|
|
379
378
|
border-collapse: collapse;
|
|
380
379
|
|
|
381
380
|
th {
|
|
382
|
-
padding-bottom:
|
|
381
|
+
padding-bottom: 4px;
|
|
383
382
|
padding-left: 4px;
|
|
384
|
-
border-top:
|
|
383
|
+
border-top: 24px solid transparent;
|
|
385
384
|
text-align: left;
|
|
386
385
|
}
|
|
387
386
|
|
|
@@ -422,6 +421,7 @@ table {
|
|
|
422
421
|
}
|
|
423
422
|
}
|
|
424
423
|
|
|
424
|
+
|
|
425
425
|
.MockSelector {
|
|
426
426
|
height: 24px;
|
|
427
427
|
padding-right: 20px;
|
|
@@ -458,7 +458,7 @@ table {
|
|
|
458
458
|
height: 22px;
|
|
459
459
|
opacity: 0;
|
|
460
460
|
|
|
461
|
-
&:focus {
|
|
461
|
+
&:focus-visible {
|
|
462
462
|
outline: 0;
|
|
463
463
|
& + svg {
|
|
464
464
|
outline: 2px solid var(--colorAccent)
|
|
@@ -556,7 +556,7 @@ table {
|
|
|
556
556
|
> input {
|
|
557
557
|
appearance: none;
|
|
558
558
|
|
|
559
|
-
&:focus {
|
|
559
|
+
&:focus-visible {
|
|
560
560
|
outline: 0;
|
|
561
561
|
& + span {
|
|
562
562
|
outline: 2px solid var(--colorAccent)
|
|
@@ -587,15 +587,6 @@ table {
|
|
|
587
587
|
}
|
|
588
588
|
|
|
589
589
|
|
|
590
|
-
.StaticFileLink {
|
|
591
|
-
display: inline-block;
|
|
592
|
-
padding: 6px 0;
|
|
593
|
-
margin-left: 4px;
|
|
594
|
-
border-radius: var(--radius);
|
|
595
|
-
color: var(--colorAccent);
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
|
|
599
590
|
.PayloadViewer {
|
|
600
591
|
display: flex;
|
|
601
592
|
height: 100%;
|
package/src/Dashboard.js
CHANGED
|
@@ -26,7 +26,6 @@ const CSS = {
|
|
|
26
26
|
Resizer: null,
|
|
27
27
|
SaveProxiedCheckbox: null,
|
|
28
28
|
SettingsMenu: null,
|
|
29
|
-
StaticFileLink: null,
|
|
30
29
|
|
|
31
30
|
chosen: null,
|
|
32
31
|
dittoDir: null,
|
|
@@ -50,6 +49,13 @@ for (const k of Object.keys(CSS))
|
|
|
50
49
|
CSS[k] = k
|
|
51
50
|
|
|
52
51
|
|
|
52
|
+
const FocusGroup = {
|
|
53
|
+
ProxyToggler: 0,
|
|
54
|
+
DelayToggler: 1,
|
|
55
|
+
StatusToggler: 2,
|
|
56
|
+
PreviewLink: 3
|
|
57
|
+
}
|
|
58
|
+
|
|
53
59
|
const state = /** @type {State} */ {
|
|
54
60
|
brokersByMethod: {},
|
|
55
61
|
staticBrokers: {},
|
|
@@ -70,13 +76,20 @@ const state = /** @type {State} */ {
|
|
|
70
76
|
updateState()
|
|
71
77
|
},
|
|
72
78
|
|
|
73
|
-
leftSideWidth:
|
|
79
|
+
leftSideWidth: window.innerWidth / 2,
|
|
80
|
+
|
|
81
|
+
chosenLink: { method: '', urlMask: '' },
|
|
82
|
+
clearChosenLink() { state.setChosenLink('', '') },
|
|
83
|
+
setChosenLink(method, urlMask) {
|
|
84
|
+
state.chosenLink = { method, urlMask }
|
|
85
|
+
}
|
|
74
86
|
}
|
|
75
87
|
|
|
76
88
|
|
|
77
89
|
const mockaton = new Commander(location.origin)
|
|
78
90
|
updateState()
|
|
79
|
-
|
|
91
|
+
initLongPoll()
|
|
92
|
+
initKeyboardNavigation()
|
|
80
93
|
|
|
81
94
|
async function updateState() {
|
|
82
95
|
try {
|
|
@@ -85,6 +98,11 @@ async function updateState() {
|
|
|
85
98
|
throw response.status
|
|
86
99
|
Object.assign(state, await response.json())
|
|
87
100
|
document.body.replaceChildren(...App())
|
|
101
|
+
|
|
102
|
+
findChosenLink()?.focus()
|
|
103
|
+
const { method, urlMask } = state.chosenLink
|
|
104
|
+
if (method && urlMask)
|
|
105
|
+
await previewMock(method, urlMask)
|
|
88
106
|
}
|
|
89
107
|
catch (error) {
|
|
90
108
|
onError(error)
|
|
@@ -132,12 +150,10 @@ function Header() {
|
|
|
132
150
|
CookieSelector(),
|
|
133
151
|
ProxyFallbackField(),
|
|
134
152
|
ResetButton(),
|
|
135
|
-
|
|
153
|
+
SettingsMenuTrigger())))
|
|
136
154
|
}
|
|
137
155
|
|
|
138
|
-
function
|
|
139
|
-
const { groupByMethod, toggleGroupByMethod } = state
|
|
140
|
-
|
|
156
|
+
function SettingsMenuTrigger() {
|
|
141
157
|
const id = '_settings_menu_'
|
|
142
158
|
return (
|
|
143
159
|
r('button', {
|
|
@@ -146,29 +162,42 @@ function SettingsMenu() {
|
|
|
146
162
|
className: CSS.MenuTrigger
|
|
147
163
|
},
|
|
148
164
|
SettingsIcon(),
|
|
165
|
+
Defer(() => SettingsMenu(id))))
|
|
166
|
+
}
|
|
149
167
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
168
|
+
function SettingsMenu(id) {
|
|
169
|
+
const { groupByMethod, toggleGroupByMethod } = state
|
|
170
|
+
|
|
171
|
+
const firstInputRef = useRef()
|
|
172
|
+
function onToggle(event) {
|
|
173
|
+
if (event.newState === 'open')
|
|
174
|
+
firstInputRef.current.focus()
|
|
175
|
+
}
|
|
176
|
+
return (
|
|
177
|
+
r('menu', {
|
|
178
|
+
id,
|
|
179
|
+
popover: '',
|
|
180
|
+
className: CSS.SettingsMenu,
|
|
181
|
+
onToggle
|
|
182
|
+
},
|
|
155
183
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
184
|
+
r('label', className(CSS.GroupByMethod),
|
|
185
|
+
r('input', {
|
|
186
|
+
ref: firstInputRef,
|
|
187
|
+
type: 'checkbox',
|
|
188
|
+
checked: groupByMethod,
|
|
189
|
+
onChange: toggleGroupByMethod
|
|
190
|
+
}),
|
|
191
|
+
r('span', null, t`Group by Method`)),
|
|
192
|
+
|
|
193
|
+
r('a', {
|
|
194
|
+
href: 'https://github.com/ericfortis/mockaton',
|
|
195
|
+
target: '_blank',
|
|
196
|
+
rel: 'noopener noreferrer'
|
|
197
|
+
}, t`Documentation`)))
|
|
170
198
|
}
|
|
171
199
|
|
|
200
|
+
|
|
172
201
|
function CookieSelector() {
|
|
173
202
|
const { cookies } = state
|
|
174
203
|
function onChange() {
|
|
@@ -196,6 +225,7 @@ function BulkSelector() {
|
|
|
196
225
|
// But this way is easier to implement, with a few hacks.
|
|
197
226
|
const firstOption = t`Pick Comment…`
|
|
198
227
|
function onChange() {
|
|
228
|
+
state.clearChosenLink()
|
|
199
229
|
const value = this.value
|
|
200
230
|
this.value = firstOption // Hack
|
|
201
231
|
mockaton.bulkSelectByComment(value)
|
|
@@ -233,8 +263,10 @@ function GlobalDelayField() {
|
|
|
233
263
|
.catch(onError)
|
|
234
264
|
}
|
|
235
265
|
function onWheel(event) {
|
|
236
|
-
|
|
237
|
-
|
|
266
|
+
if (event.deltaY > 0)
|
|
267
|
+
this.stepUp()
|
|
268
|
+
else
|
|
269
|
+
this.stepDown()
|
|
238
270
|
clearTimeout(onWheel.timer)
|
|
239
271
|
onWheel.timer = setTimeout(onChange.bind(this), 300)
|
|
240
272
|
}
|
|
@@ -254,9 +286,9 @@ function GlobalDelayField() {
|
|
|
254
286
|
|
|
255
287
|
function ProxyFallbackField() {
|
|
256
288
|
const { proxyFallback } = state
|
|
289
|
+
const checkboxRef = useRef()
|
|
257
290
|
function onChange() {
|
|
258
|
-
|
|
259
|
-
saveCheckbox.disabled = !this.validity.valid || !this.value.trim()
|
|
291
|
+
checkboxRef.current.disabled = !this.validity.valid || !this.value.trim()
|
|
260
292
|
|
|
261
293
|
if (!this.validity.valid)
|
|
262
294
|
this.reportValidity()
|
|
@@ -278,10 +310,10 @@ function ProxyFallbackField() {
|
|
|
278
310
|
value: proxyFallback,
|
|
279
311
|
onChange
|
|
280
312
|
})),
|
|
281
|
-
SaveProxiedCheckbox()))
|
|
313
|
+
SaveProxiedCheckbox(checkboxRef)))
|
|
282
314
|
}
|
|
283
315
|
|
|
284
|
-
function SaveProxiedCheckbox() {
|
|
316
|
+
function SaveProxiedCheckbox(ref) {
|
|
285
317
|
const { collectProxied, canProxy } = state
|
|
286
318
|
function onChange() {
|
|
287
319
|
mockaton.setCollectProxied(this.checked)
|
|
@@ -291,6 +323,7 @@ function SaveProxiedCheckbox() {
|
|
|
291
323
|
return (
|
|
292
324
|
r('label', className(CSS.SaveProxiedCheckbox),
|
|
293
325
|
r('input', {
|
|
326
|
+
ref,
|
|
294
327
|
type: 'checkbox',
|
|
295
328
|
disabled: !canProxy,
|
|
296
329
|
checked: collectProxied,
|
|
@@ -301,6 +334,7 @@ function SaveProxiedCheckbox() {
|
|
|
301
334
|
|
|
302
335
|
function ResetButton() {
|
|
303
336
|
function onClick() {
|
|
337
|
+
state.clearChosenLink()
|
|
304
338
|
mockaton.reset()
|
|
305
339
|
.then(parseError)
|
|
306
340
|
.then(updateState)
|
|
@@ -327,7 +361,7 @@ function MockList() {
|
|
|
327
361
|
t`No mocks found`))
|
|
328
362
|
|
|
329
363
|
if (groupByMethod)
|
|
330
|
-
return Object.keys(brokersByMethod).map(
|
|
364
|
+
return Object.keys(brokersByMethod).map(method => Fragment(
|
|
331
365
|
r('tr', null,
|
|
332
366
|
r('th', { colspan: 2 + Number(canProxy) }),
|
|
333
367
|
r('th', null, method)),
|
|
@@ -337,7 +371,7 @@ function MockList() {
|
|
|
337
371
|
}
|
|
338
372
|
|
|
339
373
|
|
|
340
|
-
function Row({ method, urlMask, urlMaskDittoed, broker }) {
|
|
374
|
+
function Row({ method, urlMask, urlMaskDittoed, broker }, i) {
|
|
341
375
|
const { canProxy, groupByMethod } = state
|
|
342
376
|
return (
|
|
343
377
|
r('tr', { 'data-method': method, 'data-urlMask': urlMask },
|
|
@@ -345,7 +379,7 @@ function Row({ method, urlMask, urlMaskDittoed, broker }) {
|
|
|
345
379
|
r('td', null, DelayRouteToggler(broker)),
|
|
346
380
|
r('td', null, InternalServerErrorToggler(broker)),
|
|
347
381
|
!groupByMethod && r('td', className(CSS.Method), method),
|
|
348
|
-
r('td', null, PreviewLink(method, urlMask, urlMaskDittoed)),
|
|
382
|
+
r('td', null, PreviewLink(method, urlMask, urlMaskDittoed, i === 0)),
|
|
349
383
|
r('td', null, MockSelector(broker))))
|
|
350
384
|
}
|
|
351
385
|
|
|
@@ -358,9 +392,7 @@ function rowsFor(targetMethod) {
|
|
|
358
392
|
for (const [urlMask, broker] of Object.entries(brokers))
|
|
359
393
|
rows.push({ method, urlMask, broker })
|
|
360
394
|
|
|
361
|
-
const sorted = rows
|
|
362
|
-
.filter((r) => r.broker.mocks.length > 1) // >1 because of autogen500
|
|
363
|
-
.sort((rA, rB) => rA.urlMask.localeCompare(rB.urlMask))
|
|
395
|
+
const sorted = rows.sort((a, b) => a.urlMask.localeCompare(b.urlMask))
|
|
364
396
|
|
|
365
397
|
const urlMasksDittoed = dittoSplitPaths(sorted.map(r => r.urlMask))
|
|
366
398
|
return sorted.map((r, i) => ({
|
|
@@ -369,39 +401,50 @@ function rowsFor(targetMethod) {
|
|
|
369
401
|
}))
|
|
370
402
|
}
|
|
371
403
|
|
|
372
|
-
|
|
404
|
+
|
|
405
|
+
function PreviewLink(method, urlMask, urlMaskDittoed, autofocus) {
|
|
373
406
|
async function onClick(event) {
|
|
374
407
|
event.preventDefault()
|
|
375
408
|
try {
|
|
376
|
-
|
|
409
|
+
findChosenLink()?.classList.remove(CSS.chosen)
|
|
377
410
|
this.classList.add(CSS.chosen)
|
|
378
|
-
|
|
411
|
+
state.setChosenLink(method, urlMask)
|
|
412
|
+
await previewMock(method, urlMask)
|
|
379
413
|
}
|
|
380
414
|
catch (error) {
|
|
381
415
|
onError(error)
|
|
382
416
|
}
|
|
383
417
|
}
|
|
418
|
+
const { chosenLink } = state
|
|
419
|
+
const isChosen = chosenLink.method === method && chosenLink.urlMask === urlMask
|
|
384
420
|
const [ditto, tail] = urlMaskDittoed
|
|
385
421
|
return (
|
|
386
422
|
r('a', {
|
|
387
|
-
className
|
|
423
|
+
...className(CSS.PreviewLink, isChosen && CSS.chosen),
|
|
388
424
|
href: urlMask,
|
|
425
|
+
autofocus,
|
|
426
|
+
'data-focus-group': FocusGroup.PreviewLink,
|
|
389
427
|
onClick
|
|
390
428
|
}, ditto
|
|
391
429
|
? [r('span', className(CSS.dittoDir), ditto), tail]
|
|
392
430
|
: tail))
|
|
393
431
|
}
|
|
394
432
|
|
|
433
|
+
function findChosenLink() {
|
|
434
|
+
return document.querySelector(
|
|
435
|
+
`body > main > .${CSS.leftSide} .${CSS.PreviewLink}.${CSS.chosen}`)
|
|
436
|
+
}
|
|
437
|
+
|
|
395
438
|
const STR_PROXIED = t`Proxied`
|
|
396
439
|
|
|
397
440
|
/** @param {ClientMockBroker} broker */
|
|
398
441
|
function MockSelector(broker) {
|
|
399
442
|
function onChange() {
|
|
400
|
-
const {
|
|
443
|
+
const { method, urlMask } = parseFilename(this.value)
|
|
444
|
+
state.setChosenLink(method, urlMask)
|
|
401
445
|
mockaton.select(this.value)
|
|
402
446
|
.then(parseError)
|
|
403
447
|
.then(updateState)
|
|
404
|
-
.then(() => linkFor(method, urlMask)?.click())
|
|
405
448
|
.catch(onError)
|
|
406
449
|
}
|
|
407
450
|
|
|
@@ -455,7 +498,8 @@ function DelayRouteToggler(broker) {
|
|
|
455
498
|
}
|
|
456
499
|
return ClickDragToggler({
|
|
457
500
|
checked: broker.currentMock.delayed,
|
|
458
|
-
commit
|
|
501
|
+
commit,
|
|
502
|
+
focusGroup: FocusGroup.DelayToggler
|
|
459
503
|
})
|
|
460
504
|
}
|
|
461
505
|
|
|
@@ -464,10 +508,10 @@ function DelayRouteToggler(broker) {
|
|
|
464
508
|
function InternalServerErrorToggler(broker) {
|
|
465
509
|
function onChange() {
|
|
466
510
|
const { method, urlMask } = parseFilename(broker.mocks[0])
|
|
511
|
+
state.setChosenLink(method, urlMask)
|
|
467
512
|
mockaton.toggle500(method, urlMask)
|
|
468
513
|
.then(parseError)
|
|
469
514
|
.then(updateState)
|
|
470
|
-
.then(() => linkFor(method, urlMask)?.click())
|
|
471
515
|
.catch(onError)
|
|
472
516
|
}
|
|
473
517
|
return (
|
|
@@ -477,6 +521,7 @@ function InternalServerErrorToggler(broker) {
|
|
|
477
521
|
},
|
|
478
522
|
r('input', {
|
|
479
523
|
type: 'checkbox',
|
|
524
|
+
'data-focus-group': FocusGroup.StatusToggler,
|
|
480
525
|
name: broker.currentMock.file,
|
|
481
526
|
checked: parseFilename(broker.currentMock.file).status === 500,
|
|
482
527
|
onChange
|
|
@@ -488,10 +533,10 @@ function InternalServerErrorToggler(broker) {
|
|
|
488
533
|
function ProxyToggler(broker) {
|
|
489
534
|
function onChange() {
|
|
490
535
|
const { urlMask, method } = parseFilename(broker.mocks[0])
|
|
536
|
+
state.setChosenLink(method, urlMask)
|
|
491
537
|
mockaton.setRouteIsProxied(method, urlMask, this.checked)
|
|
492
538
|
.then(parseError)
|
|
493
539
|
.then(updateState)
|
|
494
|
-
.then(() => linkFor(method, urlMask)?.click())
|
|
495
540
|
.catch(onError)
|
|
496
541
|
}
|
|
497
542
|
return (
|
|
@@ -502,7 +547,8 @@ function ProxyToggler(broker) {
|
|
|
502
547
|
r('input', {
|
|
503
548
|
type: 'checkbox',
|
|
504
549
|
checked: !broker.currentMock.file,
|
|
505
|
-
onChange
|
|
550
|
+
onChange,
|
|
551
|
+
'data-focus-group': FocusGroup.ProxyToggler
|
|
506
552
|
}),
|
|
507
553
|
CloudIcon()))
|
|
508
554
|
}
|
|
@@ -532,7 +578,8 @@ function StaticFilesList() {
|
|
|
532
578
|
r('td', null, r('a', {
|
|
533
579
|
href: broker.route,
|
|
534
580
|
target: '_blank',
|
|
535
|
-
className: CSS.
|
|
581
|
+
className: CSS.PreviewLink,
|
|
582
|
+
'data-focus-group': FocusGroup.PreviewLink
|
|
536
583
|
}, dp[i]))
|
|
537
584
|
))))
|
|
538
585
|
}
|
|
@@ -546,7 +593,8 @@ function DelayStaticRouteToggler(broker) {
|
|
|
546
593
|
}
|
|
547
594
|
return ClickDragToggler({
|
|
548
595
|
checked: broker.delayed,
|
|
549
|
-
commit
|
|
596
|
+
commit,
|
|
597
|
+
focusGroup: FocusGroup.DelayToggler
|
|
550
598
|
})
|
|
551
599
|
}
|
|
552
600
|
|
|
@@ -565,13 +613,14 @@ function NotFoundToggler(broker) {
|
|
|
565
613
|
r('input', {
|
|
566
614
|
type: 'checkbox',
|
|
567
615
|
checked: broker.status === 404,
|
|
568
|
-
onChange
|
|
616
|
+
onChange,
|
|
617
|
+
'data-focus-group': FocusGroup.StatusToggler
|
|
569
618
|
}),
|
|
570
619
|
r('span', null, t`404`)))
|
|
571
620
|
}
|
|
572
621
|
|
|
573
622
|
|
|
574
|
-
function ClickDragToggler({ checked, commit }) {
|
|
623
|
+
function ClickDragToggler({ checked, commit, focusGroup }) {
|
|
575
624
|
function onPointerEnter(event) {
|
|
576
625
|
if (event.buttons === 1)
|
|
577
626
|
onPointerDown.call(this)
|
|
@@ -594,6 +643,7 @@ function ClickDragToggler({ checked, commit }) {
|
|
|
594
643
|
},
|
|
595
644
|
r('input', {
|
|
596
645
|
type: 'checkbox',
|
|
646
|
+
'data-focus-group': focusGroup,
|
|
597
647
|
checked,
|
|
598
648
|
onPointerEnter,
|
|
599
649
|
onPointerDown,
|
|
@@ -604,7 +654,6 @@ function ClickDragToggler({ checked, commit }) {
|
|
|
604
654
|
}
|
|
605
655
|
|
|
606
656
|
|
|
607
|
-
|
|
608
657
|
function Resizer() {
|
|
609
658
|
return (
|
|
610
659
|
r('div', {
|
|
@@ -682,7 +731,7 @@ function PayloadViewerTitleWhenProxied({ mime, status, statusText, gatewayIsBad
|
|
|
682
731
|
' ' + mime))
|
|
683
732
|
}
|
|
684
733
|
|
|
685
|
-
async function previewMock(method, urlMask
|
|
734
|
+
async function previewMock(method, urlMask) {
|
|
686
735
|
previewMock.controller?.abort()
|
|
687
736
|
previewMock.controller = new AbortController
|
|
688
737
|
|
|
@@ -692,7 +741,7 @@ async function previewMock(method, urlMask, href) {
|
|
|
692
741
|
}, SPINNER_DELAY)
|
|
693
742
|
|
|
694
743
|
try {
|
|
695
|
-
const response = await fetch(
|
|
744
|
+
const response = await fetch(urlMask, {
|
|
696
745
|
method,
|
|
697
746
|
signal: previewMock.controller.signal
|
|
698
747
|
})
|
|
@@ -736,7 +785,7 @@ async function updatePayloadViewer(method, urlMask, response) {
|
|
|
736
785
|
else if (isXML(mime))
|
|
737
786
|
payloadViewerRef.current.replaceChildren(syntaxXML(body))
|
|
738
787
|
else
|
|
739
|
-
payloadViewerRef.current.
|
|
788
|
+
payloadViewerRef.current.textContent = body
|
|
740
789
|
}
|
|
741
790
|
}
|
|
742
791
|
|
|
@@ -746,20 +795,61 @@ function isXML(mime) {
|
|
|
746
795
|
}
|
|
747
796
|
|
|
748
797
|
|
|
749
|
-
function trFor(method, urlMask) {
|
|
750
|
-
return document.querySelector(`tr[data-method="${method}"][data-urlMask="${urlMask}"]`)
|
|
751
|
-
}
|
|
752
|
-
function linkFor(method, urlMask) {
|
|
753
|
-
return trFor(method, urlMask)?.querySelector(`a.${CSS.PreviewLink}`)
|
|
754
|
-
}
|
|
755
798
|
function mockSelectorFor(method, urlMask) {
|
|
756
799
|
return trFor(method, urlMask)?.querySelector(`select.${CSS.MockSelector}`)
|
|
757
800
|
}
|
|
801
|
+
function trFor(method, urlMask) {
|
|
802
|
+
return document.querySelector(`tr[data-method="${method}"][data-urlMask="${urlMask}"]`)
|
|
803
|
+
}
|
|
758
804
|
|
|
759
805
|
function focus(selector) {
|
|
760
806
|
document.querySelector(selector)?.focus()
|
|
761
807
|
}
|
|
762
808
|
|
|
809
|
+
function initKeyboardNavigation() {
|
|
810
|
+
addEventListener('keydown', onKeyDown)
|
|
811
|
+
|
|
812
|
+
function onKeyDown(event) {
|
|
813
|
+
const pivot = document.activeElement
|
|
814
|
+
switch (event.key) {
|
|
815
|
+
case 'ArrowDown':
|
|
816
|
+
case 'ArrowUp': {
|
|
817
|
+
let fg = pivot.getAttribute('data-focus-group')
|
|
818
|
+
if (fg !== null) {
|
|
819
|
+
const offset = event.key === 'ArrowDown' ? +1 : -1
|
|
820
|
+
circularAdjacent(offset, allInFocusGroup(+fg), pivot).focus()
|
|
821
|
+
}
|
|
822
|
+
break
|
|
823
|
+
}
|
|
824
|
+
case 'ArrowRight':
|
|
825
|
+
case 'ArrowLeft': {
|
|
826
|
+
if (pivot.hasAttribute('data-focus-group') || pivot.classList.contains(CSS.MockSelector)) {
|
|
827
|
+
const offset = event.key === 'ArrowRight' ? +1 : -1
|
|
828
|
+
rowFocusable(pivot, offset)?.focus()
|
|
829
|
+
}
|
|
830
|
+
break
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function rowFocusable(el, step) {
|
|
836
|
+
const row = el.closest('tr')
|
|
837
|
+
if (row) {
|
|
838
|
+
const focusables = Array.from(row.querySelectorAll('a, input, select:not(:disabled)'))
|
|
839
|
+
return circularAdjacent(step, focusables, el)
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
function allInFocusGroup(focusGroup) {
|
|
844
|
+
return Array.from(document.querySelectorAll(
|
|
845
|
+
`body > main > .${CSS.leftSide} table > tr > td [data-focus-group="${focusGroup}"]:is(input, a)`))
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
function circularAdjacent(step = 1, arr, pivot) {
|
|
849
|
+
return arr[(arr.indexOf(pivot) + step + arr.length) % arr.length]
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
763
853
|
/** # Misc */
|
|
764
854
|
|
|
765
855
|
async function parseError(response) {
|
|
@@ -817,7 +907,7 @@ function SettingsIcon() {
|
|
|
817
907
|
*/
|
|
818
908
|
|
|
819
909
|
function initLongPoll() {
|
|
820
|
-
poll.oldSyncVersion =
|
|
910
|
+
poll.oldSyncVersion = -1
|
|
821
911
|
poll.controller = new AbortController()
|
|
822
912
|
poll()
|
|
823
913
|
document.addEventListener('visibilitychange', () => {
|
|
@@ -835,9 +925,11 @@ async function poll() {
|
|
|
835
925
|
const response = await mockaton.getSyncVersion(poll.oldSyncVersion, poll.controller.signal)
|
|
836
926
|
if (response.ok) {
|
|
837
927
|
const syncVersion = await response.json()
|
|
928
|
+
const skipUpdate = poll.oldSyncVersion === -1
|
|
838
929
|
if (poll.oldSyncVersion !== syncVersion) { // because it could be < or >
|
|
839
930
|
poll.oldSyncVersion = syncVersion
|
|
840
|
-
|
|
931
|
+
if (!skipUpdate)
|
|
932
|
+
await updateState()
|
|
841
933
|
}
|
|
842
934
|
poll()
|
|
843
935
|
}
|
|
@@ -894,6 +986,12 @@ function Fragment(...args) {
|
|
|
894
986
|
return frag
|
|
895
987
|
}
|
|
896
988
|
|
|
989
|
+
function Defer(cb) {
|
|
990
|
+
const placeholder = document.createComment('')
|
|
991
|
+
deferred(() => placeholder.replaceWith(cb()))
|
|
992
|
+
return placeholder
|
|
993
|
+
}
|
|
994
|
+
|
|
897
995
|
function deferred(cb) {
|
|
898
996
|
return window.requestIdleCallback
|
|
899
997
|
? requestIdleCallback(cb)
|