mockaton 10.6.0 → 10.6.2
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 +3 -3
- package/src/Dashboard.css +1 -1
- package/src/Dashboard.js +117 -81
- package/src/DashboardHtml.js +11 -11
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.6.
|
|
5
|
+
"version": "10.6.2",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
8
|
"license": "MIT",
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
"start": "node src/cli.js",
|
|
24
24
|
"watch": "node --watch src/cli.js",
|
|
25
25
|
"pixaton": "node --test --import=./pixaton-tests/_setup.js --experimental-test-isolation=none 'pixaton-tests/**/*.test.js'",
|
|
26
|
-
"outdated": "npm outdated --parseable | awk -F: '{ printf \"npm i
|
|
26
|
+
"outdated": "npm outdated --parseable | awk -F: '{ printf \"npm i %s ;# %s\\n\", $4, $2 }'"
|
|
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
package/src/Dashboard.js
CHANGED
|
@@ -69,6 +69,8 @@ const state = /** @type {State} */ {
|
|
|
69
69
|
return Boolean(state.proxyFallback)
|
|
70
70
|
},
|
|
71
71
|
|
|
72
|
+
leftSideWidth: window.innerWidth / 2,
|
|
73
|
+
|
|
72
74
|
groupByMethod: initPreference('groupByMethod'),
|
|
73
75
|
toggleGroupByMethod() {
|
|
74
76
|
state.groupByMethod = !state.groupByMethod
|
|
@@ -76,12 +78,7 @@ const state = /** @type {State} */ {
|
|
|
76
78
|
updateState()
|
|
77
79
|
},
|
|
78
80
|
|
|
79
|
-
leftSideWidth: undefined,
|
|
80
|
-
|
|
81
81
|
chosenLink: { method: '', urlMask: '' },
|
|
82
|
-
clearChosenLink() {
|
|
83
|
-
state.chosenLink = { method: '', urlMask: '' }
|
|
84
|
-
},
|
|
85
82
|
setChosenLink(method, urlMask) {
|
|
86
83
|
state.chosenLink = { method, urlMask }
|
|
87
84
|
}
|
|
@@ -90,7 +87,7 @@ const state = /** @type {State} */ {
|
|
|
90
87
|
|
|
91
88
|
const mockaton = new Commander(location.origin)
|
|
92
89
|
updateState()
|
|
93
|
-
|
|
90
|
+
initRealTimeUpdates()
|
|
94
91
|
initKeyboardNavigation()
|
|
95
92
|
|
|
96
93
|
async function updateState() {
|
|
@@ -98,12 +95,18 @@ async function updateState() {
|
|
|
98
95
|
const response = await mockaton.getState()
|
|
99
96
|
if (!response.ok)
|
|
100
97
|
throw response.status
|
|
98
|
+
|
|
101
99
|
Object.assign(state, await response.json())
|
|
100
|
+
|
|
101
|
+
const focusedElem = selectorFor(document.activeElement)
|
|
102
102
|
document.body.replaceChildren(...App())
|
|
103
|
-
|
|
103
|
+
if (focusedElem)
|
|
104
|
+
document.querySelector(focusedElem)?.focus()
|
|
105
|
+
|
|
104
106
|
const { method, urlMask } = state.chosenLink
|
|
105
107
|
if (method && urlMask)
|
|
106
108
|
await previewMock(method, urlMask)
|
|
109
|
+
|
|
107
110
|
}
|
|
108
111
|
catch (error) {
|
|
109
112
|
onError(error)
|
|
@@ -168,18 +171,25 @@ function SettingsMenuTrigger() {
|
|
|
168
171
|
|
|
169
172
|
function SettingsMenu(id) {
|
|
170
173
|
const { groupByMethod, toggleGroupByMethod } = state
|
|
174
|
+
|
|
175
|
+
const firstInputRef = useRef()
|
|
176
|
+
function onToggle(event) {
|
|
177
|
+
if (event.newState === 'open')
|
|
178
|
+
firstInputRef.current.focus()
|
|
179
|
+
}
|
|
171
180
|
return (
|
|
172
181
|
r('menu', {
|
|
173
182
|
id,
|
|
174
183
|
popover: '',
|
|
175
|
-
className: CSS.SettingsMenu
|
|
184
|
+
className: CSS.SettingsMenu,
|
|
185
|
+
onToggle
|
|
176
186
|
},
|
|
177
187
|
|
|
178
188
|
r('label', className(CSS.GroupByMethod),
|
|
179
189
|
r('input', {
|
|
190
|
+
ref: firstInputRef,
|
|
180
191
|
type: 'checkbox',
|
|
181
192
|
checked: groupByMethod,
|
|
182
|
-
// autofocus: true, // TODO
|
|
183
193
|
onChange: toggleGroupByMethod
|
|
184
194
|
}),
|
|
185
195
|
r('span', null, t`Group by Method`)),
|
|
@@ -219,13 +229,11 @@ function BulkSelector() {
|
|
|
219
229
|
// But this way is easier to implement, with a few hacks.
|
|
220
230
|
const firstOption = t`Pick Comment…`
|
|
221
231
|
function onChange() {
|
|
222
|
-
state.clearChosenLink()
|
|
223
232
|
const value = this.value
|
|
224
233
|
this.value = firstOption // Hack
|
|
225
234
|
mockaton.bulkSelectByComment(value)
|
|
226
235
|
.then(parseError)
|
|
227
236
|
.then(updateState)
|
|
228
|
-
.then(() => focus(`.${CSS.BulkSelector}`))
|
|
229
237
|
.catch(onError)
|
|
230
238
|
}
|
|
231
239
|
const disabled = !comments.length
|
|
@@ -290,7 +298,6 @@ function ProxyFallbackField() {
|
|
|
290
298
|
mockaton.setProxyFallback(this.value.trim())
|
|
291
299
|
.then(parseError)
|
|
292
300
|
.then(updateState)
|
|
293
|
-
.then(() => focus(`.${CSS.FallbackBackend} input`))
|
|
294
301
|
.catch(onError)
|
|
295
302
|
}
|
|
296
303
|
return (
|
|
@@ -328,11 +335,9 @@ function SaveProxiedCheckbox(ref) {
|
|
|
328
335
|
|
|
329
336
|
function ResetButton() {
|
|
330
337
|
function onClick() {
|
|
331
|
-
state.clearChosenLink()
|
|
332
338
|
mockaton.reset()
|
|
333
339
|
.then(parseError)
|
|
334
340
|
.then(updateState)
|
|
335
|
-
.then(() => focus(`.${CSS.ResetButton}`))
|
|
336
341
|
.catch(onError)
|
|
337
342
|
}
|
|
338
343
|
return (
|
|
@@ -368,7 +373,7 @@ function MockList() {
|
|
|
368
373
|
function Row({ method, urlMask, urlMaskDittoed, broker }, i) {
|
|
369
374
|
const { canProxy, groupByMethod } = state
|
|
370
375
|
return (
|
|
371
|
-
r('tr', {
|
|
376
|
+
r('tr', { key: method + '::' + urlMask },
|
|
372
377
|
canProxy && r('td', null, ProxyToggler(broker)),
|
|
373
378
|
r('td', null, DelayRouteToggler(broker)),
|
|
374
379
|
r('td', null, InternalServerErrorToggler(broker)),
|
|
@@ -621,6 +626,7 @@ function ClickDragToggler({ checked, commit, focusGroup }) {
|
|
|
621
626
|
}
|
|
622
627
|
function onPointerDown() {
|
|
623
628
|
this.checked = !this.checked
|
|
629
|
+
this.focus()
|
|
624
630
|
commit(this.checked)
|
|
625
631
|
}
|
|
626
632
|
function onClick(event) {
|
|
@@ -649,38 +655,48 @@ function ClickDragToggler({ checked, commit, focusGroup }) {
|
|
|
649
655
|
|
|
650
656
|
|
|
651
657
|
function Resizer() {
|
|
658
|
+
let raf = 0
|
|
659
|
+
let initialX = 0
|
|
660
|
+
let panelWidth = 0
|
|
661
|
+
|
|
662
|
+
function onPointerDown(event) {
|
|
663
|
+
initialX = event.clientX
|
|
664
|
+
panelWidth = leftSideRef.current.clientWidth
|
|
665
|
+
addEventListener('pointerup', onUp, { once: true })
|
|
666
|
+
addEventListener('pointermove', onMove)
|
|
667
|
+
Object.assign(document.body.style, {
|
|
668
|
+
cursor: 'col-resize',
|
|
669
|
+
userSelect: 'none',
|
|
670
|
+
pointerEvents: 'none'
|
|
671
|
+
})
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
function onMove(event) {
|
|
675
|
+
const MIN_LEFT_WIDTH = 380
|
|
676
|
+
raf = raf || requestAnimationFrame(() => {
|
|
677
|
+
state.leftSideWidth = Math.max(panelWidth - (initialX - event.clientX), MIN_LEFT_WIDTH)
|
|
678
|
+
leftSideRef.current.style.width = state.leftSideWidth + 'px'
|
|
679
|
+
raf = 0
|
|
680
|
+
})
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function onUp() {
|
|
684
|
+
removeEventListener('pointermove', onMove)
|
|
685
|
+
cancelAnimationFrame(raf)
|
|
686
|
+
raf = 0
|
|
687
|
+
Object.assign(document.body.style, {
|
|
688
|
+
cursor: 'auto',
|
|
689
|
+
userSelect: 'auto',
|
|
690
|
+
pointerEvents: 'auto'
|
|
691
|
+
})
|
|
692
|
+
}
|
|
693
|
+
|
|
652
694
|
return (
|
|
653
695
|
r('div', {
|
|
654
696
|
className: CSS.Resizer,
|
|
655
|
-
onPointerDown
|
|
697
|
+
onPointerDown
|
|
656
698
|
}))
|
|
657
699
|
}
|
|
658
|
-
Resizer.raf = 0
|
|
659
|
-
Resizer.initialX = 0
|
|
660
|
-
Resizer.panelWidth = 0
|
|
661
|
-
Resizer.onPointerDown = function (event) {
|
|
662
|
-
Resizer.initialX = event.clientX
|
|
663
|
-
Resizer.panelWidth = leftSideRef.current.clientWidth
|
|
664
|
-
addEventListener('pointerup', Resizer.onUp, { once: true })
|
|
665
|
-
addEventListener('pointermove', Resizer.onMove)
|
|
666
|
-
document.body.style.userSelect = 'none'
|
|
667
|
-
document.body.style.cursor = 'col-resize'
|
|
668
|
-
}
|
|
669
|
-
Resizer.onMove = function (event) {
|
|
670
|
-
const MIN_LEFT_WIDTH = 380
|
|
671
|
-
Resizer.raf = Resizer.raf || requestAnimationFrame(() => {
|
|
672
|
-
state.leftSideWidth = Math.max(Resizer.panelWidth - (Resizer.initialX - event.clientX), MIN_LEFT_WIDTH)
|
|
673
|
-
leftSideRef.current.style.width = state.leftSideWidth + 'px'
|
|
674
|
-
Resizer.raf = 0
|
|
675
|
-
})
|
|
676
|
-
}
|
|
677
|
-
Resizer.onUp = function () {
|
|
678
|
-
removeEventListener('pointermove', Resizer.onMove)
|
|
679
|
-
cancelAnimationFrame(Resizer.raf)
|
|
680
|
-
Resizer.raf = 0
|
|
681
|
-
document.body.style.userSelect = 'auto'
|
|
682
|
-
document.body.style.cursor = 'auto'
|
|
683
|
-
}
|
|
684
700
|
|
|
685
701
|
|
|
686
702
|
/** # Payload Preview */
|
|
@@ -752,7 +768,9 @@ async function previewMock(method, urlMask) {
|
|
|
752
768
|
async function updatePayloadViewer(method, urlMask, response) {
|
|
753
769
|
const mime = response.headers.get('content-type') || ''
|
|
754
770
|
|
|
755
|
-
const file = mockSelectorFor(method, urlMask)
|
|
771
|
+
const file = mockSelectorFor(method, urlMask)?.value
|
|
772
|
+
if (!file)
|
|
773
|
+
return // e.g. selected was deleted
|
|
756
774
|
if (file === STR_PROXIED)
|
|
757
775
|
payloadViewerTitleRef.current.replaceChildren(PayloadViewerTitleWhenProxied({
|
|
758
776
|
status: response.status,
|
|
@@ -789,19 +807,11 @@ function isXML(mime) {
|
|
|
789
807
|
}
|
|
790
808
|
|
|
791
809
|
|
|
792
|
-
function trFor(method, urlMask) {
|
|
793
|
-
return document.querySelector(`tr[data-method="${method}"][data-urlMask="${urlMask}"]`)
|
|
794
|
-
}
|
|
795
|
-
function linkFor(method, urlMask) {
|
|
796
|
-
return trFor(method, urlMask)?.querySelector(`a.${CSS.PreviewLink}`)
|
|
797
|
-
}
|
|
798
810
|
function mockSelectorFor(method, urlMask) {
|
|
799
|
-
|
|
811
|
+
const tr = document.querySelector(`tr[key="${method}::${urlMask}"]`)
|
|
812
|
+
return tr?.querySelector(`.${CSS.MockSelector}`)
|
|
800
813
|
}
|
|
801
814
|
|
|
802
|
-
function focus(selector) {
|
|
803
|
-
document.querySelector(selector)?.focus()
|
|
804
|
-
}
|
|
805
815
|
|
|
806
816
|
function initKeyboardNavigation() {
|
|
807
817
|
addEventListener('keydown', onKeyDown)
|
|
@@ -822,7 +832,7 @@ function initKeyboardNavigation() {
|
|
|
822
832
|
case 'ArrowLeft': {
|
|
823
833
|
if (pivot.hasAttribute('data-focus-group') || pivot.classList.contains(CSS.MockSelector)) {
|
|
824
834
|
const offset = event.key === 'ArrowRight' ? +1 : -1
|
|
825
|
-
rowFocusable(pivot, offset)
|
|
835
|
+
rowFocusable(pivot, offset).focus()
|
|
826
836
|
}
|
|
827
837
|
break
|
|
828
838
|
}
|
|
@@ -847,7 +857,7 @@ function initKeyboardNavigation() {
|
|
|
847
857
|
}
|
|
848
858
|
}
|
|
849
859
|
|
|
850
|
-
/** #
|
|
860
|
+
/** # Error */
|
|
851
861
|
|
|
852
862
|
async function parseError(response) {
|
|
853
863
|
if (response.ok)
|
|
@@ -879,6 +889,9 @@ function showErrorToast(msg) {
|
|
|
879
889
|
}, msg))
|
|
880
890
|
}
|
|
881
891
|
|
|
892
|
+
|
|
893
|
+
/** # Icons */
|
|
894
|
+
|
|
882
895
|
function TimerIcon() {
|
|
883
896
|
return (
|
|
884
897
|
s('svg', { viewBox: '0 0 24 24' },
|
|
@@ -899,43 +912,43 @@ function SettingsIcon() {
|
|
|
899
912
|
}
|
|
900
913
|
|
|
901
914
|
/**
|
|
902
|
-
* #
|
|
903
|
-
* The version increments when a mock file is added or
|
|
915
|
+
* # Long polls UI sync version
|
|
916
|
+
* The version increments when a mock file is added, removed, or renamed.
|
|
904
917
|
*/
|
|
918
|
+
function initRealTimeUpdates() {
|
|
919
|
+
let oldSyncVersion = -1
|
|
920
|
+
let controller = new AbortController()
|
|
905
921
|
|
|
906
|
-
function initLongPoll() {
|
|
907
|
-
poll.oldSyncVersion = -1
|
|
908
|
-
poll.controller = new AbortController()
|
|
909
922
|
poll()
|
|
910
923
|
document.addEventListener('visibilitychange', () => {
|
|
911
924
|
if (document.hidden) {
|
|
912
|
-
|
|
913
|
-
|
|
925
|
+
controller.abort('_hidden_tab_')
|
|
926
|
+
controller = new AbortController()
|
|
914
927
|
}
|
|
915
928
|
else
|
|
916
929
|
poll()
|
|
917
930
|
})
|
|
918
|
-
}
|
|
919
931
|
|
|
920
|
-
async function poll() {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
932
|
+
async function poll() {
|
|
933
|
+
try {
|
|
934
|
+
const response = await mockaton.getSyncVersion(oldSyncVersion, controller.signal)
|
|
935
|
+
if (response.ok) {
|
|
936
|
+
const syncVersion = await response.json()
|
|
937
|
+
const skipUpdate = oldSyncVersion === -1
|
|
938
|
+
if (oldSyncVersion !== syncVersion) { // because it could be < or >
|
|
939
|
+
oldSyncVersion = syncVersion
|
|
940
|
+
if (!skipUpdate)
|
|
941
|
+
await updateState()
|
|
942
|
+
}
|
|
943
|
+
poll()
|
|
930
944
|
}
|
|
931
|
-
|
|
945
|
+
else
|
|
946
|
+
throw response.status
|
|
947
|
+
}
|
|
948
|
+
catch (error) {
|
|
949
|
+
if (error !== '_hidden_tab_')
|
|
950
|
+
setTimeout(poll, 3000)
|
|
932
951
|
}
|
|
933
|
-
else
|
|
934
|
-
throw response.status
|
|
935
|
-
}
|
|
936
|
-
catch (error) {
|
|
937
|
-
if (error !== '_hidden_tab_')
|
|
938
|
-
setTimeout(poll, 3000)
|
|
939
952
|
}
|
|
940
953
|
}
|
|
941
954
|
|
|
@@ -948,7 +961,6 @@ function className(...args) {
|
|
|
948
961
|
}
|
|
949
962
|
}
|
|
950
963
|
|
|
951
|
-
|
|
952
964
|
function createElement(tag, props, ...children) {
|
|
953
965
|
const node = document.createElement(tag)
|
|
954
966
|
for (const [k, v] of Object.entries(props || {}))
|
|
@@ -995,6 +1007,30 @@ function deferred(cb) {
|
|
|
995
1007
|
: setTimeout(cb, 100) // Safari
|
|
996
1008
|
}
|
|
997
1009
|
|
|
1010
|
+
function selectorFor(elem) {
|
|
1011
|
+
if (!(elem instanceof Element))
|
|
1012
|
+
return
|
|
1013
|
+
|
|
1014
|
+
const path = []
|
|
1015
|
+
while (elem) {
|
|
1016
|
+
let mod = ''
|
|
1017
|
+
if (elem.hasAttribute('key'))
|
|
1018
|
+
mod = `[key="${elem.getAttribute('key')}"]`
|
|
1019
|
+
else {
|
|
1020
|
+
let i = 0
|
|
1021
|
+
let sib = elem
|
|
1022
|
+
while ((sib = sib.previousElementSibling))
|
|
1023
|
+
if (sib.nodeName === elem.nodeName)
|
|
1024
|
+
i++
|
|
1025
|
+
if (i)
|
|
1026
|
+
mod = `:nth-of-type(${i + 1})`
|
|
1027
|
+
}
|
|
1028
|
+
path.push(elem.nodeName + mod)
|
|
1029
|
+
elem = elem.parentElement
|
|
1030
|
+
}
|
|
1031
|
+
return path.reverse().join('>')
|
|
1032
|
+
}
|
|
1033
|
+
|
|
998
1034
|
|
|
999
1035
|
// When false, the URL will be updated with param=false
|
|
1000
1036
|
function initPreference(param) {
|
package/src/DashboardHtml.js
CHANGED
|
@@ -9,23 +9,23 @@ export const CSP = [
|
|
|
9
9
|
export const DashboardHtml = `<!DOCTYPE html>
|
|
10
10
|
<html lang="en-US">
|
|
11
11
|
<head>
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
<meta charset="UTF-8">
|
|
13
|
+
<base href="${API.dashboard}/">
|
|
14
|
+
|
|
15
|
+
<link rel="stylesheet" href="Dashboard.css">
|
|
16
|
+
<script type="module" src="Dashboard.js"></script>
|
|
17
|
+
|
|
18
18
|
<link rel="preload" href="${API.state}" as="fetch" crossorigin>
|
|
19
19
|
|
|
20
20
|
<link rel="modulepreload" href="ApiConstants.js">
|
|
21
21
|
<link rel="modulepreload" href="ApiCommander.js">
|
|
22
22
|
<link rel="modulepreload" href="Filename.js">
|
|
23
23
|
<link rel="preload" href="Logo.svg" as="image">
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
|
|
25
|
+
<link rel="icon" href="data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m235 33.7v202c0 9.19-5.81 14-17.4 14-11.6 0-17.4-4.83-17.4-14v-151c-0.115-4.49-6.72-5.88-8.46-0.87l-48.3 155c-2.22 7.01-7.72 10.1-16 9.9-3.63-0.191-7.01-1.14-9.66-2.89-2.89-1.72-4.83-4.34-5.57-7.72-11.1-37-22.6-74.3-34.1-111-4.34-14-8.95-31.4-14-48.3-1.82-4.83-8.16-5.32-8.46 1.16v156c0 9.19-5.81 14-17.4 14-11.6 0-17.4-4.83-17.4-14v-207c0-5.74 2.62-13.2 9.39-16.3 7.5-3.14 15-4.05 21.8-3.8 3.14 0 6.03 0.686 8.95 1.46 3.14 0.797 6.03 1.98 8.7 3.63 2.65 1.38 5.32 3.14 7.5 5.57 2.22 2.22 3.87 4.83 5.07 7.72l45.8 157c4.63-15.9 32.4-117 33.3-121 4.12-13.8 7.72-26.5 10.9-38.7 1.16-2.65 2.89-5.32 5.07-7.5 2.15-2.15 4.58-4.12 7.5-5.32 2.65-1.57 5.57-2.89 8.46-3.63 3.14-0.797 9.44-0.988 12.1-0.988 11.6 1.07 29.4 9.14 29.4 27z' fill='%23808080'/%3E%3C/svg%3E">
|
|
26
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
27
|
+
<meta name="description" content="HTTP Mock Server">
|
|
28
|
+
<title>Mockaton</title>
|
|
29
29
|
</head>
|
|
30
30
|
<body>
|
|
31
31
|
</body>
|