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.
- package/README-dashboard.png +0 -0
- package/README.md +41 -26
- package/Tests.js +4 -2
- package/index.d.ts +2 -1
- package/package.json +1 -1
- package/sample-mocks/api/user/likes.GET.200.js +3 -1
- package/sample-mocks/api/user/links.GET.200.js +9 -0
- package/src/Config.js +4 -2
- package/src/Dashboard.css +31 -32
- package/src/Dashboard.js +23 -26
- package/src/MockDispatcher.js +7 -6
- package/src/Mockaton.js +2 -0
- package/sample-mocks/api/user/loves.GET.200.js +0 -7
package/README-dashboard.png
CHANGED
|
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/
|
|
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>(
|
|
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)
|
|
165
|
-
For the default route of a directory, omit the
|
|
166
|
-
the extension). For example, the following files will be
|
|
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(
|
|
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
package/package.json
CHANGED
|
@@ -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
|
-
|
|
56
|
+
menu {
|
|
76
57
|
display: flex;
|
|
77
|
-
|
|
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:
|
|
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('
|
|
57
|
+
r('menu', null,
|
|
61
58
|
r('h1', null, Strings.title),
|
|
62
|
-
r(
|
|
63
|
-
r(
|
|
64
|
-
|
|
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',
|
|
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('
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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
|
|
package/src/MockDispatcher.js
CHANGED
|
@@ -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)
|