@aimeloic/monkey-tester 1.0.5 → 1.0.7

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 (2) hide show
  1. package/htmlTemplate.js +37 -32
  2. package/package.json +1 -1
package/htmlTemplate.js CHANGED
@@ -65,7 +65,7 @@ export function getHtmlTemplate(endpoints) {
65
65
  .field-label { font-family: 'DM Mono', monospace; font-size: 11px; color: var(--text-dim); text-align: right; }
66
66
  input[type=text], input[type=number], select { background: var(--surface2); border: 1px solid var(--border); color: var(--text); font-family: 'DM Mono', monospace; font-size: 12px; padding: 7px 10px; border-radius: var(--radius); width: 100%; outline: none; }
67
67
  .btn { background: var(--accent); color: #0e0c09; border: none; padding: 10px 22px; border-radius: var(--radius); font-size: 13px; font-weight: 500; cursor: pointer; }
68
- .btn-secondary { background: var(--surface2); color: var(--text-dim); border: 1px solid var(--border); margin-left: 8px;}
68
+ .btn-secondary { background: var(--surface2); color: var(--text-dim); border: 1px solid var(--border); margin-left: 8px; }
69
69
  .response-panel { border-left: 1px solid var(--border); display: flex; flex-direction: column; overflow: hidden; }
70
70
  .response-header { padding: 14px 18px; border-bottom: 1px solid var(--border); display: flex; align-items: center; background: var(--surface); }
71
71
  .response-header-title { font-size: 11px; font-family: 'DM Mono', monospace; color: var(--text-dim); text-transform: uppercase; }
@@ -102,9 +102,7 @@ export function getHtmlTemplate(endpoints) {
102
102
  <aside id="sidebar-nav">
103
103
  <div class="section-label">Discovered Endpoints</div>
104
104
  </aside>
105
-
106
105
  <main id="main-panel"></main>
107
-
108
106
  <div class="response-panel">
109
107
  <div class="response-header">
110
108
  <span class="response-header-title">Response Output</span>
@@ -127,8 +125,8 @@ document.getElementById('base-url').value = window.location.origin;
127
125
  function buildSidebar() {
128
126
  const sidebar = document.getElementById('sidebar-nav');
129
127
  const keys = Object.keys(ENDPOINTS);
130
-
131
- if(keys.length === 0) {
128
+
129
+ if (keys.length === 0) {
132
130
  sidebar.innerHTML += '<div style="padding:15px; color:var(--text-dim)">No active application endpoints discovered.</div>';
133
131
  return;
134
132
  }
@@ -139,59 +137,64 @@ function buildSidebar() {
139
137
  div.className = index === 0 ? 'nav-item active' : 'nav-item';
140
138
  div.setAttribute('data-ep', key);
141
139
  div.innerHTML = '<span class="method-badge ' + ep.method + '">' + ep.method + '</span><span class="nav-label">' + ep.path + '</span>';
142
-
143
140
  div.addEventListener('click', () => {
144
141
  document.querySelectorAll('.nav-item').forEach(n => n.classList.remove('active'));
145
142
  div.classList.add('active');
146
143
  clearResponse();
147
144
  renderPanel(key);
148
145
  });
149
-
150
146
  sidebar.appendChild(div);
151
147
  });
152
-
153
- if(keys.length > 0) renderPanel(keys[0]);
148
+
149
+ if (keys.length > 0) renderPanel(keys[0]);
150
+ }
151
+
152
+ // FIXED: Elements are explicitly assigned attributes to enforce string literal values cleanly
153
+ function makeInputString(type, id, placeholder, defaultValue) {
154
+ const pAttr = placeholder ? ' placeholder="' + placeholder + '"' : '';
155
+ const vAttr = defaultValue !== undefined ? ' value=\'' + defaultValue + '\'' : '';
156
+ return '<input type="' + type + '" id="' + id + '"' + pAttr + vAttr + ' />';
154
157
  }
155
158
 
156
159
  function renderPanel(epKey) {
157
160
  currentEp = epKey;
158
161
  const ep = ENDPOINTS[epKey];
159
162
  const main = document.getElementById('main-panel');
160
- if(!ep) return;
163
+ if (!ep) return;
161
164
 
162
165
  let html = '<div class="endpoint-title">' + ep.title + '</div>' +
163
166
  '<div class="endpoint-path">' +
164
- '<span class="method-badge ' + ep.method + '">' + ep.method + '</span>' +
165
- '<span>' + ep.path + '</span>' +
167
+ ' <span class="method-badge ' + ep.method + '">' + ep.method + '</span>' +
168
+ ' <span>' + ep.path + '</span>' +
166
169
  '</div>' +
167
170
  '<div class="endpoint-desc">' + ep.desc + '</div>';
168
171
 
169
172
  if (ep.params && ep.params.length) {
170
173
  html += '<div class="form-section"><div class="form-section-title">Path Parameters</div>';
171
- ep.params.forEach(p => {
174
+ ep.params.forEach(function(p) {
172
175
  html += '<div class="field-row">' +
173
- '<label class="field-label">' + p.label + '</label>' +
174
- '<input type="text" id="param-' + p.name + '" placeholder="' + p.placeholder + '" />' +
175
- '</div>';
176
+ ' <label class="field-label">' + p.label + '</label>' +
177
+ makeInputString('text', 'param-' + p.name, p.placeholder, '') +
178
+ '</div>';
176
179
  });
177
180
  html += '</div>';
178
181
  }
179
182
 
180
183
  if (ep.fields && ep.fields.length) {
181
184
  html += '<div class="form-section"><div class="form-section-title">JSON Request Body Raw Payload</div>';
182
- ep.fields.forEach(f => {
185
+ ep.fields.forEach(function(f) {
183
186
  html += '<div class="field-row">' +
184
- '<label class="field-label">' + f.label + '</label>' +
185
- '<input type="text" id="field-' + f.name + '" value=\'{"key": "value"}\' />' +
186
- '</div>';
187
+ ' <label class="field-label">' + f.label + '</label>' +
188
+ makeInputString('text', 'field-' + f.name, '', '{"key": "value"}') +
189
+ '</div>';
187
190
  });
188
191
  html += '</div>';
189
192
  }
190
193
 
191
194
  html += '<div class="btn-row">' +
192
- '<button class="btn" onclick="sendRequest()">Execute Route</button>' +
193
- '<button class="btn btn-secondary" onclick="clearResponse()">Clear Context</button>' +
194
- '</div>';
195
+ ' <button class="btn" onclick="sendRequest()">Execute Route</button>' +
196
+ ' <button class="btn btn-secondary" onclick="clearResponse()">Clear Context</button>' +
197
+ '</div>';
195
198
 
196
199
  main.innerHTML = html;
197
200
  }
@@ -203,26 +206,26 @@ async function sendRequest() {
203
206
  if (ep.params) {
204
207
  for (const p of ep.params) {
205
208
  const val = document.getElementById('param-' + p.name)?.value.trim();
206
- if (!val) { showToast(' Parameter ' + p.label + ' required'); return; }
209
+ if (!val) { showToast('Warning: Parameter ' + p.label + ' is required'); return; }
207
210
  path = path.replace(':' + p.name, encodeURIComponent(val));
208
211
  }
209
212
  }
210
213
 
211
- const baseUrl = document.getElementById('base-url').value.replace(/\\/+$/, '');
214
+ const baseUrl = document.getElementById('base-url').value.replace(/[/]+$/, '');
212
215
  const url = baseUrl + path;
213
216
  const headers = { 'Content-Type': 'application/json' };
214
-
217
+
215
218
  const jwt = document.getElementById('jwt-input').value.trim();
216
219
  if (jwt) headers['Authorization'] = 'Bearer ' + jwt;
217
220
 
218
221
  let body = undefined;
219
- if (ep.fields && ep.fields.length && ['POST','PUT','PATCH'].includes(ep.method)) {
222
+ if (ep.fields && ep.fields.length && ['POST', 'PUT', 'PATCH'].includes(ep.method)) {
220
223
  const rawVal = document.getElementById('field-' + ep.fields[0].name).value.trim();
221
224
  try {
222
225
  JSON.parse(rawVal);
223
226
  body = rawVal;
224
227
  } catch(e) {
225
- showToast('Malformed JSON Body structure provided.');
228
+ showToast('Malformed JSON body structure provided.');
226
229
  return;
227
230
  }
228
231
  }
@@ -248,13 +251,14 @@ function setResponse(data, state, status, ms) {
248
251
 
249
252
  if (state === 'loading') {
250
253
  badge.className = 'status-badge status-idle';
251
- badge.textContent = '';
254
+ badge.textContent = '...';
252
255
  body.innerHTML = 'Executing transmission...';
253
256
  return;
254
257
  }
255
258
 
256
259
  badge.className = 'status-badge ' + (state === 'ok' ? 'status-ok' : 'status-err');
257
260
  badge.textContent = status + ' · ' + ms + 'ms';
261
+ body.className = 'response-body';
258
262
  body.innerHTML = highlightJson(typeof data === 'string' ? data : JSON.stringify(data, null, 2));
259
263
  }
260
264
 
@@ -269,8 +273,8 @@ function clearResponse() {
269
273
 
270
274
  function highlightJson(str) {
271
275
  return str
272
- .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
273
- .replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, match => {
276
+ .replace(/&/g, '&amp;').replace(/[<]/g, '&lt;').replace(/[>]/g, '&gt;')
277
+ .replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function(match) {
274
278
  if (/^"/.test(match)) {
275
279
  if (/:$/.test(match)) return '<span class="json-key">' + match + '</span>';
276
280
  return '<span class="json-str">' + match + '</span>';
@@ -281,7 +285,8 @@ function highlightJson(str) {
281
285
 
282
286
  function showToast(msg) {
283
287
  const t = document.getElementById('toast');
284
- t.textContent = msg; t.classList.add('show');
288
+ t.textContent = msg;
289
+ t.classList.add('show');
285
290
  setTimeout(() => t.classList.remove('show'), 2500);
286
291
  }
287
292
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aimeloic/monkey-tester",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Embedded interactive API testing UI for Node.js backends",
5
5
  "main": "index.js",
6
6
  "type":"module",