mockaton 0.10.10 → 1.1.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
@@ -26,6 +26,17 @@ UI, or programmatically, for instance, for setting up tests.
26
26
 
27
27
  The first file in **alphabetical order** becomes the default mock.
28
28
 
29
+ ### Optionally, you can write mocks in JavaScript
30
+ An Object, Array, or String is sent as JSON.
31
+
32
+ `api/user/likes.GET.200.js`
33
+ ```js
34
+ export default [
35
+ { id: 0 }
36
+ ]
37
+ ```
38
+
39
+
29
40
  ### Proxying Routes
30
41
  `Config.proxyFallback` lets you specify a target
31
42
  server for serving routes you don’t have mocks for.
@@ -78,7 +89,7 @@ interface Config {
78
89
  database?: object // for "Transforms"
79
90
  skipOpen?: boolean // Prevents opening the dashboard in a browser
80
91
  proxyFallback?: string // e.g. http://localhost:9999 Target for relaying routes without mocks
81
- allowedExt?: RegExp // /\.(json|txt|md|mjs)$/ Just for excluding temporary editor files (e.g. JetBrains appends a ~)
92
+ allowedExt?: RegExp // /\.(json|txt|md|js|mjs)$/ Just for excluding temporary editor files (e.g. JetBrains appends a ~)
82
93
  }
83
94
  ```
84
95
 
@@ -177,60 +188,64 @@ export default function capitalizeAllText(mockAsText, requestBody, config) {
177
188
  }
178
189
  ```
179
190
 
180
- ---
181
191
 
182
192
  ## API
183
193
 
184
- ### `/mockaton/edit` Select a mock for a route
185
- ```
186
- PATCH /mockaton/edit
187
- {
188
- "file": "api/foo.200.GET.json"
189
- "delayed": true // optional
190
- }
194
+ ### Select a mock for a route
195
+ ```js
196
+ fetch(addr + '/mockaton/edit', {
197
+ method: 'PATCH',
198
+ body: JSON.stringify({
199
+ file: 'api/foo.200.GET.json',
200
+ delayed: true // optional
201
+ })
202
+ })
191
203
  ```
192
- ---
193
-
194
- ### `/mockaton/bulk-select` Select all mocks that have a particular comment
195
204
 
205
+ ### Select all mocks that have a particular comment
206
+ ```js
207
+ fetch(addr + '/mockaton/bulk-select-by-comment', {
208
+ method: 'PATCH',
209
+ body: JSON.stringify('(demo-a)')
210
+ })
196
211
  ```
197
- PATCH /mockaton/bulk-select
198
- {
199
- "comment": "(demo-a)"
200
- }
201
- ```
202
- ---
203
212
 
204
- ### `/mockaton/reset` Reset
213
+ ### Reset
205
214
  Re-Initialize the collection and its states (selected mocks and cookies, delays, etc.).
215
+ ```js
216
+ fetch(add + '/mockaton/reset', {
217
+ method: 'PATCH'
218
+ })
206
219
  ```
207
- PATCH /mockaton/reset
208
- ```
209
- ---
210
220
 
211
- ### `/mockaton/cookies` Select a cookie
221
+ ### Select a cookie
212
222
  In `Config.cookies`, each key is the label used
213
223
  for changing it. Only one cookie can be set.
214
- ```
215
- PATCH /mockaton/cookies
216
- {
217
- "current_cookie_key": "My Normal User"
218
- }
224
+ ```js
225
+ fetch(addr + '/mockaton/cookies', {
226
+ method: 'PATCH',
227
+ body: JSON.stringify('My Normal User')
228
+ })
219
229
  ```
220
230
 
221
- ### `/mockaton/cookies` List Cookies
231
+ ### List Cookies
222
232
  Sends a list of the available cookies along with a flag indicated if it’s the selected.
223
- ```
224
- GET /mockaton/cookies
233
+ ```js
234
+ fetch(addr + '/mockaton/cookies')
225
235
  ```
226
236
 
227
- ---
228
-
229
- ### `/mockaton/transform` Select a Transform
237
+ ### Select a Transform
238
+ ```js
239
+ fetch(addr + '/mockaton/transform', {
240
+ method: 'PATCH',
241
+ body: JSON.stringify('api/video/list(concat newly uploaded).GET.200.mjs')
242
+ })
230
243
  ```
231
- PATCH /mockaton/transform
232
- {
233
- "file": "api/video/list(concat newly uploaded).GET.200.mjs"
234
- }
244
+
245
+ ### Update Fallback Proxy
246
+ ```js
247
+ fetch(addr + '/mockaton/fallback', {
248
+ method: 'PATCH',
249
+ body: JSON.stringify('http://example.com')
250
+ })
235
251
  ```
236
- ---
package/Tests.js CHANGED
@@ -96,6 +96,9 @@ write('api/.GET.500.txt', 'keeps non-autogenerated 500')
96
96
  write('api/alternative(comment-2).GET.200.json', JSON.stringify({ comment: 2 }))
97
97
  write('api/my-route(comment-2).GET.200.json', JSON.stringify({ comment: 2 }))
98
98
 
99
+ // JavaScript to JSON
100
+ write('/api/object.GET.200.js', 'export default { JSON_FROM_JS: true }')
101
+
99
102
  writeStatic('index.html', '<h1>Static</h1>')
100
103
  writeStatic('assets/app.js', 'const app = 1')
101
104
  writeStatic('another-entry/index.html', '<h1>Another</h1>')
@@ -118,6 +121,8 @@ async function runTests() {
118
121
  for (const [url, file, body] of fixtures)
119
122
  await testMockDispatching(url, file, body)
120
123
 
124
+ await testMockDispatching('/api/object', 'api/object.GET.200.js', { JSON_FROM_JS: true }, undefined, mimeFor('.json'))
125
+
121
126
  await testItUpdatesDelayAndFile(
122
127
  '/api/alternative',
123
128
  'api/alternative(comment-2).GET.200.json',
@@ -185,9 +190,9 @@ async function test404() {
185
190
  })
186
191
  }
187
192
 
188
- async function testMockDispatching(url, file, expectedBody, reqBody = void 0) {
193
+ async function testMockDispatching(url, file, expectedBody, reqBody = void 0, forcedMime = void 0) {
189
194
  const { urlMask, method, status } = Route.parseFilename(file)
190
- const mime = mimeFor(file)
195
+ const mime = forcedMime || mimeFor(file)
191
196
  const now = new Date()
192
197
  const res = await request(url, { method, body: reqBody })
193
198
  const body = mime === 'application/json'
@@ -269,7 +274,7 @@ async function testExtractsAllComments(expected) {
269
274
  async function testItBulkSelectsByComment(comment, tests) {
270
275
  await request(API.bulkSelect, {
271
276
  method: 'PATCH',
272
- body: JSON.stringify({ [DF.comment]: comment })
277
+ body: JSON.stringify(comment)
273
278
  })
274
279
  for (const [url, file, body] of tests)
275
280
  await testMockDispatching(url, file, body)
@@ -289,7 +294,7 @@ async function testItUpdatesUserRole() {
289
294
  it('Update the selected cookie', async () => {
290
295
  await request(API.cookies, {
291
296
  method: 'PATCH',
292
- body: JSON.stringify({ [DF.currentCookieKey]: 'userB' })
297
+ body: JSON.stringify('userB')
293
298
  })
294
299
  const res = await request(API.cookies)
295
300
  deepEqual(await res.json(), [
@@ -313,7 +318,7 @@ export default function (mock, reqBody, config) {
313
318
  await reset() // for registering the files
314
319
  await request(API.transform, {
315
320
  method: 'PATCH',
316
- body: JSON.stringify({ [DF.file]: 'api/transform.POST.200.mjs' })
321
+ body: JSON.stringify('api/transform.POST.200.mjs')
317
322
  })
318
323
  await testMockDispatching('/api/transform',
319
324
  'api/transform.POST.200.json',
@@ -367,6 +372,7 @@ async function testInvalidFilenamesAreIgnored() {
367
372
  async function testEnableFallbackSoRoutesWithoutMocksGetRelayed() {
368
373
  await describe('Fallback', async () => {
369
374
  const fallbackServer = createServer((_, response) => {
375
+ response.setHeader('custom_header', 'my_custom_header')
370
376
  response.statusCode = 423
371
377
  response.end('From_Fallback_Server')
372
378
  })
@@ -378,6 +384,7 @@ async function testEnableFallbackSoRoutesWithoutMocksGetRelayed() {
378
384
  })
379
385
  await it('Relays to fallback server', async () => {
380
386
  const res = await request('/non-existing-mock')
387
+ equal(res.headers.get('custom_header'), 'my_custom_header')
381
388
  equal(res.status, 423)
382
389
  equal(await res.text(), 'From_Fallback_Server')
383
390
  fallbackServer.close()
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": "0.10.10",
5
+ "version": "1.1.0",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "license": "MIT",
@@ -0,0 +1,7 @@
1
+ // You can write JSON responses in JavaScript.
2
+
3
+ export default [
4
+ { id: 0 },
5
+ { id: 1 },
6
+ { id: 2 },
7
+ ]
File without changes
package/src/Api.js CHANGED
@@ -51,8 +51,7 @@ function listMockBrokers(_, response) {
51
51
 
52
52
  async function selectCookie(req, response) {
53
53
  try {
54
- const body = await parseJSON(req)
55
- cookie.setCurrent(body[DF.currentCookieKey])
54
+ cookie.setCurrent(await parseJSON(req))
56
55
  sendOK(response)
57
56
  }
58
57
  catch (error) {
@@ -84,8 +83,7 @@ async function updateBroker(req, response) {
84
83
 
85
84
  async function bulkUpdateBrokersByCommentTag(req, response) {
86
85
  try {
87
- const body = await parseJSON(req)
88
- mockBrokersCollection.setMocksMatchingComment(body[DF.comment])
86
+ mockBrokersCollection.setMocksMatchingComment(await parseJSON(req))
89
87
  sendOK(response)
90
88
  }
91
89
  catch (error) {
@@ -96,9 +94,9 @@ async function bulkUpdateBrokersByCommentTag(req, response) {
96
94
 
97
95
  async function updateBrokerTransform(req, response) {
98
96
  try {
99
- const body = await parseJSON(req)
100
- const broker = mockBrokersCollection.getBrokerByFilename(body[DF.file])
101
- broker.updateTransform(body[DF.file])
97
+ const file = await parseJSON(req)
98
+ const broker = mockBrokersCollection.getBrokerByFilename(file)
99
+ broker.updateTransform(file)
102
100
  sendOK(response)
103
101
  }
104
102
  catch (error) {
@@ -1,7 +1,7 @@
1
1
  const MOUNT = '/mockaton'
2
2
  export const API = {
3
3
  dashboard: MOUNT,
4
- bulkSelect: MOUNT + '/bulk-select',
4
+ bulkSelect: MOUNT + '/bulk-select-by-comment',
5
5
  comments: MOUNT + '/comments',
6
6
  edit: MOUNT + '/edit',
7
7
  mocks: MOUNT + '/mocks',
@@ -12,9 +12,7 @@ export const API = {
12
12
  }
13
13
 
14
14
  export const DF = { // Dashboard Fields (XHR)
15
- comment: 'comment',
16
15
  delayed: 'delayed',
17
16
  file: 'file',
18
- currentCookieKey: 'current_cookie_key',
19
17
  isForDashboard: 'mock_request_payload'
20
18
  }
package/src/Config.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, lstatSync } from 'node:fs'
1
+ import { existsSync as exists, lstatSync } from 'node:fs'
2
2
  import { validate, is, optional } from './utils/validate.js'
3
3
 
4
4
 
@@ -12,7 +12,7 @@ export const Config = {
12
12
  database: {},
13
13
  skipOpen: false,
14
14
  proxyFallback: '', // e.g. http://localhost:9999
15
- allowedExt: /\.(json|txt|md|mjs)$/ // Just for excluding temporary editor files (e.g. JetBrains appends a ~)
15
+ allowedExt: /\.(json|txt|md|js|mjs)$/ // Just for excluding temporary editor files (e.g. JetBrains appends a ~)
16
16
  }
17
17
 
18
18
  export function setup(options) {
@@ -32,7 +32,7 @@ export function setup(options) {
32
32
  }
33
33
 
34
34
  function isDirectory(dir) {
35
- return existsSync(dir) && lstatSync(dir).isDirectory()
35
+ return exists(dir) && lstatSync(dir).isDirectory()
36
36
  }
37
37
 
38
38
 
package/src/Dashboard.js CHANGED
@@ -103,7 +103,7 @@ function CookieSelector({ list }) {
103
103
  onChange() {
104
104
  fetch(API.cookies, {
105
105
  method: 'PATCH',
106
- body: JSON.stringify({ [DF.currentCookieKey]: this.value })
106
+ body: JSON.stringify(this.value)
107
107
  })
108
108
  .then(init)
109
109
  .catch(console.error)
@@ -124,7 +124,7 @@ function BulkSelector({ comments }) {
124
124
  onChange() {
125
125
  fetch(API.bulkSelect, {
126
126
  method: 'PATCH',
127
- body: JSON.stringify({ [DF.comment]: this.value })
127
+ body: JSON.stringify(this.value)
128
128
  })
129
129
  .then(init)
130
130
  .catch(console.error)
@@ -274,7 +274,7 @@ function TransformSelector({ items, selected }) {
274
274
  onChange() {
275
275
  fetch(API.transform, {
276
276
  method: 'PATCH',
277
- body: JSON.stringify({ [DF.file]: this.value })
277
+ body: JSON.stringify(this.value)
278
278
  }).then(() => {
279
279
  this.closest('tr').querySelector('a').click()
280
280
  this.className = className(this.value === this.options[0].value)
@@ -31,15 +31,19 @@ export async function dispatchMock(req, response) {
31
31
  const { file, status, delay, currentTransform } = broker
32
32
  console.log('\n', req.url, '→\n ', file)
33
33
 
34
+ const shouldJavaScriptToJSON = file.endsWith('.js')
34
35
  response.statusCode = status
35
- response.setHeader('content-type', mimeFor(file))
36
+ response.setHeader('content-type', mimeFor(shouldJavaScriptToJSON ? '.json' : file))
36
37
  if (cookie.getCurrent())
37
38
  response.setHeader('set-cookie', cookie.getCurrent())
38
39
 
39
- let mockAsText = readMock(file)
40
+ let mockAsText = shouldJavaScriptToJSON
41
+ ? JSON.stringify(await importDefault(file))
42
+ : readMock(file)
43
+
40
44
  if (broker.currentTransform) {
41
45
  const body = await requestBodyForTransform(req, mockAsText)
42
- const transformFunc = await importTransformFunc(currentTransform)
46
+ const transformFunc = await importDefault(currentTransform)
43
47
  mockAsText = transformFunc(mockAsText, body, Config)
44
48
  }
45
49
  setTimeout(() => response.end(mockAsText), delay)
@@ -69,7 +73,7 @@ function readMock(file) {
69
73
  return readFileSync(join(Config.mocksDir, file), 'utf8')
70
74
  }
71
75
 
72
- async function importTransformFunc(file) {
76
+ async function importDefault(file) {
73
77
  // The date param is just for cache busting
74
78
  return (await import(join(Config.mocksDir, file) + '?' + Date.now())).default
75
79
  }
package/src/ProxyRelay.js CHANGED
@@ -6,6 +6,6 @@ export async function proxy(req, response) {
6
6
  method: req.method,
7
7
  headers: req.headers
8
8
  })
9
- response.writeHead(proxyResponse.status, proxyResponse.headers)
9
+ response.writeHead(proxyResponse.status, Object.fromEntries(proxyResponse.headers))
10
10
  response.end(await proxyResponse.text())
11
11
  }
@@ -34,7 +34,7 @@ export function init() {
34
34
  continue
35
35
  }
36
36
  collection[method] ??= {}
37
- if (!(urlMask in collection[method]))
37
+ if (!collection[method][urlMask])
38
38
  collection[method][urlMask] = new MockBroker(file)
39
39
  else
40
40
  collection[method][urlMask].register(file)