mockaton 8.8.0 → 8.8.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 CHANGED
@@ -15,12 +15,27 @@ For example, for `/api/user/1234` the mock filename would be:
15
15
  my-mocks-dir/api/user/[user-id].GET.200.json
16
16
  ```
17
17
 
18
+
19
+ ## Dashboard
20
+ In the dashboard you can select a mock variant for a particular route, among
21
+ other features such as delaying responses, or triggering an autogenerated
22
+ `500` (Internal Server Error). Nonetheless, there’s a programmatic API,
23
+ which is handy for setting up tests (see **Commander API** below).
24
+
25
+ <picture>
26
+ <source media="(prefers-color-scheme: light)" srcset="./pixaton-tests/pic-for-readme.vp830x800.light.gold.png">
27
+ <source media="(prefers-color-scheme: dark)" srcset="./pixaton-tests/pic-for-readme.vp830x800.dark.gold.png">
28
+ <img alt="Mockaton Dashboard" src="./pixaton-tests/pic-for-readme.vp830x800.light.gold.png">
29
+ </picture>
30
+
31
+
32
+
18
33
  ## No Need to Mock Everything
19
34
  Mockaton can fallback to your real backend on routes you don’t have mocks
20
35
  for. For that, type your backend address in the **Fallback Backend** field.
21
36
 
22
- Similarly, if already have mocks for a route you can check the ☁️ **Cloud
23
- checkbox** and Mockaton will request it from your backend.
37
+ Similarly, if you already have mocks for a route you can check the
38
+ ☁️ **Cloud checkbox** and that route will be requested from your backend.
24
39
 
25
40
 
26
41
  ## Scrapping Mocks from you Backend
@@ -43,19 +58,6 @@ For instance, you can have mocks with a `4xx` or `5xx` status code for triggerin
43
58
  error responses. Or with a `204` (No Content) for testing empty collections.
44
59
 
45
60
 
46
- ## Dashboard
47
- In the dashboard you can select a mock variant for a particular route, among
48
- other features such as delaying responses, or triggering an autogenerated
49
- `500` (Internal Server Error). Nonetheless, there’s a programmatic API,
50
- which is handy for setting up tests (see **Commander API** below).
51
-
52
- <picture>
53
- <source media="(prefers-color-scheme: light)" srcset="./pixaton-tests/pic-for-readme.vp860x800.light.gold.png">
54
- <source media="(prefers-color-scheme: dark)" srcset="./pixaton-tests/pic-for-readme.vp860x800.dark.gold.png">
55
- <img alt="Mockaton Dashboard" src="./pixaton-tests/pic-for-readme.vp860x800.light.gold.png">
56
- </picture>
57
-
58
-
59
61
 
60
62
  ## Basic Usage
61
63
  `tsx` is only needed if you want to write mocks in TypeScript.
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "mockaton",
3
3
  "description": "A deterministic server-side for developing and testing frontend clients",
4
4
  "type": "module",
5
- "version": "8.8.0",
5
+ "version": "8.8.2",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "license": "MIT",
package/src/Dashboard.css CHANGED
@@ -1,13 +1,14 @@
1
1
  :root {
2
2
  --boxShadow1: 0 2px 1px -1px rgba(0, 0, 0, 0.15), 0 1px 1px 0 rgba(0, 0, 0, 0.15), 0 1px 3px 0 rgba(0, 0, 0, 0.1);
3
- --radius: 6px
3
+ --radius: 4px;
4
+ --radiusSmall: 2px;
4
5
  }
5
6
 
6
7
  @media (prefers-color-scheme: light) {
7
8
  :root {
8
9
  --color4xxBackground: #ffedd1;
9
10
  --colorAccent: #0075db;
10
- --colorAccentAlt: #068564;
11
+ --colorAccentAlt: #068185;
11
12
  --colorBackground: #fff;
12
13
  --colorComboBoxHeaderBackground: #fff;
13
14
  --colorComboBoxBackground: #f7f7f7;
@@ -183,13 +184,14 @@ select {
183
184
  }
184
185
 
185
186
  .ResetButton {
186
- padding: 4px 12px;
187
+ padding: 6px 12px;
187
188
  border: 1px solid var(--colorRed);
188
- margin-bottom: 4px;
189
+ margin-left: 4px;
189
190
  background: transparent;
190
191
  color: var(--colorRed);
191
192
  border-radius: 50px;
192
193
 
194
+
193
195
  &:hover {
194
196
  background: var(--colorRed);
195
197
  color: white;
@@ -255,7 +257,7 @@ select {
255
257
  position: relative;
256
258
  left: -6px;
257
259
  display: inline-block;
258
- width: 300px;
260
+ width: 278px;
259
261
  padding: 8px 6px;
260
262
  border-radius: var(--radius);
261
263
  color: var(--colorAccent);
@@ -280,6 +282,10 @@ select {
280
282
  text-overflow: ellipsis;
281
283
  font-size: 12px;
282
284
 
285
+ &.nonDefault {
286
+ font-weight: bold;
287
+ font-size: 0.92rem;
288
+ }
283
289
  &.status4xx {
284
290
  background: var(--color4xxBackground);
285
291
  }
@@ -315,32 +321,37 @@ select {
315
321
  }
316
322
 
317
323
  &:checked ~ svg {
324
+ border-color: transparent;
318
325
  background: var(--colorAccent);
319
326
  fill: white;
327
+ box-shadow: var(--boxShadow1);
320
328
  }
321
329
 
322
330
  &:disabled ~ svg {
323
331
  opacity: .5;
324
332
  cursor: not-allowed;
333
+ box-shadow: none;
325
334
  }
326
335
  }
327
336
 
328
337
  > svg {
329
- width: 20px;
330
- height: 20px;
338
+ width: 18px;
339
+ height: 18px;
340
+ border: 1px solid var(--colorSecondaryAction);
331
341
  vertical-align: bottom;
332
342
  fill: var(--colorSecondaryAction);
333
343
  border-radius: 50%;
334
344
  background: var(--colorSecondaryButtonBackground);
335
- box-shadow: var(--boxShadow1);
336
345
  }
337
346
  }
338
347
 
339
348
  .ProxyToggler {
349
+ margin-left: 4px;
340
350
  > svg {
341
- width: 24px;
342
- padding: 3px;
343
- border-radius: 4px;
351
+ width: 22px;
352
+ padding: 1px;
353
+ border-color: transparent;
354
+ border-radius: var(--radiusSmall);
344
355
  }
345
356
  }
346
357
 
@@ -362,18 +373,19 @@ select {
362
373
  &:checked ~ span {
363
374
  color: white;
364
375
  background: var(--colorRed);
376
+ box-shadow: var(--boxShadow1);
365
377
  }
366
378
  }
367
379
 
368
380
  > span {
369
381
  padding: 5px 4px;
370
- box-shadow: var(--boxShadow1);
371
382
  font-size: 10px;
372
383
  color: var(--colorSecondaryAction);
373
- border-radius: 4px;
384
+ border-radius: var(--radiusSmall);
374
385
  background: var(--colorSecondaryButtonBackground);
375
386
 
376
387
  &:hover {
388
+ box-shadow: var(--boxShadow1);
377
389
  background: var(--colorLightRed);
378
390
  color: var(--colorRed);
379
391
  }
@@ -406,10 +418,6 @@ select {
406
418
  }
407
419
  }
408
420
 
409
- .bold {
410
- font-weight: bold;
411
- }
412
-
413
421
  .StaticFilesList {
414
422
  margin-top: 20px;
415
423
 
package/src/Dashboard.js CHANGED
@@ -16,11 +16,12 @@ const Strings = {
16
16
  bulk_select_disabled_title: 'No mock files have comments, which are anything within parentheses on the filename.',
17
17
  click_link_to_preview: 'Click a link to preview it',
18
18
  cookie: 'Cookie',
19
- cookie_disabled_title: 'No cookies specified in Config.cookies',
19
+ cookie_disabled_title: 'No cookies specified in config.cookies',
20
20
  delay: 'Delay',
21
21
  empty_response_body: '/* Empty Response Body */',
22
22
  fallback_server: 'Fallback Backend',
23
23
  fallback_server_placeholder: 'Type Server Address',
24
+ got: 'Got',
24
25
  internal_server_error: 'Internal Server Error',
25
26
  mock: 'Mock',
26
27
  no_mocks_found: 'No mocks found',
@@ -49,10 +50,10 @@ const CSS = {
49
50
  SaveProxiedCheckbox: 'SaveProxiedCheckbox',
50
51
  StaticFilesList: 'StaticFilesList',
51
52
 
52
- bold: 'bold',
53
53
  empty: 'empty',
54
54
  chosen: 'chosen',
55
- status4xx: 'status4xx'
55
+ status4xx: 'status4xx',
56
+ nonDefault: 'nonDefault'
56
57
  }
57
58
 
58
59
  const r = createElement
@@ -105,8 +106,7 @@ function Logo() {
105
106
 
106
107
  function CookieSelector({ cookies }) {
107
108
  function onChange() {
108
- mockaton.selectCookie(this.value)
109
- .catch(onError)
109
+ mockaton.selectCookie(this.value).catch(onError)
110
110
  }
111
111
  const disabled = cookies.length <= 1
112
112
  return (
@@ -165,9 +165,7 @@ function ProxyFallbackField({ fallbackAddress, collectProxied }) {
165
165
  return (
166
166
  r('div', { className: cssClass(CSS.Field, CSS.FallbackBackend) },
167
167
  r('label', null,
168
- r('span', null,
169
- r(CloudIcon),
170
- Strings.fallback_server),
168
+ r('span', null, r(CloudIcon), Strings.fallback_server),
171
169
  r('input', {
172
170
  type: 'url',
173
171
  autocomplete: 'none',
@@ -267,9 +265,6 @@ function PreviewLink({ method, urlMask }) {
267
265
  function MockSelector({ broker }) {
268
266
  function onChange() {
269
267
  const { urlMask, method } = parseFilename(this.value)
270
- this.style.fontWeight = this.value === this.options[0].value // default is selected
271
- ? 'normal'
272
- : 'bold'
273
268
  mockaton.select(this.value)
274
269
  .then(init)
275
270
  .then(() => linkFor(method, urlMask)?.click())
@@ -288,14 +283,14 @@ function MockSelector({ broker }) {
288
283
 
289
284
  return (
290
285
  r('select', {
291
- 'data-qaid': urlMask,
286
+ onChange,
292
287
  autocomplete: 'off',
288
+ 'data-qaid': urlMask,
289
+ disabled: files.length <= 1,
293
290
  className: cssClass(
294
291
  CSS.MockSelector,
295
- selected !== files[0] && CSS.bold,
296
- status >= 400 && status < 500 && CSS.status4xx),
297
- disabled: files.length <= 1,
298
- onChange
292
+ selected !== files[0] && CSS.nonDefault,
293
+ status >= 400 && status < 500 && CSS.status4xx)
299
294
  }, files.map(file =>
300
295
  r('option', {
301
296
  value: file,
@@ -303,12 +298,10 @@ function MockSelector({ broker }) {
303
298
  }, file))))
304
299
  }
305
300
 
306
-
307
301
  function DelayRouteToggler({ broker }) {
308
302
  function onChange() {
309
- const { method, urlMask } = parseFilename(this.name)
310
- mockaton.setRouteIsDelayed(method, urlMask, this.checked)
311
- .catch(onError)
303
+ const { method, urlMask } = parseFilename(broker.mocks[0])
304
+ mockaton.setRouteIsDelayed(method, urlMask, this.checked).catch(onError)
312
305
  }
313
306
  return (
314
307
  r('label', {
@@ -317,7 +310,6 @@ function DelayRouteToggler({ broker }) {
317
310
  },
318
311
  r('input', {
319
312
  type: 'checkbox',
320
- name: broker.currentMock.file,
321
313
  checked: Boolean(broker.currentMock.delay),
322
314
  onChange
323
315
  }),
@@ -347,14 +339,13 @@ function InternalServerErrorToggler({ broker }) {
347
339
  checked: parseFilename(broker.currentMock.file).status === 500,
348
340
  onChange
349
341
  }),
350
- r('span', null, '500')
351
- )
352
- )
342
+ r('span', null, '500')))
353
343
  }
354
344
 
345
+
355
346
  function ProxyToggler({ broker, disabled }) {
356
347
  function onChange() {
357
- const { urlMask, method } = parseFilename(this.name)
348
+ const { urlMask, method } = parseFilename(broker.mocks[0])
358
349
  mockaton.setRouteIsProxied(method, urlMask, this.checked)
359
350
  .then(init)
360
351
  .then(() => linkFor(method, urlMask)?.click())
@@ -368,7 +359,6 @@ function ProxyToggler({ broker, disabled }) {
368
359
  r('input', {
369
360
  type: 'checkbox',
370
361
  disabled,
371
- name: broker.currentMock.file,
372
362
  checked: !broker.currentMock.file,
373
363
  onChange
374
364
  }),
@@ -396,14 +386,21 @@ function PayloadViewerProgressBar() {
396
386
  r('div', { style: { animationDuration: '1000ms' } }))) // TODO from Config.delay - 180
397
387
  }
398
388
 
399
- function PayloadViewerTitle({ file }) {
400
- const { urlMask, method, status, ext } = parseFilename(file)
389
+ function PayloadViewerTitle({ file, status, statusText }) {
390
+ const { urlMask, method, ext } = parseFilename(file)
401
391
  return (
402
392
  r('span', null,
403
393
  urlMask + '.' + method + '.',
404
- r('abbr', { title: HttpStatus[status] }, status),
394
+ r('abbr', { title: statusText }, status),
405
395
  '.' + ext))
406
396
  }
397
+ function PayloadViewerTitleWhenProxied({ mime, status, statusText }) {
398
+ return (
399
+ r('span', null,
400
+ Strings.got + ' ',
401
+ r('abbr', { title: statusText }, status),
402
+ ' ' + mime))
403
+ }
407
404
 
408
405
  async function previewMock(method, urlMask, href) {
409
406
  const timer = setTimeout(renderProgressBar, 180)
@@ -417,10 +414,22 @@ async function previewMock(method, urlMask, href) {
417
414
  }
418
415
 
419
416
  async function updatePayloadViewer(method, urlMask, response) {
420
- payloadViewerTitleRef.current.replaceChildren(
421
- PayloadViewerTitle({ file: mockSelectorFor(method, urlMask).value }))
422
-
423
417
  const mime = response.headers.get('content-type') || ''
418
+
419
+ const file = mockSelectorFor(method, urlMask).value
420
+ if (file === Strings.proxied)
421
+ payloadViewerTitleRef.current.replaceChildren(PayloadViewerTitleWhenProxied({
422
+ status: response.status,
423
+ statusText: response.statusText,
424
+ mime
425
+ }))
426
+ else
427
+ payloadViewerTitleRef.current.replaceChildren(PayloadViewerTitle({
428
+ status: response.status,
429
+ statusText: response.statusText,
430
+ file
431
+ }))
432
+
424
433
  if (mime.startsWith('image/')) { // Naively assumes GET.200
425
434
  payloadViewerRef.current.replaceChildren(
426
435
  r('img', {
@@ -447,9 +456,6 @@ function trFor(method, urlMask) {
447
456
  function linkFor(method, urlMask) {
448
457
  return trFor(method, urlMask)?.querySelector(`a.${CSS.PreviewLink}`)
449
458
  }
450
- function checkbox500For(method, urlMask) {
451
- return trFor(method, urlMask)?.querySelector(`.${CSS.InternalServerErrorToggler} > input`)
452
- }
453
459
  function mockSelectorFor(method, urlMask) {
454
460
  return trFor(method, urlMask)?.querySelector(`select.${CSS.MockSelector}`)
455
461
  }
@@ -473,6 +479,7 @@ function StaticFilesList({ staticFiles }) {
473
479
  }
474
480
 
475
481
 
482
+ // Misc ===============
476
483
 
477
484
  function onError(error) {
478
485
  if (error?.message === 'Failed to fetch')
@@ -480,7 +487,6 @@ function onError(error) {
480
487
  console.error(error)
481
488
  }
482
489
 
483
-
484
490
  function TimerIcon() {
485
491
  return (
486
492
  r('svg', { viewBox: '0 0 24 24' },
@@ -540,91 +546,3 @@ function createSvgElement(tagName, props, ...children) {
540
546
  function useRef() {
541
547
  return { current: null }
542
548
  }
543
-
544
- const HttpStatus = {
545
- 100: 'Continue',
546
- 101: 'Switching Protocols',
547
- 102: 'Processing',
548
- 103: 'Early Hints',
549
- 200: 'OK',
550
- 201: 'Created',
551
- 202: 'Accepted',
552
- 203: 'Non-Authoritative Information',
553
- 204: 'No Content',
554
- 205: 'Reset Content',
555
- 206: 'Partial Content',
556
- 207: 'Multi-Status',
557
- 208: 'Already Reported',
558
- 218: 'This is fine (Apache Web Server)',
559
- 226: 'IM Used',
560
- 300: 'Multiple Choices',
561
- 301: 'Moved Permanently',
562
- 302: 'Found',
563
- 303: 'See Other',
564
- 304: 'Not Modified',
565
- 306: 'Switch Proxy',
566
- 307: 'Temporary Redirect',
567
- 308: 'Resume Incomplete',
568
- 400: 'Bad Request',
569
- 401: 'Unauthorized',
570
- 402: 'Payment Required',
571
- 403: 'Forbidden',
572
- 404: 'Not Found',
573
- 405: 'Method Not Allowed',
574
- 406: 'Not Acceptable',
575
- 407: 'Proxy Authentication Required',
576
- 408: 'Request Timeout',
577
- 409: 'Conflict',
578
- 410: 'Gone',
579
- 411: 'Length Required',
580
- 412: 'Precondition Failed',
581
- 413: 'Request Entity Too Large',
582
- 414: 'Request-URI Too Long',
583
- 415: 'Unsupported Media Type',
584
- 416: 'Requested Range Not Satisfiable',
585
- 417: 'Expectation Failed',
586
- 418: 'I’m a teapot',
587
- 419: 'Page Expired (Laravel Framework)',
588
- 420: 'Method Failure (Spring Framework)',
589
- 421: 'Misdirected Request',
590
- 422: 'Unprocessable Entity',
591
- 423: 'Locked',
592
- 424: 'Failed Dependency',
593
- 426: 'Upgrade Required',
594
- 428: 'Precondition Required',
595
- 429: 'Too Many Requests',
596
- 431: 'Request Header Fields Too Large',
597
- 440: 'Login Time-out',
598
- 444: 'Connection Closed Without Response',
599
- 449: 'Retry With',
600
- 450: 'Blocked by Windows Parental Controls',
601
- 451: 'Unavailable For Legal Reasons',
602
- 494: 'Request Header Too Large',
603
- 495: 'SSL Certificate Error',
604
- 496: 'SSL Certificate Required',
605
- 497: 'HTTP Request Sent to HTTPS Port',
606
- 498: 'Invalid Token (Esri)',
607
- 499: 'Client Closed Request',
608
- 500: 'Internal Server Error',
609
- 501: 'Not Implemented',
610
- 502: 'Bad Gateway',
611
- 503: 'Service Unavailable',
612
- 504: 'Gateway Timeout',
613
- 505: 'HTTP Version Not Supported',
614
- 506: 'Variant Also Negotiates',
615
- 507: 'Insufficient Storage',
616
- 508: 'Loop Detected',
617
- 509: 'Bandwidth Limit Exceeded',
618
- 510: 'Not Extended',
619
- 511: 'Network Authentication Required',
620
- 520: 'Unknown Error',
621
- 521: 'Web Server Is Down',
622
- 522: 'Connection Timed Out',
623
- 523: 'Origin Is Unreachable',
624
- 524: 'A Timeout Occurred',
625
- 525: 'SSL Handshake Failed',
626
- 526: 'Invalid SSL Certificate',
627
- 527: 'Railgun Listener to Origin Error',
628
- 530: 'Origin DNS Error',
629
- 598: 'Network Read Timeout Error'
630
- }
@@ -2,7 +2,7 @@
2
2
  <svg version="1.1" viewBox="0 0 570 100" xmlns="http://www.w3.org/2000/svg">
3
3
  <style>
4
4
  :root { --color: #000000; }
5
- @media (prefers-color-scheme: light) { :root { --color: #555 } }
5
+ @media (prefers-color-scheme: light) { :root { --color: #444 } }
6
6
  @media (prefers-color-scheme: dark) { :root { --color: #eee } }
7
7
  path { fill: var(--color) }
8
8
  </style>