mockaton 8.2.22 → 8.2.23

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
@@ -6,7 +6,7 @@
6
6
 
7
7
  _Mockaton_ is a mock server for improving the frontend development and testing experience.
8
8
 
9
- With Mockaton, you don’t need to write code for wiring your mocks. Instead, it
9
+ With Mockaton you don’t need to write code for wiring your mocks. Instead, it
10
10
  scans a given directory for filenames following a convention similar to the
11
11
  URL paths. For example, the following file will be served on `/api/user/1234`
12
12
  ```
@@ -37,7 +37,7 @@ which is handy for setting up tests (see **Commander API** below).
37
37
  <picture>
38
38
  <source media="(prefers-color-scheme: light)" srcset="./README-dashboard-light.png">
39
39
  <source media="(prefers-color-scheme: dark)" srcset="./README-dashboard-dark.png">
40
- <img alt="Mockaton Dashboard Demo" src="./README-dashboard-light.png" style="max-width: 860px">
40
+ <img alt="Mockaton Dashboard Demo" src="./README-dashboard-light.png">
41
41
  </picture>
42
42
 
43
43
 
@@ -93,7 +93,7 @@ The _Reset_ button is for registering newly added, removed, or renamed mocks.
93
93
  - Polled resources (for triggering their different states)
94
94
  - alerts
95
95
  - notifications
96
- - slow to build assets
96
+ - slow to build resources
97
97
 
98
98
  ### Time Travel
99
99
  If you commit the mocks to your repo, it’s straightforward to bisect bugs and
@@ -103,8 +103,8 @@ backends to old API contracts or databases.
103
103
  ### Deterministic Standalone Demo Server
104
104
  Perhaps you need to demo your app, but the ideal flow is too complex to
105
105
  simulate from the actual backend. In this case, compile your frontend app and
106
- put its built assets in `config.staticDir`. Then, from the Mockaton dashboard
107
- you can "Bulk Select" mocks to simulate the complete states you want to demo.
106
+ put its built assets in `config.staticDir`. Then, on the dashboard
107
+ "Bulk Select" mocks to simulate the complete states you want to demo.
108
108
  For bulk-selecting, you just need to add a comment to the mock
109
109
  filename, such as `(demo-part1)`, `(demo-part2)`.
110
110
 
@@ -113,7 +113,7 @@ filename, such as `(demo-part1)`, `(demo-part2)`.
113
113
  - Avoids spinning up and maintaining hefty backends when developing UIs.
114
114
  - For a deterministic, comprehensive, and consistent backend state. For example, having
115
115
  a collection with all the possible state variants helps for spotting inadvertent bugs.
116
- - Sometimes, frontend progress is blocked waiting for some backend API. Similarly,
116
+ - Sometimes frontend progress is blocked waiting for some backend API. Similarly,
117
117
  it’s often delayed due to missing data or inconvenient contracts. Therefore,
118
118
  many meetings can be saved by prototyping frontend features with mocks, and
119
119
  then showing those contracts to the backend team.
@@ -271,8 +271,9 @@ Defaults to `0`, which means auto-assigned
271
271
  Defaults to `/(\.DS_Store|~)$/`
272
272
 
273
273
 
274
- ### `delay?: number` 🕓
275
- The delay is globally configurable, it defaults to `1200` (milliseconds).
274
+ ### `delay?: number`
275
+ Routes can individually be delayed with the 🕓 checkbox. On the other hand,
276
+ the amount is globally configurable. It defaults to `config.delay=1200` milliseconds.
276
277
 
277
278
 
278
279
  ### `proxyFallback?: string`
@@ -349,9 +350,10 @@ type Plugin = (
349
350
  body: string | Uint8Array
350
351
  }>
351
352
  ```
352
- Plugins are for processing mocks before sending them.
353
+ Plugins are for processing mocks before sending them. If no regex matches the filename,
354
+ it fallbacks to reading the file from disk and computing the MIME from the extension.
353
355
 
354
- Note: don’t call `response.end()` on them.
356
+ Note: don’t call `response.end()` on any plugin.
355
357
 
356
358
  <details>
357
359
  <summary><b> See Plugin Examples </b></summary>
@@ -365,7 +367,11 @@ import { readFileSync } from 'node:js'
365
367
  import { jsToJsonPlugin } from 'mockaton'
366
368
 
367
369
  config.plugins = [
368
- [/\.(js|ts)$/, jsToJsonPlugin], // Default but you need to add it to your list if you need it
370
+
371
+ // Although `jsToJsonPlugin` is set by default, you need to add it to your list if you need it.
372
+ // In other words, your plugins array overwrites the default list. This way you can remove it.
373
+ [/\.(js|ts)$/, jsToJsonPlugin],
374
+
369
375
  [/\.yml$/, yamlToJsonPlugin],
370
376
  [/foo\.GET\.200\.txt$/, capitalizePlugin], // e.g. GET /api/foo would be capitalized
371
377
  ]
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.2.22",
5
+ "version": "8.2.23",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "license": "MIT",
package/src/Api.js CHANGED
@@ -39,6 +39,8 @@ export const apiPatchRequests = new Map([
39
39
  [API.cors, setCorsAllowed]
40
40
  ])
41
41
 
42
+ /* GET */
43
+
42
44
  function serveDashboard(_, response) { sendFile(response, join(import.meta.dirname, 'Dashboard.html')) }
43
45
  function serveDashboardAsset(req, response) { sendFile(response, join(import.meta.dirname, req.url)) }
44
46
 
@@ -48,13 +50,26 @@ function listMockBrokers(_, response) { sendJSON(response, mockBrokersCollection
48
50
  function getProxyFallback(_, response) { sendJSON(response, Config.proxyFallback) }
49
51
  function getIsCorsAllowed(_, response) { sendJSON(response, Config.corsAllowed) }
50
52
 
53
+ async function listStaticFiles(req, response) { // TESTME
54
+ try {
55
+ const files = Config.staticDir
56
+ ? listFilesRecursively(Config.staticDir).filter(f => !Config.ignore.test(f))
57
+ : []
58
+ sendJSON(response, files)
59
+ }
60
+ catch (error) {
61
+ sendBadRequest(response, error)
62
+ }
63
+ }
64
+
65
+
66
+ /* PATCH */
51
67
 
52
68
  function reinitialize(_, response) {
53
69
  mockBrokersCollection.init()
54
70
  sendOK(response)
55
71
  }
56
72
 
57
-
58
73
  async function selectCookie(req, response) {
59
74
  try {
60
75
  cookie.setCurrent(await parseJSON(req))
@@ -65,14 +80,12 @@ async function selectCookie(req, response) {
65
80
  }
66
81
  }
67
82
 
68
-
69
83
  async function selectMock(req, response) {
70
84
  try {
71
85
  const file = await parseJSON(req)
72
86
  const broker = mockBrokersCollection.getBrokerByFilename(file)
73
87
  if (!broker || !broker.mockExists(file))
74
88
  throw `Missing Mock: ${file}`
75
-
76
89
  broker.updateFile(file)
77
90
  sendOK(response)
78
91
  }
@@ -81,17 +94,14 @@ async function selectMock(req, response) {
81
94
  }
82
95
  }
83
96
 
84
-
85
97
  async function setRouteIsDelayed(req, response) {
86
98
  try {
87
99
  const body = await parseJSON(req)
88
100
  const broker = mockBrokersCollection.getBrokerForUrl(
89
101
  body[DF.routeMethod],
90
102
  body[DF.routeUrlMask])
91
-
92
103
  if (!broker)
93
104
  throw `Route does not exist: ${body[DF.routeUrlMask]} ${body[DF.routeUrlMask]}`
94
-
95
105
  broker.updateDelay(body[DF.delayed])
96
106
  sendOK(response)
97
107
  }
@@ -100,7 +110,6 @@ async function setRouteIsDelayed(req, response) {
100
110
  }
101
111
  }
102
112
 
103
-
104
113
  async function updateProxyFallback(req, response) {
105
114
  try {
106
115
  const fallback = await parseJSON(req)
@@ -116,7 +125,6 @@ async function updateProxyFallback(req, response) {
116
125
  }
117
126
  }
118
127
 
119
-
120
128
  async function bulkUpdateBrokersByCommentTag(req, response) {
121
129
  try {
122
130
  mockBrokersCollection.setMocksMatchingComment(await parseJSON(req))
@@ -127,7 +135,6 @@ async function bulkUpdateBrokersByCommentTag(req, response) {
127
135
  }
128
136
  }
129
137
 
130
-
131
138
  async function setCorsAllowed(req, response) {
132
139
  try {
133
140
  Config.corsAllowed = await parseJSON(req)
@@ -138,16 +145,3 @@ async function setCorsAllowed(req, response) {
138
145
  }
139
146
  }
140
147
 
141
-
142
- // TESTME
143
- async function listStaticFiles(req, response) {
144
- try {
145
- const files = Config.staticDir
146
- ? listFilesRecursively(Config.staticDir).filter(f => !Config.ignore.test(f))
147
- : []
148
- sendJSON(response, files)
149
- }
150
- catch (error) {
151
- sendBadRequest(response, error)
152
- }
153
- }
package/src/Commander.js CHANGED
@@ -7,6 +7,16 @@ export class Commander {
7
7
  this.#addr = addr
8
8
  }
9
9
 
10
+ #get(api) {
11
+ return fetch(this.#addr + api)
12
+ }
13
+ #patch(api, body) {
14
+ return fetch(this.#addr + api, {
15
+ method: 'PATCH',
16
+ body: JSON.stringify(body)
17
+ })
18
+ }
19
+
10
20
  listMocks() {
11
21
  return this.#get(API.mocks)
12
22
  }
@@ -58,15 +68,4 @@ export class Commander {
58
68
  listStaticFiles() {
59
69
  return this.#get(API.static)
60
70
  }
61
-
62
-
63
- #get(api) {
64
- return fetch(this.#addr + api)
65
- }
66
- #patch(api, body) {
67
- return fetch(this.#addr + api, {
68
- method: 'PATCH',
69
- body: JSON.stringify(body)
70
- })
71
- }
72
71
  }
package/src/Dashboard.js CHANGED
@@ -423,7 +423,7 @@ function createElement(elem, props = null, ...children) {
423
423
  node[key] = value
424
424
  else
425
425
  node.setAttribute(key, value)
426
- node.append(...children.flat())
426
+ node.append(...children.flat().filter(a => a))
427
427
  return node
428
428
  }
429
429
 
@@ -431,7 +431,7 @@ function createSvgElement(tagName, props, ...children) {
431
431
  const elem = document.createElementNS('http://www.w3.org/2000/svg', tagName)
432
432
  for (const [key, value] of Object.entries(props))
433
433
  elem.setAttribute(key, value)
434
- elem.append(...children.flat())
434
+ elem.append(...children.flat().filter(a => a))
435
435
  return elem
436
436
  }
437
437
 
@@ -7,10 +7,6 @@ export async function applyPlugins(filePath, req, response) {
7
7
  for (const [regex, plugin] of Config.plugins) // TESTME capitalizePlugin
8
8
  if (regex.test(filePath))
9
9
  return await plugin(filePath, req, response)
10
- return defaultPlugin(filePath)
11
- }
12
-
13
- export function defaultPlugin(filePath) {
14
10
  return {
15
11
  mime: mimeFor(filePath),
16
12
  body: read(filePath)
package/src/utils/fs.js CHANGED
@@ -8,6 +8,9 @@ export const isDirectory = path => lstatSync(path, { throwIfNoEntry: false })?.i
8
8
  export const read = path => readFileSync(path)
9
9
 
10
10
  /** @returns {Array<string>} paths relative to `dir` */
11
- export const listFilesRecursively = dir => readdirSync(dir, { recursive: true })
12
- .map(f => f.replaceAll(path.sep, path.posix.sep)) // TESTME
13
- .filter(f => isFile(join(dir, f)))
11
+ export const listFilesRecursively = dir => {
12
+ const files = readdirSync(dir, { recursive: true }).filter(f => isFile(join(dir, f)))
13
+ return process.platform === 'win32'
14
+ ? files.map(f => f.replaceAll(path.sep, path.posix.sep)) // TESTME
15
+ : files
16
+ }