mockaton 8.1.7 → 8.2.1

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
Binary file
package/README.md CHANGED
@@ -127,6 +127,7 @@ database, or pull data from a backend.
127
127
  Don’t call `response.end()`, just return a `string | Buffer | Uint8Array`.
128
128
 
129
129
  ```js
130
+
130
131
  export default function optionalName(request, response) {
131
132
  globalThis.myDatabase ??= { count: 0 }
132
133
  globalThis.myDatabase.count++
@@ -142,6 +143,19 @@ export default function optionalName(request, response) {
142
143
  If you need to serve a static `.js` file, put it in your
143
144
  `Config.staticDir` without the mock filename convention.
144
145
 
146
+ This example will echo back the request body concatenated with another fixture.
147
+ ```js
148
+ // api/color.POST.201.js
149
+
150
+ import colors from './colors.json' with { type: 'json' }
151
+ import { parseJSON } from 'mockaton' // body-parser alike
152
+
153
+ export default async function concatColor(request, response) {
154
+ const newColor = await parseJSON(request)
155
+ return JSON.stringify(colors.concat(newColor))
156
+ }
157
+ ```
158
+
145
159
  ---
146
160
 
147
161
  ## Mock File Name Convention
package/index.d.ts CHANGED
@@ -42,8 +42,13 @@ export function Mockaton(options: Config): Server
42
42
 
43
43
  export const jsToJsonPlugin: Plugin
44
44
 
45
+
46
+ // Utils
47
+
45
48
  export function jwtCookie(cookieName: string, payload: any): string
46
49
 
50
+ export function parseJSON(request: IncomingMessage): Promise<any>
51
+
47
52
 
48
53
  export class Commander {
49
54
  constructor(addr: string)
package/index.js CHANGED
@@ -1,4 +1,6 @@
1
1
  export { Mockaton } from './src/Mockaton.js'
2
- export { jwtCookie } from './src/utils/jwt.js'
3
2
  export { Commander } from './src/Commander.js'
4
3
  export { jsToJsonPlugin } from './src/MockDispatcherPlugins.js'
4
+
5
+ export { jwtCookie } from './src/utils/jwt.js'
6
+ export { parseJSON } from './src/utils/http-request.js'
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.1.7",
5
+ "version": "8.2.1",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "license": "MIT",
@@ -11,10 +11,11 @@
11
11
  "test": "node --test src/**.test.js",
12
12
  "demo": "node _usage_example.js",
13
13
  "demo:ts": "node --import=tsx _usage_example.js",
14
- "demo:test-ui": "node --test --import=./ui-tests/_setup.js --experimental-test-isolation=none \"./ui-tests/**/*.test.js\""
14
+ "demo:test-ui": "node --test --import=./ui-tests/_setup.js --experimental-test-isolation=none \"./ui-tests/**/*.test.js\"",
15
+ "outdated": "npm outdated --parseable | awk -F: '{ printf \"npm i %-30s ;# %s\\n\", $4, $2 }'"
15
16
  },
16
17
  "optionalDependencies": {
17
- "puppeteer": "23.7.1",
18
- "pixaton": "0.1.0"
18
+ "pixaton": ">=1.0.1",
19
+ "puppeteer": ">=23.10.1"
19
20
  }
20
21
  }
package/src/Api.js CHANGED
@@ -10,7 +10,7 @@ import { DF, API } from './ApiConstants.js'
10
10
  import { parseJSON } from './utils/http-request.js'
11
11
  import { listFilesRecursively } from './utils/fs.js'
12
12
  import * as mockBrokersCollection from './mockBrokersCollection.js'
13
- import { sendOK, sendBadRequest, sendJSON, sendFile } from './utils/http-response.js'
13
+ import { sendOK, sendBadRequest, sendJSON, sendFile, sendUnprocessableContent } from './utils/http-response.js'
14
14
 
15
15
 
16
16
  export const apiGetRequests = new Map([
@@ -24,6 +24,7 @@ export const apiGetRequests = new Map([
24
24
  [API.mocks, listMockBrokers],
25
25
  [API.cookies, listCookies],
26
26
  [API.comments, listComments],
27
+ [API.fallback, getProxyFallback],
27
28
  [API.cors, getIsCorsAllowed],
28
29
  [API.static, listStaticFiles]
29
30
  ])
@@ -44,6 +45,7 @@ function serveDashboardAsset(req, response) { sendFile(response, join(import.met
44
45
  function listCookies(_, response) { sendJSON(response, cookie.list()) }
45
46
  function listComments(_, response) { sendJSON(response, mockBrokersCollection.extractAllComments()) }
46
47
  function listMockBrokers(_, response) { sendJSON(response, mockBrokersCollection.getAll()) }
48
+ function getProxyFallback(_, response) { sendJSON(response, Config.proxyFallback) }
47
49
  function getIsCorsAllowed(_, response) { sendJSON(response, Config.corsAllowed) }
48
50
 
49
51
 
@@ -101,8 +103,13 @@ async function setRouteIsDelayed(req, response) {
101
103
 
102
104
  async function updateProxyFallback(req, response) {
103
105
  try {
104
- Config.proxyFallback = await parseJSON(req)
105
- sendOK(response)
106
+ const fallback = await parseJSON(req)
107
+ if (fallback && !URL.canParse(fallback)) // TESTME
108
+ sendUnprocessableContent(response)
109
+ else {
110
+ Config.proxyFallback = fallback
111
+ sendOK(response)
112
+ }
106
113
  }
107
114
  catch (error) {
108
115
  sendBadRequest(response, error)
package/src/Commander.js CHANGED
@@ -37,6 +37,9 @@ export class Commander {
37
37
  return this.#get(API.comments)
38
38
  }
39
39
 
40
+ getProxyFallback() {
41
+ return this.#get(API.fallback)
42
+ }
40
43
  setProxyFallback(proxyAddr) {
41
44
  return this.#patch(API.fallback, proxyAddr)
42
45
  }
package/src/Dashboard.css CHANGED
@@ -1,14 +1,14 @@
1
1
  :root {
2
- --boxShadow1: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12);
2
+ --boxShadow1: 0 2px 1px -1px rgba(0, 0, 0, 0.1), 0 1px 1px 0 rgba(0, 0, 0, 0.1), 0 1px 3px 0 rgba(0, 0, 0, 0.08);
3
3
  }
4
4
 
5
5
  @media (prefers-color-scheme: light) {
6
6
  :root {
7
7
  --color4xxBackground: #ffedd1;
8
- --colorAccent: #0072d6;
9
- --colorAccentAlt: #0e906c;
8
+ --colorAccent: #0081ef;
9
+ --colorAccentAlt: #009c71;
10
10
  --colorBackground: #fff;
11
- --colorHeaderBackground: #f4f4f4;
11
+ --colorHeaderBackground: #f7f7f7;
12
12
  --colorComboBoxBackground: #fafafa;
13
13
  --colorComboBoxHeaderBackground: #fff;
14
14
  --colorDisabled: #444;
@@ -23,8 +23,8 @@
23
23
  @media (prefers-color-scheme: dark) {
24
24
  :root {
25
25
  --color4xxBackground: #403630;
26
- --colorAccent: #1f91ff;
27
- --colorAccentAlt: #00E676;
26
+ --colorAccent: #2495ff;
27
+ --colorAccentAlt: #00bf64;
28
28
  --colorBackground: #161616;
29
29
  --colorHeaderBackground: #090909;
30
30
  --colorComboBoxBackground: #252525;
@@ -61,8 +61,8 @@ select {
61
61
  background: var(--colorComboBoxBackground);
62
62
  color: var(--colorText);
63
63
  cursor: pointer;
64
- border-radius: 4px;
65
64
  outline: 0;
65
+ border-radius: 6px;
66
66
 
67
67
  &:enabled {
68
68
  box-shadow: var(--boxShadow1);
@@ -84,15 +84,16 @@ menu {
84
84
  display: flex;
85
85
  width: 100%;
86
86
  align-items: flex-end;
87
- padding: 20px 16px;
87
+ padding: 16px;
88
88
  border-bottom: 1px solid rgba(127, 127, 127, 0.1);
89
89
  background: var(--colorHeaderBackground);
90
- gap: 16px;
90
+ box-shadow: var(--boxShadow1);
91
+ gap: 12px;
91
92
 
92
93
  img {
93
94
  width: 130px;
94
95
  align-self: center;
95
- margin-right: 85px;
96
+ margin-right: 18px;
96
97
  }
97
98
 
98
99
  label {
@@ -102,13 +103,23 @@ menu {
102
103
  font-size: 11px;
103
104
  }
104
105
 
106
+ input[type=url],
105
107
  select {
108
+ height: 24px;
106
109
  width: 150px;
107
- padding: 4px;
110
+ padding: 4px 2px;
108
111
  border-right: 3px solid transparent;
109
112
  margin-top: 2px;
110
113
  font-size: 11px;
111
114
  background: var(--colorComboBoxHeaderBackground);
115
+ border-radius: 6px;
116
+ }
117
+
118
+ input[type=url] {
119
+ outline: 0;
120
+ padding: 0 6px;
121
+ box-shadow: var(--boxShadow1);
122
+ color: var(--colorText);
112
123
  }
113
124
  }
114
125
 
@@ -139,7 +150,7 @@ menu {
139
150
  main {
140
151
  display: flex;
141
152
  align-items: flex-start;
142
- margin-top: 64px;
153
+ margin-top: 56px;
143
154
 
144
155
  > table {
145
156
  border-collapse: collapse;
@@ -159,7 +170,7 @@ main {
159
170
 
160
171
  .PayloadViewer {
161
172
  position: sticky;
162
- top: 72px;
173
+ top: 62px;
163
174
  width: 50%;
164
175
  margin-left: 20px;
165
176
 
@@ -190,7 +201,7 @@ main {
190
201
  display: inline-block;
191
202
  width: 280px;
192
203
  padding: 8px 6px;
193
- border-radius: 0;
204
+ border-radius: 6px;
194
205
  color: var(--colorAccent);
195
206
  text-decoration: none;
196
207
 
@@ -209,7 +220,6 @@ main {
209
220
  padding: 8px 1px;
210
221
  border: 0;
211
222
  border-left: 3px solid transparent;
212
- border-radius: 0;
213
223
  text-align: right;
214
224
  direction: rtl;
215
225
  text-overflow: ellipsis;
@@ -260,7 +270,7 @@ main {
260
270
 
261
271
  .InternalServerErrorToggler {
262
272
  display: flex;
263
- margin-left: 6px;
273
+ margin-left: 8px;
264
274
  cursor: pointer;
265
275
 
266
276
  > input {
package/src/Dashboard.js CHANGED
@@ -12,6 +12,8 @@ const Strings = {
12
12
  cookie_disabled_title: 'No cookies specified in Config.cookies',
13
13
  delay: 'Delay',
14
14
  empty_response_body: '/* Empty Response Body */',
15
+ fallback_server: 'Fallback Server',
16
+ fallback_server_placeholder: 'Type Server Address',
15
17
  internal_server_error: 'Internal Server Error',
16
18
  mock: 'Mock',
17
19
  reset: 'Reset',
@@ -46,6 +48,7 @@ function init() {
46
48
  mockaton.listCookies(),
47
49
  mockaton.listComments(),
48
50
  mockaton.getCorsAllowed(),
51
+ mockaton.getProxyFallback(),
49
52
  mockaton.listStaticFiles()
50
53
  ].map(api => api.then(response => response.ok && response.json())))
51
54
  .then(App)
@@ -59,13 +62,14 @@ function App(apiResponses) {
59
62
  .render(DevPanel(apiResponses))
60
63
  }
61
64
 
62
- function DevPanel([brokersByMethod, cookies, comments, corsAllowed, staticFiles]) {
65
+ function DevPanel([brokersByMethod, cookies, comments, corsAllowed, fallbackAddress, staticFiles]) {
63
66
  return (
64
67
  r('div', null,
65
68
  r('menu', null,
66
69
  r('img', { src: '/mockaton-logo.svg', width: 160, alt: Strings.title }),
67
70
  r(CookieSelector, { list: cookies }),
68
71
  r(BulkSelector, { comments }),
72
+ r(ProxyFallbackField, { fallbackAddress }),
69
73
  r(CorsCheckbox, { corsAllowed }),
70
74
  r(ResetButton)),
71
75
  r('main', null,
@@ -124,6 +128,29 @@ function BulkSelector({ comments }) {
124
128
  }
125
129
 
126
130
 
131
+ function ProxyFallbackField({ fallbackAddress = '' }) {
132
+ function onChange(event) {
133
+ const input = event.currentTarget
134
+ if (!input.validity.valid)
135
+ input.reportValidity()
136
+ else
137
+ mockaton.setProxyFallback(input.value)
138
+ .catch(console.error)
139
+ }
140
+
141
+ return (
142
+ r('label', null,
143
+ r('span', null, Strings.fallback_server),
144
+ r('input', {
145
+ type: 'url',
146
+ autocomplete: 'none',
147
+ placeholder: Strings.fallback_server_placeholder,
148
+ value: fallbackAddress,
149
+ onChange
150
+ })))
151
+ }
152
+
153
+
127
154
  function CorsCheckbox({ corsAllowed }) {
128
155
  function onChange(event) {
129
156
  mockaton.setCorsAllowed(event.currentTarget.checked)
@@ -148,8 +175,7 @@ function ResetButton() {
148
175
  .then(init)
149
176
  .catch(console.error)
150
177
  }
151
- }, Strings.reset)
152
- )
178
+ }, Strings.reset))
153
179
  }
154
180
 
155
181
 
@@ -272,7 +298,6 @@ function MockSelector({ broker }) {
272
298
  .catch(console.error)
273
299
  }
274
300
 
275
-
276
301
  function className(defaultIsSelected, status) {
277
302
  return cssClass(
278
303
  CSS.MockSelector,
@@ -2,7 +2,7 @@
2
2
  <svg version="1.1" viewBox="0 0 570 100" xmlns="http://www.w3.org/2000/svg">
3
3
  <style>
4
4
  :root { --color: #000000; }
5
- @media (prefers-color-scheme: light) { :root { --color: #444 } }
5
+ @media (prefers-color-scheme: light) { :root { --color: #555 } }
6
6
  @media (prefers-color-scheme: dark) { :root { --color: #eee } }
7
7
  path { fill: var(--color) }
8
8
  </style>
@@ -17,12 +17,12 @@ export function sendJSON(response, payload) {
17
17
  response.end(JSON.stringify(payload))
18
18
  }
19
19
 
20
- export function sendFile(response, file) {
21
- if (!isFile(file))
20
+ export function sendFile(response, filePath) {
21
+ if (!isFile(filePath))
22
22
  sendNotFound(response)
23
23
  else {
24
- response.setHeader('Content-Type', mimeFor(file))
25
- response.end(read(file))
24
+ response.setHeader('Content-Type', mimeFor(filePath))
25
+ response.end(read(filePath))
26
26
  }
27
27
  }
28
28
 
@@ -64,6 +64,12 @@ export function sendNotFound(response) {
64
64
  response.end()
65
65
  }
66
66
 
67
+ export function sendUnprocessableContent(response, error) {
68
+ console.error(error)
69
+ response.statusCode = 422
70
+ response.end(error)
71
+ }
72
+
67
73
  export function sendInternalServerError(response, error) {
68
74
  console.error(error)
69
75
  response.statusCode = 500
@@ -26,7 +26,12 @@ after(() => {
26
26
 
27
27
  export function testPixels(testFileName, options = {}) {
28
28
  options.beforeSuite = async () => await mockaton.reset()
29
- options.viewports ??= [{ width: 1024, height: 800 }]
29
+ options.viewports ??= [{
30
+ width: 1024,
31
+ height: 800,
32
+ deviceScaleFactor: 1.5 // for better screenshots
33
+ }]
30
34
  options.colorSchemes ??= ['light', 'dark']
35
+ options.screenshotOptions ??= {}
31
36
  _testPixels(page, testFileName, MOCKATON_ADDR + '/mockaton', 'body', options)
32
37
  }