mockaton 8.7.0 → 8.7.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/README.md +22 -18
- package/package.json +1 -1
- package/src/Dashboard.css +11 -2
- package/src/Dashboard.js +148 -108
package/README.md
CHANGED
|
@@ -5,27 +5,27 @@
|
|
|
5
5
|
|
|
6
6
|
## Mock your APIs, Enhance your Development Workflow
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
Mockaton is an HTTP mock server with the goal of making
|
|
9
|
+
your frontend development and testing easier—and a lot more fun.
|
|
10
10
|
|
|
11
|
-
With Mockaton you don’t need to write code for wiring your mocks.
|
|
12
|
-
place your mocks in a directory and
|
|
13
|
-
|
|
11
|
+
With Mockaton you don’t need to write code for wiring your mocks.
|
|
12
|
+
Instead, just place your mocks in a directory, and it will scan it
|
|
13
|
+
for filenames that follow a convention similar to the URL paths.
|
|
14
14
|
|
|
15
15
|
For example, for this route `/api/user/1234`, the mock filename would be:
|
|
16
16
|
```
|
|
17
17
|
my-mocks-dir/api/user/[user-id].GET.200.json
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
## Scrapping Mocks from you Backend
|
|
21
|
+
|
|
22
|
+
Mockaton can fallback to your real backend on routes you don’t have mocks for. That’s
|
|
23
|
+
done by typing your backend address in the **Fallback Backend** field. And if you
|
|
24
|
+
check **Save Mocks**, you can collect those responses that hit your backend.
|
|
25
|
+
Those mocks will be saved to your `config.mocksDir` following the filename convention.
|
|
25
26
|
|
|
26
27
|
|
|
27
28
|
## Multiple Mock Variants
|
|
28
|
-
Here’s how you can create multiple mocks for a particular route:
|
|
29
29
|
|
|
30
30
|
### Adding comments in filenames
|
|
31
31
|
Want to mock a locked-out user or an invalid login attempt? You
|
|
@@ -214,6 +214,9 @@ Response Status Code, and File Extension.
|
|
|
214
214
|
api/user.GET.200.json
|
|
215
215
|
```
|
|
216
216
|
|
|
217
|
+
You can also use `.empty` if you don’t want the response to have a
|
|
218
|
+
`Content-Type` header.
|
|
219
|
+
|
|
217
220
|
|
|
218
221
|
### Dynamic Parameters
|
|
219
222
|
Anything within square brackets is always matched. For example, for this route
|
|
@@ -269,7 +272,7 @@ api/foo/bar.GET.200.json
|
|
|
269
272
|
---
|
|
270
273
|
## Config
|
|
271
274
|
### `mocksDir: string`
|
|
272
|
-
This is the only required field
|
|
275
|
+
This is the only required field. The directory must exist.
|
|
273
276
|
|
|
274
277
|
|
|
275
278
|
### `host?: string`
|
|
@@ -285,10 +288,10 @@ Defaults to `/(\.DS_Store|~)$/`
|
|
|
285
288
|
|
|
286
289
|
|
|
287
290
|
### `delay?: number`
|
|
288
|
-
Defaults to `
|
|
291
|
+
Defaults to `1200` milliseconds.
|
|
289
292
|
|
|
290
293
|
Although routes can individually be delayed with the 🕓
|
|
291
|
-
checkbox, delay
|
|
294
|
+
checkbox, the delay amount is globally configurable.
|
|
292
295
|
|
|
293
296
|
|
|
294
297
|
### `proxyFallback?: string`
|
|
@@ -298,7 +301,7 @@ For example, `config.proxyFallback = 'http://example.com'`
|
|
|
298
301
|
### `collectProxied?: boolean`
|
|
299
302
|
Defaults to `false`. With this flag you can save mocks that hit
|
|
300
303
|
your proxy fallback to `config.mocksDir`. If there are UUIDv4 in the
|
|
301
|
-
URL the filename will have `[id]` in their place. For example,
|
|
304
|
+
URL, the filename will have `[id]` in their place. For example,
|
|
302
305
|
|
|
303
306
|
```
|
|
304
307
|
/api/user/d14e09c8-d970-4b07-be42-b2f4ee22f0a6/likes =>
|
|
@@ -377,7 +380,7 @@ config.extraMimes = {
|
|
|
377
380
|
jpe: 'application/jpeg'
|
|
378
381
|
}
|
|
379
382
|
```
|
|
380
|
-
|
|
383
|
+
Those extra media types take precedence over the built-in
|
|
381
384
|
[utils/mime.js](src/utils/mime.js), so you can override them.
|
|
382
385
|
|
|
383
386
|
|
|
@@ -392,8 +395,9 @@ type Plugin = (
|
|
|
392
395
|
body: string | Uint8Array
|
|
393
396
|
}>
|
|
394
397
|
```
|
|
395
|
-
Plugins are for processing mocks before sending them. If no
|
|
396
|
-
|
|
398
|
+
Plugins are for processing mocks before sending them. If no
|
|
399
|
+
regex matches the filename, the fallback plugin will read
|
|
400
|
+
the file from disk and compute the MIME from the extension.
|
|
397
401
|
|
|
398
402
|
Note: don’t call `response.end()` on any plugin.
|
|
399
403
|
|
package/package.json
CHANGED
package/src/Dashboard.css
CHANGED
|
@@ -84,7 +84,7 @@ select {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
.Header {
|
|
88
88
|
position: fixed;
|
|
89
89
|
z-index: 100;
|
|
90
90
|
top: 0;
|
|
@@ -153,6 +153,11 @@ menu {
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
+
.BulkSelector {
|
|
157
|
+
appearance: none;
|
|
158
|
+
text-align: center;
|
|
159
|
+
}
|
|
160
|
+
|
|
156
161
|
.ResetButton {
|
|
157
162
|
padding: 4px 12px;
|
|
158
163
|
border: 1px solid var(--colorRed);
|
|
@@ -171,7 +176,7 @@ menu {
|
|
|
171
176
|
}
|
|
172
177
|
|
|
173
178
|
|
|
174
|
-
|
|
179
|
+
.MockList {
|
|
175
180
|
display: flex;
|
|
176
181
|
align-items: flex-start;
|
|
177
182
|
margin-top: 64px;
|
|
@@ -189,6 +194,10 @@ main {
|
|
|
189
194
|
border-top: 1px solid transparent;
|
|
190
195
|
}
|
|
191
196
|
}
|
|
197
|
+
|
|
198
|
+
&.empty {
|
|
199
|
+
margin-top: 80px;
|
|
200
|
+
}
|
|
192
201
|
}
|
|
193
202
|
|
|
194
203
|
|
package/src/Dashboard.js
CHANGED
|
@@ -16,17 +16,20 @@ const Strings = {
|
|
|
16
16
|
internal_server_error: 'Internal Server Error',
|
|
17
17
|
mock: 'Mock',
|
|
18
18
|
no_mocks_found: 'No mocks found',
|
|
19
|
+
pick: 'Pick…',
|
|
19
20
|
reset: 'Reset',
|
|
20
21
|
save_proxied: 'Save Mocks',
|
|
21
|
-
select_one: 'Select One',
|
|
22
22
|
static: 'Static'
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
const CSS = {
|
|
26
|
+
BulkSelector: 'BulkSelector',
|
|
26
27
|
DelayToggler: 'DelayToggler',
|
|
27
28
|
FallbackBackend: 'FallbackBackend',
|
|
28
29
|
Field: 'Field',
|
|
30
|
+
Header: 'Header',
|
|
29
31
|
InternalServerErrorToggler: 'InternalServerErrorToggler',
|
|
32
|
+
MockList: 'MockList',
|
|
30
33
|
MockSelector: 'MockSelector',
|
|
31
34
|
PayloadViewer: 'PayloadViewer',
|
|
32
35
|
PreviewLink: 'PreviewLink',
|
|
@@ -36,6 +39,7 @@ const CSS = {
|
|
|
36
39
|
StaticFilesList: 'StaticFilesList',
|
|
37
40
|
|
|
38
41
|
bold: 'bold',
|
|
42
|
+
empty: 'empty',
|
|
39
43
|
chosen: 'chosen',
|
|
40
44
|
status4xx: 'status4xx'
|
|
41
45
|
}
|
|
@@ -55,45 +59,50 @@ function init() {
|
|
|
55
59
|
mockaton.getProxyFallback(),
|
|
56
60
|
mockaton.listStaticFiles()
|
|
57
61
|
].map(api => api.then(response => response.ok && response.json())))
|
|
58
|
-
.then(
|
|
62
|
+
.then(data => {
|
|
63
|
+
empty(document.body)
|
|
64
|
+
document.body.append(...App(data))
|
|
65
|
+
})
|
|
59
66
|
.catch(onError)
|
|
60
67
|
}
|
|
61
68
|
init()
|
|
62
69
|
|
|
63
|
-
function App(
|
|
64
|
-
|
|
65
|
-
|
|
70
|
+
function App([brokersByMethod, cookies, comments, collectProxied, fallbackAddress, staticFiles]) {
|
|
71
|
+
return [
|
|
72
|
+
r(Header, { cookies, comments, fallbackAddress, collectProxied }),
|
|
73
|
+
r(MockList, { brokersByMethod }),
|
|
74
|
+
r(StaticFilesList, { staticFiles })
|
|
75
|
+
]
|
|
66
76
|
}
|
|
67
77
|
|
|
68
|
-
|
|
69
|
-
|
|
78
|
+
|
|
79
|
+
// Header ===============
|
|
80
|
+
|
|
81
|
+
function Header({ cookies, comments, fallbackAddress, collectProxied }) {
|
|
82
|
+
return (
|
|
83
|
+
r('menu', { className: CSS.Header },
|
|
84
|
+
r(Logo),
|
|
85
|
+
r(CookieSelector, { cookies }),
|
|
86
|
+
r(BulkSelector, { comments }),
|
|
87
|
+
r(ProxyFallbackField, { fallbackAddress, collectProxied }),
|
|
88
|
+
r(ResetButton)))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function Logo() {
|
|
70
92
|
return (
|
|
71
|
-
r('
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
? r('main', null, Strings.no_mocks_found)
|
|
80
|
-
: r('main', null,
|
|
81
|
-
r('table', null, Object.entries(brokersByMethod).map(([method, brokers]) =>
|
|
82
|
-
r(SectionByMethod, { method, brokers }))),
|
|
83
|
-
r('div', { className: CSS.PayloadViewer },
|
|
84
|
-
r('h2', { ref: refPayloadViewerFileTitle }, Strings.mock),
|
|
85
|
-
r('pre', null,
|
|
86
|
-
r('code', { ref: refPayloadViewer }, Strings.click_link_to_preview)))),
|
|
87
|
-
r(StaticFilesList, { staticFiles })))
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
function CookieSelector({ list }) {
|
|
93
|
+
r('img', {
|
|
94
|
+
alt: Strings.title,
|
|
95
|
+
src: '/mockaton-logo.svg',
|
|
96
|
+
width: 160
|
|
97
|
+
}))
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function CookieSelector({ cookies }) {
|
|
92
101
|
function onChange() {
|
|
93
102
|
mockaton.selectCookie(this.value)
|
|
94
103
|
.catch(onError)
|
|
95
104
|
}
|
|
96
|
-
const disabled =
|
|
105
|
+
const disabled = cookies.length <= 1
|
|
97
106
|
return (
|
|
98
107
|
r('label', { className: CSS.Field },
|
|
99
108
|
r('span', null, Strings.cookie),
|
|
@@ -102,25 +111,29 @@ function CookieSelector({ list }) {
|
|
|
102
111
|
disabled,
|
|
103
112
|
title: disabled ? Strings.cookie_disabled_title : '',
|
|
104
113
|
onChange
|
|
105
|
-
},
|
|
114
|
+
}, cookies.map(([value, selected]) =>
|
|
106
115
|
r('option', { value, selected }, value)))))
|
|
107
116
|
}
|
|
108
117
|
|
|
109
|
-
|
|
110
118
|
function BulkSelector({ comments }) {
|
|
119
|
+
// UX wise this should be a menu instead of this `select`.
|
|
120
|
+
// But this way is easier to implement, with a few hacks.
|
|
111
121
|
function onChange() {
|
|
112
|
-
|
|
122
|
+
const value = this.value
|
|
123
|
+
this.value = Strings.pick // Hack
|
|
124
|
+
mockaton.bulkSelectByComment(value)
|
|
113
125
|
.then(init)
|
|
114
126
|
.catch(onError)
|
|
115
127
|
}
|
|
116
128
|
const disabled = !comments.length
|
|
117
129
|
const list = disabled
|
|
118
130
|
? []
|
|
119
|
-
: [Strings.
|
|
131
|
+
: [Strings.pick].concat(comments)
|
|
120
132
|
return (
|
|
121
133
|
r('label', { className: CSS.Field },
|
|
122
134
|
r('span', null, Strings.bulk_select_by_comment),
|
|
123
135
|
r('select', {
|
|
136
|
+
className: CSS.BulkSelector,
|
|
124
137
|
'data-qaid': 'BulkSelector',
|
|
125
138
|
autocomplete: 'off',
|
|
126
139
|
disabled,
|
|
@@ -130,7 +143,6 @@ function BulkSelector({ comments }) {
|
|
|
130
143
|
r('option', { value }, value)))))
|
|
131
144
|
}
|
|
132
145
|
|
|
133
|
-
|
|
134
146
|
function ProxyFallbackField({ fallbackAddress = '', collectProxied }) {
|
|
135
147
|
const refSaveProxiedCheckbox = useRef()
|
|
136
148
|
function onChange(event) {
|
|
@@ -139,8 +151,7 @@ function ProxyFallbackField({ fallbackAddress = '', collectProxied }) {
|
|
|
139
151
|
if (!input.validity.valid)
|
|
140
152
|
input.reportValidity()
|
|
141
153
|
else
|
|
142
|
-
mockaton.setProxyFallback(input.value.trim())
|
|
143
|
-
.catch(onError)
|
|
154
|
+
mockaton.setProxyFallback(input.value.trim()).catch(onError)
|
|
144
155
|
}
|
|
145
156
|
return (
|
|
146
157
|
r('div', { className: cssClass(CSS.Field, CSS.FallbackBackend) },
|
|
@@ -160,7 +171,6 @@ function ProxyFallbackField({ fallbackAddress = '', collectProxied }) {
|
|
|
160
171
|
})))
|
|
161
172
|
}
|
|
162
173
|
|
|
163
|
-
|
|
164
174
|
function SaveProxiedCheckbox({ ref, disabled, collectProxied }) {
|
|
165
175
|
function onChange(event) {
|
|
166
176
|
mockaton.setCollectProxied(event.currentTarget.checked)
|
|
@@ -178,7 +188,6 @@ function SaveProxiedCheckbox({ ref, disabled, collectProxied }) {
|
|
|
178
188
|
r('span', null, Strings.save_proxied)))
|
|
179
189
|
}
|
|
180
190
|
|
|
181
|
-
|
|
182
191
|
function ResetButton() {
|
|
183
192
|
return (
|
|
184
193
|
r('button', {
|
|
@@ -192,18 +201,20 @@ function ResetButton() {
|
|
|
192
201
|
}
|
|
193
202
|
|
|
194
203
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
204
|
+
|
|
205
|
+
// MockList ===============
|
|
206
|
+
|
|
207
|
+
function MockList({ brokersByMethod }) {
|
|
208
|
+
const hasMocks = Object.keys(brokersByMethod).length
|
|
209
|
+
if (!hasMocks)
|
|
210
|
+
return (
|
|
211
|
+
r('main', { className: cssClass(CSS.MockList, CSS.empty) },
|
|
212
|
+
Strings.no_mocks_found))
|
|
198
213
|
return (
|
|
199
|
-
r('
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
r('summary', null, Strings.static),
|
|
204
|
-
r('ul', null, staticFiles.map(f =>
|
|
205
|
-
r('li', null,
|
|
206
|
-
r('a', { href: f, target: '_blank' }, f))))))
|
|
214
|
+
r('main', { className: CSS.MockList },
|
|
215
|
+
r('table', null, Object.entries(brokersByMethod).map(([method, brokers]) =>
|
|
216
|
+
r(SectionByMethod, { method, brokers }))),
|
|
217
|
+
r(PayloadViewer)))
|
|
207
218
|
}
|
|
208
219
|
|
|
209
220
|
|
|
@@ -227,25 +238,16 @@ function PreviewLink({ method, urlMask }) {
|
|
|
227
238
|
async function onClick(event) {
|
|
228
239
|
event.preventDefault()
|
|
229
240
|
try {
|
|
230
|
-
const
|
|
241
|
+
const preloader = setTimeout(() => {
|
|
231
242
|
empty(refPayloadViewer.current)
|
|
232
|
-
refPayloadViewer.current.append(
|
|
243
|
+
refPayloadViewer.current.append(PayloadViewerProgressBar())
|
|
233
244
|
}, 180)
|
|
234
|
-
|
|
245
|
+
|
|
246
|
+
const response = await fetch(this.href, { method })
|
|
247
|
+
clearTimeout(preloader)
|
|
248
|
+
await updatePayloadViewer(method, urlMask, this.href, response)
|
|
235
249
|
document.querySelector(`.${CSS.PreviewLink}.${CSS.chosen}`)?.classList.remove(CSS.chosen)
|
|
236
250
|
this.classList.add(CSS.chosen)
|
|
237
|
-
clearTimeout(spinner)
|
|
238
|
-
|
|
239
|
-
const mime = res.headers.get('content-type') || ''
|
|
240
|
-
if (mime.startsWith('image/')) // naively assumes GET.200
|
|
241
|
-
renderPayloadImage(this.href)
|
|
242
|
-
else
|
|
243
|
-
updatePayloadViewer(await res.text() || Strings.empty_response_body, mime)
|
|
244
|
-
|
|
245
|
-
empty(refPayloadViewerFileTitle.current)
|
|
246
|
-
refPayloadViewerFileTitle.current.append(PayloadViewerTitle({
|
|
247
|
-
file: mockSelectorFor(method, urlMask).value
|
|
248
|
-
}))
|
|
249
251
|
}
|
|
250
252
|
catch (error) {
|
|
251
253
|
onError(error)
|
|
@@ -260,37 +262,14 @@ function PreviewLink({ method, urlMask }) {
|
|
|
260
262
|
}
|
|
261
263
|
|
|
262
264
|
|
|
263
|
-
function PayloadViewerTitle({ file }) {
|
|
264
|
-
const { urlMask, method, status, ext } = parseFilename(file)
|
|
265
|
-
return (
|
|
266
|
-
r('span', null,
|
|
267
|
-
urlMask + '.' + method + '.',
|
|
268
|
-
r('abbr', { title: HttpStatus[status] }, status),
|
|
269
|
-
'.' + ext))
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
function ProgressBar() {
|
|
274
|
-
return (
|
|
275
|
-
r('div', { className: CSS.ProgressBar },
|
|
276
|
-
r('div', { style: { animationDuration: '1000ms' } }))) // TODO from Config.delay - 180
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
function renderPayloadImage(href) {
|
|
281
|
-
empty(refPayloadViewer.current)
|
|
282
|
-
refPayloadViewer.current.append(r('img', { src: href }))
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function updatePayloadViewer(body, mime) {
|
|
286
|
-
if (mime === 'application/json' && window?.Prism.languages)
|
|
287
|
-
refPayloadViewer.current.innerHTML = window.Prism.highlight(body, window.Prism.languages.json, 'json')
|
|
288
|
-
else
|
|
289
|
-
refPayloadViewer.current.innerText = body
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
|
|
293
265
|
function MockSelector({ broker }) {
|
|
266
|
+
function className(defaultIsSelected, status) {
|
|
267
|
+
return cssClass(
|
|
268
|
+
CSS.MockSelector,
|
|
269
|
+
!defaultIsSelected && CSS.bold,
|
|
270
|
+
status >= 400 && status < 500 && CSS.status4xx)
|
|
271
|
+
}
|
|
272
|
+
|
|
294
273
|
function onChange() {
|
|
295
274
|
const { status, urlMask, method } = parseFilename(this.value)
|
|
296
275
|
this.style.fontWeight = this.value === this.options[0].value // default is selected
|
|
@@ -305,13 +284,6 @@ function MockSelector({ broker }) {
|
|
|
305
284
|
.catch(onError)
|
|
306
285
|
}
|
|
307
286
|
|
|
308
|
-
function className(defaultIsSelected, status) {
|
|
309
|
-
return cssClass(
|
|
310
|
-
CSS.MockSelector,
|
|
311
|
-
!defaultIsSelected && CSS.bold,
|
|
312
|
-
status >= 400 && status < 500 && CSS.status4xx)
|
|
313
|
-
}
|
|
314
|
-
|
|
315
287
|
const selected = broker.currentMock.file
|
|
316
288
|
const { status, urlMask } = parseFilename(selected)
|
|
317
289
|
const files = broker.mocks.filter(item =>
|
|
@@ -351,13 +323,12 @@ function DelayRouteToggler({ broker }) {
|
|
|
351
323
|
onChange
|
|
352
324
|
}),
|
|
353
325
|
TimerIcon()))
|
|
354
|
-
}
|
|
355
326
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
327
|
+
function TimerIcon() {
|
|
328
|
+
return (
|
|
329
|
+
r('svg', { viewBox: '0 0 24 24' },
|
|
330
|
+
r('path', { d: 'M12 7H11v6l5 3.2.75-1.23-4.5-3z' })))
|
|
331
|
+
}
|
|
361
332
|
}
|
|
362
333
|
|
|
363
334
|
|
|
@@ -387,6 +358,54 @@ function InternalServerErrorToggler({ broker }) {
|
|
|
387
358
|
)
|
|
388
359
|
}
|
|
389
360
|
|
|
361
|
+
function PayloadViewerProgressBar() {
|
|
362
|
+
return (
|
|
363
|
+
r('div', { className: CSS.ProgressBar },
|
|
364
|
+
r('div', { style: { animationDuration: '1000ms' } }))) // TODO from Config.delay - 180
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function PayloadViewer() {
|
|
368
|
+
return (
|
|
369
|
+
r('div', { className: CSS.PayloadViewer },
|
|
370
|
+
r('h2', { ref: refPayloadViewerFileTitle }, Strings.mock),
|
|
371
|
+
r('pre', null,
|
|
372
|
+
r('code', { ref: refPayloadViewer }, Strings.click_link_to_preview))))
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function PayloadViewerTitle({ file }) {
|
|
376
|
+
const { urlMask, method, status, ext } = parseFilename(file)
|
|
377
|
+
return (
|
|
378
|
+
r('span', null,
|
|
379
|
+
urlMask + '.' + method + '.',
|
|
380
|
+
r('abbr', { title: HttpStatus[status] }, status),
|
|
381
|
+
'.' + ext))
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async function updatePayloadViewer(method, urlMask, imgSrc, response) {
|
|
385
|
+
empty(refPayloadViewerFileTitle.current)
|
|
386
|
+
refPayloadViewerFileTitle.current.append(PayloadViewerTitle({
|
|
387
|
+
file: mockSelectorFor(method, urlMask).value
|
|
388
|
+
}))
|
|
389
|
+
|
|
390
|
+
const mime = response.headers.get('content-type') || ''
|
|
391
|
+
if (mime.startsWith('image/')) // naively assumes GET.200
|
|
392
|
+
renderPayloadImage(imgSrc) // TESTME in pixaton
|
|
393
|
+
else
|
|
394
|
+
renderPayloadBody(await response.text() || Strings.empty_response_body, mime)
|
|
395
|
+
|
|
396
|
+
function renderPayloadImage(src) {
|
|
397
|
+
empty(refPayloadViewer.current)
|
|
398
|
+
refPayloadViewer.current.append(r('img', { src }))
|
|
399
|
+
}
|
|
400
|
+
function renderPayloadBody(body, mime) {
|
|
401
|
+
if (mime === 'application/json' && window.Prism?.highlight && window.Prism?.languages)
|
|
402
|
+
refPayloadViewer.current.innerHTML = window.Prism.highlight(body, window.Prism.languages.json, 'json')
|
|
403
|
+
else
|
|
404
|
+
refPayloadViewer.current.innerText = body
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
|
|
390
409
|
function trFor(method, urlMask) {
|
|
391
410
|
return document.querySelector(`tr[data-method="${method}"][data-urlMask="${urlMask}"]`)
|
|
392
411
|
}
|
|
@@ -401,6 +420,25 @@ function mockSelectorFor(method, urlMask) {
|
|
|
401
420
|
}
|
|
402
421
|
|
|
403
422
|
|
|
423
|
+
|
|
424
|
+
// StaticFilesList ===============
|
|
425
|
+
|
|
426
|
+
function StaticFilesList({ staticFiles }) {
|
|
427
|
+
if (!staticFiles.length)
|
|
428
|
+
return null
|
|
429
|
+
return (
|
|
430
|
+
r('details', {
|
|
431
|
+
open: true,
|
|
432
|
+
className: CSS.StaticFilesList
|
|
433
|
+
},
|
|
434
|
+
r('summary', null, Strings.static),
|
|
435
|
+
r('ul', null, staticFiles.map(f =>
|
|
436
|
+
r('li', null,
|
|
437
|
+
r('a', { href: f, target: '_blank' }, f))))))
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
|
|
404
442
|
function onError(error) {
|
|
405
443
|
if (error?.message === 'Failed to fetch')
|
|
406
444
|
alert('Looks like the Mockaton server is not running')
|
|
@@ -408,7 +446,9 @@ function onError(error) {
|
|
|
408
446
|
}
|
|
409
447
|
|
|
410
448
|
|
|
411
|
-
|
|
449
|
+
|
|
450
|
+
// Utils ============
|
|
451
|
+
|
|
412
452
|
function cssClass(...args) {
|
|
413
453
|
return args.filter(a => a).join(' ')
|
|
414
454
|
}
|