mockaton 11.0.1 → 11.0.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/Makefile +2 -2
- package/README.md +78 -35
- package/package.json +1 -1
- package/src/Api.js +2 -1
- package/src/ApiCommander.js +27 -58
- package/src/Dashboard.js +2 -4
- package/src/MockBroker.js +1 -1
- package/src/Watcher.js +3 -2
package/Makefile
CHANGED
|
@@ -6,7 +6,7 @@ watch:
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
test:
|
|
9
|
-
@node --test 'src/**/*.test.js'
|
|
9
|
+
@MOCKATON_WATCHER_DEBOUNCE_MS=0 node --test 'src/**/*.test.js'
|
|
10
10
|
|
|
11
11
|
test-docker:
|
|
12
12
|
@docker run --rm --interactive --tty \
|
|
@@ -16,7 +16,7 @@ test-docker:
|
|
|
16
16
|
make test
|
|
17
17
|
|
|
18
18
|
coverage:
|
|
19
|
-
@node --test --experimental-test-coverage \
|
|
19
|
+
@MOCKATON_WATCHER_DEBOUNCE_MS=0 node --test --experimental-test-coverage \
|
|
20
20
|
--test-reporter=spec --test-reporter-destination=stdout \
|
|
21
21
|
--test-reporter=lcov --test-reporter-destination=lcov.info \
|
|
22
22
|
'src/**/*.test.js'
|
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ get saved following Mockaton’s filename convention.
|
|
|
77
77
|
|
|
78
78
|
### Option 2: Fallback to Your Backend
|
|
79
79
|
<details>
|
|
80
|
-
<summary>
|
|
80
|
+
<summary>Learn more…</summary>
|
|
81
81
|
|
|
82
82
|
This option could be a bit elaborate if your backend uses third-party authentication,
|
|
83
83
|
because you’d have to manually inject cookies or `sessionStorage` tokens.
|
|
@@ -93,7 +93,12 @@ They will be saved in your `config.mocksDir` following the filename convention.
|
|
|
93
93
|
|
|
94
94
|
<br/>
|
|
95
95
|
|
|
96
|
+
<br/>
|
|
97
|
+
|
|
98
|
+
|
|
96
99
|
## Motivation
|
|
100
|
+
<details>
|
|
101
|
+
<summary>Motivation…</summary>
|
|
97
102
|
|
|
98
103
|
**No API state should be too hard to test.**
|
|
99
104
|
With Mockaton, developers can achieve correctness and speed.
|
|
@@ -116,15 +121,41 @@ With Mockaton, developers can achieve correctness and speed.
|
|
|
116
121
|
- checking out long-lived branches
|
|
117
122
|
- bisecting bugs
|
|
118
123
|
|
|
124
|
+
</details>
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
## Use Cases
|
|
129
|
+
<details>
|
|
130
|
+
<summary>Use Cases…</summary>
|
|
131
|
+
|
|
132
|
+
### Testing Backend or Frontend
|
|
133
|
+
- Empty responses
|
|
134
|
+
- Errors such as _Bad Request_ and _Internal Server Error_
|
|
135
|
+
- Mocking third-party APIs
|
|
136
|
+
- Polled resources (for triggering their different states)
|
|
137
|
+
- alerts
|
|
138
|
+
- notifications
|
|
139
|
+
- slow to build resources
|
|
140
|
+
|
|
141
|
+
### Testing Frontend
|
|
142
|
+
- Spinners by delaying responses
|
|
143
|
+
- Setting up UI tests
|
|
144
|
+
|
|
145
|
+
### Demoing complex backend states
|
|
146
|
+
Sometimes, the ideal flow you need is too difficult to reproduce from the actual backend.
|
|
147
|
+
For this, you can **Bulk Select** mocks by comments to simulate the complete states
|
|
148
|
+
you want. For example, by adding `(demo-part1)`, `(demo-part2)` to the filenames.
|
|
149
|
+
|
|
150
|
+
Similarly, you can deploy a **Standalone Demo Server** by compiling the frontend app and
|
|
151
|
+
putting its built assets in `config.staticDir`. And simulate the flow by Bulk Selecting mocks.
|
|
152
|
+
The [aot-fetch-demo repo](https://github.com/ericfortis/aot-fetch-demo) has a working example.
|
|
153
|
+
|
|
154
|
+
</details>
|
|
119
155
|
|
|
120
156
|
<br/>
|
|
121
157
|
|
|
122
|
-
|
|
123
|
-
- Zero dependencies (no runtime and no build packages).
|
|
124
|
-
- Does not write to disk. Except when you select ✅ **Save Mocks** for scraping mocks from a backend.
|
|
125
|
-
- Does not initiate network connections (no logs, no telemetry).
|
|
126
|
-
- Does not hijack your HTTP client.
|
|
127
|
-
- Auditable. Organized and small — under 4 KLoC (half is UI and tests).
|
|
158
|
+
|
|
128
159
|
|
|
129
160
|
<br/>
|
|
130
161
|
|
|
@@ -147,6 +178,16 @@ npx mockaton --port 2345
|
|
|
147
178
|
curl localhost:2345/api/foo
|
|
148
179
|
```
|
|
149
180
|
|
|
181
|
+
5. Optionally, use a `mockaton.config.js`
|
|
182
|
+
```js
|
|
183
|
+
import { defineConfig } from 'mockaton'
|
|
184
|
+
|
|
185
|
+
export default defineConfig({
|
|
186
|
+
port: 2345,
|
|
187
|
+
})
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
|
|
150
191
|
### Alternative Installations
|
|
151
192
|
<details>
|
|
152
193
|
<summary>With NPM (package.json)…</summary>
|
|
@@ -185,6 +226,9 @@ ln -s `realpath mockaton/src/cli.js` ~/bin/mockaton # some dir in your $PATH
|
|
|
185
226
|
## CLI Options
|
|
186
227
|
The CLI options override their counterparts in `mockaton.config.js`
|
|
187
228
|
|
|
229
|
+
<details>
|
|
230
|
+
<summary>CLI Options…</summary>
|
|
231
|
+
|
|
188
232
|
```txt
|
|
189
233
|
-c, --config <file> (default: ./mockaton.config.js)
|
|
190
234
|
|
|
@@ -200,10 +244,17 @@ The CLI options override their counterparts in `mockaton.config.js`
|
|
|
200
244
|
-h, --help Show this help
|
|
201
245
|
-v, --version Show version
|
|
202
246
|
```
|
|
247
|
+
</details>
|
|
203
248
|
|
|
204
249
|
|
|
205
250
|
## mockaton.config.js (Optional)
|
|
251
|
+
Mockaton looks for a file `mockaton.config.js` in its current working directory.
|
|
252
|
+
|
|
253
|
+
<details>
|
|
254
|
+
<summary>Defaults Overview… </summary>
|
|
255
|
+
|
|
206
256
|
As an overview, these are the defaults:
|
|
257
|
+
|
|
207
258
|
```js
|
|
208
259
|
import {
|
|
209
260
|
defineConfig,
|
|
@@ -250,8 +301,10 @@ export default defineConfig({
|
|
|
250
301
|
})
|
|
251
302
|
```
|
|
252
303
|
|
|
304
|
+
</details>
|
|
305
|
+
|
|
253
306
|
<details>
|
|
254
|
-
<summary
|
|
307
|
+
<summary>Config Documentation…</summary>
|
|
255
308
|
|
|
256
309
|
### `mocksDir?: string`
|
|
257
310
|
Defaults to `'mockaton-mocks'`.
|
|
@@ -499,7 +552,7 @@ Defaults to `'normal'`.
|
|
|
499
552
|
|
|
500
553
|
|
|
501
554
|
<details>
|
|
502
|
-
<summary>Programmatic Launch (Optional)
|
|
555
|
+
<summary>Programmatic Launch (Optional)…</summary>
|
|
503
556
|
|
|
504
557
|
|
|
505
558
|
```js
|
|
@@ -536,32 +589,6 @@ permutations for out-of-stock, new-arrival, and discontinued.
|
|
|
536
589
|
<br/>
|
|
537
590
|
|
|
538
591
|
|
|
539
|
-
## Use Cases
|
|
540
|
-
### Testing Backend or Frontend
|
|
541
|
-
- Empty responses
|
|
542
|
-
- Errors such as _Bad Request_ and _Internal Server Error_
|
|
543
|
-
- Mocking third-party APIs
|
|
544
|
-
- Polled resources (for triggering their different states)
|
|
545
|
-
- alerts
|
|
546
|
-
- notifications
|
|
547
|
-
- slow to build resources
|
|
548
|
-
|
|
549
|
-
### Testing Frontend
|
|
550
|
-
- Spinners by delaying responses
|
|
551
|
-
- Setting up UI tests
|
|
552
|
-
|
|
553
|
-
### Demoing complex backend states
|
|
554
|
-
Sometimes, the ideal flow you need is too difficult to reproduce from the actual backend.
|
|
555
|
-
For this, you can **Bulk Select** mocks by comments to simulate the complete states
|
|
556
|
-
you want. For example, by adding `(demo-part1)`, `(demo-part2)` to the filenames.
|
|
557
|
-
|
|
558
|
-
Similarly, you can deploy a **Standalone Demo Server** by compiling the frontend app and
|
|
559
|
-
putting its built assets in `config.staticDir`. And simulate the flow by Bulk Selecting mocks.
|
|
560
|
-
The [aot-fetch-demo repo](https://github.com/ericfortis/aot-fetch-demo) has a working example.
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
<br/>
|
|
564
|
-
|
|
565
592
|
|
|
566
593
|
## You can write JSON mocks in JavaScript or TypeScript
|
|
567
594
|
For example, `api/foo.GET.200.js`
|
|
@@ -841,6 +868,16 @@ await mockaton.reset()
|
|
|
841
868
|
</details>
|
|
842
869
|
|
|
843
870
|
|
|
871
|
+
<br/>
|
|
872
|
+
|
|
873
|
+
## Privacy and Security
|
|
874
|
+
- Zero dependencies (no runtime and no build packages).
|
|
875
|
+
- Does not write to disk. Except when you select ✅ **Save Mocks** for scraping mocks from a backend.
|
|
876
|
+
- Does not initiate network connections (no logs, no telemetry).
|
|
877
|
+
- Does not hijack your HTTP client.
|
|
878
|
+
- Auditable. Organized and small — under 4 KLoC (half is UI and tests).
|
|
879
|
+
|
|
880
|
+
|
|
844
881
|
<br/>
|
|
845
882
|
|
|
846
883
|
## Alternatives worth learning as well
|
|
@@ -862,6 +899,12 @@ hijack the HTTP client in Node.js and browsers.
|
|
|
862
899
|
- [Fetch Mock](https://github.com/wheresrhys/fetch-mock)
|
|
863
900
|
- [Mentoss](https://github.com/humanwhocodes/mentoss) Has a server side too
|
|
864
901
|
|
|
902
|
+
### Server Side
|
|
903
|
+
|
|
904
|
+
- [Wire Mock](https://github.com/wiremock/wiremock)
|
|
905
|
+
- [Mock](https://github.com/dhuan/mock)
|
|
906
|
+
- [Swagger](https://swagger.io/)
|
|
907
|
+
|
|
865
908
|
<br/>
|
|
866
909
|
|
|
867
910
|
---
|
package/package.json
CHANGED
package/src/Api.js
CHANGED
|
@@ -81,7 +81,8 @@ function getState(_, response) {
|
|
|
81
81
|
|
|
82
82
|
|
|
83
83
|
function longPollClientSyncVersion(req, response) {
|
|
84
|
-
|
|
84
|
+
const clientVersion = req.headers[HEADER_SYNC_VERSION]
|
|
85
|
+
if (clientVersion !== undefined && uiSyncVersion.version !== Number(clientVersion)) {
|
|
85
86
|
// e.g., tab was hidden while new mocks were added or removed
|
|
86
87
|
sendJSON(response, uiSyncVersion.version)
|
|
87
88
|
return
|
package/src/ApiCommander.js
CHANGED
|
@@ -9,80 +9,49 @@ export class Commander {
|
|
|
9
9
|
this.#addr = addr
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
#patch = (api, body) =>
|
|
13
|
-
fetch(this.#addr + api, {
|
|
14
|
-
method: 'PATCH',
|
|
15
|
-
body: JSON.stringify(body)
|
|
16
|
-
})
|
|
17
|
-
|
|
18
12
|
/** @returns {JsonPromise<State>} */
|
|
19
13
|
getState = () =>
|
|
20
14
|
fetch(this.#addr + API.state)
|
|
21
15
|
|
|
22
|
-
/**
|
|
23
|
-
|
|
16
|
+
/**
|
|
17
|
+
* @param {number?} currSyncVer - On mismatch, it responds immediately. Otherwise, long polls.
|
|
18
|
+
* @param {AbortSignal} abortSignal
|
|
19
|
+
* @returns {JsonPromise<number>}
|
|
20
|
+
*/
|
|
21
|
+
getSyncVersion = (currSyncVer = undefined, abortSignal = undefined) =>
|
|
24
22
|
fetch(this.#addr + API.syncVersion, {
|
|
25
23
|
signal: AbortSignal.any([
|
|
26
24
|
abortSignal,
|
|
27
25
|
AbortSignal.timeout(LONG_POLL_SERVER_TIMEOUT + 1000)
|
|
28
26
|
].filter(Boolean)),
|
|
29
|
-
headers:
|
|
30
|
-
[HEADER_SYNC_VERSION]: currSyncVer
|
|
31
|
-
|
|
27
|
+
headers: currSyncVer !== undefined
|
|
28
|
+
? { [HEADER_SYNC_VERSION]: currSyncVer }
|
|
29
|
+
: {}
|
|
32
30
|
})
|
|
33
31
|
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
return this.#
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return this.#patch(API.globalDelay, delay)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
bulkSelectByComment(comment) {
|
|
44
|
-
return this.#patch(API.bulkSelect, comment)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
selectCookie(cookieKey) {
|
|
48
|
-
return this.#patch(API.cookies, cookieKey)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
setProxyFallback(proxyAddr) {
|
|
52
|
-
return this.#patch(API.fallback, proxyAddr)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
setCollectProxied(shouldCollect) {
|
|
56
|
-
return this.#patch(API.collectProxied, shouldCollect)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
setCorsAllowed(value) {
|
|
60
|
-
return this.#patch(API.cors, value)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
select(file) {
|
|
65
|
-
return this.#patch(API.select, file)
|
|
33
|
+
#patch(api, body) {
|
|
34
|
+
return fetch(this.#addr + api, {
|
|
35
|
+
method: 'PATCH',
|
|
36
|
+
body: JSON.stringify(body)
|
|
37
|
+
})
|
|
66
38
|
}
|
|
67
39
|
|
|
68
|
-
|
|
69
|
-
return this.#patch(API.toggle500, [method, urlMask])
|
|
70
|
-
}
|
|
40
|
+
reset = () => this.#patch(API.reset)
|
|
71
41
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
42
|
+
selectCookie = label => this.#patch(API.cookies, label)
|
|
43
|
+
setGlobalDelay = delay => this.#patch(API.globalDelay, delay)
|
|
44
|
+
setCorsAllowed = value => this.#patch(API.cors, value)
|
|
45
|
+
setProxyFallback = proxyAddr => this.#patch(API.fallback, proxyAddr)
|
|
46
|
+
setCollectProxied = shouldCollect => this.#patch(API.collectProxied, shouldCollect)
|
|
75
47
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
48
|
+
select = file => this.#patch(API.select, file)
|
|
49
|
+
bulkSelectByComment = comment => this.#patch(API.bulkSelect, comment)
|
|
80
50
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
51
|
+
toggle500 = (method, urlMask) => this.#patch(API.toggle500, [method, urlMask])
|
|
52
|
+
setRouteIsProxied = (method, urlMask, proxied) => this.#patch(API.proxied, [method, urlMask, proxied])
|
|
53
|
+
setRouteIsDelayed = (method, urlMask, delayed) => this.#patch(API.delay, [method, urlMask, delayed])
|
|
84
54
|
|
|
85
|
-
setStaticRouteStatus(urlMask, status)
|
|
86
|
-
|
|
87
|
-
}
|
|
55
|
+
setStaticRouteStatus = (urlMask, status) => this.#patch(API.staticStatus, [urlMask, status])
|
|
56
|
+
setStaticRouteIsDelayed = (urlMask, delayed) => this.#patch(API.delayStatic, [urlMask, delayed])
|
|
88
57
|
}
|
package/src/Dashboard.js
CHANGED
|
@@ -734,7 +734,7 @@ function SettingsIcon() {
|
|
|
734
734
|
* The version increments when a mock file is added, removed, or renamed.
|
|
735
735
|
*/
|
|
736
736
|
function initRealTimeUpdates() {
|
|
737
|
-
let oldVersion =
|
|
737
|
+
let oldVersion = undefined
|
|
738
738
|
let controller = new AbortController()
|
|
739
739
|
|
|
740
740
|
longPoll()
|
|
@@ -755,11 +755,9 @@ function initRealTimeUpdates() {
|
|
|
755
755
|
ErrorToast.close()
|
|
756
756
|
|
|
757
757
|
const version = await response.json()
|
|
758
|
-
const skipUpdate = oldVersion === -1
|
|
759
758
|
if (oldVersion !== version) { // because it could be < or >
|
|
760
759
|
oldVersion = version
|
|
761
|
-
|
|
762
|
-
store.fetchState()
|
|
760
|
+
store.fetchState()
|
|
763
761
|
}
|
|
764
762
|
longPoll()
|
|
765
763
|
}
|
package/src/MockBroker.js
CHANGED
|
@@ -114,7 +114,7 @@ class UrlMatcher {
|
|
|
114
114
|
}
|
|
115
115
|
|
|
116
116
|
#disregardVariables(str) { // Stars out all parts that are in square brackets
|
|
117
|
-
return str.replace(/\[.*?]/g, '[^/]
|
|
117
|
+
return str.replace(/\[.*?]/g, '[^/]+')
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
// Appending a '/' so URLs ending with variables don't match
|
package/src/Watcher.js
CHANGED
|
@@ -15,6 +15,7 @@ import * as mockBrokerCollection from './mockBrokersCollection.js'
|
|
|
15
15
|
* and also renames, which are two events (delete + add).
|
|
16
16
|
*/
|
|
17
17
|
export const uiSyncVersion = new class extends EventEmitter {
|
|
18
|
+
delay = Number(process.env.MOCKATON_WATCHER_DEBOUNCE_MS ?? 80)
|
|
18
19
|
version = 0
|
|
19
20
|
|
|
20
21
|
increment = this.#debounce(() => {
|
|
@@ -33,7 +34,7 @@ export const uiSyncVersion = new class extends EventEmitter {
|
|
|
33
34
|
let timer
|
|
34
35
|
return () => {
|
|
35
36
|
clearTimeout(timer)
|
|
36
|
-
timer = setTimeout(fn,
|
|
37
|
+
timer = setTimeout(fn, this.delay)
|
|
37
38
|
}
|
|
38
39
|
}
|
|
39
40
|
}
|
|
@@ -64,7 +65,7 @@ export function watchStaticDir() {
|
|
|
64
65
|
const dir = config.staticDir
|
|
65
66
|
if (!dir)
|
|
66
67
|
return
|
|
67
|
-
|
|
68
|
+
|
|
68
69
|
watch(dir, { recursive: true, persistent: false }, (_, file) => {
|
|
69
70
|
if (!file)
|
|
70
71
|
return
|