mockaton 6.4.0 → 6.4.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.
Binary file
package/README.md CHANGED
@@ -8,9 +8,31 @@ example, the following file will be served for `/api/user/1234`
8
8
  my-mocks-dir/api/user/[user-id].GET.200.json
9
9
  ```
10
10
 
11
- By the way, [this browser
12
- extension](https://github.com/ericfortis/devtools-ext-tar-http-requests) can
13
- be used for downloading a TAR of your XHR requests following that convention.
11
+ [This browser extension](https://github.com/ericfortis/devtools-ext-tar-http-requests)
12
+ can be used for downloading a TAR of your XHR requests following that convention.
13
+
14
+ ## What do I use Mockaton for?
15
+ - I’m a frontend dev, so I don’t have to spin up and maintain hefty or complex backends.
16
+ - For a deterministic and comprehensive state. Having all the possible
17
+ state variants at once lets me visually spot inadvertent bugs right away.
18
+ - Testing empty responses.
19
+ - Testing spinners by delaying responses.
20
+ - Triggering errors such as Bad Request and Internal Server Error.
21
+ - Triggering notifications and alerts.
22
+ - As check-in the mocks in the repo, when bisecting a bug, I don’t
23
+ have to sync the frontend with many backend repos.
24
+ - Similarly, I can check out long-lived branches that have old API contracts.
25
+ - Prototyping before the backend API is developed.
26
+ - As API documentation.
27
+ - Setting up tests.
28
+
29
+ ## Alternatives
30
+ - Chrome DevTools allows for [overriding responses](https://developer.chrome.com/docs/devtools/overrides)
31
+ - Reverse Proxies such as [Burp](https://portswigger.net/burp) are also handy for overriding responses.
32
+ - [Storybook](https://storybook.js.org)
33
+
34
+ ### Caveats
35
+ - Syncing the mocks. The browser extension mentioned above helps.
14
36
 
15
37
 
16
38
  ## Getting Started
@@ -45,22 +67,23 @@ node my-mockaton.js
45
67
  ```ts
46
68
  interface Config {
47
69
  mocksDir: string
48
- ignore?: RegExp // defaults to /(\.DS_Store|~)$/
70
+ ignore?: RegExp // Defaults to /(\.DS_Store|~)$/
49
71
 
50
- staticDir?: string
72
+ staticDir?: string // These files don’t use the mock-filename convention
51
73
 
52
- host?: string, // defaults to 'localhost'
53
- port?: number // defaults to 0, which means auto-assigned
74
+ host?: string, // Defaults to 'localhost'
75
+ port?: number // Defaults to 0, which means auto-assigned
54
76
  proxyFallback?: string // e.g. http://localhost:9999 Target for relaying routes without mocks
55
77
 
56
- delay?: number // defaults to 1200 (ms)
78
+ delay?: number // Defaults to 1200 (ms)
57
79
  cookies?: { [label: string]: string }
58
80
  extraMimes?: { [fileExt: string]: string }
59
81
  extraHeaders?: []
60
82
 
61
- onReady?: (dashboardUrl: string) => void // defaults to trying to open macOS default browser. pass a noop to prevent opening the dashboard
83
+ onReady?: (dashboardUrl: string) => void // Defaults to trying to open macOS and Win default browser.
62
84
  }
63
85
  ```
86
+ There’s a Config section below with more details.
64
87
 
65
88
  ---
66
89
 
@@ -78,7 +101,6 @@ You can add the comment: `(default)` to a filename.
78
101
  Otherwise, the first file in **alphabetical order** wins.
79
102
 
80
103
  ```
81
- api/user(some comment).GET.200.json
82
104
  api/user(default).GET.200.json
83
105
  ```
84
106
 
@@ -174,6 +196,16 @@ api/foo.GET.200.json
174
196
  api/foo/.GET.200.json
175
197
  ```
176
198
 
199
+ ---
200
+ ## `Config.staticDir`
201
+ These files don’t use the mock filename convention. They take precedence
202
+ over mocks. Also, they get served on the same address, so no CORS issues.
203
+
204
+ Use Case 1: If you have a bunch of static assets you don’t want to add `.GET.200.ext`
205
+
206
+ Use Case 2: For a standalone demo server. For example,
207
+ build your frontend bundle, and serve it from Mockaton.
208
+
177
209
 
178
210
  ## `Config.cookies`
179
211
  The selected cookie is sent in every response in the `Set-Cookie` header.
@@ -219,6 +251,33 @@ Config.extraMimes = {
219
251
  }
220
252
  ```
221
253
 
254
+ ## `Config.onReady`
255
+ This is a callback `(dashboardAddress: string) => void`, which defaults to
256
+ trying to open the dashboard in your default browser in macOS and Windows.
257
+
258
+ If you don’t want to open a browser, pass a noop, such as
259
+ ```js
260
+ Config.onReady = () => {}
261
+ ```
262
+
263
+ On Linux, you could pass:
264
+ ```js
265
+ import { exec } from 'node:child_process'
266
+
267
+
268
+ Config.onReady = function openInBrowser(address) {
269
+ exec(`xdg-open ${address}`)
270
+ }
271
+ ```
272
+
273
+ Or, for more cross-platform utility, you could `npm install open` and pass it.
274
+ ```js
275
+ import open from 'open'
276
+
277
+
278
+ Config.onReady = open
279
+ ```
280
+
222
281
  ---
223
282
 
224
283
  ## API
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": "6.4.0",
5
+ "version": "6.4.3",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "license": "MIT",
@@ -1,5 +1,5 @@
1
1
  {
2
- "_": "This file has a comment: `(entirely unverified)`. i.e. the route is /api/user/videos",
2
+ "_": "This file has a comment: `(default)`. Which makes the file with comment: (all variants) not to win",
3
3
  "videos": [
4
4
  {
5
5
  "url": "https://example.com/1",
@@ -15,5 +15,5 @@ export const DF = { // Dashboard Fields (XHR)
15
15
  file: 'file'
16
16
  }
17
17
 
18
- export const DEFAULT_500_COMMENT = '(Mockaton Temp 500)'
18
+ export const DEFAULT_500_COMMENT = '(Mockaton 500)'
19
19
  export const DEFAULT_MOCK_COMMENT = '(default)'
package/src/Config.js CHANGED
@@ -21,6 +21,7 @@ export const Config = Object.seal({
21
21
  onReady: openInBrowser
22
22
  })
23
23
 
24
+
24
25
  export function setup(options) {
25
26
  Object.assign(Config, options)
26
27
  validate(Config, {
package/src/Filename.js CHANGED
@@ -4,7 +4,6 @@ const httpMethods = [
4
4
  'POST', 'PUT', 'TRACE'
5
5
  ]
6
6
 
7
-
8
7
  const reComments = /\(.*?\)/g // Anything within parentheses
9
8
 
10
9
  export const extractComments = filename =>
@@ -20,21 +19,18 @@ export function filenameIsValid(file) {
20
19
  console.error(error, file)
21
20
  return !error
22
21
  }
23
-
24
22
  function validateFilename(file) {
25
23
  const tokens = file.replace(reComments, '').split('.')
26
24
  if (tokens.length < 4)
27
25
  return 'Invalid Filename Convention'
28
-
29
26
  const { status, method } = parseFilename(file)
30
-
31
27
  if (!responseStatusIsValid(status))
32
28
  return `Invalid HTTP Response Status: "${status}"`
33
-
34
29
  if (!httpMethods.includes(method))
35
30
  return `Unrecognized HTTP Method: "${method}"`
36
31
  }
37
32
 
33
+
38
34
  export function parseFilename(file) {
39
35
  const tokens = file.replace(reComments, '').split('.')
40
36
  return {
@@ -44,6 +40,7 @@ export function parseFilename(file) {
44
40
  }
45
41
  }
46
42
 
43
+
47
44
  function removeTrailingSlash(url = '') {
48
45
  return url
49
46
  .replace(/\/$/, '')
package/src/MockBroker.js CHANGED
@@ -7,23 +7,18 @@ import { includesComment, extractComments, parseFilename } from './Filename.js'
7
7
  // that can be served for the route, the currently selected file, and its delay.
8
8
  export class MockBroker {
9
9
  #urlRegex
10
-
11
10
  constructor(file) {
12
11
  const { urlMask } = parseFilename(file)
13
12
  this.#urlRegex = new RegExp('^' + disregardVariables(removeQueryStringAndFragment(urlMask)) + '/*$')
14
-
15
13
  this.mocks = []
16
14
  this.currentMock = {
17
15
  file: '',
18
16
  delay: 0
19
17
  }
20
-
21
18
  this.register(file)
22
19
  }
23
20
 
24
- register(file) {
25
- this.mocks.push(file)
26
- }
21
+ register(file) { this.mocks.push(file) }
27
22
 
28
23
  // Appending a '/' so URLs ending with variables don't match
29
24
  // URLs that have a path after that variable. For example,
@@ -39,9 +34,16 @@ export class MockBroker {
39
34
  get file() { return this.currentMock.file }
40
35
  get delay() { return this.currentMock.delay }
41
36
  get status() { return parseFilename(this.file).status }
37
+ get isTemp500() { return includesComment(this.file, DEFAULT_500_COMMENT) }
42
38
 
43
39
  selectDefaultFile() {
44
- this.updateFile(this.#findMockWithDefaultComment() || this.mocks[0])
40
+ const userSpecifiedDefault = this.#findMockWithDefaultComment()
41
+ if (userSpecifiedDefault) // Sort for dashboard list TESTME
42
+ this.mocks = [
43
+ userSpecifiedDefault,
44
+ ...this.mocks.filter(m => m !== userSpecifiedDefault)
45
+ ]
46
+ this.updateFile(userSpecifiedDefault || this.mocks[0])
45
47
  }
46
48
  #findMockWithDefaultComment() {
47
49
  for (const f of this.mocks)
@@ -49,17 +51,9 @@ export class MockBroker {
49
51
  return f
50
52
  }
51
53
 
52
- mockExists(file) {
53
- return this.mocks.includes(file)
54
- }
55
-
56
- updateFile(filename) {
57
- this.currentMock.file = filename
58
- }
59
-
60
- updateDelay(delayed) {
61
- this.currentMock.delay = Number(delayed) * Config.delay
62
- }
54
+ mockExists(file) { return this.mocks.includes(file) }
55
+ updateFile(filename) { this.currentMock.file = filename }
56
+ updateDelay(delayed) { this.currentMock.delay = Number(delayed) * Config.delay }
63
57
 
64
58
  setByMatchingComment(comment) {
65
59
  for (const file of this.mocks)
@@ -88,9 +82,6 @@ export class MockBroker {
88
82
  const file = urlMask.replace(/^\//, '') // Removes leading slash TESTME
89
83
  this.register(`${file}${DEFAULT_500_COMMENT}.${method}.500.txt`)
90
84
  }
91
- get isTemp500() {
92
- return includesComment(this.file, DEFAULT_500_COMMENT)
93
- }
94
85
  }
95
86
 
96
87
  // Stars out (for regex) all the paths that are in square brackets