mockaton 0.0.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.
Files changed (58) hide show
  1. package/Api.js +108 -0
  2. package/ApiConstants.js +22 -0
  3. package/Config.js +41 -0
  4. package/Dashboard.css +206 -0
  5. package/Dashboard.html +12 -0
  6. package/Dashboard.js +355 -0
  7. package/LICENSE +21 -0
  8. package/MockBroker.js +107 -0
  9. package/MockDispatcher.js +72 -0
  10. package/Mockaton.js +39 -0
  11. package/README-dashboard-dropdown.png +0 -0
  12. package/README-dashboard.png +0 -0
  13. package/README-mocks-with-comments.png +0 -0
  14. package/README.md +211 -0
  15. package/Route.js +90 -0
  16. package/StaticDispatcher.js +29 -0
  17. package/Tests.js +367 -0
  18. package/_usage_example.js +14 -0
  19. package/cookie.js +29 -0
  20. package/index.d.ts +17 -0
  21. package/index.js +2 -0
  22. package/mockBrokersCollection.js +84 -0
  23. package/package.json +12 -0
  24. package/sample-mocks/api/user/.GET.200.json +1 -0
  25. package/sample-mocks/api/user/.GET.501.txt +7 -0
  26. package/sample-mocks/api/user/edit-name.PATCH.200.json +1 -0
  27. package/sample-mocks/api/user/edit-name.PATCH.200.md +12 -0
  28. package/sample-mocks/api/user/edit-name.PATCH.501.txt +0 -0
  29. package/sample-mocks/api/user/friends.GET.200.json +1 -0
  30. package/sample-mocks/api/user/friends.GET.204.json +4 -0
  31. package/sample-mocks/api/user/friends.GET.501.txt +0 -0
  32. package/sample-mocks/api/user/logout.POST.200.json +1 -0
  33. package/sample-mocks/api/user/logout.POST.501.txt +0 -0
  34. package/sample-mocks/api/user/videos(assorted).GET.200.json +13 -0
  35. package/sample-mocks/api/user/videos(entirely unverified).GET.200.json +13 -0
  36. package/sample-mocks/api/user/videos(entirely verified)(another comment).GET.200.json +13 -0
  37. package/sample-mocks/api/user/videos.GET.501.txt +0 -0
  38. package/sample-mocks/api/video/[id].GET.200.json +4 -0
  39. package/sample-mocks/api/video/[id].GET.501.txt +0 -0
  40. package/sample-mocks/api/video/list(concat newly uploaded).GET.200.mjs +8 -0
  41. package/sample-mocks/api/video/list.GET.200.json +11 -0
  42. package/sample-mocks/api/video/list.GET.501.txt +0 -0
  43. package/sample-mocks/api/video/stat/[stat-id]/[video-id].GET.200.json +1 -0
  44. package/sample-mocks/api/video/stat/[stat-id]/[video-id].GET.501.txt +0 -0
  45. package/sample-mocks/api/video/stat/[stat-id]/all-videos?limit=[limit].GET.200.json +4 -0
  46. package/sample-mocks/api/video/stat/[stat-id]/all-videos?limit=[limit].GET.501.txt +0 -0
  47. package/sample-mocks/api/video/upload(insert newly uploaded).POST.201.mjs +10 -0
  48. package/sample-mocks/api/video/upload.POST.201.json +3 -0
  49. package/sample-mocks/api/video/upload.POST.501.txt +0 -0
  50. package/sample-static/another-entry/index.html +12 -0
  51. package/sample-static/assets/app.js +1 -0
  52. package/sample-static/assets/video.mp4 +0 -0
  53. package/sample-static/index.html +13 -0
  54. package/utils/http-request.js +36 -0
  55. package/utils/http-response.js +60 -0
  56. package/utils/jwt.js +21 -0
  57. package/utils/mime.js +47 -0
  58. package/utils/validate.js +17 -0
package/Api.js ADDED
@@ -0,0 +1,108 @@
1
+ /**
2
+ * API for controlling the mock server. For example,
3
+ * setting a specific mock-file for a particular route.
4
+ */
5
+
6
+ import { join } from 'node:path'
7
+ import { cookie } from './cookie.js'
8
+ import { Config } from './Config.js'
9
+ import { DF, DP } from './ApiConstants.js'
10
+ import { parseJSON } from './utils/http-request.js'
11
+ import * as mockBrokersCollection from './mockBrokersCollection.js'
12
+ import { sendOK, sendBadRequest, sendJSON, sendFile } from './utils/http-response.js'
13
+
14
+
15
+ export const apiGetRequests = new Map([
16
+ [DP.dashboard, serveDashboard],
17
+ ['/Route.js', serveDashboardAsset],
18
+ ['/Dashboard.js', serveDashboardAsset],
19
+ ['/Dashboard.css', serveDashboardAsset],
20
+ ['/ApiConstants.js', serveDashboardAsset],
21
+ [DP.mocks, listMockBrokers],
22
+ [DP.cookies, listCookies],
23
+ [DP.comments, listComments]
24
+ ])
25
+
26
+ export const apiPatchRequests = new Map([
27
+ [DP.bulkSelect, bulkUpdateBrokersByCommentTag],
28
+ [DP.edit, updateBroker],
29
+ [DP.reset, reinitialize],
30
+ [DP.cookies, selectCookie],
31
+ [DP.transform, updateBrokerTransform]
32
+ ])
33
+
34
+ function serveDashboard(_, response) {
35
+ sendFile(response, join(import.meta.dirname, 'Dashboard.html'))
36
+ }
37
+ function serveDashboardAsset(req, response) {
38
+ sendFile(response, join(import.meta.dirname, req.url))
39
+ }
40
+ function listCookies(_, response) {
41
+ sendJSON(response, cookie.list())
42
+ }
43
+ function listComments(_, response) {
44
+ sendJSON(response, mockBrokersCollection.extractAllComments())
45
+ }
46
+ function listMockBrokers(_, response) {
47
+ sendJSON(response, mockBrokersCollection.getAll())
48
+ }
49
+
50
+
51
+ async function selectCookie(req, response) {
52
+ try {
53
+ const body = await parseJSON(req)
54
+ cookie.setCurrent(body[DF.currentCookieKey])
55
+ sendOK(response)
56
+ }
57
+ catch (error) {
58
+ console.error(error)
59
+ sendBadRequest(response)
60
+ }
61
+ }
62
+
63
+ function reinitialize(_, response) {
64
+ Config.database = {}
65
+ mockBrokersCollection.init()
66
+ sendOK(response)
67
+ }
68
+
69
+ async function updateBroker(req, response) {
70
+ try {
71
+ const body = await parseJSON(req)
72
+ const broker = mockBrokersCollection.getBrokerByFilename(body[DF.file])
73
+ if (DF.delayed in body)
74
+ broker.updateDelay(body[DF.delayed])
75
+ else
76
+ broker.updateFile(body[DF.file])
77
+ sendOK(response)
78
+ }
79
+ catch (error) {
80
+ console.error(error)
81
+ sendBadRequest(response)
82
+ }
83
+ }
84
+
85
+ async function bulkUpdateBrokersByCommentTag(req, response) {
86
+ try {
87
+ const body = await parseJSON(req)
88
+ mockBrokersCollection.setMocksMatchingComment(body[DF.comment])
89
+ sendOK(response)
90
+ }
91
+ catch (error) {
92
+ console.error(error)
93
+ sendBadRequest(response)
94
+ }
95
+ }
96
+
97
+ async function updateBrokerTransform(req, response) {
98
+ try {
99
+ const body = await parseJSON(req)
100
+ const broker = mockBrokersCollection.getBroker(body[DF.method], body[DF.urlMask])
101
+ broker.updateTransform(body[DF.file])
102
+ sendOK(response)
103
+ }
104
+ catch (error) {
105
+ console.error(error)
106
+ sendBadRequest(response)
107
+ }
108
+ }
@@ -0,0 +1,22 @@
1
+ const MOUNT = '/mockaton'
2
+ export const DP = { // Dashboard Paths
3
+ dashboard: MOUNT,
4
+ bulkSelect: MOUNT + '/bulk-select',
5
+ comments: MOUNT + '/comments',
6
+ edit: MOUNT + '/edit',
7
+ mocks: MOUNT + '/mocks',
8
+ reset: MOUNT + '/reset',
9
+ transform: MOUNT + '/transform',
10
+ cookies: MOUNT + '/cookies'
11
+ }
12
+
13
+ export const DF = { // Dashboard Fields (XHR)
14
+ comment: 'comment',
15
+ delayed: 'delayed',
16
+ file: 'file',
17
+ isAdmin: 'is_admin',
18
+ currentCookieKey: 'current_cookie_key',
19
+ isForDashboard: 'mock_request_payload',
20
+ method: 'method',
21
+ urlMask: 'url_mask'
22
+ }
package/Config.js ADDED
@@ -0,0 +1,41 @@
1
+ import { existsSync, lstatSync } from 'node:fs'
2
+ import { validate } from './utils/validate.js'
3
+
4
+
5
+ export const Config = {
6
+ mocksDir: '',
7
+ staticDir: '',
8
+ host: '127.0.0.1',
9
+ port: 0, // auto-assigned
10
+ delay: 1200, // milliseconds
11
+ cookies: {}, // defaults to the first kv
12
+ database: {},
13
+ skipOpen: false, // Prevents opening the dashboard in a browser
14
+ allowedExt: /\.(json|txt|md|mjs)$/ // Just for excluding temporary editor files (e.g. JetBrains appends a ~)
15
+ }
16
+
17
+ export function setup(options) {
18
+ Object.assign(Config, options)
19
+ validate(Config, {
20
+ mocksDir: isDirectory,
21
+ staticDir: optional(isDirectory),
22
+ host: String,
23
+ port: port => Number.isInteger(port) && port >= 0 && port < 2 ** 16,
24
+ delay: ms => Number.isInteger(ms) && ms > 0,
25
+ cookies: Object,
26
+ database: Object,
27
+ skipOpen: Boolean,
28
+ allowedExt: RegExp
29
+ })
30
+ }
31
+
32
+
33
+ function optional(tester) {
34
+ return val => !val || tester(val)
35
+ }
36
+
37
+ function isDirectory(dir) {
38
+ return existsSync(dir) && lstatSync(dir).isDirectory()
39
+ }
40
+
41
+
package/Dashboard.css ADDED
@@ -0,0 +1,206 @@
1
+ :root {
2
+ --colorAccent: #0069d0;
3
+ --colorHover: #dfefff;
4
+ --colorRed: #da0f00;
5
+ --colorLightRed: #ffe4ee;
6
+ --colorLightOrange: #ffedd1;
7
+ --colorLightGrey: rgba(0, 0, 0, 0.02);
8
+ }
9
+ html, body {
10
+ margin: 0;
11
+ font-size: 13px;
12
+ }
13
+ body {
14
+ padding: 16px;
15
+ }
16
+ * {
17
+ border: 0;
18
+ margin: 0;
19
+ font-family: system-ui, sans-serif;
20
+ font-size: 100%;
21
+ }
22
+
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
+
35
+ fieldset {
36
+ width: 120px;
37
+ border: 1px solid #ccc;
38
+
39
+ label {
40
+ display: flex;
41
+ align-items: center;
42
+ padding: 4px 0;
43
+
44
+ input {
45
+ margin-right: 6px;
46
+ }
47
+ }
48
+ }
49
+
50
+ .CookieSelector {
51
+ display: flex;
52
+ align-items: center;
53
+ margin-left: 20px;
54
+
55
+ select {
56
+ margin-left: 5px;
57
+ }
58
+ }
59
+
60
+ main {
61
+ display: flex;
62
+ align-items: flex-start;
63
+
64
+ > table {
65
+ border-collapse: collapse;
66
+
67
+ th {
68
+ padding-top: 20px;
69
+ padding-bottom: 2px;
70
+ text-align: left;
71
+ }
72
+ }
73
+ }
74
+
75
+ .TitleWrap {
76
+ display: flex;
77
+ align-items: center;
78
+
79
+ button {
80
+ padding: 3px 12px;
81
+ border: 1px solid var(--colorRed);
82
+ margin-left: 20px;
83
+ background: transparent;
84
+ color: var(--colorRed);
85
+ border-radius: 50px;
86
+ cursor: pointer;
87
+
88
+ &:hover {
89
+ background: var(--colorRed);
90
+ color: white;
91
+ }
92
+ }
93
+ }
94
+
95
+ .PayloadViewer {
96
+ position: sticky;
97
+ top: 0;
98
+ width: 50%;
99
+ margin-left: 16px;
100
+
101
+ pre {
102
+ &:not(:empty) {
103
+ overflow: auto;
104
+ max-height: calc(100vh - 160px);
105
+ padding: 12px;
106
+ margin-top: 6px;
107
+ background: var(--colorLightGrey);
108
+ font-family: monospace;
109
+ }
110
+
111
+ &.Documentation {
112
+ margin-bottom: 12px;
113
+ }
114
+ }
115
+ }
116
+
117
+ .PreviewLink {
118
+ position: relative;
119
+ left: -6px;
120
+ display: inline-block;
121
+ width: 280px;
122
+ padding: 8px 6px;
123
+ color: var(--colorAccent);
124
+ text-decoration: none;
125
+
126
+ &:hover {
127
+ background: var(--colorHover);
128
+ }
129
+ &.chosen {
130
+ color: white;
131
+ background: var(--colorAccent);
132
+ }
133
+ }
134
+
135
+ .BulkSelectSection {
136
+ margin: 20px 0;
137
+ }
138
+ .TransformsSection {
139
+ padding-top: 30px;
140
+ border-top: 1px solid #ccc;
141
+ margin: 30px 0;
142
+ }
143
+
144
+ .BulkSelectSection select {
145
+ margin-top: 5px;
146
+ }
147
+
148
+ .TransformSelector,
149
+ .MockSelector {
150
+ width: 300px;
151
+ padding: 8px 1px;
152
+ border: 0;
153
+ background: #eee;
154
+ text-align: right;
155
+ direction: rtl;
156
+ text-overflow: ellipsis;
157
+
158
+ &:disabled {
159
+ background: transparent;
160
+ color: #222;
161
+ cursor: not-allowed
162
+ }
163
+ &:enabled:hover {
164
+ cursor: pointer;
165
+ background: var(--colorHover);
166
+ }
167
+ &.status4xx {
168
+ background: var(--colorLightOrange);
169
+ }
170
+ &.status5xx {
171
+ background: var(--colorLightRed);
172
+ }
173
+ }
174
+
175
+ .DelayCheckbox {
176
+ display: flex;
177
+ margin-left: 6px;
178
+ cursor: pointer;
179
+
180
+ > input {
181
+ display: none;
182
+
183
+ &:checked ~ svg {
184
+ border-color: white;
185
+ filter: invert();
186
+ }
187
+ }
188
+
189
+ > svg {
190
+ width: 14px;
191
+ height: 14px;
192
+ border: 1px solid #000;
193
+ vertical-align: bottom;
194
+ fill: #000;
195
+ border-radius: 50%;
196
+ background: white;
197
+
198
+ &:hover {
199
+ background: #ddd
200
+ }
201
+ }
202
+ }
203
+
204
+ .bold {
205
+ font-weight: bold;
206
+ }
package/Dashboard.html ADDED
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en-US">
3
+ <head>
4
+ <link rel="stylesheet" href="../Dashboard.css">
5
+ <link rel="icon" href="data:">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1">
7
+ <title>Mock Server</title>
8
+ </head>
9
+ <body>
10
+ <script src="../Dashboard.js" type="module"></script>
11
+ </body>
12
+ </html>