mockaton 0.9.10 → 0.10.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.
package/README.md CHANGED
@@ -1,13 +1,11 @@
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 directory
5
- and file name convention, which is similar to the URL paths. For
4
+ It scans `Config.mocksDir` for files following a specific
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
  ```
8
- -- api/
9
- |-- user/
10
- |-- [user-id].GET.200.json
8
+ my-mocks-dir/api/user/[user-id].GET.200.json
11
9
  ```
12
10
 
13
11
  By the way, [this browser
@@ -17,14 +15,21 @@ be used for downloading a TAR of your XHR requests following that convention.
17
15
 
18
16
  ### Mock Variants
19
17
  Each route can have many mocks, which could either be:
20
- - Different response status code. For example, for testing error responses.
21
- - Comment on the filename, which is anything within parentheses.
18
+ - Different response __status code__.
19
+ - e.g. for testing error responses. BTW, an _Internal Server
20
+ Error_ mock is autogenerated for routes that have no 500.
21
+ - __Comment__ on the filename, which is anything within parentheses.
22
+ - e.g. `api/user(my-comment).POST.201.json`
22
23
 
23
24
  Those alternatives can be manually selected in the dashboard
24
25
  UI, or programmatically, for instance, for setting up tests.
25
26
 
26
27
  About the default mock file, the first file in **alphabetical order** wins.
27
28
 
29
+ ### Proxying Routes
30
+ `Config.proxyFallback` lets you specify a target
31
+ server for serving routes you don’t have mocks for.
32
+
28
33
 
29
34
  ## Getting Started
30
35
  The best way to learn _Mockaton_ is by checking out this repo and
@@ -34,20 +39,6 @@ exploring its [sample-mocks/](./sample-mocks) directory. Then, run
34
39
  ![](./README-dashboard.png)
35
40
 
36
41
 
37
- ### Mock Variants of Status Code
38
- The **sample-mocks/** directory has three mock alternatives for serving
39
- `/api/user/friends`:
40
- - _200 - OK_
41
- - _204 - No Content_ with an empty list of friends
42
- - _500 - Internal Server Error_
43
- - BTW, 500 mocks get autogenerated for routes that have no 500’s.
44
-
45
- ![](./README-dashboard-dropdown.png)
46
-
47
- ### Mock Variants with Comments
48
- Comments are anything within parentheses, including them.
49
- ![](./README-mocks-with-comments.png)
50
-
51
42
  ## Delay 🕓
52
43
  The clock icon next to the mock selector is a checkbox for delaying a
53
44
  particular response. They are handy for testing spinners.
@@ -63,12 +54,11 @@ npm install mockaton
63
54
  Create a `my-mockaton.js` file
64
55
  ```js
65
56
  import { resolve } from 'node:path'
66
- import { Mockaton } from 'src/Mockaton'
67
-
57
+ import { Mockaton } from 'mockaton'
68
58
 
69
- Mockaton({ // Config options
70
- port: 2345,
71
- mocksDir: resolve('my-mocks-dir')
59
+ Mockaton({
60
+ mocksDir: resolve('my-mocks-dir'),
61
+ port: 2345
72
62
  })
73
63
  ```
74
64
 
@@ -84,9 +74,10 @@ interface Config {
84
74
  host?: string, // 'localhost'
85
75
  port?: number // 0 auto-assigned
86
76
  delay?: number // 1200 ms
87
- cookies?(): object
77
+ cookies?: object
88
78
  database?: object // for "Transforms"
89
79
  skipOpen?: boolean // Prevents opening the dashboard in a browser
80
+ proxyFallback?: string // e.g. http://localhost:9999 Target for relaying routes without mocks
90
81
  allowedExt?: RegExp // /\.(json|txt|md|mjs)$/ Just for excluding temporary editor files (e.g. JetBrains appends a ~)
91
82
  }
92
83
  ```
@@ -95,16 +86,16 @@ interface Config {
95
86
  ```js
96
87
  import { jwtCookie } from 'src/Mockaton'
97
88
 
98
-
99
89
  Config.cookies = {
100
- 'My Admin User': 'my-cookie=1;Path=/;SameSite=strict',
101
- 'My Normal User': 'my-cookie=0;Path=/;SameSite=strict',
102
- 'My JWT': jwtCookie('my-cookie', {
103
- email: 'john.doe@example.com',
104
- picture: 'https://cdn.auth0.com/avatars/jd.png'
105
- })
90
+ 'My Admin User': 'my-cookie=1;Path=/;SameSite=strict',
91
+ 'My Normal User': 'my-cookie=0;Path=/;SameSite=strict',
92
+ 'My JWT': jwtCookie('my-cookie', {
93
+ email: 'john.doe@example.com',
94
+ picture: 'https://cdn.auth0.com/avatars/jd.png'
95
+ })
106
96
  }
107
97
  ```
98
+ The key is just a label used in dashboard for selecting the desired cookie.
108
99
 
109
100
  That `jwtCookie` has a hardcoded header and signature. In other
110
101
  words, it’s useful iff you care about its payload in the frontend.
@@ -179,9 +170,9 @@ with `.mjs` will process the mock before serving it.
179
170
 
180
171
  For example, this handler will capitalize the mock body and increment a counter.
181
172
  ```js
182
- export default function capitalizeAllText(mockAsText, requestBody, Config) {
183
- Config.database.myCount ??= 0
184
- Config.database.myCount++
173
+ export default function capitalizeAllText(mockAsText, requestBody, config) {
174
+ config.database.myCount ??= 0
175
+ config.database.myCount++
185
176
  return mockAsText.toUpperCase()
186
177
  }
187
178
  ```
package/Tests.js CHANGED
@@ -3,6 +3,7 @@
3
3
  import { tmpdir } from 'node:os'
4
4
  import { dirname } from 'node:path'
5
5
  import { describe, it } from 'node:test'
6
+ import { createServer } from 'node:http'
6
7
  import { equal, deepEqual, match } from 'node:assert/strict'
7
8
  import { writeFileSync, mkdtempSync, mkdirSync } from 'node:fs'
8
9
 
@@ -98,17 +99,22 @@ writeStatic('index.html', '<h1>Static</h1>')
98
99
  writeStatic('assets/app.js', 'const app = 1')
99
100
  writeStatic('another-entry/index.html', '<h1>Another</h1>')
100
101
 
101
-
102
- const server = Mockaton({
103
- mocksDir: tmpDir,
104
- staticDir: staticTmpDir,
105
- skipOpen: true,
106
- cookies: {
107
- userA: 'CookieA',
108
- userB: 'CookieB'
109
- }
102
+ let server
103
+ const fallbackServer = createServer((_, response) => {
104
+ response.end('From_Fallback_Server')
105
+ }).listen(0, 'localhost', function () {
106
+ server = Mockaton({
107
+ mocksDir: tmpDir,
108
+ staticDir: staticTmpDir,
109
+ skipOpen: true,
110
+ cookies: {
111
+ userA: 'CookieA',
112
+ userB: 'CookieB'
113
+ },
114
+ proxyFallback: `http://localhost:${fallbackServer.address().port}`
115
+ })
116
+ server.on('listening', runTests)
110
117
  })
111
- server.on('listening', runTests)
112
118
 
113
119
  async function runTests() {
114
120
  await testItRendersDashboard()
@@ -157,7 +163,9 @@ async function runTests() {
157
163
  await testTransforms()
158
164
  await testStaticFileServing()
159
165
  await testInvalidFilenamesAreIgnored()
166
+ await testRouteWithoutMocksRelaysGetsProxied()
160
167
  server.close()
168
+ fallbackServer.close()
161
169
  }
162
170
 
163
171
  async function reset() {
@@ -350,6 +358,12 @@ async function testInvalidFilenamesAreIgnored() {
350
358
  })
351
359
  }
352
360
 
361
+ async function testRouteWithoutMocksRelaysGetsProxied() {
362
+ await it('Fallback relay', async () => {
363
+ const res = await request('/non-existing-mock')
364
+ equal(await res.text(), 'From_Fallback_Server')
365
+ })
366
+ }
353
367
 
354
368
  // Utils
355
369
 
package/index.d.ts CHANGED
@@ -6,11 +6,10 @@ interface Config {
6
6
  host?: string,
7
7
  port?: number
8
8
  delay?: number
9
-
10
- cookies?(): object
11
-
9
+ cookies?: object
12
10
  database?: object
13
11
  skipOpen?: boolean
12
+ proxyFallback?: string
14
13
  allowedExt?: RegExp
15
14
  }
16
15
 
package/package.json CHANGED
@@ -2,13 +2,13 @@
2
2
  "name": "mockaton",
3
3
  "description": "A deterministic server-side for developing and testing frontend clients",
4
4
  "type": "module",
5
- "version": "0.9.10",
5
+ "version": "0.10.1",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "license": "MIT",
9
9
  "repository": "https://github.com/ericfortis/mockaton",
10
10
  "scripts": {
11
11
  "test": "./Tests.js",
12
- "demo": "_usage_example.js"
12
+ "demo": "./_usage_example.js"
13
13
  }
14
14
  }
package/src/Config.js CHANGED
@@ -11,6 +11,7 @@ export const Config = {
11
11
  cookies: {}, // defaults to the first kv
12
12
  database: {},
13
13
  skipOpen: false,
14
+ proxyFallback: '', // e.g. http://localhost:9999
14
15
  allowedExt: /\.(json|txt|md|mjs)$/ // Just for excluding temporary editor files (e.g. JetBrains appends a ~)
15
16
  }
16
17
 
@@ -25,6 +26,7 @@ export function setup(options) {
25
26
  cookies: is(Object),
26
27
  database: is(Object),
27
28
  skipOpen: is(Boolean),
29
+ proxyFallback: is(String),
28
30
  allowedExt: is(RegExp)
29
31
  })
30
32
  }
@@ -2,6 +2,7 @@ import { join } from 'node:path'
2
2
  import { readFileSync } from 'node:fs'
3
3
 
4
4
  import { DF } from './ApiConstants.js'
5
+ import { proxy } from './ProxyRelay.js'
5
6
  import { cookie } from './cookie.js'
6
7
  import { Config } from './Config.js'
7
8
  import { mimeFor } from './utils/mime.js'
@@ -19,10 +20,13 @@ export async function dispatchMock(req, response) {
19
20
 
20
21
  const broker = mockBrokerCollection.getBrokerForUrl(req.method, req.url)
21
22
  if (!broker) {
22
- sendNotFound(response)
23
+ if (Config.proxyFallback)
24
+ await proxy(req, response)
25
+ else
26
+ sendNotFound(response) // TESTME
23
27
  return
24
28
  }
25
-
29
+
26
30
  try {
27
31
  const { file, status, delay, currentTransform } = broker
28
32
  console.log('\n', req.url, '→\n ', file)
@@ -0,0 +1,11 @@
1
+ import { Config } from './Config.js'
2
+
3
+
4
+ export async function proxy(req, response) {
5
+ const proxyResponse = await fetch(Config.proxyFallback + req.url, {
6
+ method: req.method,
7
+ headers: req.headers
8
+ })
9
+ response.writeHead(proxyResponse.status, proxyResponse.headers)
10
+ response.end(await proxyResponse.text())
11
+ }
Binary file
Binary file