mockaton 8.15.0 → 8.16.0
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 +2 -2
- package/package.json +1 -1
- package/src/Dashboard.css +25 -23
- package/src/Dashboard.js +17 -11
- package/src/Mockaton.js +3 -2
- package/src/StaticDispatcher.js +15 -4
- package/src/Watcher.js +20 -1
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://github.com/ericfortis/mockaton/actions/workflows/github-code-scanning/codeql)
|
|
7
7
|
|
|
8
8
|
An HTTP mock server for simulating APIs with minimal setup
|
|
9
|
-
— ideal for
|
|
9
|
+
— ideal for testing difficult to reproduce backend states.
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
## Overview
|
|
@@ -96,7 +96,7 @@ Mockaton({
|
|
|
96
96
|
node my-mockaton.js
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
### Node < 23.6
|
|
99
|
+
### TypeScript with Node < 23.6
|
|
100
100
|
If you want to write mocks in TypeScript in a version older than Node 23.6:
|
|
101
101
|
```shell
|
|
102
102
|
npm install tsx
|
package/package.json
CHANGED
package/src/Dashboard.css
CHANGED
|
@@ -257,9 +257,9 @@ table {
|
|
|
257
257
|
|
|
258
258
|
.PayloadViewer {
|
|
259
259
|
position: sticky;
|
|
260
|
-
margin-top: 62px;
|
|
261
260
|
top: 62px;
|
|
262
261
|
width: 50%;
|
|
262
|
+
margin-top: 62px;
|
|
263
263
|
margin-left: 20px;
|
|
264
264
|
|
|
265
265
|
h2 {
|
|
@@ -434,10 +434,11 @@ table {
|
|
|
434
434
|
}
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
-
.InternalServerErrorToggler
|
|
437
|
+
.InternalServerErrorToggler,
|
|
438
|
+
.NotFoundToggler {
|
|
438
439
|
display: flex;
|
|
439
|
-
margin-left: 8px;
|
|
440
440
|
margin-right: 12px;
|
|
441
|
+
margin-left: 8px;
|
|
441
442
|
cursor: pointer;
|
|
442
443
|
|
|
443
444
|
> input {
|
|
@@ -472,32 +473,33 @@ table {
|
|
|
472
473
|
}
|
|
473
474
|
}
|
|
474
475
|
|
|
475
|
-
.
|
|
476
|
-
|
|
477
|
-
width:
|
|
478
|
-
height:
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
476
|
+
.SpinnerClock {
|
|
477
|
+
display: flex;
|
|
478
|
+
width: 36px;
|
|
479
|
+
height: 36px;
|
|
480
|
+
margin-top: 92px;
|
|
481
|
+
justify-self: center;
|
|
482
|
+
fill: none;
|
|
483
|
+
stroke: var(--colorSecondaryAction);
|
|
484
|
+
stroke-width: 2px;
|
|
485
|
+
|
|
486
|
+
.HourHand,
|
|
487
|
+
.MinuteHand {
|
|
488
|
+
stroke-linecap: round;
|
|
489
|
+
transform-origin: center;
|
|
490
|
+
animation: _kfRotate 9s linear infinite
|
|
491
|
+
}
|
|
492
|
+
.MinuteHand {
|
|
493
|
+
animation-duration: .75s;
|
|
489
494
|
}
|
|
490
|
-
|
|
491
495
|
}
|
|
492
|
-
@keyframes
|
|
493
|
-
0% {
|
|
494
|
-
width: 0;
|
|
495
|
-
}
|
|
496
|
+
@keyframes _kfRotate {
|
|
496
497
|
100% {
|
|
497
|
-
|
|
498
|
+
transform: rotate(360deg)
|
|
498
499
|
}
|
|
499
500
|
}
|
|
500
501
|
|
|
502
|
+
|
|
501
503
|
.StaticFilesList {
|
|
502
504
|
margin-top: 8px;
|
|
503
505
|
|
package/src/Dashboard.js
CHANGED
|
@@ -40,19 +40,22 @@ const CSS = {
|
|
|
40
40
|
DelayToggler: 'DelayToggler',
|
|
41
41
|
FallbackBackend: 'FallbackBackend',
|
|
42
42
|
Field: 'Field',
|
|
43
|
+
GlobalDelayField: 'GlobalDelayField',
|
|
43
44
|
Header: 'Header',
|
|
44
45
|
InternalServerErrorToggler: 'InternalServerErrorToggler',
|
|
46
|
+
Main: 'Main',
|
|
45
47
|
MockList: 'MockList',
|
|
46
48
|
MockSelector: 'MockSelector',
|
|
49
|
+
NotFoundToggler: 'NotFoundToggler',
|
|
47
50
|
PayloadViewer: 'PayloadViewer',
|
|
48
51
|
PreviewLink: 'PreviewLink',
|
|
49
|
-
ProgressBar: 'ProgressBar',
|
|
50
52
|
ProxyToggler: 'ProxyToggler',
|
|
51
53
|
ResetButton: 'ResetButton',
|
|
52
|
-
GlobalDelayField: 'GlobalDelayField',
|
|
53
54
|
SaveProxiedCheckbox: 'SaveProxiedCheckbox',
|
|
55
|
+
SpinnerClock: 'SpinnerClock',
|
|
56
|
+
SpinnerClockHourHand: 'HourHand',
|
|
57
|
+
SpinnerClockMinuteHand: 'MinuteHand',
|
|
54
58
|
StaticFilesList: 'StaticFilesList',
|
|
55
|
-
Main: 'Main',
|
|
56
59
|
|
|
57
60
|
red: 'red',
|
|
58
61
|
empty: 'empty',
|
|
@@ -65,7 +68,7 @@ const r = createElement
|
|
|
65
68
|
const s = createSvgElement
|
|
66
69
|
const mockaton = new Commander(window.location.origin)
|
|
67
70
|
|
|
68
|
-
const
|
|
71
|
+
const SPINNER_DELAY = 180
|
|
69
72
|
let globalDelay = 1200
|
|
70
73
|
|
|
71
74
|
|
|
@@ -449,7 +452,7 @@ function NotFoundToggler({ broker }) {
|
|
|
449
452
|
}
|
|
450
453
|
return (
|
|
451
454
|
r('label', {
|
|
452
|
-
className: CSS.
|
|
455
|
+
className: CSS.NotFoundToggler,
|
|
453
456
|
title: Strings.not_found
|
|
454
457
|
},
|
|
455
458
|
r('input', {
|
|
@@ -492,10 +495,13 @@ function PayloadViewer() {
|
|
|
492
495
|
r('code', { ref: payloadViewerRef }, Strings.click_link_to_preview))))
|
|
493
496
|
}
|
|
494
497
|
|
|
495
|
-
function
|
|
498
|
+
function PayloadViewerSpinner() {
|
|
496
499
|
return (
|
|
497
|
-
|
|
498
|
-
|
|
500
|
+
s('svg', { viewBox: '0 0 24 24', class: CSS.SpinnerClock },
|
|
501
|
+
s('circle', { cx: 12, cy: 12, r: 10 }),
|
|
502
|
+
s('line', { class: CSS.SpinnerClockHourHand, x1: 12, y1: 12, x2: 12, y2: 8 }),
|
|
503
|
+
s('line', { class: CSS.SpinnerClockMinuteHand, x1: 12, y1: 12, x2: 12, y2: 5 })
|
|
504
|
+
))
|
|
499
505
|
}
|
|
500
506
|
|
|
501
507
|
function PayloadViewerTitle({ file, status, statusText }) {
|
|
@@ -517,13 +523,13 @@ function PayloadViewerTitleWhenProxied({ mime, status, statusText, gatewayIsBad
|
|
|
517
523
|
}
|
|
518
524
|
|
|
519
525
|
async function previewMock(method, urlMask, href) {
|
|
520
|
-
const timer = setTimeout(
|
|
526
|
+
const timer = setTimeout(renderSpinner, SPINNER_DELAY)
|
|
521
527
|
const response = await fetch(href, { method })
|
|
522
528
|
clearTimeout(timer)
|
|
523
529
|
await updatePayloadViewer(method, urlMask, response)
|
|
524
530
|
|
|
525
|
-
function
|
|
526
|
-
payloadViewerRef.current.replaceChildren(
|
|
531
|
+
function renderSpinner() {
|
|
532
|
+
payloadViewerRef.current.replaceChildren(PayloadViewerSpinner())
|
|
527
533
|
}
|
|
528
534
|
}
|
|
529
535
|
|
package/src/Mockaton.js
CHANGED
|
@@ -3,11 +3,11 @@ import { createServer } from 'node:http'
|
|
|
3
3
|
import { API } from './ApiConstants.js'
|
|
4
4
|
import { config, setup } from './config.js'
|
|
5
5
|
import { dispatchMock } from './MockDispatcher.js'
|
|
6
|
-
import { watchMocksDir } from './Watcher.js'
|
|
7
6
|
import { BodyReaderError } from './utils/http-request.js'
|
|
8
7
|
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
9
8
|
import { setCorsHeaders, isPreflight } from './utils/http-cors.js'
|
|
10
9
|
import { apiPatchRequests, apiGetRequests } from './Api.js'
|
|
10
|
+
import { watchMocksDir, watchStaticMocksDir } from './Watcher.js'
|
|
11
11
|
import { dispatchStatic, initStaticCollection, findStaticBrokerByRoute } from './StaticDispatcher.js'
|
|
12
12
|
import { sendNoContent, sendInternalServerError, sendUnprocessableContent } from './utils/http-response.js'
|
|
13
13
|
|
|
@@ -17,8 +17,9 @@ process.on('unhandledRejection', error => { throw error })
|
|
|
17
17
|
export function Mockaton(options) {
|
|
18
18
|
setup(options)
|
|
19
19
|
mockBrokerCollection.init()
|
|
20
|
-
watchMocksDir()
|
|
21
20
|
initStaticCollection()
|
|
21
|
+
watchMocksDir()
|
|
22
|
+
watchStaticMocksDir()
|
|
22
23
|
|
|
23
24
|
return createServer(onRequest).listen(config.port, config.host, function (error) {
|
|
24
25
|
if (error) {
|
package/src/StaticDispatcher.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { join } from 'node:path'
|
|
1
|
+
import { join, basename } from 'node:path'
|
|
2
2
|
import { readFileSync } from 'node:fs'
|
|
3
3
|
|
|
4
4
|
import { mimeFor } from './utils/mime.js'
|
|
@@ -37,14 +37,25 @@ let collection = {}
|
|
|
37
37
|
export function initStaticCollection() {
|
|
38
38
|
collection = {}
|
|
39
39
|
listFilesRecursively(config.staticDir)
|
|
40
|
-
.filter(isFileAllowed)
|
|
41
40
|
.sort()
|
|
42
|
-
.forEach(
|
|
41
|
+
.forEach(registerStaticMock)
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
|
|
44
|
+
/** @returns {boolean} registered */
|
|
45
|
+
export function registerStaticMock(file) {
|
|
46
|
+
if (!isFileAllowed(basename(file)))
|
|
47
|
+
return false
|
|
48
|
+
|
|
46
49
|
file = '/' + file
|
|
50
|
+
if (findStaticBrokerByRoute(file))
|
|
51
|
+
return false
|
|
52
|
+
|
|
47
53
|
collection[file] = new StaticBroker(file)
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function unregisterStaticMock(file) {
|
|
58
|
+
delete collection['/' + file]
|
|
48
59
|
}
|
|
49
60
|
|
|
50
61
|
export function findStaticBrokerByRoute(route) {
|
package/src/Watcher.js
CHANGED
|
@@ -5,6 +5,7 @@ import { EventEmitter } from 'node:events'
|
|
|
5
5
|
import { config } from './config.js'
|
|
6
6
|
import { isFile } from './utils/fs.js'
|
|
7
7
|
import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
8
|
+
import { registerStaticMock, unregisterStaticMock } from './StaticDispatcher.js'
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
/** # AR = Add or Remove Mock Event */
|
|
@@ -39,5 +40,23 @@ export function watchMocksDir() {
|
|
|
39
40
|
})
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
export function watchStaticMocksDir() {
|
|
44
|
+
const dir = config.staticDir
|
|
45
|
+
if (!dir)
|
|
46
|
+
return
|
|
47
|
+
watch(dir, { recursive: true, persistent: false }, (_, file) => {
|
|
48
|
+
if (!file)
|
|
49
|
+
return
|
|
50
|
+
if (isFile(join(dir, file))) {
|
|
51
|
+
if (registerStaticMock(file))
|
|
52
|
+
uiSyncVersion.increment()
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
unregisterStaticMock(file)
|
|
56
|
+
uiSyncVersion.increment()
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// TODO config changes
|
|
43
62
|
// TODO think about throttling e.g. bulk deletes/remove files
|