mockaton 2.3.0 → 3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  # Mockaton
2
2
  _Mockaton_ is a mock server for developing and testing frontends.
3
3
 
4
- It scans `Config.mocksDir` for files following a specific
4
+ It scans a given directory for files following a specific
5
5
  file name convention, which is similar to the URL paths. For
6
6
  example, the following file will be served for `/api/user/1234`
7
7
  ```
@@ -13,7 +13,54 @@ extension](https://github.com/ericfortis/devtools-ext-tar-http-requests) can
13
13
  be used for downloading a TAR of your XHR requests following that convention.
14
14
 
15
15
 
16
- ### Mock Variants
16
+ ## Getting Started
17
+ The best way to learn _Mockaton_ is by checking out this repo and
18
+ exploring its [sample-mocks/](./sample-mocks) directory. Then, run
19
+ [`./_usage_example.js`](./_usage_example.js) and you’ll see this dashboard:
20
+
21
+
22
+ <img src="./README-dashboard.png" style="max-width:820px"/>
23
+
24
+
25
+ ## Basic Usage
26
+ ```
27
+ npm install mockaton
28
+ ```
29
+ Create a `my-mockaton.js` file
30
+ ```js
31
+ import { resolve } from 'node:path'
32
+ import { Mockaton } from 'mockaton'
33
+
34
+ Mockaton({
35
+ mocksDir: resolve('my-mocks-dir'),
36
+ port: 2345
37
+ })
38
+ ```
39
+
40
+ ```sh
41
+ node my-mockaton.js
42
+ ```
43
+
44
+ ## Config Options
45
+ ```ts
46
+ interface Config {
47
+ mocksDir: string
48
+ staticDir?: string
49
+ host?: string, // defaults to 'localhost'
50
+ port?: number // defaults to 0, which means auto-assigned
51
+ delay?: number // defaults to 1200 (ms)
52
+ open?: (dashboardUrl: string) => void // pass a noop to prevent opening the dashboard
53
+ cookies?: object
54
+ proxyFallback?: string // e.g. http://localhost:9999 Target for relaying routes without mocks
55
+ allowedExt?: RegExp // /\.(json|txt|md|js)$/ Just for excluding temporary editor files (e.g. JetBrains appends a ~)
56
+ generate500?: boolean // autogenerates an Internal Server Error empty mock for routes that have no 500
57
+ extraHeaders?: []
58
+ }
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Mock Variants
17
64
  Each route can have many mocks, which could either be:
18
65
  - Different response __status code__.
19
66
  - e.g. for testing error responses.
@@ -25,7 +72,7 @@ UI, or programmatically, for instance, for setting up tests.
25
72
 
26
73
  The first file in **alphabetical order** becomes the default mock.
27
74
 
28
- ### Optionally, you can write mocks in JavaScript
75
+ ## You can write JSON mocks in JavaScript
29
76
  An Object, Array, or String is sent as JSON.
30
77
 
31
78
  `api/foo.GET.200.js`
@@ -45,18 +92,11 @@ export default function (req, response) {
45
92
  ```
46
93
 
47
94
 
48
- ### Proxying Routes
95
+ ## Proxying Routes
49
96
  `Config.proxyFallback` lets you specify a target
50
97
  server for serving routes you don’t have mocks for.
51
98
 
52
99
 
53
- ## Getting Started
54
- The best way to learn _Mockaton_ is by checking out this repo and
55
- exploring its [sample-mocks/](./sample-mocks) directory. Then, run
56
- [`./_usage_example.js`](./_usage_example.js) and you’ll see this dashboard:
57
-
58
- <img src="./README-dashboard.png" style="max-width:890px"/>
59
-
60
100
 
61
101
  ## Delay 🕓
62
102
  The clock icon next to the mock selector is a checkbox for delaying a
@@ -64,44 +104,6 @@ particular response. They are handy for testing spinners.
64
104
 
65
105
  The delay is globally configurable via `Config.delay = 1200` (milliseconds).
66
106
 
67
- ---
68
-
69
- ## Basic Usage (see [_usage_example.js](./_usage_example.js))
70
- ```
71
- npm install mockaton
72
- ```
73
- Create a `my-mockaton.js` file
74
- ```js
75
- import { resolve } from 'node:path'
76
- import { Mockaton } from 'mockaton'
77
-
78
- Mockaton({
79
- mocksDir: resolve('my-mocks-dir'),
80
- port: 2345
81
- })
82
- ```
83
-
84
- ```sh
85
- node my-mockaton.js
86
- ```
87
-
88
- ## Config Options
89
- ```ts
90
- interface Config {
91
- mocksDir: string
92
- staticDir?: string
93
- host?: string, // defaults to 'localhost'
94
- port?: number // defaults to 0, which means auto-assigned
95
- delay?: number // defaults to 1200 (ms)
96
- cookies?: object
97
- skipOpen?: boolean // Prevents opening the dashboard in a browser
98
- proxyFallback?: string // e.g. http://localhost:9999 Target for relaying routes without mocks
99
- allowedExt?: RegExp // /\.(json|txt|md|js)$/ Just for excluding temporary editor files (e.g. JetBrains appends a ~)
100
- generate500?: boolean // autogenerates an Internal Server Error empty mock for routes that have no 500
101
- extraHeaders?: []
102
- }
103
- ```
104
- ---
105
107
 
106
108
  ## File Name Convention
107
109
 
package/Tests.js CHANGED
@@ -106,7 +106,7 @@ writeStatic('another-entry/index.html', '<h1>Another</h1>')
106
106
  const server = Mockaton({
107
107
  mocksDir: tmpDir,
108
108
  staticDir: staticTmpDir,
109
- skipOpen: true,
109
+ open: () => {},
110
110
  cookies: {
111
111
  userA: 'CookieA',
112
112
  userB: 'CookieB'
package/index.d.ts CHANGED
@@ -6,8 +6,8 @@ interface Config {
6
6
  host?: string,
7
7
  port?: number
8
8
  delay?: number
9
+ open?: (address: string) => void
9
10
  cookies?: object
10
- skipOpen?: boolean
11
11
  proxyFallback?: string
12
12
  allowedExt?: RegExp
13
13
  generate500?: boolean,
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": "2.3.0",
5
+ "version": "3.0.0",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "license": "MIT",
package/src/Api.js CHANGED
@@ -9,7 +9,7 @@ import { Config } from './Config.js'
9
9
  import { DF, API } from './ApiConstants.js'
10
10
  import { parseJSON } from './utils/http-request.js'
11
11
  import * as mockBrokersCollection from './mockBrokersCollection.js'
12
- import { sendOK, sendBadRequest, sendJSON, sendFile } from './utils/http-response.js'
12
+ import { sendOK, sendBadRequest, sendJSON, sendFile, sendUnprocessableContent } from './utils/http-response.js'
13
13
 
14
14
 
15
15
  export const apiGetRequests = new Map([
@@ -24,11 +24,11 @@ export const apiGetRequests = new Map([
24
24
  ])
25
25
 
26
26
  export const apiPatchRequests = new Map([
27
- [API.bulkSelect, bulkUpdateBrokersByCommentTag],
28
27
  [API.edit, updateBroker],
29
28
  [API.reset, reinitialize],
30
29
  [API.cookies, selectCookie],
31
- [API.fallback, updateProxyFallback]
30
+ [API.fallback, updateProxyFallback],
31
+ [API.bulkSelect, bulkUpdateBrokersByCommentTag]
32
32
  ])
33
33
 
34
34
  function serveDashboard(_, response) {
@@ -48,6 +48,11 @@ function listMockBrokers(_, response) {
48
48
  }
49
49
 
50
50
 
51
+ function reinitialize(_, response) {
52
+ mockBrokersCollection.init()
53
+ sendOK(response)
54
+ }
55
+
51
56
  async function selectCookie(req, response) {
52
57
  try {
53
58
  cookie.setCurrent(await parseJSON(req))
@@ -59,15 +64,14 @@ async function selectCookie(req, response) {
59
64
  }
60
65
  }
61
66
 
62
- function reinitialize(_, response) {
63
- mockBrokersCollection.init()
64
- sendOK(response)
65
- }
66
-
67
67
  async function updateBroker(req, response) {
68
68
  try {
69
69
  const body = await parseJSON(req)
70
70
  const broker = mockBrokersCollection.getBrokerByFilename(body[DF.file])
71
+ if (!broker) {
72
+ sendUnprocessableContent(response)
73
+ return
74
+ }
71
75
  if (DF.delayed in body)
72
76
  broker.updateDelay(body[DF.delayed])
73
77
  broker.updateFile(body[DF.file])
@@ -79,9 +83,9 @@ async function updateBroker(req, response) {
79
83
  }
80
84
  }
81
85
 
82
- async function bulkUpdateBrokersByCommentTag(req, response) {
86
+ async function updateProxyFallback(req, response) {
83
87
  try {
84
- mockBrokersCollection.setMocksMatchingComment(await parseJSON(req))
88
+ Config.proxyFallback = await parseJSON(req)
85
89
  sendOK(response)
86
90
  }
87
91
  catch (error) {
@@ -90,9 +94,9 @@ async function bulkUpdateBrokersByCommentTag(req, response) {
90
94
  }
91
95
  }
92
96
 
93
- async function updateProxyFallback(req, response) {
97
+ async function bulkUpdateBrokersByCommentTag(req, response) {
94
98
  try {
95
- Config.proxyFallback = await parseJSON(req)
99
+ mockBrokersCollection.setMocksMatchingComment(await parseJSON(req))
96
100
  sendOK(response)
97
101
  }
98
102
  catch (error) {
package/src/Config.js CHANGED
@@ -1,5 +1,5 @@
1
- import { existsSync as exists, lstatSync } from 'node:fs'
2
- import { validate, is, optional } from './utils/validate.js'
1
+ import { openInBrowser } from './utils/openInBrowser.js'
2
+ import { validate, is, optional, isDirectory } from './utils/validate.js'
3
3
 
4
4
 
5
5
  export const Config = {
@@ -8,8 +8,8 @@ export const Config = {
8
8
  host: '127.0.0.1',
9
9
  port: 0, // auto-assigned
10
10
  delay: 1200, // milliseconds
11
+ open: openInBrowser,
11
12
  cookies: {}, // defaults to the first kv
12
- skipOpen: false,
13
13
  proxyFallback: '', // e.g. http://localhost:9999
14
14
  allowedExt: /\.(json|txt|md|js)$/, // Just for excluding temporary editor files (e.g. JetBrains appends a ~)
15
15
  generate500: false,
@@ -24,17 +24,13 @@ export function setup(options) {
24
24
  host: is(String),
25
25
  port: port => Number.isInteger(port) && port >= 0 && port < 2 ** 16,
26
26
  delay: ms => Number.isInteger(ms) && ms > 0,
27
+ open: is(Function),
27
28
  cookies: is(Object),
28
- skipOpen: is(Boolean),
29
- proxyFallback: is(String),
29
+ proxyFallback: optional(URL.canParse),
30
30
  allowedExt: is(RegExp),
31
31
  generate500: is(Boolean),
32
32
  extraHeaders: Array.isArray
33
33
  })
34
34
  }
35
35
 
36
- function isDirectory(dir) {
37
- return exists(dir) && lstatSync(dir).isDirectory()
38
- }
39
-
40
36
 
package/src/Mockaton.js CHANGED
@@ -36,7 +36,7 @@ export function Mockaton(options) {
36
36
  console.log('Dashboard', url + API.dashboard)
37
37
  if (error)
38
38
  console.error(error)
39
- else if (!Config.skipOpen)
40
- exec(`open ${url + API.dashboard}`)
39
+ else
40
+ Config.open(url + API.dashboard)
41
41
  })
42
42
  }
@@ -54,7 +54,8 @@ export const getAll = () => collection
54
54
 
55
55
  export const getBrokerByFilename = file => {
56
56
  const { method, urlMask } = Route.parseFilename(file)
57
- return collection[method][urlMask]
57
+ if (collection[method])
58
+ return collection[method][urlMask]
58
59
  }
59
60
 
60
61
  // Searching the routes in reverse order so dynamic params (e.g.
@@ -54,6 +54,11 @@ export function sendNotFound(response) {
54
54
  response.end()
55
55
  }
56
56
 
57
+ export function sendUnprocessableContent(response) {
58
+ response.statusCode = 422
59
+ response.end()
60
+ }
61
+
57
62
  export function sendInternalServerError(response) {
58
63
  response.statusCode = 500
59
64
  response.end()
@@ -0,0 +1,8 @@
1
+ import { exec } from 'node:child_process'
2
+
3
+
4
+ export function openInBrowser(address) {
5
+ if (process.platform === 'darwin')
6
+ exec(`open ${address}`)
7
+ }
8
+
@@ -1,3 +1,6 @@
1
+ import { existsSync as exists, lstatSync } from 'node:fs'
2
+
3
+
1
4
  export function validate(obj, shape) {
2
5
  for (const [field, value] of Object.entries(obj))
3
6
  if (!shape[field](value))
@@ -6,3 +9,4 @@ export function validate(obj, shape) {
6
9
 
7
10
  export const is = ctor => val => val.constructor === ctor
8
11
  export const optional = tester => val => !val || tester(val)
12
+ export const isDirectory = dir => exists(dir) && lstatSync(dir).isDirectory()