@testsmith/api-spector 0.2.0 → 0.2.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/bin/cli.js +12 -2
- package/out/main/agents.js +212 -0
- package/out/main/chunks/{request-handler-AFBOd__c.js → request-collection-Dx0ZqB54.js} +72 -2
- package/out/main/index.js +918 -178
- package/out/main/runner.js +132 -89
- package/out/renderer/assets/{index-CnjhlKQP.js → index-DPHZdvL5.js} +13002 -840
- package/out/renderer/assets/{index-FZtc_UAN.css → index-DVaubmCJ.css} +1 -1
- package/out/renderer/index.html +3 -3
- package/package.json +5 -3
- package/readme.md +1 -1
- package/resources/splash.html +21 -9
package/out/main/runner.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const promises = require("fs/promises");
|
|
4
4
|
const path = require("path");
|
|
5
5
|
const undici = require("undici");
|
|
6
|
-
const
|
|
6
|
+
const requestCollection = require("./chunks/request-collection-Dx0ZqB54.js");
|
|
7
7
|
require("crypto");
|
|
8
8
|
require("dayjs");
|
|
9
9
|
require("vm");
|
|
@@ -136,80 +136,109 @@ function buildHtmlReport(results, summary, meta = {}) {
|
|
|
136
136
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
137
137
|
<title>${esc(collection)} — Test Results</title>
|
|
138
138
|
<style>
|
|
139
|
+
/* ─── CSS custom properties for theming ──────────────────────────────── */
|
|
140
|
+
:root {
|
|
141
|
+
--bg: #0f1117; --bg-surface: #161b22; --bg-panel: #0d1117;
|
|
142
|
+
--border: #21262d; --border-hover:#30363d;
|
|
143
|
+
--text: #c9d1d9; --text-bright: #e6edf3; --text-muted: #8b949e; --text-dim: #484f58;
|
|
144
|
+
--green: #3fb950; --red: #f85149; --amber: #d29922; --blue: #79c0ff;
|
|
145
|
+
--badge-pass-bg: #0d3a1e; --badge-fail-bg: #3d1014; --badge-err-bg: #3d2a00; --badge-skip-bg: #21262d;
|
|
146
|
+
--hook-before-border: #7c3aed; --hook-after-border: #0e7490;
|
|
147
|
+
--hook-before-bg: #4c1d95; --hook-before-fg: #c4b5fd;
|
|
148
|
+
--hook-after-bg: #164e63; --hook-after-fg: #67e8f9;
|
|
149
|
+
--link: #3d7fb2;
|
|
150
|
+
}
|
|
151
|
+
html.light {
|
|
152
|
+
--bg: #ffffff; --bg-surface: #f6f8fa; --bg-panel: #ffffff;
|
|
153
|
+
--border: #d0d7de; --border-hover:#c4c9cf;
|
|
154
|
+
--text: #1f2328; --text-bright: #1f2328; --text-muted: #656d76; --text-dim: #8b949e;
|
|
155
|
+
--green: #1a7f37; --red: #cf222e; --amber: #9a6700; --blue: #0969da;
|
|
156
|
+
--badge-pass-bg: #dafbe1; --badge-fail-bg: #ffebe9; --badge-err-bg: #fff8c5; --badge-skip-bg: #f6f8fa;
|
|
157
|
+
--hook-before-border: #8b5cf6; --hook-after-border: #06b6d4;
|
|
158
|
+
--hook-before-bg: #ede9fe; --hook-before-fg: #6d28d9;
|
|
159
|
+
--hook-after-bg: #ecfeff; --hook-after-fg: #0e7490;
|
|
160
|
+
--link: #0969da;
|
|
161
|
+
}
|
|
162
|
+
/* ─── Base ───────────────────────────────────────────────────────────── */
|
|
139
163
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
140
|
-
body { font-family: system-ui, sans-serif; background:
|
|
164
|
+
body { font-family: system-ui, sans-serif; background: var(--bg); color: var(--text); font-size: 13px; line-height: 1.5; }
|
|
141
165
|
.wrap { max-width: 1200px; margin: 0 auto; padding: 32px 24px; }
|
|
142
|
-
h1 { font-size: 20px; font-weight: 600; color:
|
|
143
|
-
.meta-line { color:
|
|
166
|
+
h1 { font-size: 20px; font-weight: 600; color: var(--text-bright); margin-bottom: 4px; }
|
|
167
|
+
.meta-line { color: var(--text-muted); font-size: 11px; margin-bottom: 24px; }
|
|
168
|
+
/* Theme toggle */
|
|
169
|
+
.theme-toggle { position: fixed; top: 16px; right: 16px; background: var(--bg-surface); border: 1px solid var(--border); border-radius: 6px; padding: 4px 10px; font-size: 16px; cursor: pointer; z-index: 100; line-height: 1; }
|
|
170
|
+
.theme-toggle:hover { border-color: var(--blue); }
|
|
171
|
+
/* Summary */
|
|
144
172
|
.summary { display: flex; gap: 12px; margin-bottom: 28px; flex-wrap: wrap; }
|
|
145
|
-
.stat { background:
|
|
146
|
-
.stat-val { font-size: 22px; font-weight: 700; color:
|
|
147
|
-
.stat-lbl { font-size: 11px; color:
|
|
148
|
-
.stat-pass .stat-val { color:
|
|
149
|
-
.stat-fail .stat-val { color:
|
|
150
|
-
.stat-err .stat-val { color:
|
|
151
|
-
.stat-skip .stat-val { color:
|
|
173
|
+
.stat { background: var(--bg-surface); border: 1px solid var(--border); border-radius: 8px; padding: 12px 20px; min-width: 90px; }
|
|
174
|
+
.stat-val { font-size: 22px; font-weight: 700; color: var(--text-bright); }
|
|
175
|
+
.stat-lbl { font-size: 11px; color: var(--text-muted); margin-top: 2px; }
|
|
176
|
+
.stat-pass .stat-val { color: var(--green); }
|
|
177
|
+
.stat-fail .stat-val { color: var(--red); }
|
|
178
|
+
.stat-err .stat-val { color: var(--amber); }
|
|
179
|
+
.stat-skip .stat-val { color: var(--text-muted); }
|
|
152
180
|
/* Cards */
|
|
153
|
-
.card { border: 1px solid
|
|
154
|
-
.scope-heading { margin: 18px 0 6px; padding: 4px 2px; font-size: 11px; font-weight: 600; color:
|
|
155
|
-
.scope-heading::before { content: "
|
|
181
|
+
.card { border: 1px solid var(--border); border-radius: 8px; margin-bottom: 8px; overflow: hidden; }
|
|
182
|
+
.scope-heading { margin: 18px 0 6px; padding: 4px 2px; font-size: 11px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; border-bottom: 1px solid var(--border); }
|
|
183
|
+
.scope-heading::before { content: "\\25B8 "; color: var(--text-dim); }
|
|
156
184
|
.card-header { display: flex; align-items: baseline; gap: 8px; padding: 10px 14px; cursor: pointer; user-select: none; }
|
|
157
|
-
.card-header:hover { background:
|
|
158
|
-
.chevron { font-size: 10px; color:
|
|
159
|
-
.card-name { font-weight: 500; color:
|
|
185
|
+
.card-header:hover { background: var(--bg-surface); }
|
|
186
|
+
.chevron { font-size: 10px; color: var(--text-muted); min-width: 10px; transition: transform .15s; }
|
|
187
|
+
.card-name { font-weight: 500; color: var(--text-bright); white-space: nowrap; }
|
|
160
188
|
.card-url { font-family: monospace; font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
|
|
161
|
-
.card-body { padding: 12px 14px; border-top: 1px solid
|
|
189
|
+
.card-body { padding: 12px 14px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 10px; }
|
|
162
190
|
/* Badges */
|
|
163
191
|
.badge { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 11px; font-weight: 600; white-space: nowrap; }
|
|
164
|
-
.badge-pass { background:
|
|
165
|
-
.badge-fail { background:
|
|
166
|
-
.badge-err { background:
|
|
167
|
-
.badge-skip { background:
|
|
168
|
-
.method { font-family: monospace; font-size: 11px; font-weight: 700; color:
|
|
169
|
-
.method-badge { display: inline-block; font-family: monospace; font-size: 11px; font-weight: 700; color:
|
|
192
|
+
.badge-pass { background: var(--badge-pass-bg); color: var(--green); }
|
|
193
|
+
.badge-fail { background: var(--badge-fail-bg); color: var(--red); }
|
|
194
|
+
.badge-err { background: var(--badge-err-bg); color: var(--amber); }
|
|
195
|
+
.badge-skip { background: var(--badge-skip-bg); color: var(--text-muted); }
|
|
196
|
+
.method { font-family: monospace; font-size: 11px; font-weight: 700; color: var(--blue); white-space: nowrap; }
|
|
197
|
+
.method-badge { display: inline-block; font-family: monospace; font-size: 11px; font-weight: 700; color: var(--blue); min-width: 52px; }
|
|
170
198
|
.http-badge { font-family: monospace; font-size: 11px; font-weight: 600; white-space: nowrap; }
|
|
171
|
-
.http-ok { color:
|
|
172
|
-
.http-redir { color:
|
|
173
|
-
.http-err { color:
|
|
199
|
+
.http-ok { color: var(--green); }
|
|
200
|
+
.http-redir { color: var(--blue); }
|
|
201
|
+
.http-err { color: var(--red); }
|
|
174
202
|
.dur { font-family: monospace; font-size: 11px; white-space: nowrap; }
|
|
175
|
-
.muted { color:
|
|
203
|
+
.muted { color: var(--text-muted); }
|
|
176
204
|
.mono { font-family: monospace; font-size: 12px; }
|
|
177
205
|
/* Request / Response */
|
|
178
206
|
.req-resp-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
|
179
207
|
@media (max-width: 700px) { .req-resp-grid { grid-template-columns: 1fr; } }
|
|
180
|
-
.panel-label { font-size: 11px; font-weight: 600; color:
|
|
181
|
-
.panel { background:
|
|
182
|
-
.req-line { font-family: monospace; font-size: 12px; color:
|
|
208
|
+
.panel-label { font-size: 11px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 6px; }
|
|
209
|
+
.panel { background: var(--bg-panel); border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; display: flex; flex-direction: column; gap: 8px; }
|
|
210
|
+
.req-line { font-family: monospace; font-size: 12px; color: var(--text); word-break: break-all; }
|
|
183
211
|
.resp-status { font-family: monospace; font-size: 13px; font-weight: 700; }
|
|
184
|
-
.section-label { font-size: 10px; font-weight: 600; color:
|
|
212
|
+
.section-label { font-size: 10px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; margin-top: 2px; }
|
|
185
213
|
.htable { width: 100%; border-collapse: collapse; }
|
|
186
214
|
.htable td { font-family: monospace; font-size: 11px; padding: 1px 0; vertical-align: top; }
|
|
187
|
-
.hk { color:
|
|
188
|
-
.hv { color:
|
|
189
|
-
.code-block { font-family: monospace; font-size: 11px; color:
|
|
215
|
+
.hk { color: var(--blue); padding-right: 12px; white-space: nowrap; }
|
|
216
|
+
.hv { color: var(--text); word-break: break-all; }
|
|
217
|
+
.code-block { font-family: monospace; font-size: 11px; color: var(--text); background: var(--bg-panel); border: 1px solid var(--border); border-radius: 4px; padding: 8px; white-space: pre-wrap; word-break: break-all; max-height: 300px; overflow-y: auto; }
|
|
190
218
|
/* Tests */
|
|
191
|
-
.section-label { font-size: 11px; font-weight: 600; color:
|
|
219
|
+
.section-label { font-size: 11px; font-weight: 600; color: var(--text-muted); }
|
|
192
220
|
.tests-wrap { display: flex; flex-direction: column; gap: 2px; }
|
|
193
221
|
.test-row { font-size: 12px; display: flex; flex-wrap: wrap; gap: 4px; }
|
|
194
|
-
.test-pass { color:
|
|
195
|
-
.test-fail { color:
|
|
196
|
-
.test-err { color:
|
|
222
|
+
.test-pass { color: var(--green); }
|
|
223
|
+
.test-fail { color: var(--red); }
|
|
224
|
+
.test-err { color: var(--text-muted); padding-left: 16px; width: 100%; font-family: monospace; font-size: 11px; }
|
|
197
225
|
.dot { font-weight: 700; }
|
|
198
226
|
/* Console */
|
|
199
|
-
.err-row { color:
|
|
227
|
+
.err-row { color: var(--red); font-size: 12px; }
|
|
200
228
|
/* Footer */
|
|
201
|
-
.report-footer { margin-top: 32px; text-align: center; font-size: 11px; color:
|
|
202
|
-
.footer-link { color:
|
|
229
|
+
.report-footer { margin-top: 32px; text-align: center; font-size: 11px; color: var(--text-dim); }
|
|
230
|
+
.footer-link { color: var(--link); text-decoration: none; }
|
|
203
231
|
.footer-link:hover { text-decoration: underline; }
|
|
204
232
|
/* Hook cards */
|
|
205
|
-
.card-hook-before { border-left: 3px solid
|
|
206
|
-
.card-hook-after { border-left: 3px solid
|
|
233
|
+
.card-hook-before { border-left: 3px solid var(--hook-before-border); }
|
|
234
|
+
.card-hook-after { border-left: 3px solid var(--hook-after-border); }
|
|
207
235
|
.hook-badge { display: inline-block; padding: 1px 7px; border-radius: 10px; font-size: 10px; font-weight: 700; white-space: nowrap; letter-spacing: .04em; }
|
|
208
|
-
.hook-before { background:
|
|
209
|
-
.hook-after { background:
|
|
236
|
+
.hook-before { background: var(--hook-before-bg); color: var(--hook-before-fg); }
|
|
237
|
+
.hook-after { background: var(--hook-after-bg); color: var(--hook-after-fg); }
|
|
210
238
|
</style>
|
|
211
239
|
</head>
|
|
212
240
|
<body>
|
|
241
|
+
<button class="theme-toggle" onclick="toggleTheme()" title="Toggle dark/light mode" id="themeBtn">☀️</button>
|
|
213
242
|
<div class="wrap">
|
|
214
243
|
<h1>${esc(collection)}</h1>
|
|
215
244
|
<div class="meta-line">Environment: ${esc(env)} · ${esc(ts)}</div>
|
|
@@ -223,7 +252,7 @@ function buildHtmlReport(results, summary, meta = {}) {
|
|
|
223
252
|
<div class="stat"><div class="stat-val">${summary.durationMs} ms</div><div class="stat-lbl">Duration</div></div>
|
|
224
253
|
</div>
|
|
225
254
|
<div class="cards">${cards}</div>
|
|
226
|
-
<div class="report-footer">Generated by <a href="https://testsmith.io" target="_blank" rel="noopener" class="footer-link">Testsmith</a> ·
|
|
255
|
+
<div class="report-footer">Generated by <a href="https://testsmith.io" target="_blank" rel="noopener" class="footer-link">Testsmith</a> · API Spector</div>
|
|
227
256
|
</div>
|
|
228
257
|
<script>
|
|
229
258
|
function toggle(i) {
|
|
@@ -233,6 +262,22 @@ function buildHtmlReport(results, summary, meta = {}) {
|
|
|
233
262
|
body.style.display = open ? 'none' : 'block'
|
|
234
263
|
ch.style.transform = open ? '' : 'rotate(90deg)'
|
|
235
264
|
}
|
|
265
|
+
function toggleTheme() {
|
|
266
|
+
const html = document.documentElement
|
|
267
|
+
const isLight = html.classList.toggle('light')
|
|
268
|
+
document.getElementById('themeBtn').textContent = isLight ? '🌙' : '☀️'
|
|
269
|
+
try { localStorage.setItem('theme', isLight ? 'light' : 'dark') } catch {}
|
|
270
|
+
}
|
|
271
|
+
// Restore saved preference or respect OS preference
|
|
272
|
+
(function() {
|
|
273
|
+
try {
|
|
274
|
+
const saved = localStorage.getItem('theme')
|
|
275
|
+
if (saved === 'light' || (!saved && window.matchMedia('(prefers-color-scheme: light)').matches)) {
|
|
276
|
+
document.documentElement.classList.add('light')
|
|
277
|
+
document.getElementById('themeBtn').textContent = '🌙'
|
|
278
|
+
}
|
|
279
|
+
} catch {}
|
|
280
|
+
})()
|
|
236
281
|
<\/script>
|
|
237
282
|
</body>
|
|
238
283
|
</html>
|
|
@@ -289,23 +334,6 @@ ${failures.join("\n")}
|
|
|
289
334
|
];
|
|
290
335
|
return lines.join("\n") + "\n";
|
|
291
336
|
}
|
|
292
|
-
function collectTagged(folder, requests, collectionVars, filterTags, parentPath = [], isRoot = true) {
|
|
293
|
-
const results = [];
|
|
294
|
-
const scopePath = isRoot ? parentPath : [...parentPath, folder.name];
|
|
295
|
-
for (const reqId of folder.requestIds) {
|
|
296
|
-
const req = requests[reqId];
|
|
297
|
-
if (!req || req.hookType || req.disabled) continue;
|
|
298
|
-
const tags = req.meta?.tags ?? [];
|
|
299
|
-
if (filterTags.length > 0 && !filterTags.some((t) => tags.includes(t))) continue;
|
|
300
|
-
results.push({ request: req, collectionVars, scopePath });
|
|
301
|
-
}
|
|
302
|
-
for (const sub of folder.folders) {
|
|
303
|
-
const folderTags = sub.tags ?? [];
|
|
304
|
-
const effectiveTags = filterTags.length === 0 ? filterTags : folderTags.some((t) => filterTags.includes(t)) ? [] : filterTags;
|
|
305
|
-
results.push(...collectTagged(sub, requests, collectionVars, effectiveTags, scopePath, false));
|
|
306
|
-
}
|
|
307
|
-
return results;
|
|
308
|
-
}
|
|
309
337
|
const C = {
|
|
310
338
|
reset: "\x1B[0m",
|
|
311
339
|
bold: "\x1B[1m",
|
|
@@ -374,6 +402,10 @@ async function loadEnvironments(workspace, dir) {
|
|
|
374
402
|
return envs;
|
|
375
403
|
}
|
|
376
404
|
async function executeRequest(req, collectionVars, envVars, globals, localVars, verbose, tls) {
|
|
405
|
+
if (!req.headers) req.headers = [];
|
|
406
|
+
if (!req.params) req.params = [];
|
|
407
|
+
if (!req.body) req.body = { mode: "none" };
|
|
408
|
+
if (!req.auth) req.auth = { type: "none" };
|
|
377
409
|
const base = {
|
|
378
410
|
requestId: req.id,
|
|
379
411
|
name: req.name,
|
|
@@ -385,10 +417,10 @@ async function executeRequest(req, collectionVars, envVars, globals, localVars,
|
|
|
385
417
|
let updatedCollectionVars = { ...collectionVars };
|
|
386
418
|
let updatedGlobals = { ...globals };
|
|
387
419
|
let preScriptError;
|
|
388
|
-
const dynamicVars = await
|
|
389
|
-
let vars =
|
|
420
|
+
const dynamicVars = await requestCollection.buildDynamicVars();
|
|
421
|
+
let vars = requestCollection.mergeVars(envVars, collectionVars, globals, localVars, dynamicVars);
|
|
390
422
|
if (req.preRequestScript?.trim()) {
|
|
391
|
-
const r = await
|
|
423
|
+
const r = await requestCollection.runScript(requestCollection.interpolate(req.preRequestScript, vars), {
|
|
392
424
|
envVars: { ...envVars },
|
|
393
425
|
collectionVars: { ...collectionVars },
|
|
394
426
|
globals: { ...globals },
|
|
@@ -399,41 +431,41 @@ async function executeRequest(req, collectionVars, envVars, globals, localVars,
|
|
|
399
431
|
updatedEnvVars = r.updatedEnvVars;
|
|
400
432
|
updatedCollectionVars = r.updatedCollectionVars;
|
|
401
433
|
updatedGlobals = r.updatedGlobals;
|
|
402
|
-
|
|
403
|
-
await
|
|
434
|
+
requestCollection.patchGlobals(r.updatedGlobals);
|
|
435
|
+
await requestCollection.persistGlobals();
|
|
404
436
|
if (verbose && r.consoleOutput.length) r.consoleOutput.forEach((l) => console.log(color(` [pre] ${l}`, C.gray)));
|
|
405
437
|
if (r.error) console.error(color(` [pre-script error] ${r.error}`, C.red));
|
|
406
438
|
}
|
|
407
|
-
vars =
|
|
408
|
-
const resolvedUrl =
|
|
439
|
+
vars = requestCollection.mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars);
|
|
440
|
+
const resolvedUrl = requestCollection.buildUrl(req.url, req.params, vars);
|
|
409
441
|
base.resolvedUrl = resolvedUrl;
|
|
410
442
|
const start = Date.now();
|
|
411
443
|
try {
|
|
412
444
|
const authH = {};
|
|
413
445
|
if (req.auth.type === "bearer") {
|
|
414
446
|
let token = req.auth.token ?? "";
|
|
415
|
-
if (!token && req.auth.tokenSecretRef) token = await
|
|
416
|
-
if (token) authH["Authorization"] = `Bearer ${
|
|
447
|
+
if (!token && req.auth.tokenSecretRef) token = await requestCollection.getSecret(req.auth.tokenSecretRef) ?? "";
|
|
448
|
+
if (token) authH["Authorization"] = `Bearer ${requestCollection.interpolate(token, vars)}`;
|
|
417
449
|
}
|
|
418
450
|
const headers = new undici.Headers();
|
|
419
451
|
for (const h of req.headers) {
|
|
420
|
-
if (h.enabled && h.key) headers.set(
|
|
452
|
+
if (h.enabled && h.key) headers.set(requestCollection.interpolate(h.key, vars), requestCollection.interpolate(h.value, vars));
|
|
421
453
|
}
|
|
422
454
|
for (const [k, v] of Object.entries(authH)) headers.set(k, v);
|
|
423
455
|
let body;
|
|
424
456
|
if (req.body.mode === "json" && req.body.json) {
|
|
425
|
-
body =
|
|
457
|
+
body = requestCollection.interpolate(req.body.json, vars);
|
|
426
458
|
if (!headers.has("content-type")) headers.set("Content-Type", "application/json");
|
|
427
459
|
} else if (req.body.mode === "raw" && req.body.raw) {
|
|
428
|
-
body =
|
|
460
|
+
body = requestCollection.interpolate(req.body.raw, vars);
|
|
429
461
|
if (!headers.has("content-type")) headers.set("Content-Type", req.body.rawContentType ?? "text/plain");
|
|
430
462
|
} else if (req.body.mode === "graphql" && req.body.graphql) {
|
|
431
463
|
const gql = req.body.graphql;
|
|
432
|
-
const gqlBody = { query:
|
|
464
|
+
const gqlBody = { query: requestCollection.interpolate(gql.query, vars) };
|
|
433
465
|
const rawVars = gql.variables?.trim();
|
|
434
466
|
if (rawVars) {
|
|
435
467
|
try {
|
|
436
|
-
gqlBody.variables = JSON.parse(
|
|
468
|
+
gqlBody.variables = JSON.parse(requestCollection.interpolate(rawVars, vars));
|
|
437
469
|
} catch {
|
|
438
470
|
}
|
|
439
471
|
}
|
|
@@ -441,7 +473,7 @@ async function executeRequest(req, collectionVars, envVars, globals, localVars,
|
|
|
441
473
|
body = JSON.stringify(gqlBody);
|
|
442
474
|
if (!headers.has("content-type")) headers.set("Content-Type", "application/json");
|
|
443
475
|
}
|
|
444
|
-
const dispatcher = await
|
|
476
|
+
const dispatcher = await requestCollection.buildDispatcher(void 0, tls);
|
|
445
477
|
const fetchResp = await undici.fetch(resolvedUrl, {
|
|
446
478
|
method: req.method,
|
|
447
479
|
headers,
|
|
@@ -466,7 +498,7 @@ async function executeRequest(req, collectionVars, envVars, globals, localVars,
|
|
|
466
498
|
let consoleOutput = [];
|
|
467
499
|
let postScriptError;
|
|
468
500
|
if (req.postRequestScript?.trim()) {
|
|
469
|
-
const r = await
|
|
501
|
+
const r = await requestCollection.runScript(requestCollection.interpolate(req.postRequestScript, vars), {
|
|
470
502
|
envVars: updatedEnvVars,
|
|
471
503
|
collectionVars: updatedCollectionVars,
|
|
472
504
|
globals: updatedGlobals,
|
|
@@ -480,15 +512,15 @@ async function executeRequest(req, collectionVars, envVars, globals, localVars,
|
|
|
480
512
|
updatedCollectionVars = r.updatedCollectionVars;
|
|
481
513
|
updatedGlobals = r.updatedGlobals;
|
|
482
514
|
localVars = r.updatedLocalVars;
|
|
483
|
-
|
|
484
|
-
await
|
|
515
|
+
requestCollection.patchGlobals(r.updatedGlobals);
|
|
516
|
+
await requestCollection.persistGlobals();
|
|
485
517
|
if (verbose && r.consoleOutput.length) r.consoleOutput.forEach((l) => console.log(color(` [post] ${l}`, C.gray)));
|
|
486
518
|
}
|
|
487
519
|
const allPassed = testResults.every((t) => t.passed);
|
|
488
520
|
const httpFailed = fetchResp.status >= 400;
|
|
489
521
|
const hasTests = testResults.length > 0;
|
|
490
|
-
const status = postScriptError ? "error" :
|
|
491
|
-
if (httpFailed &&
|
|
522
|
+
const status = postScriptError ? "error" : hasTests ? allPassed ? "passed" : "failed" : httpFailed ? "failed" : "skipped";
|
|
523
|
+
if (httpFailed && testResults.length === 0) {
|
|
492
524
|
testResults = [
|
|
493
525
|
...testResults,
|
|
494
526
|
{
|
|
@@ -581,7 +613,7 @@ async function main() {
|
|
|
581
613
|
console.error(color(`Error: could not read workspace file: ${wsPath}`, C.red));
|
|
582
614
|
process.exit(1);
|
|
583
615
|
}
|
|
584
|
-
await
|
|
616
|
+
await requestCollection.loadGlobals(wsDir);
|
|
585
617
|
const collections = await loadCollections(workspace, wsDir);
|
|
586
618
|
const environments = await loadEnvironments(workspace, wsDir);
|
|
587
619
|
const env = envName ? environments.find((e) => e.name.toLowerCase() === envName.toLowerCase()) ?? null : null;
|
|
@@ -594,7 +626,7 @@ async function main() {
|
|
|
594
626
|
console.log(color(` Environment: ${env?.name ?? "(none)"}`, C.gray));
|
|
595
627
|
if (filterTags.length) console.log(color(` Tags: ${filterTags.join(", ")}`, C.gray));
|
|
596
628
|
console.log("");
|
|
597
|
-
const envVarsSnapshot = await
|
|
629
|
+
const envVarsSnapshot = await requestCollection.buildEnvVars(env);
|
|
598
630
|
const secretValuesToMask = (env?.variables ?? []).filter((v) => v.secret && v.enabled).map((v) => envVarsSnapshot[v.key]).filter((v) => typeof v === "string" && v.length > 0);
|
|
599
631
|
function redact(s) {
|
|
600
632
|
let out = s;
|
|
@@ -621,16 +653,27 @@ async function main() {
|
|
|
621
653
|
let firstColName;
|
|
622
654
|
for (const col of collections) {
|
|
623
655
|
if (colName && col.name.toLowerCase() !== colName.toLowerCase()) continue;
|
|
624
|
-
const items = collectTagged(col.rootFolder, col.requests, col.collectionVariables ?? {}, filterTags);
|
|
656
|
+
const items = requestCollection.collectTagged(col.rootFolder, col.requests, col.collectionVariables ?? {}, filterTags);
|
|
625
657
|
if (items.length === 0) continue;
|
|
626
658
|
if (!firstColName) firstColName = col.name;
|
|
627
|
-
let runEnvVars = await
|
|
628
|
-
let runGlobals =
|
|
659
|
+
let runEnvVars = await requestCollection.buildEnvVars(env);
|
|
660
|
+
let runGlobals = requestCollection.getGlobals();
|
|
629
661
|
let runCollectionVars = { ...col.collectionVariables ?? {} };
|
|
630
662
|
let runLocalVars = {};
|
|
631
663
|
console.log(color(` ┌ ${col.name}`, C.bold, C.white));
|
|
632
664
|
const workspaceTls = workspace.settings?.tls;
|
|
633
665
|
const effectiveTls = col.tls ? { ...workspaceTls, ...col.tls } : workspaceTls;
|
|
666
|
+
for (const item of items) {
|
|
667
|
+
const req = item.request;
|
|
668
|
+
const inherited = requestCollection.resolveInheritedAuthAndHeaders(req.id, col);
|
|
669
|
+
if (req.auth.type === "none" && inherited.auth && inherited.auth.type !== "none") {
|
|
670
|
+
req.auth = inherited.auth;
|
|
671
|
+
}
|
|
672
|
+
const inheritedHeaders = inherited.headers.filter((h) => h.enabled && h.key);
|
|
673
|
+
if (inheritedHeaders.length) {
|
|
674
|
+
req.headers = [...inheritedHeaders, ...req.headers];
|
|
675
|
+
}
|
|
676
|
+
}
|
|
634
677
|
let bailed = false;
|
|
635
678
|
let lastPrintedScope = null;
|
|
636
679
|
for (const item of items) {
|