mockaton 8.16.1 → 8.16.3
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 +1 -1
- package/src/Api.js +16 -16
- package/src/ApiConstants.js +2 -2
- package/src/Commander.js +3 -3
- package/src/Dashboard.css +90 -78
- package/src/Dashboard.js +53 -47
- package/src/MockBroker.js +2 -2
- package/src/Mockaton.js +1 -1
- package/src/StaticDispatcher.js +22 -30
- package/src/mockBrokersCollection.js +3 -1
package/package.json
CHANGED
package/src/Api.js
CHANGED
|
@@ -25,7 +25,9 @@ const dashboardAssets = [
|
|
|
25
25
|
|
|
26
26
|
export const apiGetRequests = new Map([
|
|
27
27
|
[API.dashboard, serveDashboard],
|
|
28
|
-
...dashboardAssets.map(f => [
|
|
28
|
+
...dashboardAssets.map(f => [
|
|
29
|
+
API.dashboard + f, serveDashboardAsset(f)
|
|
30
|
+
]),
|
|
29
31
|
[API.cors, getIsCorsAllowed],
|
|
30
32
|
[API.static, listStaticFiles],
|
|
31
33
|
[API.mocks, listMockBrokers],
|
|
@@ -49,7 +51,7 @@ export const apiPatchRequests = new Map([
|
|
|
49
51
|
[API.globalDelay, setGlobalDelay],
|
|
50
52
|
[API.collectProxied, setCollectProxied],
|
|
51
53
|
[API.delayStatic, setStaticRouteIsDelayed],
|
|
52
|
-
[API.
|
|
54
|
+
[API.staticStatus, setStaticRouteStatusCode]
|
|
53
55
|
])
|
|
54
56
|
|
|
55
57
|
|
|
@@ -66,7 +68,7 @@ function serveDashboardAsset(f) {
|
|
|
66
68
|
|
|
67
69
|
function listCookies(_, response) { sendJSON(response, cookie.list()) }
|
|
68
70
|
function listComments(_, response) { sendJSON(response, mockBrokersCollection.extractAllComments()) }
|
|
69
|
-
function listStaticFiles(
|
|
71
|
+
function listStaticFiles(_, response) { sendJSON(response, getStaticFilesCollection()) }
|
|
70
72
|
function getGlobalDelay(_, response) { sendJSON(response, config.delay) }
|
|
71
73
|
function listMockBrokers(_, response) { sendJSON(response, mockBrokersCollection.getAll()) }
|
|
72
74
|
function getProxyFallback(_, response) { sendJSON(response, config.proxyFallback) }
|
|
@@ -86,12 +88,10 @@ function longPollClientSyncVersion(req, response) {
|
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
response.setTimeout(LONG_POLL_SERVER_TIMEOUT, onAddOrRemoveMock)
|
|
89
|
-
|
|
90
91
|
req.on('error', () => {
|
|
91
92
|
uiSyncVersion.unsubscribe(onAddOrRemoveMock)
|
|
92
93
|
response.destroy()
|
|
93
94
|
})
|
|
94
|
-
|
|
95
95
|
uiSyncVersion.subscribe(onAddOrRemoveMock)
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -136,7 +136,7 @@ async function setRouteIsDelayed(req, response) {
|
|
|
136
136
|
else if (typeof delayed !== 'boolean')
|
|
137
137
|
sendUnprocessableContent(response, `Expected a boolean for "delayed"`) // TESTME
|
|
138
138
|
else {
|
|
139
|
-
broker.
|
|
139
|
+
broker.setDelayed(delayed)
|
|
140
140
|
sendOK(response)
|
|
141
141
|
}
|
|
142
142
|
}
|
|
@@ -155,7 +155,7 @@ async function setRouteIsProxied(req, response) { // TESTME
|
|
|
155
155
|
else if (proxied && !config.proxyFallback)
|
|
156
156
|
sendUnprocessableContent(response, `There’s no proxy fallback`)
|
|
157
157
|
else {
|
|
158
|
-
broker.
|
|
158
|
+
broker.setProxied(proxied)
|
|
159
159
|
sendOK(response)
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -198,17 +198,17 @@ async function setGlobalDelay(req, response) { // TESTME
|
|
|
198
198
|
}
|
|
199
199
|
|
|
200
200
|
|
|
201
|
-
async function
|
|
201
|
+
async function setStaticRouteStatusCode(req, response) {
|
|
202
202
|
const body = await parseJSON(req)
|
|
203
|
-
const
|
|
203
|
+
const status = Number(body[DF.statusCode])
|
|
204
204
|
const broker = findStaticBrokerByRoute(body[DF.routeUrlMask])
|
|
205
205
|
|
|
206
206
|
if (!broker) // TESTME
|
|
207
207
|
sendUnprocessableContent(response, `Route does not exist: ${body[DF.routeUrlMask]}`)
|
|
208
|
-
else if (
|
|
209
|
-
sendUnprocessableContent(response, `Expected a
|
|
208
|
+
else if (!(status === 200 || status === 404))
|
|
209
|
+
sendUnprocessableContent(response, `Expected a 200 or 404 status code. Received ${status}`) // TESTME
|
|
210
210
|
else {
|
|
211
|
-
broker.
|
|
211
|
+
broker.setStatus(status)
|
|
212
212
|
sendOK(response)
|
|
213
213
|
}
|
|
214
214
|
}
|
|
@@ -216,15 +216,15 @@ async function setStaticRouteIsNotFound(req, response) {
|
|
|
216
216
|
|
|
217
217
|
async function setStaticRouteIsDelayed(req, response) {
|
|
218
218
|
const body = await parseJSON(req)
|
|
219
|
-
const
|
|
219
|
+
const delayed = body[DF.delayed]
|
|
220
220
|
const broker = findStaticBrokerByRoute(body[DF.routeUrlMask])
|
|
221
221
|
|
|
222
222
|
if (!broker) // TESTME
|
|
223
223
|
sendUnprocessableContent(response, `Route does not exist: ${body[DF.routeUrlMask]}`)
|
|
224
|
-
else if (typeof
|
|
225
|
-
sendUnprocessableContent(response, `Expected a boolean for "delayed"`) // TESTME
|
|
224
|
+
else if (typeof delayed !== 'boolean')
|
|
225
|
+
sendUnprocessableContent(response, `Expected a boolean for "delayed". Received ${delayed}`) // TESTME
|
|
226
226
|
else {
|
|
227
|
-
broker.
|
|
227
|
+
broker.setDelayed(delayed)
|
|
228
228
|
sendOK(response)
|
|
229
229
|
}
|
|
230
230
|
}
|
package/src/ApiConstants.js
CHANGED
|
@@ -11,11 +11,11 @@ export const API = {
|
|
|
11
11
|
fallback: MOUNT + '/fallback',
|
|
12
12
|
globalDelay: MOUNT + '/global-delay',
|
|
13
13
|
mocks: MOUNT + '/mocks',
|
|
14
|
-
notFoundStatic: MOUNT + '/not-found-static',
|
|
15
14
|
proxied: MOUNT + '/proxied',
|
|
16
15
|
reset: MOUNT + '/reset',
|
|
17
16
|
select: MOUNT + '/select',
|
|
18
17
|
static: MOUNT + '/static',
|
|
18
|
+
staticStatus: MOUNT + '/static-status',
|
|
19
19
|
syncVersion: MOUNT + '/sync_version'
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -24,7 +24,7 @@ export const DF = { // Dashboard Fields (XHR)
|
|
|
24
24
|
routeUrlMask: 'route_url_mask',
|
|
25
25
|
delayed: 'delayed',
|
|
26
26
|
proxied: 'proxied',
|
|
27
|
-
|
|
27
|
+
statusCode: 'status_code',
|
|
28
28
|
syncVersion: 'last_received_sync_version'
|
|
29
29
|
}
|
|
30
30
|
|
package/src/Commander.js
CHANGED
|
@@ -44,10 +44,10 @@ export class Commander {
|
|
|
44
44
|
})
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
return this.#patch(API.
|
|
47
|
+
setStaticRouteStatus(routeUrlMask, status) {
|
|
48
|
+
return this.#patch(API.staticStatus, {
|
|
49
49
|
[DF.routeUrlMask]: routeUrlMask,
|
|
50
|
-
[DF.
|
|
50
|
+
[DF.statusCode]: status
|
|
51
51
|
})
|
|
52
52
|
}
|
|
53
53
|
|
package/src/Dashboard.css
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
:root {
|
|
2
|
-
--boxShadow1: 0
|
|
2
|
+
--boxShadow1: 0 3px 1px -1px rgba(0, 0, 0, 0.04), 0 1px 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
3
3
|
--radius: 4px;
|
|
4
|
-
--radiusSmall: 4px;
|
|
5
4
|
}
|
|
6
5
|
|
|
7
6
|
@media (prefers-color-scheme: light) {
|
|
@@ -12,9 +11,10 @@
|
|
|
12
11
|
--colorBackground: #fff;
|
|
13
12
|
--colorComboBoxHeaderBackground: #fff;
|
|
14
13
|
--colorComboBoxBackground: #eee;
|
|
15
|
-
--colorHeaderBackground: #
|
|
16
|
-
--colorSecondaryButtonBackground: #
|
|
17
|
-
--
|
|
14
|
+
--colorHeaderBackground: #f0f0f0;
|
|
15
|
+
--colorSecondaryButtonBackground: #fcfcfc;
|
|
16
|
+
--colorSecondaryActionBorder: #ddd;
|
|
17
|
+
--colorSecondaryAction: #666;
|
|
18
18
|
--colorDisabledMockSelector: #444;
|
|
19
19
|
--colorHover: #dfefff;
|
|
20
20
|
--colorLabel: #444;
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
--colorHeaderBackground: #111;
|
|
32
32
|
--colorComboBoxBackground: #2a2a2a;
|
|
33
33
|
--colorSecondaryButtonBackground: #2a2a2a;
|
|
34
|
+
--colorSecondaryActionBorder: #333;
|
|
34
35
|
--colorSecondaryAction: #999;
|
|
35
36
|
--colorComboBoxHeaderBackground: #222;
|
|
36
37
|
--colorDisabledMockSelector: #b9b9b9;
|
|
@@ -42,15 +43,22 @@
|
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
html,
|
|
46
|
+
html,
|
|
47
|
+
body {
|
|
48
|
+
overflow: hidden;
|
|
49
|
+
height: 100%;
|
|
50
|
+
padding: 0;
|
|
46
51
|
margin: 0;
|
|
52
|
+
background: var(--colorHeaderBackground);
|
|
47
53
|
font-size: 12px;
|
|
48
54
|
}
|
|
49
55
|
body {
|
|
50
|
-
|
|
56
|
+
display: grid;
|
|
57
|
+
grid-template-rows: auto 1fr;
|
|
51
58
|
background: var(--colorBackground);
|
|
52
59
|
color: var(--colorText);
|
|
53
60
|
}
|
|
61
|
+
|
|
54
62
|
* {
|
|
55
63
|
box-sizing: border-box;
|
|
56
64
|
padding: 0;
|
|
@@ -59,6 +67,7 @@ body {
|
|
|
59
67
|
font-family: system-ui, sans-serif;
|
|
60
68
|
font-size: 100%;
|
|
61
69
|
outline: 0;
|
|
70
|
+
scrollbar-width: thin;
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
select, a, input, button, summary {
|
|
@@ -79,6 +88,7 @@ input[type=checkbox] ~ svg {
|
|
|
79
88
|
}
|
|
80
89
|
|
|
81
90
|
select {
|
|
91
|
+
border: 1px solid transparent;
|
|
82
92
|
font-size: 100%;
|
|
83
93
|
color: var(--colorText);
|
|
84
94
|
cursor: pointer;
|
|
@@ -86,16 +96,17 @@ select {
|
|
|
86
96
|
border-radius: var(--radius);
|
|
87
97
|
appearance: none;
|
|
88
98
|
background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23888888'><path d='M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6z'/></svg>") no-repeat;
|
|
89
|
-
background-color: var(--colorComboBoxBackground);
|
|
90
99
|
background-size: 16px;
|
|
91
100
|
background-position: 100% center;
|
|
92
101
|
|
|
93
102
|
&:enabled {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
border-color: var(--colorSecondaryActionBorder);
|
|
104
|
+
background-color: var(--colorSecondaryButtonBackground);
|
|
105
|
+
&:hover {
|
|
106
|
+
border-color: var(--colorHover);
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
background-color: var(--colorHover);
|
|
109
|
+
}
|
|
99
110
|
}
|
|
100
111
|
&:disabled {
|
|
101
112
|
cursor: not-allowed;
|
|
@@ -106,18 +117,19 @@ select {
|
|
|
106
117
|
color: var(--colorRed);
|
|
107
118
|
}
|
|
108
119
|
|
|
109
|
-
|
|
110
|
-
position: fixed;
|
|
111
|
-
z-index: 100;
|
|
112
|
-
top: 0;
|
|
113
|
-
left: 0;
|
|
120
|
+
header {
|
|
114
121
|
display: flex;
|
|
115
122
|
width: 100%;
|
|
116
|
-
align-items: flex-end;
|
|
117
123
|
padding: 16px;
|
|
118
124
|
background: var(--colorHeaderBackground);
|
|
119
125
|
box-shadow: var(--boxShadow1);
|
|
120
|
-
|
|
126
|
+
|
|
127
|
+
> div {
|
|
128
|
+
display: flex;
|
|
129
|
+
flex-wrap: wrap;
|
|
130
|
+
align-items: flex-end;
|
|
131
|
+
gap: 10px;
|
|
132
|
+
}
|
|
121
133
|
|
|
122
134
|
img {
|
|
123
135
|
width: 130px;
|
|
@@ -151,11 +163,11 @@ select {
|
|
|
151
163
|
width: 100%;
|
|
152
164
|
height: 28px;
|
|
153
165
|
padding: 4px 8px;
|
|
166
|
+
border: 0;
|
|
154
167
|
border-right: 3px solid transparent;
|
|
155
168
|
margin-top: 4px;
|
|
156
169
|
color: var(--colorText);
|
|
157
170
|
font-size: 11px;
|
|
158
|
-
box-shadow: var(--boxShadow1);
|
|
159
171
|
background-color: var(--colorComboBoxHeaderBackground);
|
|
160
172
|
border-radius: var(--radius);
|
|
161
173
|
}
|
|
@@ -219,56 +231,61 @@ select {
|
|
|
219
231
|
&:hover {
|
|
220
232
|
background: var(--colorRed);
|
|
221
233
|
color: white;
|
|
222
|
-
box-shadow: var(--boxShadow1);
|
|
223
234
|
}
|
|
224
235
|
}
|
|
225
236
|
}
|
|
226
237
|
|
|
227
238
|
|
|
228
|
-
|
|
229
|
-
display:
|
|
239
|
+
main {
|
|
240
|
+
display: grid;
|
|
241
|
+
min-height: 0;
|
|
242
|
+
grid-template-columns: 50% 50%;
|
|
243
|
+
|
|
244
|
+
@media (max-width: 960px) {
|
|
245
|
+
grid-template-columns: 100%;
|
|
246
|
+
grid-template-rows: 50% 50%;
|
|
247
|
+
gap: 32px;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.leftSide {
|
|
251
|
+
padding: 16px;
|
|
252
|
+
padding-bottom: 0;
|
|
253
|
+
overflow-y: auto;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.rightSide {
|
|
257
|
+
padding-top: 16px;
|
|
258
|
+
padding-left: 16px;
|
|
259
|
+
overflow-y: auto;
|
|
260
|
+
}
|
|
230
261
|
}
|
|
231
262
|
|
|
232
263
|
table {
|
|
233
264
|
border-collapse: collapse;
|
|
234
265
|
|
|
235
266
|
th {
|
|
236
|
-
padding-top: 20px;
|
|
237
267
|
padding-bottom: 2px;
|
|
238
|
-
padding-left:
|
|
268
|
+
padding-left: 110px;
|
|
239
269
|
text-align: left;
|
|
240
270
|
}
|
|
241
271
|
|
|
242
|
-
|
|
243
|
-
border-
|
|
272
|
+
tbody {
|
|
273
|
+
border-bottom: 20px solid transparent;
|
|
244
274
|
}
|
|
245
275
|
}
|
|
246
276
|
|
|
247
|
-
.
|
|
248
|
-
|
|
249
|
-
align-items: flex-start;
|
|
250
|
-
margin-top: 64px;
|
|
251
|
-
|
|
252
|
-
&.empty {
|
|
253
|
-
margin-top: 80px;
|
|
254
|
-
}
|
|
277
|
+
.empty {
|
|
278
|
+
margin-top: 80px;
|
|
255
279
|
}
|
|
256
280
|
|
|
257
281
|
|
|
258
282
|
.PayloadViewer {
|
|
259
|
-
|
|
260
|
-
top:
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
h2 {
|
|
266
|
-
padding-top: 20px;
|
|
267
|
-
}
|
|
268
|
-
|
|
283
|
+
/*h2 {*/
|
|
284
|
+
/* top: 0;*/
|
|
285
|
+
/* position: sticky;*/
|
|
286
|
+
/* background: var(--colorBackground);*/
|
|
287
|
+
/*}*/
|
|
269
288
|
pre {
|
|
270
|
-
overflow: auto;
|
|
271
|
-
max-height: calc(100vh - 72px);
|
|
272
289
|
padding-top: 12px;
|
|
273
290
|
|
|
274
291
|
code {
|
|
@@ -285,20 +302,16 @@ table {
|
|
|
285
302
|
|
|
286
303
|
.PreviewLink {
|
|
287
304
|
position: relative;
|
|
288
|
-
left: -
|
|
305
|
+
left: -8px;
|
|
289
306
|
display: inline-block;
|
|
290
|
-
width:
|
|
291
|
-
padding: 8px
|
|
307
|
+
width: 240px;
|
|
308
|
+
padding: 8px;
|
|
309
|
+
margin-left: 4px;
|
|
292
310
|
border-radius: var(--radius);
|
|
293
311
|
color: var(--colorAccent);
|
|
294
312
|
text-decoration: none;
|
|
295
313
|
word-break: break-word;
|
|
296
314
|
|
|
297
|
-
span {
|
|
298
|
-
opacity: 0.6;
|
|
299
|
-
filter: saturate(0);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
315
|
&:hover {
|
|
303
316
|
background: var(--colorHover);
|
|
304
317
|
}
|
|
@@ -309,11 +322,10 @@ table {
|
|
|
309
322
|
}
|
|
310
323
|
|
|
311
324
|
.MockSelector {
|
|
312
|
-
width:
|
|
313
|
-
height:
|
|
325
|
+
width: 100%;
|
|
326
|
+
height: 26px;
|
|
314
327
|
padding-right: 5px;
|
|
315
328
|
padding-left: 16px;
|
|
316
|
-
border: 0;
|
|
317
329
|
text-align: right;
|
|
318
330
|
direction: rtl;
|
|
319
331
|
text-overflow: ellipsis;
|
|
@@ -329,6 +341,7 @@ table {
|
|
|
329
341
|
}
|
|
330
342
|
&:disabled {
|
|
331
343
|
padding-right: 4px;
|
|
344
|
+
border-color: transparent;
|
|
332
345
|
appearance: none;
|
|
333
346
|
background: transparent;
|
|
334
347
|
cursor: default;
|
|
@@ -363,33 +376,32 @@ table {
|
|
|
363
376
|
margin-left: 8px;
|
|
364
377
|
> input {
|
|
365
378
|
&:checked ~ svg {
|
|
379
|
+
border-color: var(--colorAccent);
|
|
366
380
|
fill: var(--colorAccent);
|
|
367
381
|
background: var(--colorAccent);
|
|
368
382
|
stroke: var(--colorBackground);
|
|
369
383
|
}
|
|
370
384
|
|
|
371
385
|
&:enabled:hover:not(:checked) ~ svg {
|
|
372
|
-
border-color: var(--
|
|
386
|
+
border-color: var(--colorHover);
|
|
373
387
|
background: var(--colorHover);
|
|
374
388
|
stroke: var(--colorText);
|
|
375
389
|
}
|
|
376
390
|
}
|
|
377
391
|
|
|
378
392
|
> svg {
|
|
379
|
-
width:
|
|
380
|
-
height:
|
|
393
|
+
width: 22px;
|
|
394
|
+
height: 22px;
|
|
395
|
+
border: 1px solid var(--colorSecondaryActionBorder);
|
|
381
396
|
stroke-width: 2.5px;
|
|
382
397
|
border-radius: 50%;
|
|
383
|
-
background: var(--colorSecondaryButtonBackground);
|
|
384
|
-
box-shadow: var(--boxShadow1);
|
|
385
398
|
}
|
|
386
399
|
}
|
|
387
400
|
|
|
388
401
|
.ProxyToggler {
|
|
389
402
|
padding: 1px 3px;
|
|
390
|
-
|
|
391
|
-
border-radius: var(--
|
|
392
|
-
box-shadow: var(--boxShadow1);
|
|
403
|
+
border: 1px solid var(--colorSecondaryActionBorder);
|
|
404
|
+
border-radius: var(--radius);
|
|
393
405
|
|
|
394
406
|
&:has(input:checked),
|
|
395
407
|
&:has(input:disabled) {
|
|
@@ -414,7 +426,7 @@ table {
|
|
|
414
426
|
}
|
|
415
427
|
|
|
416
428
|
&:disabled ~ svg {
|
|
417
|
-
stroke-opacity: 0.
|
|
429
|
+
stroke-opacity: 0.4;
|
|
418
430
|
cursor: not-allowed;
|
|
419
431
|
box-shadow: none;
|
|
420
432
|
fill: transparent;
|
|
@@ -430,7 +442,7 @@ table {
|
|
|
430
442
|
width: 18px;
|
|
431
443
|
height: 18px;
|
|
432
444
|
stroke-width: 2px;
|
|
433
|
-
border-radius: var(--
|
|
445
|
+
border-radius: var(--radius);
|
|
434
446
|
}
|
|
435
447
|
}
|
|
436
448
|
|
|
@@ -452,6 +464,7 @@ table {
|
|
|
452
464
|
}
|
|
453
465
|
|
|
454
466
|
&:checked ~ span {
|
|
467
|
+
border-color: var(--colorRed);
|
|
455
468
|
color: white;
|
|
456
469
|
background: var(--colorRed);
|
|
457
470
|
}
|
|
@@ -459,14 +472,14 @@ table {
|
|
|
459
472
|
|
|
460
473
|
> span {
|
|
461
474
|
padding: 4px;
|
|
475
|
+
border: 1px solid var(--colorSecondaryActionBorder);
|
|
462
476
|
font-size: 10px;
|
|
463
477
|
font-weight: bold;
|
|
464
478
|
color: var(--colorSecondaryAction);
|
|
465
|
-
border-radius: var(--
|
|
466
|
-
background: var(--colorSecondaryButtonBackground);
|
|
467
|
-
box-shadow: var(--boxShadow1);
|
|
479
|
+
border-radius: var(--radius);
|
|
468
480
|
|
|
469
481
|
&:hover {
|
|
482
|
+
border-color: var(--colorLightRed);
|
|
470
483
|
background: var(--colorLightRed);
|
|
471
484
|
color: var(--colorRed);
|
|
472
485
|
}
|
|
@@ -501,7 +514,6 @@ table {
|
|
|
501
514
|
|
|
502
515
|
|
|
503
516
|
.StaticFilesList {
|
|
504
|
-
margin-top: 8px;
|
|
505
517
|
|
|
506
518
|
a {
|
|
507
519
|
display: inline-block;
|
|
@@ -513,14 +525,14 @@ table {
|
|
|
513
525
|
&:hover {
|
|
514
526
|
text-decoration: underline;
|
|
515
527
|
}
|
|
516
|
-
|
|
517
|
-
span {
|
|
518
|
-
opacity: 0.6;
|
|
519
|
-
filter: saturate(0);
|
|
520
|
-
}
|
|
521
528
|
}
|
|
522
529
|
}
|
|
523
530
|
|
|
531
|
+
.dittoDir {
|
|
532
|
+
opacity: 0.9;
|
|
533
|
+
filter: saturate(0);
|
|
534
|
+
}
|
|
535
|
+
|
|
524
536
|
|
|
525
537
|
/*
|
|
526
538
|
* Prism
|
package/src/Dashboard.js
CHANGED
|
@@ -24,15 +24,16 @@ const Strings = {
|
|
|
24
24
|
fallback_server_placeholder: 'Type Server Address',
|
|
25
25
|
got: 'Got',
|
|
26
26
|
internal_server_error: 'Internal Server Error',
|
|
27
|
-
mock: 'Mock',
|
|
28
27
|
no_mocks_found: 'No mocks found',
|
|
29
28
|
not_found: 'Not Found',
|
|
30
29
|
pick_comment: 'Pick Comment…',
|
|
30
|
+
preview: 'Preview',
|
|
31
31
|
proxied: 'Proxied',
|
|
32
32
|
proxy_toggler: 'Proxy Toggler',
|
|
33
33
|
reset: 'Reset',
|
|
34
34
|
save_proxied: 'Save Mocks',
|
|
35
|
-
static_get: 'Static GET'
|
|
35
|
+
static_get: 'Static GET',
|
|
36
|
+
title: 'Mockaton'
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
const CSS = {
|
|
@@ -41,9 +42,9 @@ const CSS = {
|
|
|
41
42
|
FallbackBackend: 'FallbackBackend',
|
|
42
43
|
Field: 'Field',
|
|
43
44
|
GlobalDelayField: 'GlobalDelayField',
|
|
44
|
-
Header: 'Header',
|
|
45
45
|
InternalServerErrorToggler: 'InternalServerErrorToggler',
|
|
46
|
-
|
|
46
|
+
MainLeftSide: 'leftSide',
|
|
47
|
+
MainRightSide: 'rightSide',
|
|
47
48
|
MockList: 'MockList',
|
|
48
49
|
MockSelector: 'MockSelector',
|
|
49
50
|
NotFoundToggler: 'NotFoundToggler',
|
|
@@ -57,21 +58,20 @@ const CSS = {
|
|
|
57
58
|
SpinnerClockMinuteHand: 'MinuteHand',
|
|
58
59
|
StaticFilesList: 'StaticFilesList',
|
|
59
60
|
|
|
60
|
-
red: 'red',
|
|
61
|
-
empty: 'empty',
|
|
62
61
|
chosen: 'chosen',
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
dittoDir: 'dittoDir',
|
|
63
|
+
empty: 'empty',
|
|
64
|
+
nonDefault: 'nonDefault',
|
|
65
|
+
red: 'red',
|
|
66
|
+
status4xx: 'status4xx'
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
const r = createElement
|
|
68
70
|
const s = createSvgElement
|
|
69
|
-
const mockaton = new Commander(window.location.origin)
|
|
70
71
|
|
|
71
|
-
const
|
|
72
|
+
const mockaton = new Commander(window.location.origin)
|
|
72
73
|
let globalDelay = 1200
|
|
73
74
|
|
|
74
|
-
|
|
75
75
|
init()
|
|
76
76
|
initLongPoll()
|
|
77
77
|
|
|
@@ -85,20 +85,20 @@ function init() {
|
|
|
85
85
|
mockaton.getProxyFallback(),
|
|
86
86
|
mockaton.listStaticFiles()
|
|
87
87
|
].map(api => api.then(response => response.ok && response.json())))
|
|
88
|
-
.then(data => document.body.replaceChildren(App(data)))
|
|
88
|
+
.then(data => document.body.replaceChildren(...App(data)))
|
|
89
89
|
.catch(onError)
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
function App([brokersByMethod, cookies, comments, delay, collectProxied, fallbackAddress,
|
|
92
|
+
function App([brokersByMethod, cookies, comments, delay, collectProxied, fallbackAddress, staticBrokers]) {
|
|
93
93
|
globalDelay = delay
|
|
94
|
-
return
|
|
95
|
-
r(
|
|
96
|
-
|
|
97
|
-
r('
|
|
98
|
-
r(
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
r(PayloadViewer)))
|
|
94
|
+
return [
|
|
95
|
+
r(Header, { cookies, comments, delay, fallbackAddress, collectProxied }),
|
|
96
|
+
r('main', null,
|
|
97
|
+
r('div', { className: CSS.MainLeftSide },
|
|
98
|
+
r(MockList, { brokersByMethod, canProxy: Boolean(fallbackAddress) }),
|
|
99
|
+
r(StaticFilesList, { brokers: staticBrokers })),
|
|
100
|
+
r('div', { className: CSS.MainRightSide },
|
|
101
|
+
r(PayloadViewer)))]
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
|
|
@@ -106,13 +106,14 @@ function App([brokersByMethod, cookies, comments, delay, collectProxied, fallbac
|
|
|
106
106
|
|
|
107
107
|
function Header({ cookies, comments, delay, fallbackAddress, collectProxied }) {
|
|
108
108
|
return (
|
|
109
|
-
r('
|
|
109
|
+
r('header', null,
|
|
110
110
|
r(Logo),
|
|
111
|
-
r(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
r('div', null,
|
|
112
|
+
r(CookieSelector, { cookies }),
|
|
113
|
+
r(BulkSelector, { comments }),
|
|
114
|
+
r(GlobalDelayField, { delay }),
|
|
115
|
+
r(ProxyFallbackField, { fallbackAddress, collectProxied }),
|
|
116
|
+
r(ResetButton))))
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
function Logo() {
|
|
@@ -251,10 +252,10 @@ function MockList({ brokersByMethod, canProxy }) {
|
|
|
251
252
|
const hasMocks = Object.keys(brokersByMethod).length
|
|
252
253
|
if (!hasMocks)
|
|
253
254
|
return (
|
|
254
|
-
r('div', { className:
|
|
255
|
+
r('div', { className: CSS.empty },
|
|
255
256
|
Strings.no_mocks_found))
|
|
256
257
|
return (
|
|
257
|
-
r('div',
|
|
258
|
+
r('div', null,
|
|
258
259
|
r('table', null, Object.entries(brokersByMethod).map(([method, brokers]) =>
|
|
259
260
|
r(SectionByMethod, { method, brokers, canProxy })))))
|
|
260
261
|
}
|
|
@@ -298,10 +299,11 @@ function PreviewLink({ method, urlMask, urlMaskDittoed }) {
|
|
|
298
299
|
href: urlMask,
|
|
299
300
|
onClick
|
|
300
301
|
}, ditto
|
|
301
|
-
? [r('span',
|
|
302
|
+
? [r('span', { className: CSS.dittoDir }, ditto), tail]
|
|
302
303
|
: tail))
|
|
303
304
|
}
|
|
304
305
|
|
|
306
|
+
/** @param {{ broker: MockBroker }} props */
|
|
305
307
|
function MockSelector({ broker }) {
|
|
306
308
|
function onChange() {
|
|
307
309
|
const { urlMask, method } = parseFilename(this.value)
|
|
@@ -338,6 +340,7 @@ function MockSelector({ broker }) {
|
|
|
338
340
|
}, file))))
|
|
339
341
|
}
|
|
340
342
|
|
|
343
|
+
/** @param {{ broker: MockBroker }} props */
|
|
341
344
|
function DelayRouteToggler({ broker }) {
|
|
342
345
|
function onChange() {
|
|
343
346
|
const { method, urlMask } = parseFilename(broker.mocks[0])
|
|
@@ -356,6 +359,7 @@ function DelayRouteToggler({ broker }) {
|
|
|
356
359
|
TimerIcon()))
|
|
357
360
|
}
|
|
358
361
|
|
|
362
|
+
/** @param {{ broker: MockBroker }} props */
|
|
359
363
|
function InternalServerErrorToggler({ broker }) {
|
|
360
364
|
function onChange() {
|
|
361
365
|
const { urlMask, method } = parseFilename(broker.mocks[0])
|
|
@@ -381,6 +385,7 @@ function InternalServerErrorToggler({ broker }) {
|
|
|
381
385
|
r('span', null, '500')))
|
|
382
386
|
}
|
|
383
387
|
|
|
388
|
+
/** @param {{ broker: MockBroker, disabled: boolean }} props */
|
|
384
389
|
function ProxyToggler({ broker, disabled }) {
|
|
385
390
|
function onChange() {
|
|
386
391
|
const { urlMask, method } = parseFilename(broker.mocks[0])
|
|
@@ -404,32 +409,34 @@ function ProxyToggler({ broker, disabled }) {
|
|
|
404
409
|
}
|
|
405
410
|
|
|
406
411
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
+
/**
|
|
413
|
+
* # StaticFilesList
|
|
414
|
+
* @param {{ brokers: StaticBroker[] }} props
|
|
415
|
+
*/
|
|
416
|
+
function StaticFilesList({ brokers }) {
|
|
417
|
+
if (!Object.keys(brokers).length)
|
|
412
418
|
return null
|
|
413
|
-
const
|
|
414
|
-
? [r('span',
|
|
419
|
+
const dp = dittoSplitPaths(Object.keys(brokers)).map(([ditto, tail]) => ditto
|
|
420
|
+
? [r('span', { className: CSS.dittoDir }, ditto), tail]
|
|
415
421
|
: tail)
|
|
416
422
|
return (
|
|
417
423
|
r('table', { className: CSS.StaticFilesList },
|
|
418
424
|
r('tbody', null,
|
|
419
425
|
r('th', { colspan: 4 }, Strings.static_get),
|
|
420
|
-
Object.values(
|
|
426
|
+
Object.values(brokers).map((broker, i) =>
|
|
421
427
|
r('tr', null,
|
|
422
428
|
r('td', null, r(ProxyStaticToggler, {})),
|
|
423
429
|
r('td', null, r(DelayStaticRouteToggler, { broker })),
|
|
424
430
|
r('td', null, r(NotFoundToggler, { broker })),
|
|
425
|
-
r('td', null, r('a', { href: broker.
|
|
431
|
+
r('td', null, r('a', { href: broker.route, target: '_blank' }, dp[i]))
|
|
426
432
|
)))))
|
|
427
433
|
}
|
|
428
434
|
|
|
429
435
|
|
|
436
|
+
/** @param {{ broker: StaticBroker }} props */
|
|
430
437
|
function DelayStaticRouteToggler({ broker }) {
|
|
431
438
|
function onChange() {
|
|
432
|
-
mockaton.setStaticRouteIsDelayed(broker.
|
|
439
|
+
mockaton.setStaticRouteIsDelayed(broker.route, this.checked)
|
|
433
440
|
.catch(onError)
|
|
434
441
|
}
|
|
435
442
|
return (
|
|
@@ -445,9 +452,10 @@ function DelayStaticRouteToggler({ broker }) {
|
|
|
445
452
|
TimerIcon()))
|
|
446
453
|
}
|
|
447
454
|
|
|
455
|
+
/** @param {{ broker: StaticBroker }} props */
|
|
448
456
|
function NotFoundToggler({ broker }) {
|
|
449
457
|
function onChange() {
|
|
450
|
-
mockaton.
|
|
458
|
+
mockaton.setStaticRouteStatus(broker.route, this.checked ? 404 : 200)
|
|
451
459
|
.catch(onError)
|
|
452
460
|
}
|
|
453
461
|
return (
|
|
@@ -457,15 +465,13 @@ function NotFoundToggler({ broker }) {
|
|
|
457
465
|
},
|
|
458
466
|
r('input', {
|
|
459
467
|
type: 'checkbox',
|
|
460
|
-
checked: broker.
|
|
468
|
+
checked: broker.status === 404,
|
|
461
469
|
onChange
|
|
462
470
|
}),
|
|
463
471
|
r('span', null, '404')))
|
|
464
472
|
}
|
|
465
473
|
|
|
466
|
-
|
|
467
|
-
// TODO
|
|
468
|
-
function ProxyStaticToggler({}) {
|
|
474
|
+
function ProxyStaticToggler({}) { // TODO
|
|
469
475
|
function onChange() {
|
|
470
476
|
}
|
|
471
477
|
return (
|
|
@@ -490,7 +496,7 @@ const payloadViewerRef = useRef()
|
|
|
490
496
|
function PayloadViewer() {
|
|
491
497
|
return (
|
|
492
498
|
r('div', { className: CSS.PayloadViewer },
|
|
493
|
-
r('h2', { ref: payloadViewerTitleRef }, Strings.
|
|
499
|
+
r('h2', { ref: payloadViewerTitleRef }, Strings.preview),
|
|
494
500
|
r('pre', null,
|
|
495
501
|
r('code', { ref: payloadViewerRef }, Strings.click_link_to_preview))))
|
|
496
502
|
}
|
|
@@ -523,7 +529,7 @@ function PayloadViewerTitleWhenProxied({ mime, status, statusText, gatewayIsBad
|
|
|
523
529
|
}
|
|
524
530
|
|
|
525
531
|
async function previewMock(method, urlMask, href) {
|
|
526
|
-
const timer = setTimeout(renderSpinner,
|
|
532
|
+
const timer = setTimeout(renderSpinner, 180)
|
|
527
533
|
const response = await fetch(href, { method })
|
|
528
534
|
clearTimeout(timer)
|
|
529
535
|
await updatePayloadViewer(method, urlMask, response)
|
package/src/MockBroker.js
CHANGED
|
@@ -83,11 +83,11 @@ export class MockBroker {
|
|
|
83
83
|
this.currentMock.file = filename
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
setDelayed(delayed) {
|
|
87
87
|
this.currentMock.delayed = delayed
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
setProxied(proxied) {
|
|
91
91
|
if (proxied)
|
|
92
92
|
this.selectFile('')
|
|
93
93
|
else
|
package/src/Mockaton.js
CHANGED
|
@@ -54,7 +54,7 @@ async function onRequest(req, response) {
|
|
|
54
54
|
else if (method === 'GET' && apiGetRequests.has(url))
|
|
55
55
|
apiGetRequests.get(url)(req, response)
|
|
56
56
|
|
|
57
|
-
else if (method === 'GET' && findStaticBrokerByRoute(
|
|
57
|
+
else if (method === 'GET' && findStaticBrokerByRoute(url))
|
|
58
58
|
await dispatchStatic(req, response)
|
|
59
59
|
|
|
60
60
|
else
|
package/src/StaticDispatcher.js
CHANGED
|
@@ -1,37 +1,24 @@
|
|
|
1
|
-
import { join, basename } from 'node:path'
|
|
2
1
|
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { join, basename } from 'node:path'
|
|
3
3
|
|
|
4
4
|
import { mimeFor } from './utils/mime.js'
|
|
5
|
+
import { listFilesRecursively } from './utils/fs.js'
|
|
5
6
|
import { config, isFileAllowed, calcDelay } from './config.js'
|
|
6
7
|
import { sendPartialContent, sendNotFound } from './utils/http-response.js'
|
|
7
|
-
import { isDirectory, isFile, listFilesRecursively } from './utils/fs.js'
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class StaticBroker {
|
|
11
|
-
constructor(
|
|
12
|
-
this.
|
|
11
|
+
constructor(route) {
|
|
12
|
+
this.route = route
|
|
13
13
|
this.delayed = false
|
|
14
|
-
this.
|
|
15
|
-
this.resolvedPath = this.#staticFilePath()
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
#staticFilePath() { // url is absolute e.g. /home/../.. => /
|
|
19
|
-
let candidate = join(config.staticDir, this.file)
|
|
20
|
-
if (isDirectory(candidate))
|
|
21
|
-
candidate = join(candidate, 'index.html')
|
|
22
|
-
if (isFile(candidate))
|
|
23
|
-
return candidate
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
updateDelayed(value) {
|
|
27
|
-
this.delayed = value
|
|
14
|
+
this.status = 200 // 200 or 404
|
|
28
15
|
}
|
|
29
16
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
17
|
+
setDelayed(value) { this.delayed = value }
|
|
18
|
+
setStatus(value) { this.status = value }
|
|
33
19
|
}
|
|
34
20
|
|
|
21
|
+
/** @type {{ [route: string]: StaticBroker }} */
|
|
35
22
|
let collection = {}
|
|
36
23
|
|
|
37
24
|
export function initStaticCollection() {
|
|
@@ -41,23 +28,27 @@ export function initStaticCollection() {
|
|
|
41
28
|
.forEach(registerStaticMock)
|
|
42
29
|
}
|
|
43
30
|
|
|
31
|
+
|
|
44
32
|
/** @returns {boolean} registered */
|
|
45
|
-
export function registerStaticMock(
|
|
46
|
-
if (!isFileAllowed(basename(
|
|
33
|
+
export function registerStaticMock(relativeFile) {
|
|
34
|
+
if (!isFileAllowed(basename(relativeFile)))
|
|
47
35
|
return false
|
|
48
36
|
|
|
49
|
-
|
|
50
|
-
if (findStaticBrokerByRoute(
|
|
37
|
+
const route = '/' + relativeFile
|
|
38
|
+
if (findStaticBrokerByRoute(route))
|
|
51
39
|
return false
|
|
52
40
|
|
|
53
|
-
collection[
|
|
41
|
+
collection[route] = new StaticBroker(route)
|
|
54
42
|
return true
|
|
55
43
|
}
|
|
56
44
|
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
|
|
46
|
+
export function unregisterStaticMock(relativeFile) {
|
|
47
|
+
delete collection['/' + relativeFile]
|
|
59
48
|
}
|
|
60
49
|
|
|
50
|
+
|
|
51
|
+
/** @returns {StaticBroker | undefined} */
|
|
61
52
|
export function findStaticBrokerByRoute(route) {
|
|
62
53
|
return collection[route] || collection[join(route, 'index.html')]
|
|
63
54
|
}
|
|
@@ -66,15 +57,16 @@ export function getStaticFilesCollection() {
|
|
|
66
57
|
return collection
|
|
67
58
|
}
|
|
68
59
|
|
|
60
|
+
|
|
69
61
|
export async function dispatchStatic(req, response) {
|
|
70
62
|
const broker = findStaticBrokerByRoute(req.url)
|
|
71
63
|
|
|
72
64
|
setTimeout(async () => {
|
|
73
|
-
if (!broker || broker.
|
|
65
|
+
if (!broker || broker.status === 404) { // TESTME
|
|
74
66
|
sendNotFound(response)
|
|
75
67
|
return
|
|
76
68
|
}
|
|
77
|
-
const file = broker.
|
|
69
|
+
const file = join(config.staticDir, broker.route)
|
|
78
70
|
if (req.headers.range)
|
|
79
71
|
await sendPartialContent(response, req.headers.range, file)
|
|
80
72
|
else {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { basename } from 'node:path'
|
|
2
|
+
|
|
1
3
|
import { cookie } from './cookie.js'
|
|
2
4
|
import { MockBroker } from './MockBroker.js'
|
|
3
5
|
import { listFilesRecursively } from './utils/fs.js'
|
|
@@ -38,7 +40,7 @@ export function init() {
|
|
|
38
40
|
/** @returns {boolean} registered */
|
|
39
41
|
export function registerMock(file, isFromWatcher = false) {
|
|
40
42
|
if (findBrokerByFilename(file)?.hasMock(file)
|
|
41
|
-
|| !isFileAllowed(file)
|
|
43
|
+
|| !isFileAllowed(basename(file)) // TESTME
|
|
42
44
|
|| !filenameIsValid(file))
|
|
43
45
|
return false
|
|
44
46
|
|