mockaton 13.10.1 → 13.11.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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "mockaton",
3
3
  "description": "HTTP Mock Server",
4
4
  "type": "module",
5
- "version": "13.10.1",
5
+ "version": "13.11.1",
6
6
  "exports": {
7
7
  ".": {
8
8
  "import": "./index.js",
@@ -60,4 +60,6 @@ export class Commander {
60
60
  getSyncVersion = () => fetch(this.addr + API.syncVersion)
61
61
 
62
62
  getOpenAPI = () => fetch(this.addr + API.openAPI)
63
+
64
+ health = () => fetch(this.addr + API.health)
63
65
  }
@@ -28,6 +28,7 @@ export const API = {
28
28
  throws: MOUNT + '/throws',
29
29
  syncVersion: MOUNT + '/sync-version',
30
30
  watchHotReload: MOUNT + '/watch-hot-reload',
31
+ health: MOUNT + '/health',
31
32
 
32
33
  openAPI: MOUNT + '/openapi'
33
34
  }
@@ -31,7 +31,7 @@ function GlobalDelayField() {
31
31
  function onChange() {
32
32
  store.setGlobalDelay(this.valueAsNumber)
33
33
  }
34
- return SlideableNumberField({
34
+ return SlidableNumberField({
35
35
  className: CSS.GlobalDelay,
36
36
  label: t`Delay (ms)`,
37
37
  min: 0,
@@ -50,7 +50,7 @@ function GlobalDelayJitterField() {
50
50
  this.value = Math.min(300, this.valueAsNumber)
51
51
  store.setGlobalDelayJitter(this.valueAsNumber / 100)
52
52
  }
53
- return SlideableNumberField({
53
+ return SlidableNumberField({
54
54
  className: CSS.GlobalDelayJitter,
55
55
  label: t`Max Jitter %`,
56
56
  min: 0,
@@ -140,38 +140,48 @@ function HelpLink() {
140
140
  }
141
141
 
142
142
 
143
- function SlideableNumberField({ className, label, onChange, min, max, step, value }) {
143
+ function SlidableNumberField({ name, className, label, onChange, min, max, step, value }) {
144
+ function clamp(val) {
145
+ return Math.min(Math.max(val, min), max)
146
+ }
147
+
144
148
  function onPointerDown(event) {
145
- const input = event.target
146
149
  let lastX = event.clientX
150
+ const input = /** @type {HTMLInputElement} */ event.target
151
+ const initialVal = input.value
152
+
147
153
  input.setPointerCapture(event.pointerId)
154
+ window.addEventListener('pointerup', onPointerUp, { once: true })
155
+ window.addEventListener('pointermove', onPointerMove)
156
+
157
+ function onPointerUp(ev) {
158
+ input.releasePointerCapture(ev.pointerId)
159
+ window.removeEventListener('pointermove', onPointerMove)
160
+ if (input.value !== initialVal)
161
+ input.dispatchEvent(new Event('change'))
162
+ }
148
163
 
149
164
  function onPointerMove(ev) {
150
165
  const diff = ev.clientX - lastX
151
166
  if (Math.abs(diff) > 10) {
152
- const v = input.value
153
- if (diff > 0)
154
- input.stepUp()
155
- else
156
- input.stepDown()
157
- if (v !== input.value)
158
- input.dispatchEvent(new Event('change'))
159
167
  lastX = ev.clientX
168
+
169
+ let s = step
170
+ if (ev.shiftKey) s *= 2
171
+ else if (ev.altKey) s /= 2
172
+
173
+ input.valueAsNumber = clamp(input.valueAsNumber + Math.sign(diff) * s)
160
174
  }
161
175
  }
162
- function onPointerUp(ev) {
163
- window.removeEventListener('pointermove', onPointerMove)
164
- input.releasePointerCapture(ev.pointerId)
165
- }
166
- window.addEventListener('pointermove', onPointerMove)
167
- window.addEventListener('pointerup', onPointerUp, { once: true })
168
176
  }
177
+
169
178
  return (
170
179
  r('label', { className },
171
180
  r('span', null, label),
172
181
  r('input', {
173
182
  type: 'number',
174
183
  autocomplete: 'none',
184
+ style: { cursor: 'col-resize' },
175
185
  min,
176
186
  max,
177
187
  step,
@@ -59,7 +59,8 @@ body {
59
59
  }
60
60
 
61
61
  label,
62
- button {
62
+ button,
63
+ select {
63
64
  user-select: none;
64
65
  }
65
66
 
@@ -260,8 +261,6 @@ header {
260
261
  &.GlobalDelay,
261
262
  &.GlobalDelayJitter {
262
263
  input {
263
- cursor: col-resize;
264
-
265
264
  &&::selection {
266
265
  background-color: transparent;
267
266
  }
package/src/server/Api.js CHANGED
@@ -63,6 +63,12 @@ const patchReqs = new Map([
63
63
 
64
64
  export async function handleApiRequest(req, response) {
65
65
  const url = removeQueryStringAndFragment(req.url)
66
+
67
+ if ((req.method === 'GET' || req.method === 'HEAD') && url === API.health) {
68
+ response.ok()
69
+ return
70
+ }
71
+
66
72
  const handler = (
67
73
  req.method === 'GET' && getReqs.get(url) ||
68
74
  req.method === 'PATCH' && patchReqs.get(url))
@@ -161,6 +161,19 @@ describe('Filename Convention', () => {
161
161
  })
162
162
 
163
163
 
164
+ describe('Health Check', () => {
165
+ test('HEAD', async () => {
166
+ const r = await request(API.health, { method: 'HEAD' })
167
+ equal(r.status, 200)
168
+ })
169
+
170
+ test('GET', async () => {
171
+ const r = await request(API.health)
172
+ equal(r.status, 200)
173
+ })
174
+ })
175
+
176
+
164
177
  describe('CORS', () => {
165
178
  describe('Set CORS allowed', () => {
166
179
  test('422 for non boolean', async () => {
@@ -34,7 +34,7 @@ export async function rm(path) {
34
34
  export async function resolveIn(baseDir, file) {
35
35
  try {
36
36
  const parent = await realpath(baseDir)
37
- const child = resolve(join(parent, file))
37
+ const child = resolve(join(parent, file)) // realpath not needed because we don't write symlinks
38
38
  return child.startsWith(join(parent, sep))
39
39
  ? child
40
40
  : null
@@ -525,6 +525,18 @@
525
525
  }
526
526
  }
527
527
  }
528
+ },
529
+ "/mockaton/health": {
530
+ "get": {
531
+ "summary": "Health check",
532
+ "description": "Returns 200 OK if the server is running. Accepts both GET and HEAD.",
533
+ "x-js-client-example": "await mockaton.health()",
534
+ "responses": {
535
+ "200": {
536
+ "description": "OK"
537
+ }
538
+ }
539
+ }
528
540
  }
529
541
  },
530
542
  "components": {