mockaton 2.1.0 → 2.3.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.
Binary file
package/README.md CHANGED
@@ -28,7 +28,7 @@ The first file in **alphabetical order** becomes the default mock.
28
28
  ### Optionally, you can write mocks in JavaScript
29
29
  An Object, Array, or String is sent as JSON.
30
30
 
31
- `api/user/likes.GET.200.js`
31
+ `api/foo.GET.200.js`
32
32
  ```js
33
33
  export default [
34
34
  { id: 0 }
@@ -98,27 +98,9 @@ interface Config {
98
98
  proxyFallback?: string // e.g. http://localhost:9999 Target for relaying routes without mocks
99
99
  allowedExt?: RegExp // /\.(json|txt|md|js)$/ Just for excluding temporary editor files (e.g. JetBrains appends a ~)
100
100
  generate500?: boolean // autogenerates an Internal Server Error empty mock for routes that have no 500
101
+ extraHeaders?: []
101
102
  }
102
103
  ```
103
-
104
- ## Cookies
105
- ```js
106
- import { jwtCookie } from 'mockaton'
107
-
108
- Config.cookies = {
109
- 'My Admin User': 'my-cookie=1;Path=/;SameSite=strict',
110
- 'My Normal User': 'my-cookie=0;Path=/;SameSite=strict',
111
- 'My JWT': jwtCookie('my-cookie', {
112
- email: 'john.doe@example.com',
113
- picture: 'https://cdn.auth0.com/avatars/jd.png'
114
- })
115
- }
116
- ```
117
- The key is just a label used for selecting a particular cookie in the dashboard.
118
-
119
- `jwtCookie` has a hardcoded header and signature. In other
120
- words, it’s useful if you only care about its payload.
121
-
122
104
  ---
123
105
 
124
106
  ## File Name Convention
@@ -143,7 +125,7 @@ Comments are anything within parentheses, including them.
143
125
  They are ignored for URL purposes, so they have no effect
144
126
  on the URL mask. For example, these two are for `/api/foo`
145
127
  <pre>
146
- api/foo<b>(my comment)</b>.GET.200.json<b>(foo)</b>
128
+ api/foo<b>(my comment)</b>.GET.200.json<b>(bar)</b>
147
129
  api/foo.GET.200.json
148
130
  </pre>
149
131
 
@@ -161,16 +143,49 @@ but since that’s part of the query string, it’s ignored anyway.
161
143
 
162
144
 
163
145
 
164
- ### Default (index-like) file
165
- For the default route of a directory, omit the name (just use
166
- the extension). For example, the following files will be routed
167
- to `api/foo` because comments and the query string are ignored.
146
+ ### Default (index-like) route
147
+ For the default route of a directory, omit the mock filename name
148
+ (<b>just use the extension</b>). For example, the following files will be
149
+ routed to `api/foo` because comments and the query string are ignored.
168
150
  ```text
169
151
  api/foo/.GET.200.json
170
152
  api/foo/?bar=[bar].GET.200.json
171
153
  api/foo/(my comment).GET.200.json
172
154
  ```
173
155
 
156
+
157
+ ## `Config.cookies`
158
+ The selected cookie is sent in every response in the `Set-Cookie` header.
159
+ ```js
160
+ import { jwtCookie } from 'mockaton'
161
+
162
+ Config.cookies = {
163
+ 'My Admin User': 'my-cookie=1;Path=/;SameSite=strict',
164
+ 'My Normal User': 'my-cookie=0;Path=/;SameSite=strict',
165
+ 'My JWT': jwtCookie('my-cookie', {
166
+ email: 'john.doe@example.com',
167
+ picture: 'https://cdn.auth0.com/avatars/jd.png'
168
+ })
169
+ }
170
+ ```
171
+ The key is just a label used for selecting a particular cookie in the dashboard.
172
+
173
+ `jwtCookie` has a hardcoded header and signature. In other
174
+ words, it’s useful if you only care about its payload.
175
+
176
+
177
+ ## `Config.extraHeaders`
178
+ They are applied last, right before ending the response. In other words, they
179
+ can overwrite the `Content-Type`. The header name goes in even indices.
180
+
181
+ ```js
182
+ Config.extraHeaders = [
183
+ 'Server', 'Mockaton',
184
+ 'Set-Cookie', 'foo=FOO;Path=/;SameSite=strict',
185
+ 'Set-Cookie', 'bar=BAR;Path=/;SameSite=strict'
186
+ ]
187
+ ```
188
+
174
189
  ## Documenting Contracts (.md)
175
190
  This is handy for documenting request payload parameters. The dashboard will
176
191
  print the Markdown document (as plain text) above the actual payload content.
@@ -207,7 +222,7 @@ fetch(addr + '/mockaton/bulk-select-by-comment', {
207
222
  ### Reset
208
223
  Re-Initialize the collection and its states (selected mocks and cookies, delays, etc.).
209
224
  ```js
210
- fetch(add + '/mockaton/reset', {
225
+ fetch(addr + '/mockaton/reset', {
211
226
  method: 'PATCH'
212
227
  })
213
228
  ```
package/Tests.js CHANGED
@@ -111,7 +111,8 @@ const server = Mockaton({
111
111
  userA: 'CookieA',
112
112
  userB: 'CookieB'
113
113
  },
114
- generate500: true
114
+ generate500: true,
115
+ extraHeaders: ['Server', 'MockatonTester']
115
116
  })
116
117
  server.on('listening', runTests)
117
118
 
@@ -158,7 +159,7 @@ async function runTests() {
158
159
  await reset()
159
160
  for (const [url, file, body] of fixtures)
160
161
  await testMockDispatching(url, file, body)
161
-
162
+
162
163
  await testMockDispatching('/api/object', 'api/object.GET.200.js', { JSON_FROM_JS: true }, mimeFor('.json'))
163
164
  await testJsFunctionMocks()
164
165
 
@@ -205,6 +206,7 @@ async function testMockDispatching(url, file, expectedBody, forcedMime = void 0)
205
206
  it('status: ' + status, () => equal(res.status, status))
206
207
  it('cookie: ' + mime, () => equal(res.headers.get('set-cookie'), 'CookieA'))
207
208
  it('delay is under 1 sec', () => equal((new Date()).getTime() - now.getTime() < 1000, true))
209
+ it('extra header', () => equal(res.headers.get('server'), 'MockatonTester'))
208
210
  })
209
211
  }
210
212
 
package/index.d.ts CHANGED
@@ -10,7 +10,8 @@ interface Config {
10
10
  skipOpen?: boolean
11
11
  proxyFallback?: string
12
12
  allowedExt?: RegExp
13
- generate500?: boolean
13
+ generate500?: boolean,
14
+ extraHeaders?: [string, string][]
14
15
  }
15
16
 
16
17
  export function Mockaton(options: Config): Server
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.1.0",
5
+ "version": "2.3.0",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "license": "MIT",
@@ -1,7 +1,9 @@
1
- // You can write JSON responses in JavaScript.
1
+ // You can write JSON responses in JavaScript.
2
+ // You can export an: Object, Array, or String
2
3
 
3
4
  export default [
4
5
  { id: 0 },
5
6
  { id: 1 },
6
7
  { id: 2 },
7
8
  ]
9
+
@@ -0,0 +1,9 @@
1
+ // You can write JSON responses in JavaScript as a function.
2
+ // Must return a String and they should NOT call `response.end()`
3
+
4
+ export default function (req, response) {
5
+ return JSON.stringify([
6
+ 'http://example.com/foo',
7
+ 'http://example.com/bar',
8
+ ])
9
+ }
package/src/Config.js CHANGED
@@ -12,7 +12,8 @@ export const Config = {
12
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
- generate500: false
15
+ generate500: false,
16
+ extraHeaders: []
16
17
  }
17
18
 
18
19
  export function setup(options) {
@@ -27,7 +28,8 @@ export function setup(options) {
27
28
  skipOpen: is(Boolean),
28
29
  proxyFallback: is(String),
29
30
  allowedExt: is(RegExp),
30
- generate500: is(Boolean)
31
+ generate500: is(Boolean),
32
+ extraHeaders: Array.isArray
31
33
  })
32
34
  }
33
35
 
package/src/Dashboard.css CHANGED
@@ -14,23 +14,13 @@ body {
14
14
  padding: 16px;
15
15
  }
16
16
  * {
17
+ padding: 0;
17
18
  border: 0;
18
19
  margin: 0;
19
20
  font-family: system-ui, sans-serif;
20
21
  font-size: 100%;
21
22
  }
22
23
 
23
- h1 {
24
- padding: 12px 0;
25
- margin: 0;
26
- font-size: 2rem;
27
- }
28
-
29
-
30
- select {
31
- padding: 3px 0;
32
- border: 1px solid #444;
33
- }
34
24
 
35
25
  fieldset {
36
26
  width: 120px;
@@ -47,15 +37,6 @@ fieldset {
47
37
  }
48
38
  }
49
39
 
50
- .CookieSelector {
51
- display: flex;
52
- align-items: center;
53
- margin-left: 20px;
54
-
55
- select {
56
- margin-left: 5px;
57
- }
58
- }
59
40
 
60
41
  main {
61
42
  display: flex;
@@ -72,14 +53,39 @@ main {
72
53
  }
73
54
  }
74
55
 
75
- .TitleWrap {
56
+ menu {
76
57
  display: flex;
77
- align-items: center;
58
+ margin-bottom: 12px;
59
+ gap: 14px;
60
+ align-items: flex-end;
61
+
62
+ h1 {
63
+ margin: 0;
64
+ margin-right: 14px;
65
+ font-size: 2rem;
66
+ }
67
+
68
+ label {
69
+ span {
70
+ display: block;
71
+ color: #555;
72
+ font-size: .85rem;
73
+ }
74
+
75
+ select {
76
+ width: 143px;
77
+ padding: 3px 0;
78
+ border: 1px solid #bbb;
79
+ margin-top: 1px;
80
+ cursor: pointer;
81
+ border-radius: 4px;
82
+ font-size: 0.9rem;
83
+ }
84
+ }
78
85
 
79
86
  button {
80
- padding: 3px 12px;
87
+ padding: 4px 12px;
81
88
  border: 1px solid var(--colorRed);
82
- margin-left: 20px;
83
89
  background: transparent;
84
90
  color: var(--colorRed);
85
91
  border-radius: 50px;
@@ -132,18 +138,11 @@ main {
132
138
  }
133
139
  }
134
140
 
135
- .BulkSelectSection {
136
- margin: 20px 0;
137
- }
138
-
139
- .BulkSelectSection select {
140
- margin-top: 5px;
141
- }
142
-
143
141
  .MockSelector {
144
142
  width: 300px;
145
143
  padding: 8px 1px;
146
144
  border: 0;
145
+ border-radius: 4px;
147
146
  background: #eee;
148
147
  text-align: right;
149
148
  direction: rtl;
package/src/Dashboard.js CHANGED
@@ -16,14 +16,11 @@ const Strings = {
16
16
  }
17
17
 
18
18
  const CSS = {
19
- BulkSelectSection: 'BulkSelectSection',
20
- CookieSelector: 'CookieSelector',
21
19
  DelayCheckbox: 'DelayCheckbox',
22
20
  Documentation: 'Documentation',
23
21
  MockSelector: 'MockSelector',
24
22
  PayloadViewer: 'PayloadViewer',
25
23
  PreviewLink: 'PreviewLink',
26
- TitleWrap: 'TitleWrap',
27
24
 
28
25
  bold: 'bold',
29
26
  chosen: 'chosen',
@@ -57,13 +54,11 @@ function DevPanel(brokersByMethod, cookies, comments) {
57
54
  document.title = Strings.title
58
55
  return (
59
56
  r('div', null,
60
- r('div', { className: CSS.TitleWrap },
57
+ r('menu', null,
61
58
  r('h1', null, Strings.title),
62
- r(ResetButton),
63
- r(CookieSelector, { list: cookies })),
64
- r('div', { className: CSS.BulkSelectSection },
65
- r('h2', null, Strings.bulk_select_by_comment),
66
- r(BulkSelector, { comments })),
59
+ r(CookieSelector, { list: cookies }),
60
+ r(BulkSelector, { comments }),
61
+ r(ResetButton)),
67
62
  r('main', null,
68
63
  r('table', null, Object.entries(brokersByMethod).map(([method, brokers]) =>
69
64
  r(SectionByMethod, { method, brokers }))),
@@ -88,8 +83,8 @@ function ResetButton() {
88
83
 
89
84
  function CookieSelector({ list }) {
90
85
  return (
91
- r('label', { className: CSS.CookieSelector },
92
- Strings.cookie,
86
+ r('label', null,
87
+ r('span', null, Strings.cookie),
93
88
  r('select', {
94
89
  autocomplete: 'off',
95
90
  disabled: list.length <= 1,
@@ -111,21 +106,23 @@ function CookieSelector({ list }) {
111
106
 
112
107
  function BulkSelector({ comments }) {
113
108
  return (
114
- r('select', {
115
- autocomplete: 'off',
116
- disabled: comments.length <= 1,
117
- onChange() {
118
- fetch(API.bulkSelect, {
119
- method: 'PATCH',
120
- body: JSON.stringify(this.value)
121
- })
122
- .then(init)
123
- .catch(console.error)
124
- }
125
- }, [Strings.select_one].concat(comments).map(item =>
126
- r('option', {
127
- value: item
128
- }, item))))
109
+ r('label', null,
110
+ r('span', null, Strings.bulk_select_by_comment),
111
+ r('select', {
112
+ autocomplete: 'off',
113
+ disabled: comments.length <= 1,
114
+ onChange() {
115
+ fetch(API.bulkSelect, {
116
+ method: 'PATCH',
117
+ body: JSON.stringify(this.value)
118
+ })
119
+ .then(init)
120
+ .catch(console.error)
121
+ }
122
+ }, [Strings.select_one].concat(comments).map(item =>
123
+ r('option', {
124
+ value: item
125
+ }, item)))))
129
126
  }
130
127
 
131
128
 
@@ -31,22 +31,23 @@ export async function dispatchMock(req, response) {
31
31
  const { file, status, delay } = broker
32
32
  console.log('\n', req.url, '→\n ', file)
33
33
 
34
- response.statusCode = status
35
- if (cookie.getCurrent())
36
- response.setHeader('set-cookie', cookie.getCurrent())
37
-
38
34
  let mockText
39
35
  if (file.endsWith('.js')) {
40
36
  response.setHeader('content-type', mimeFor('.json'))
41
37
  const jsExport = await importDefault(file)
42
38
  mockText = typeof jsExport === 'function'
43
- ? jsExport(req, response)
44
- : JSON.stringify(jsExport)
39
+ ? await jsExport(req, response)
40
+ : JSON.stringify(jsExport, null, 2)
45
41
  }
46
42
  else {
47
43
  response.setHeader('content-type', mimeFor(file))
48
44
  mockText = readMock(file)
49
45
  }
46
+
47
+ if (cookie.getCurrent())
48
+ response.setHeader('set-cookie', cookie.getCurrent())
49
+
50
+ response.writeHead(status, Config.extraHeaders)
50
51
  setTimeout(() => response.end(mockText), delay)
51
52
  }
52
53
  catch (error) {
package/src/Mockaton.js CHANGED
@@ -14,6 +14,8 @@ export function Mockaton(options) {
14
14
  mockBrokerCollection.init()
15
15
 
16
16
  return createServer(async (req, response) => {
17
+ response.setHeader('Server', 'Mockaton')
18
+
17
19
  const { url, method } = req
18
20
  if (method === 'GET' && apiGetRequests.has(url))
19
21
  apiGetRequests.get(url)(req, response)
@@ -1,7 +0,0 @@
1
- // You can write JSON responses in JavaScript.
2
-
3
- export default function (req, response) {
4
- return JSON.stringify([
5
- { id: 0 }
6
- ])
7
- }