@testsmith/api-spector 0.3.4 → 0.3.6
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 +11 -7
- package/out/main/agents.js +1 -1
- package/out/main/chunks/environments-iM3SUM-4.js +28 -0
- package/out/main/chunks/{handle-C0IQL-Vl.js → handle-BtRMtQJg.js} +3 -1
- package/out/main/chunks/{import-C5Ngq8hk.js → import-CZxL6J96.js} +4 -4
- package/out/main/chunks/{recorder-DFxJgn9c.js → recorder-0Ij921El.js} +1 -1
- package/out/main/chunks/request-collection-CBuPjBwa.js +118 -0
- package/out/main/chunks/{request-collection-8TOVNXE0.js → request-exec-DbNHbA8x.js} +602 -146
- package/out/main/chunks/snapshots-rV5tPwAd.js +1658 -0
- package/out/main/chunks/{soap-handler-B9x_YCtj.js → soap-handler-CqXVreNa.js} +3 -3
- package/out/main/contract.js +508 -91
- package/out/main/index.js +158 -87
- package/out/main/record.js +3 -3
- package/out/main/runner.js +23 -20
- package/out/main/wsdl.js +4 -4
- package/out/preload/index.js +5 -1
- package/out/renderer/assets/index-DWiG0dQo.css +2 -0
- package/out/renderer/assets/{index-BjjOh-E5.js → index-htBErIFG.js} +915 -176
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
- package/readme.md +2 -2
- package/out/main/chunks/auth-builder-CUs9yzOF.js +0 -623
- package/out/main/chunks/snapshots-UFd3XgSS.js +0 -928
- package/out/renderer/assets/index-CjvjKHtF.css +0 -2
package/out/main/index.js
CHANGED
|
@@ -22,28 +22,28 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
22
22
|
mod
|
|
23
23
|
));
|
|
24
24
|
const electron = require("electron");
|
|
25
|
-
const handle = require("./chunks/handle-
|
|
25
|
+
const handle = require("./chunks/handle-BtRMtQJg.js");
|
|
26
26
|
const path = require("path");
|
|
27
27
|
const fs = require("fs");
|
|
28
28
|
const promises = require("fs/promises");
|
|
29
|
-
const
|
|
30
|
-
const authBuilder = require("./chunks/auth-builder-CUs9yzOF.js");
|
|
29
|
+
const requestExec = require("./chunks/request-exec-DbNHbA8x.js");
|
|
31
30
|
const ipcValidate = require("./chunks/ipc-validate-k6KI8adf.js");
|
|
32
31
|
const uuid = require("uuid");
|
|
33
32
|
const jsYaml = require("js-yaml");
|
|
34
33
|
const undici = require("undici");
|
|
35
34
|
const JSZip = require("jszip");
|
|
35
|
+
const requestCollection = require("./chunks/request-collection-CBuPjBwa.js");
|
|
36
36
|
const mockServer = require("./chunks/mock-server-DSLR2ulH.js");
|
|
37
37
|
const http = require("http");
|
|
38
38
|
const WebSocket = require("ws");
|
|
39
|
-
const soapHandler = require("./chunks/soap-handler-
|
|
39
|
+
const soapHandler = require("./chunks/soap-handler-CqXVreNa.js");
|
|
40
40
|
const os = require("os");
|
|
41
41
|
const crypto = require("crypto");
|
|
42
|
-
const snapshots = require("./chunks/snapshots-
|
|
42
|
+
const snapshots = require("./chunks/snapshots-rV5tPwAd.js");
|
|
43
43
|
const simpleGit = require("simple-git");
|
|
44
|
-
const recorder = require("./chunks/recorder-
|
|
45
|
-
require("vm");
|
|
44
|
+
const recorder = require("./chunks/recorder-0Ij921El.js");
|
|
46
45
|
require("dayjs");
|
|
46
|
+
require("vm");
|
|
47
47
|
require("tv4");
|
|
48
48
|
require("jsonpath-plus");
|
|
49
49
|
require("@xmldom/xmldom");
|
|
@@ -67,7 +67,7 @@ function atomicWrite(path2, data) {
|
|
|
67
67
|
return promises.writeFile(path2, data, "utf8");
|
|
68
68
|
}
|
|
69
69
|
const SPECTOR_GITIGNORE = [
|
|
70
|
-
"# API Spector
|
|
70
|
+
"# API Spector - never commit secrets",
|
|
71
71
|
"*.secrets",
|
|
72
72
|
".env",
|
|
73
73
|
".env.local",
|
|
@@ -102,7 +102,7 @@ function readmeContents(workspaceFileName) {
|
|
|
102
102
|
`# API tests`,
|
|
103
103
|
``,
|
|
104
104
|
`This folder is an [API Spector](https://github.com/testsmith-io/api-spector) workspace.`,
|
|
105
|
-
`Everything here is plain JSON
|
|
105
|
+
`Everything here is plain JSON - diff it, commit it, review it like any other code.`,
|
|
106
106
|
``,
|
|
107
107
|
`## Layout`,
|
|
108
108
|
``,
|
|
@@ -144,7 +144,7 @@ function readmeContents(workspaceFileName) {
|
|
|
144
144
|
`## A note on secrets`,
|
|
145
145
|
``,
|
|
146
146
|
`Secret values (passwords, OAuth client secrets, API keys) are stored in your`,
|
|
147
|
-
`OS keychain
|
|
147
|
+
`OS keychain - **not** in this folder. Environment files only reference the`,
|
|
148
148
|
`keychain entry by name, so it's safe to commit them.`,
|
|
149
149
|
``
|
|
150
150
|
].join("\n");
|
|
@@ -223,7 +223,7 @@ function registerFileHandlers(ipc) {
|
|
|
223
223
|
const wsPath = result.filePaths[0];
|
|
224
224
|
workspaceDir = path.dirname(wsPath);
|
|
225
225
|
workspaceFile = wsPath;
|
|
226
|
-
await
|
|
226
|
+
await requestExec.loadGlobals(workspaceDir);
|
|
227
227
|
await saveLastWorkspacePath(wsPath);
|
|
228
228
|
const raw = await promises.readFile(wsPath, "utf8");
|
|
229
229
|
return { workspace: JSON.parse(raw), workspacePath: wsPath };
|
|
@@ -239,7 +239,7 @@ function registerFileHandlers(ipc) {
|
|
|
239
239
|
if (result.canceled || !result.filePath) return null;
|
|
240
240
|
workspaceDir = path.dirname(result.filePath);
|
|
241
241
|
workspaceFile = result.filePath;
|
|
242
|
-
await
|
|
242
|
+
await requestExec.loadGlobals(workspaceDir);
|
|
243
243
|
await promises.mkdir(path.join(workspaceDir, "collections"), { recursive: true });
|
|
244
244
|
await promises.mkdir(path.join(workspaceDir, "environments"), { recursive: true });
|
|
245
245
|
await ensureGitignore(workspaceDir);
|
|
@@ -317,10 +317,10 @@ function registerFileHandlers(ipc) {
|
|
|
317
317
|
await promises.writeFile(result.filePath, content, "utf8");
|
|
318
318
|
return true;
|
|
319
319
|
});
|
|
320
|
-
handle.handleIpc(ipc, handle.IPC.globals.get, () =>
|
|
320
|
+
handle.handleIpc(ipc, handle.IPC.globals.get, () => requestExec.getGlobals());
|
|
321
321
|
handle.handleIpc(ipc, handle.IPC.globals.set, async (_e, patch) => {
|
|
322
|
-
|
|
323
|
-
await
|
|
322
|
+
requestExec.setGlobals(patch);
|
|
323
|
+
await requestExec.persistGlobals();
|
|
324
324
|
});
|
|
325
325
|
handle.handleIpc(ipc, handle.IPC.file.closeWorkspace, async () => {
|
|
326
326
|
workspaceDir = null;
|
|
@@ -342,7 +342,7 @@ function registerFileHandlers(ipc) {
|
|
|
342
342
|
const workspace = JSON.parse(raw);
|
|
343
343
|
workspaceDir = path.dirname(wsPath);
|
|
344
344
|
workspaceFile = wsPath;
|
|
345
|
-
await
|
|
345
|
+
await requestExec.loadGlobals(workspaceDir);
|
|
346
346
|
return { workspace, workspacePath: wsPath };
|
|
347
347
|
} catch {
|
|
348
348
|
return null;
|
|
@@ -364,7 +364,7 @@ async function tryOpenWorkspaceInDir(dir) {
|
|
|
364
364
|
const workspace = JSON.parse(raw);
|
|
365
365
|
workspaceDir = dir;
|
|
366
366
|
workspaceFile = wsPath;
|
|
367
|
-
await
|
|
367
|
+
await requestExec.loadGlobals(workspaceDir);
|
|
368
368
|
return { workspace, workspacePath: wsPath };
|
|
369
369
|
} catch {
|
|
370
370
|
return null;
|
|
@@ -383,7 +383,7 @@ function readStringField(obj, key) {
|
|
|
383
383
|
function safeProxySummary(proxy) {
|
|
384
384
|
if (!proxy?.url?.trim()) return "off";
|
|
385
385
|
try {
|
|
386
|
-
const normalized =
|
|
386
|
+
const normalized = requestExec.buildProxyUri({ url: proxy.url });
|
|
387
387
|
const parsed = new URL(normalized);
|
|
388
388
|
const host = parsed.port ? `${parsed.hostname}:${parsed.port}` : parsed.hostname;
|
|
389
389
|
const auth = proxy.auth ? "yes" : "no";
|
|
@@ -438,11 +438,11 @@ function registerRequestHandler(ipc) {
|
|
|
438
438
|
tls,
|
|
439
439
|
piiMaskPatterns = []
|
|
440
440
|
} = payload;
|
|
441
|
-
|
|
441
|
+
requestExec.applyRequestDefaults(req);
|
|
442
442
|
const start = Date.now();
|
|
443
|
-
const liveGlobals =
|
|
443
|
+
const liveGlobals = requestExec.getGlobals();
|
|
444
444
|
const mergedGlobals = { ...payloadGlobals, ...liveGlobals };
|
|
445
|
-
const envVars = await
|
|
445
|
+
const envVars = await requestExec.buildEnvVars(environment);
|
|
446
446
|
let localVars = {};
|
|
447
447
|
const decryptionWarnings = [];
|
|
448
448
|
if (environment) {
|
|
@@ -456,14 +456,14 @@ function registerRequestHandler(ipc) {
|
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
458
|
}
|
|
459
|
-
const dynamicVars = await
|
|
460
|
-
let vars =
|
|
459
|
+
const dynamicVars = await requestExec.buildDynamicVars();
|
|
460
|
+
let vars = requestExec.mergeVars(envVars, collectionVars, mergedGlobals, localVars, dynamicVars);
|
|
461
461
|
let preScriptMeta = { consoleOutput: [] };
|
|
462
462
|
let updatedCollectionVars = { ...collectionVars };
|
|
463
463
|
let updatedEnvVars = { ...envVars };
|
|
464
464
|
let updatedGlobals = { ...mergedGlobals };
|
|
465
465
|
if (req.preRequestScript?.trim()) {
|
|
466
|
-
const result = await
|
|
466
|
+
const result = await requestExec.runScript(requestExec.interpolate(req.preRequestScript, vars), {
|
|
467
467
|
envVars: { ...envVars },
|
|
468
468
|
collectionVars: { ...collectionVars },
|
|
469
469
|
globals: { ...mergedGlobals },
|
|
@@ -474,14 +474,14 @@ function registerRequestHandler(ipc) {
|
|
|
474
474
|
updatedEnvVars = result.updatedEnvVars;
|
|
475
475
|
updatedCollectionVars = result.updatedCollectionVars;
|
|
476
476
|
updatedGlobals = result.updatedGlobals;
|
|
477
|
-
|
|
478
|
-
await
|
|
479
|
-
vars =
|
|
477
|
+
requestExec.patchGlobals(result.updatedGlobals);
|
|
478
|
+
await requestExec.persistGlobals();
|
|
479
|
+
vars = requestExec.mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars);
|
|
480
480
|
}
|
|
481
481
|
let response;
|
|
482
482
|
let scriptResponse;
|
|
483
483
|
let sentRequest = { method: req.method, url: "", headers: {} };
|
|
484
|
-
const resolvedUrl =
|
|
484
|
+
const resolvedUrl = requestExec.buildUrl(req.url, req.params, vars);
|
|
485
485
|
const secretValues = /* @__PURE__ */ new Set();
|
|
486
486
|
if (environment) {
|
|
487
487
|
for (const v of environment.variables) {
|
|
@@ -512,8 +512,8 @@ function registerRequestHandler(ipc) {
|
|
|
512
512
|
};
|
|
513
513
|
}
|
|
514
514
|
try {
|
|
515
|
-
const dispatcher = await
|
|
516
|
-
const exchange = await
|
|
515
|
+
const dispatcher = await requestExec.buildDispatcher(proxy, tls);
|
|
516
|
+
const exchange = await requestExec.performHttpExchange({
|
|
517
517
|
req,
|
|
518
518
|
vars,
|
|
519
519
|
resolvedUrl,
|
|
@@ -524,8 +524,8 @@ function registerRequestHandler(ipc) {
|
|
|
524
524
|
sentRequest = sent;
|
|
525
525
|
}
|
|
526
526
|
});
|
|
527
|
-
const maskedBody =
|
|
528
|
-
const maskedHeaders =
|
|
527
|
+
const maskedBody = requestExec.maskPii(exchange.responseBody, piiMaskPatterns);
|
|
528
|
+
const maskedHeaders = requestExec.maskHeaders(exchange.rawHeaders, piiMaskPatterns);
|
|
529
529
|
const bodySize = Buffer.byteLength(exchange.responseBody, "utf8");
|
|
530
530
|
response = {
|
|
531
531
|
status: exchange.status,
|
|
@@ -563,12 +563,12 @@ function registerRequestHandler(ipc) {
|
|
|
563
563
|
};
|
|
564
564
|
scriptResponse = response;
|
|
565
565
|
}
|
|
566
|
-
const schemaTestResults = !response.error ?
|
|
566
|
+
const schemaTestResults = !response.error ? requestExec.buildSchemaTestResults(req.schema, scriptResponse.body) : [];
|
|
567
567
|
let postTestResults = [];
|
|
568
568
|
let postConsole = [];
|
|
569
569
|
let postError;
|
|
570
570
|
if (req.postRequestScript?.trim() && !response.error) {
|
|
571
|
-
const result = await
|
|
571
|
+
const result = await requestExec.runScript(requestExec.interpolate(req.postRequestScript, vars), {
|
|
572
572
|
envVars: { ...updatedEnvVars },
|
|
573
573
|
collectionVars: { ...updatedCollectionVars },
|
|
574
574
|
globals: { ...updatedGlobals },
|
|
@@ -584,15 +584,15 @@ function registerRequestHandler(ipc) {
|
|
|
584
584
|
updatedCollectionVars = result.updatedCollectionVars;
|
|
585
585
|
updatedGlobals = result.updatedGlobals;
|
|
586
586
|
localVars = result.updatedLocalVars;
|
|
587
|
-
|
|
588
|
-
await
|
|
587
|
+
requestExec.patchGlobals(result.updatedGlobals);
|
|
588
|
+
await requestExec.persistGlobals();
|
|
589
589
|
}
|
|
590
590
|
const combinedTestResults = [...schemaTestResults, ...postTestResults];
|
|
591
591
|
if (!response.error && response.status >= 400 && combinedTestResults.length === 0) {
|
|
592
592
|
combinedTestResults.push({
|
|
593
593
|
name: `HTTP status ${response.status} ${response.statusText}`.trim(),
|
|
594
594
|
passed: false,
|
|
595
|
-
error: `Request returned ${response.status}
|
|
595
|
+
error: `Request returned ${response.status} - no assertion was defined to verify the status code.`
|
|
596
596
|
});
|
|
597
597
|
}
|
|
598
598
|
const scriptResult = {
|
|
@@ -610,9 +610,9 @@ function registerRequestHandler(ipc) {
|
|
|
610
610
|
});
|
|
611
611
|
handle.handleIpc(ipc, handle.IPC.script.runHook, async (_e, payload) => {
|
|
612
612
|
const { script, envVars, collectionVars, globals } = payload;
|
|
613
|
-
const result = await
|
|
614
|
-
|
|
615
|
-
await
|
|
613
|
+
const result = await requestExec.runScript(script, { envVars, collectionVars, globals, localVars: {} });
|
|
614
|
+
requestExec.patchGlobals(result.updatedGlobals);
|
|
615
|
+
await requestExec.persistGlobals();
|
|
616
616
|
return {
|
|
617
617
|
updatedEnvVars: result.updatedEnvVars,
|
|
618
618
|
updatedCollectionVars: result.updatedCollectionVars,
|
|
@@ -1015,7 +1015,7 @@ function buildAuth(security, securitySchemes) {
|
|
|
1015
1015
|
}
|
|
1016
1016
|
return { type: "none" };
|
|
1017
1017
|
}
|
|
1018
|
-
const HTTP_METHODS$1 = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
1018
|
+
const HTTP_METHODS$1 = ["get", "post", "put", "patch", "delete", "head", "options", "query"];
|
|
1019
1019
|
function buildCollection(spec) {
|
|
1020
1020
|
const info = spec.info ?? {};
|
|
1021
1021
|
const servers = spec.servers ?? [{}];
|
|
@@ -1262,7 +1262,7 @@ function parseKv(blockContent) {
|
|
|
1262
1262
|
}
|
|
1263
1263
|
return result;
|
|
1264
1264
|
}
|
|
1265
|
-
const HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
1265
|
+
const HTTP_METHODS = ["get", "post", "put", "patch", "delete", "head", "options", "query"];
|
|
1266
1266
|
function parseBruFile(content, fileName) {
|
|
1267
1267
|
const meta = parseKv(extractBlock(content, "meta"));
|
|
1268
1268
|
let method = "GET";
|
|
@@ -1375,7 +1375,7 @@ async function importBruno(filePath) {
|
|
|
1375
1375
|
requests
|
|
1376
1376
|
};
|
|
1377
1377
|
}
|
|
1378
|
-
const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
1378
|
+
const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS", "QUERY"];
|
|
1379
1379
|
const HTTP_TO_SPECTOR = {
|
|
1380
1380
|
$guid: "$uuid",
|
|
1381
1381
|
$randomInt: "$randomInt",
|
|
@@ -1734,6 +1734,13 @@ function renderTree(paths) {
|
|
|
1734
1734
|
}
|
|
1735
1735
|
return [".", ...render(root)].join("\n");
|
|
1736
1736
|
}
|
|
1737
|
+
const PLAYWRIGHT_VERBS = ["get", "post", "put", "patch", "delete", "head"];
|
|
1738
|
+
const SUPERTEST_VERBS = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
1739
|
+
function restAssuredCall(method, pathArg) {
|
|
1740
|
+
const verbs = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
1741
|
+
return verbs.includes(method) ? `.${method}(${pathArg})` : `.request("${method.toUpperCase()}", ${pathArg})`;
|
|
1742
|
+
}
|
|
1743
|
+
const ROBOT_REQUESTS_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"];
|
|
1737
1744
|
function safeName(name) {
|
|
1738
1745
|
return name.replace(/[^\w\s]/g, " ").split(/\s+/).filter(Boolean).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
1739
1746
|
}
|
|
@@ -1774,14 +1781,14 @@ function buildNameMap(root, requests) {
|
|
|
1774
1781
|
function buildVariablesFile(environment) {
|
|
1775
1782
|
const lines = ["*** Variables ***"];
|
|
1776
1783
|
if (!environment) {
|
|
1777
|
-
lines.push("# No environment
|
|
1784
|
+
lines.push("# No environment - add your variables here");
|
|
1778
1785
|
lines.push("${BASE_URL} http://localhost:8080");
|
|
1779
1786
|
return lines.join("\n") + "\n";
|
|
1780
1787
|
}
|
|
1781
1788
|
for (const v of environment.variables) {
|
|
1782
1789
|
if (!v.enabled) continue;
|
|
1783
1790
|
if (v.secret) {
|
|
1784
|
-
lines.push(`# ${envVar(v.key)}
|
|
1791
|
+
lines.push(`# ${envVar(v.key)} - stored in OS keychain, never hardcoded`);
|
|
1785
1792
|
} else {
|
|
1786
1793
|
lines.push(`${robotVar(v.key)} ${v.value}`);
|
|
1787
1794
|
}
|
|
@@ -1817,7 +1824,13 @@ function buildKeywordsFile(collection, varMap, nameMap, hookExtractedVars) {
|
|
|
1817
1824
|
const url = interpolate(req.url, varMap);
|
|
1818
1825
|
const method = req.method.charAt(0) + req.method.slice(1).toLowerCase();
|
|
1819
1826
|
lines.push(kwName);
|
|
1820
|
-
lines.push(` [Documentation] Hook: ${req.hookType}
|
|
1827
|
+
lines.push(` [Documentation] Hook: ${req.hookType} - ${req.name}`);
|
|
1828
|
+
if (!ROBOT_REQUESTS_METHODS.includes(req.method)) {
|
|
1829
|
+
lines.push(` Log ${req.method} is not supported by robotframework-requests - hook skipped WARN`);
|
|
1830
|
+
lines.push(` RETURN \${None}`);
|
|
1831
|
+
lines.push("");
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1821
1834
|
const { body } = req;
|
|
1822
1835
|
if (hasBody(req) && body.mode === "json" && body.json) {
|
|
1823
1836
|
const bodyPairs = jsonToRfDictPairs(body.json, varMap);
|
|
@@ -1853,6 +1866,12 @@ function buildKeywordsFile(collection, varMap, nameMap, hookExtractedVars) {
|
|
|
1853
1866
|
const url = interpolate(req.url, varMap);
|
|
1854
1867
|
lines.push(kwName);
|
|
1855
1868
|
lines.push(` [Documentation] ${req.description || req.name}`);
|
|
1869
|
+
if (!ROBOT_REQUESTS_METHODS.includes(req.method)) {
|
|
1870
|
+
lines.push(` Log ${req.method} is not supported by robotframework-requests - request skipped WARN`);
|
|
1871
|
+
lines.push(` RETURN \${None}`);
|
|
1872
|
+
lines.push("");
|
|
1873
|
+
continue;
|
|
1874
|
+
}
|
|
1856
1875
|
const inherited = requestCollection.resolveInheritedAuthAndHeaders(reqId, collection);
|
|
1857
1876
|
const effectiveAuth = resolveEffectiveAuth(req, inherited);
|
|
1858
1877
|
const allHeaders = mergeHeaders(req, inherited);
|
|
@@ -1996,7 +2015,7 @@ function buildTestSuite(collection, environment, nameMap) {
|
|
|
1996
2015
|
}
|
|
1997
2016
|
function buildReadme$6(collectionName, filePaths) {
|
|
1998
2017
|
const tree = renderTree(filePaths);
|
|
1999
|
-
return `# ${collectionName}
|
|
2018
|
+
return `# ${collectionName} - API Tests (Robot Framework)
|
|
2000
2019
|
|
|
2001
2020
|
## Project structure
|
|
2002
2021
|
|
|
@@ -2005,7 +2024,7 @@ ${tree}
|
|
|
2005
2024
|
\`\`\`
|
|
2006
2025
|
|
|
2007
2026
|
> Secrets are read from OS environment variables (e.g. \`%{API_TOKEN}\`).
|
|
2008
|
-
> Never hardcode secrets
|
|
2027
|
+
> Never hardcode secrets - export them in your shell or CI environment.
|
|
2009
2028
|
|
|
2010
2029
|
## Setup
|
|
2011
2030
|
|
|
@@ -2084,12 +2103,15 @@ function buildHookLines$1(req, sharedVars) {
|
|
|
2084
2103
|
} catch {
|
|
2085
2104
|
}
|
|
2086
2105
|
}
|
|
2106
|
+
const nativeVerb = PLAYWRIGHT_VERBS.includes(method);
|
|
2107
|
+
if (!nativeVerb) optionParts.unshift(`method: '${req.method}'`);
|
|
2087
2108
|
const opts = optionParts.length ? `, { ${optionParts.join(", ")} }` : "";
|
|
2109
|
+
const hookCall = nativeVerb ? `request.${method}(${pathExpr}${opts})` : `request.fetch(${pathExpr}${opts})`;
|
|
2088
2110
|
const lines = [];
|
|
2089
2111
|
lines.push(` // ${req.name}`);
|
|
2090
2112
|
const parsed = parsePostScript(req.postRequestScript);
|
|
2091
2113
|
if (parsed.extractions.length > 0) {
|
|
2092
|
-
lines.push(` const hookResponse = await
|
|
2114
|
+
lines.push(` const hookResponse = await ${hookCall};`);
|
|
2093
2115
|
lines.push(` const hookJson = await hookResponse.json();`);
|
|
2094
2116
|
for (const e of parsed.extractions) {
|
|
2095
2117
|
const jsonPath = e.accessor.replace(/^json\.?/, "");
|
|
@@ -2099,7 +2121,7 @@ function buildHookLines$1(req, sharedVars) {
|
|
|
2099
2121
|
lines.push(` ${varName} = String(${expr});`);
|
|
2100
2122
|
}
|
|
2101
2123
|
} else {
|
|
2102
|
-
lines.push(` await
|
|
2124
|
+
lines.push(` await ${hookCall};`);
|
|
2103
2125
|
}
|
|
2104
2126
|
return lines;
|
|
2105
2127
|
}
|
|
@@ -2199,13 +2221,16 @@ ${lines.join("\n")}
|
|
|
2199
2221
|
optionParts.push(` data: \`${interpolateEnvVars(req.body.json, sharedVars)}\``);
|
|
2200
2222
|
}
|
|
2201
2223
|
}
|
|
2224
|
+
const nativeVerb = PLAYWRIGHT_VERBS.includes(method);
|
|
2225
|
+
if (!nativeVerb) optionParts.unshift(` method: '${req.method}'`);
|
|
2202
2226
|
const optionsStr = optionParts.length ? `, {
|
|
2203
2227
|
${optionParts.join(",\n")},
|
|
2204
2228
|
}` : "";
|
|
2229
|
+
const callExpr = nativeVerb ? `request.${method}(${pathExpr}${optionsStr})` : `request.fetch(${pathExpr}${optionsStr})`;
|
|
2205
2230
|
const parsed = parsePostScript(req.postRequestScript);
|
|
2206
2231
|
const lines = [
|
|
2207
2232
|
` test('${testName}', async ({ request }) => {`,
|
|
2208
|
-
` const response = await
|
|
2233
|
+
` const response = await ${callExpr};`
|
|
2209
2234
|
];
|
|
2210
2235
|
const needsJson = parsed.assertions.some((a) => a.accessor.startsWith("json")) || parsed.extractions.length > 0;
|
|
2211
2236
|
if (needsJson) {
|
|
@@ -2284,7 +2309,7 @@ function buildPackageJson$3(collectionName) {
|
|
|
2284
2309
|
}
|
|
2285
2310
|
function buildReadme$5(collectionName, filePaths) {
|
|
2286
2311
|
const tree = renderTree([...filePaths, ".env.local"]);
|
|
2287
|
-
return `# ${collectionName}
|
|
2312
|
+
return `# ${collectionName} - API Tests (Playwright TypeScript)
|
|
2288
2313
|
|
|
2289
2314
|
## Project structure
|
|
2290
2315
|
|
|
@@ -2292,7 +2317,7 @@ function buildReadme$5(collectionName, filePaths) {
|
|
|
2292
2317
|
${tree}
|
|
2293
2318
|
\`\`\`
|
|
2294
2319
|
|
|
2295
|
-
> \`.env.local\` is git-ignored
|
|
2320
|
+
> \`.env.local\` is git-ignored - fill in your secrets before running.
|
|
2296
2321
|
|
|
2297
2322
|
## Setup
|
|
2298
2323
|
|
|
@@ -2364,11 +2389,14 @@ function buildHookLines(req, sharedVars) {
|
|
|
2364
2389
|
} catch {
|
|
2365
2390
|
}
|
|
2366
2391
|
}
|
|
2392
|
+
const nativeVerb = PLAYWRIGHT_VERBS.includes(method);
|
|
2393
|
+
if (!nativeVerb) optParts.unshift(`method: '${req.method}'`);
|
|
2367
2394
|
const opts = optParts.length ? `, { ${optParts.join(", ")} }` : "";
|
|
2395
|
+
const hookCall = nativeVerb ? `request.${method}(${pathExpr}${opts})` : `request.fetch(${pathExpr}${opts})`;
|
|
2368
2396
|
const lines = [` // ${req.name}`];
|
|
2369
2397
|
const parsed = parsePostScript(req.postRequestScript);
|
|
2370
2398
|
if (parsed.extractions.length > 0) {
|
|
2371
|
-
lines.push(` const hookResponse = await
|
|
2399
|
+
lines.push(` const hookResponse = await ${hookCall};`);
|
|
2372
2400
|
lines.push(` const hookJson = await hookResponse.json();`);
|
|
2373
2401
|
for (const e of parsed.extractions) {
|
|
2374
2402
|
const jp = e.accessor.replace(/^json\.?/, "");
|
|
@@ -2378,7 +2406,7 @@ function buildHookLines(req, sharedVars) {
|
|
|
2378
2406
|
lines.push(` ${varName} = String(${expr});`);
|
|
2379
2407
|
}
|
|
2380
2408
|
} else {
|
|
2381
|
-
lines.push(` await
|
|
2409
|
+
lines.push(` await ${hookCall};`);
|
|
2382
2410
|
}
|
|
2383
2411
|
return lines;
|
|
2384
2412
|
}
|
|
@@ -2476,13 +2504,16 @@ ${lines.join("\n")}
|
|
|
2476
2504
|
optionParts.push(` data: \`${interpolateEnvVars(req.body.json, sharedVars)}\``);
|
|
2477
2505
|
}
|
|
2478
2506
|
}
|
|
2507
|
+
const nativeVerb = PLAYWRIGHT_VERBS.includes(method);
|
|
2508
|
+
if (!nativeVerb) optionParts.unshift(` method: '${req.method}'`);
|
|
2479
2509
|
const optionsStr = optionParts.length ? `, {
|
|
2480
2510
|
${optionParts.join(",\n")},
|
|
2481
2511
|
}` : "";
|
|
2512
|
+
const callExpr = nativeVerb ? `request.${method}(${pathExpr}${optionsStr})` : `request.fetch(${pathExpr}${optionsStr})`;
|
|
2482
2513
|
const parsed = parsePostScript(req.postRequestScript);
|
|
2483
2514
|
const lines = [
|
|
2484
2515
|
` test('${testName}', async ({ request }) => {`,
|
|
2485
|
-
` const response = await
|
|
2516
|
+
` const response = await ${callExpr};`
|
|
2486
2517
|
];
|
|
2487
2518
|
const needsJson = parsed.assertions.some((a) => a.accessor.startsWith("json")) || parsed.extractions.length > 0;
|
|
2488
2519
|
if (needsJson) {
|
|
@@ -2551,7 +2582,7 @@ function buildPackageJson$2(collectionName) {
|
|
|
2551
2582
|
}
|
|
2552
2583
|
function buildReadme$4(collectionName, filePaths) {
|
|
2553
2584
|
const tree = renderTree([...filePaths, ".env.local"]);
|
|
2554
|
-
return `# ${collectionName}
|
|
2585
|
+
return `# ${collectionName} - API Tests (Playwright JavaScript)
|
|
2555
2586
|
|
|
2556
2587
|
## Project structure
|
|
2557
2588
|
|
|
@@ -2559,7 +2590,7 @@ function buildReadme$4(collectionName, filePaths) {
|
|
|
2559
2590
|
${tree}
|
|
2560
2591
|
\`\`\`
|
|
2561
2592
|
|
|
2562
|
-
> \`.env.local\` is git-ignored
|
|
2593
|
+
> \`.env.local\` is git-ignored - fill in your secrets before running.
|
|
2563
2594
|
|
|
2564
2595
|
## Setup
|
|
2565
2596
|
|
|
@@ -2643,6 +2674,10 @@ function buildTestFile$1(folderName, folder, collection) {
|
|
|
2643
2674
|
function buildSupertestHookLines(h) {
|
|
2644
2675
|
const lines = [` // ${h.name}`];
|
|
2645
2676
|
const method = h.method.toLowerCase();
|
|
2677
|
+
if (!SUPERTEST_VERBS.includes(method)) {
|
|
2678
|
+
lines.push(` // ${h.method} (RFC 10008) is not supported by supertest -- hook skipped`);
|
|
2679
|
+
return lines;
|
|
2680
|
+
}
|
|
2646
2681
|
const path2 = h.url.replace(/^https?:\/\/[^/]+/, "") || "/";
|
|
2647
2682
|
const parsed = parsePostScript(h.postRequestScript);
|
|
2648
2683
|
if (parsed.extractions.length > 0) {
|
|
@@ -2687,6 +2722,13 @@ ${lines.join("\n")}
|
|
|
2687
2722
|
const allHeaders = mergeHeaders(req, inherited);
|
|
2688
2723
|
const enabledParams = req.params.filter((p) => p.enabled && p.key);
|
|
2689
2724
|
const lines = [];
|
|
2725
|
+
if (!SUPERTEST_VERBS.includes(method)) {
|
|
2726
|
+
lines.push(` // ${req.method} (RFC 10008) is not supported by supertest`);
|
|
2727
|
+
lines.push(` it.skip('${nameMap.get(reqId)} [${req.method} unsupported]', () => {});`);
|
|
2728
|
+
lines.push("");
|
|
2729
|
+
tests.push(...lines);
|
|
2730
|
+
continue;
|
|
2731
|
+
}
|
|
2690
2732
|
lines.push(` it('${nameMap.get(reqId)}', async () => {`);
|
|
2691
2733
|
lines.push(` const res = await api`);
|
|
2692
2734
|
lines.push(` .${method}(\`${path2}\`)`);
|
|
@@ -2800,7 +2842,7 @@ function buildTsConfig() {
|
|
|
2800
2842
|
}
|
|
2801
2843
|
function buildReadme$3(collectionName, filePaths) {
|
|
2802
2844
|
const tree = renderTree([...filePaths, ".env.local"]);
|
|
2803
|
-
return `# ${collectionName}
|
|
2845
|
+
return `# ${collectionName} - API Tests (Supertest + Jest TypeScript)
|
|
2804
2846
|
|
|
2805
2847
|
## Project structure
|
|
2806
2848
|
|
|
@@ -2808,7 +2850,7 @@ function buildReadme$3(collectionName, filePaths) {
|
|
|
2808
2850
|
${tree}
|
|
2809
2851
|
\`\`\`
|
|
2810
2852
|
|
|
2811
|
-
> \`.env.local\` is git-ignored
|
|
2853
|
+
> \`.env.local\` is git-ignored - fill in your secrets before running.
|
|
2812
2854
|
|
|
2813
2855
|
## Setup
|
|
2814
2856
|
|
|
@@ -2896,6 +2938,10 @@ function buildTestFile(folderName, folder, collection) {
|
|
|
2896
2938
|
function buildJsHookLines(h) {
|
|
2897
2939
|
const lines = [` // ${h.name}`];
|
|
2898
2940
|
const method = h.method.toLowerCase();
|
|
2941
|
+
if (!SUPERTEST_VERBS.includes(method)) {
|
|
2942
|
+
lines.push(` // ${h.method} (RFC 10008) is not supported by supertest -- hook skipped`);
|
|
2943
|
+
return lines;
|
|
2944
|
+
}
|
|
2899
2945
|
const path2 = h.url.replace(/^https?:\/\/[^/]+/, "") || "/";
|
|
2900
2946
|
const parsed = parsePostScript(h.postRequestScript);
|
|
2901
2947
|
if (parsed.extractions.length > 0) {
|
|
@@ -2940,6 +2986,13 @@ ${lines.join("\n")}
|
|
|
2940
2986
|
const allHeaders = mergeHeaders(req, inherited);
|
|
2941
2987
|
const enabledParams = req.params.filter((p) => p.enabled && p.key);
|
|
2942
2988
|
const lines = [];
|
|
2989
|
+
if (!SUPERTEST_VERBS.includes(method)) {
|
|
2990
|
+
lines.push(` // ${req.method} (RFC 10008) is not supported by supertest`);
|
|
2991
|
+
lines.push(` it.skip('${nameMap.get(reqId)} [${req.method} unsupported]', () => {});`);
|
|
2992
|
+
lines.push("");
|
|
2993
|
+
tests.push(...lines);
|
|
2994
|
+
continue;
|
|
2995
|
+
}
|
|
2943
2996
|
lines.push(` it('${nameMap.get(reqId)}', async () => {`);
|
|
2944
2997
|
lines.push(` const res = await api`);
|
|
2945
2998
|
lines.push(` .${method}(\`${path2}\`)`);
|
|
@@ -3035,7 +3088,7 @@ function buildPackageJson(collectionName) {
|
|
|
3035
3088
|
}
|
|
3036
3089
|
function buildReadme$2(collectionName, filePaths) {
|
|
3037
3090
|
const tree = renderTree([...filePaths, ".env.local"]);
|
|
3038
|
-
return `# ${collectionName}
|
|
3091
|
+
return `# ${collectionName} - API Tests (Supertest + Jest JavaScript)
|
|
3039
3092
|
|
|
3040
3093
|
## Project structure
|
|
3041
3094
|
|
|
@@ -3043,7 +3096,7 @@ function buildReadme$2(collectionName, filePaths) {
|
|
|
3043
3096
|
${tree}
|
|
3044
3097
|
\`\`\`
|
|
3045
3098
|
|
|
3046
|
-
> \`.env.local\` is git-ignored
|
|
3099
|
+
> \`.env.local\` is git-ignored - fill in your secrets before running.
|
|
3047
3100
|
|
|
3048
3101
|
## Setup
|
|
3049
3102
|
|
|
@@ -3234,7 +3287,7 @@ function buildTestClass(folderName, folder, collection) {
|
|
|
3234
3287
|
const escaped = h.body.json.replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
3235
3288
|
lines.push(` .body("${escaped}")`);
|
|
3236
3289
|
}
|
|
3237
|
-
lines.push(` .when()
|
|
3290
|
+
lines.push(` .when()${restAssuredCall(method, `"${path2}"`)};`);
|
|
3238
3291
|
for (const e of parsed.extractions) {
|
|
3239
3292
|
const jp = accessorToJsonPath(e.accessor);
|
|
3240
3293
|
const varName = toEnvVar(e.varName);
|
|
@@ -3242,7 +3295,7 @@ function buildTestClass(folderName, folder, collection) {
|
|
|
3242
3295
|
lines.push(` ${varName} = hookResponse.jsonPath().getString("${jp}");`);
|
|
3243
3296
|
}
|
|
3244
3297
|
} else {
|
|
3245
|
-
lines.push(` given().spec(requestSpec).when()
|
|
3298
|
+
lines.push(` given().spec(requestSpec).when()${restAssuredCall(method, `"${path2}"`)};`);
|
|
3246
3299
|
}
|
|
3247
3300
|
return lines;
|
|
3248
3301
|
}
|
|
@@ -3318,7 +3371,7 @@ ${lines.join("\n")}
|
|
|
3318
3371
|
}
|
|
3319
3372
|
}
|
|
3320
3373
|
lines.push(` .when()`);
|
|
3321
|
-
lines.push(`
|
|
3374
|
+
lines.push(` ${restAssuredCall(method, javaPath)}`);
|
|
3322
3375
|
lines.push(` .then()`);
|
|
3323
3376
|
const parsed = parsePostScript(req.postRequestScript);
|
|
3324
3377
|
if (parsed.assertions.length > 0) {
|
|
@@ -3374,7 +3427,7 @@ ${methods.join("\n\n")}
|
|
|
3374
3427
|
}
|
|
3375
3428
|
function buildReadme$1(collectionName, filePaths) {
|
|
3376
3429
|
const tree = renderTree(filePaths);
|
|
3377
|
-
return `# ${collectionName}
|
|
3430
|
+
return `# ${collectionName} - API Tests (REST Assured + JUnit 5)
|
|
3378
3431
|
|
|
3379
3432
|
## Project structure
|
|
3380
3433
|
|
|
@@ -3774,10 +3827,10 @@ ${scenarios.join("\n\n")}
|
|
|
3774
3827
|
}
|
|
3775
3828
|
function buildReadme(collectionName, filePaths) {
|
|
3776
3829
|
const tree = renderTree(filePaths);
|
|
3777
|
-
return `# ${collectionName}
|
|
3830
|
+
return `# ${collectionName} - API Tests (Karate + JUnit 5)
|
|
3778
3831
|
|
|
3779
3832
|
Karate is a BDD-flavoured API test framework that uses Gherkin feature files
|
|
3780
|
-
(no glue code)
|
|
3833
|
+
(no glue code) - see https://docs.karatelabs.io for the full reference.
|
|
3781
3834
|
|
|
3782
3835
|
## Project structure
|
|
3783
3836
|
|
|
@@ -3982,17 +4035,17 @@ const sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
|
3982
4035
|
function registerRunnerHandler(ipc) {
|
|
3983
4036
|
handle.handleIpc(ipc, handle.IPC.runner.start, async (event, payload) => {
|
|
3984
4037
|
const { items, environment, globals: payloadGlobals, proxy, tls, piiMaskPatterns = [], requestDelay = 0 } = payload;
|
|
3985
|
-
const envVars = await
|
|
3986
|
-
const liveGlobals =
|
|
4038
|
+
const envVars = await requestExec.buildEnvVars(environment);
|
|
4039
|
+
const liveGlobals = requestExec.getGlobals();
|
|
3987
4040
|
const globals = { ...payloadGlobals, ...liveGlobals };
|
|
3988
|
-
const dispatcher = await
|
|
4041
|
+
const dispatcher = await requestExec.buildDispatcher(proxy, tls);
|
|
3989
4042
|
const summary = { total: items.length, passed: 0, failed: 0, errors: 0, skipped: 0, durationMs: 0 };
|
|
3990
4043
|
const totalStart = Date.now();
|
|
3991
4044
|
let runEnvVars = { ...envVars };
|
|
3992
4045
|
let runCollectionVars = {};
|
|
3993
4046
|
let runGlobals = { ...globals };
|
|
3994
4047
|
let runLocalVars = {};
|
|
3995
|
-
const skipTracker = new
|
|
4048
|
+
const skipTracker = new requestExec.HookSkipTracker();
|
|
3996
4049
|
for (const item of items) {
|
|
3997
4050
|
const skipReason = skipTracker.shouldSkip(item);
|
|
3998
4051
|
if (skipReason) {
|
|
@@ -4022,7 +4075,7 @@ function registerRunnerHandler(ipc) {
|
|
|
4022
4075
|
scopePath: item.scopePath
|
|
4023
4076
|
};
|
|
4024
4077
|
event.sender.send(handle.IPC.runner.progress, { requestId: item.request.id, ...runningUpdate });
|
|
4025
|
-
const { result, updatedEnvVars, updatedCollectionVars, updatedGlobals, updatedLocalVars } = await
|
|
4078
|
+
const { result, updatedEnvVars, updatedCollectionVars, updatedGlobals, updatedLocalVars } = await requestExec.executeRunnerRequest({
|
|
4026
4079
|
req: item.request,
|
|
4027
4080
|
collectionVars: { ...item.collectionVars, ...runCollectionVars },
|
|
4028
4081
|
envVars: runEnvVars,
|
|
@@ -4089,14 +4142,14 @@ function registerOAuth2Handlers(ipc) {
|
|
|
4089
4142
|
handle.handleIpc(ipc, handle.IPC.oauth2.startFlow, async (_e, auth, vars) => {
|
|
4090
4143
|
const port = auth.oauth2RedirectPort ?? 9876;
|
|
4091
4144
|
const redirectUri = `http://localhost:${port}/callback`;
|
|
4092
|
-
const authUrl =
|
|
4093
|
-
const tokenUrl =
|
|
4094
|
-
const clientId =
|
|
4145
|
+
const authUrl = requestExec.interpolate(auth.oauth2AuthUrl ?? "", vars);
|
|
4146
|
+
const tokenUrl = requestExec.interpolate(auth.oauth2TokenUrl ?? "", vars);
|
|
4147
|
+
const clientId = requestExec.interpolate(auth.oauth2ClientId ?? "", vars);
|
|
4095
4148
|
let clientSecret = auth.oauth2ClientSecret ?? "";
|
|
4096
4149
|
if (!clientSecret && auth.oauth2ClientSecretRef) {
|
|
4097
|
-
clientSecret = await
|
|
4150
|
+
clientSecret = await requestExec.getSecret(auth.oauth2ClientSecretRef) ?? "";
|
|
4098
4151
|
}
|
|
4099
|
-
clientSecret =
|
|
4152
|
+
clientSecret = requestExec.interpolate(clientSecret, vars);
|
|
4100
4153
|
if (!authUrl) throw new Error("OAuth 2.0: authUrl is required for authorization_code flow.");
|
|
4101
4154
|
if (!tokenUrl) throw new Error("OAuth 2.0: tokenUrl is required for authorization_code flow.");
|
|
4102
4155
|
if (!clientId) throw new Error("OAuth 2.0: clientId is required.");
|
|
@@ -4171,13 +4224,13 @@ function registerOAuth2Handlers(ipc) {
|
|
|
4171
4224
|
};
|
|
4172
4225
|
});
|
|
4173
4226
|
handle.handleIpc(ipc, handle.IPC.oauth2.refreshToken, async (_e, auth, vars, refreshToken) => {
|
|
4174
|
-
const tokenUrl =
|
|
4175
|
-
const clientId =
|
|
4227
|
+
const tokenUrl = requestExec.interpolate(auth.oauth2TokenUrl ?? "", vars);
|
|
4228
|
+
const clientId = requestExec.interpolate(auth.oauth2ClientId ?? "", vars);
|
|
4176
4229
|
let clientSecret = auth.oauth2ClientSecret ?? "";
|
|
4177
4230
|
if (!clientSecret && auth.oauth2ClientSecretRef) {
|
|
4178
|
-
clientSecret = await
|
|
4231
|
+
clientSecret = await requestExec.getSecret(auth.oauth2ClientSecretRef) ?? "";
|
|
4179
4232
|
}
|
|
4180
|
-
clientSecret =
|
|
4233
|
+
clientSecret = requestExec.interpolate(clientSecret, vars);
|
|
4181
4234
|
if (!tokenUrl) throw new Error("OAuth 2.0: tokenUrl is required for refresh.");
|
|
4182
4235
|
const { fetch: nodeFetch } = await import("undici");
|
|
4183
4236
|
const params = new URLSearchParams();
|
|
@@ -4585,7 +4638,7 @@ function inferSchemaFromJson(json) {
|
|
|
4585
4638
|
}
|
|
4586
4639
|
async function resolveSnapshotSpec(relPath) {
|
|
4587
4640
|
const dir = getWorkspaceDir();
|
|
4588
|
-
if (!dir) throw new Error("No workspace open
|
|
4641
|
+
if (!dir) throw new Error("No workspace open - cannot resolve snapshot.");
|
|
4589
4642
|
const snap = await snapshots.loadSnapshot(dir, relPath);
|
|
4590
4643
|
const tmp = path.join(os.tmpdir(), `api-spector-${crypto.randomUUID()}.${snap.format === "yaml" ? "yaml" : "json"}`);
|
|
4591
4644
|
await promises.writeFile(tmp, snap.spec, "utf8");
|
|
@@ -4605,7 +4658,7 @@ function registerContractHandlers(ipc) {
|
|
|
4605
4658
|
case "consumer":
|
|
4606
4659
|
return snapshots.runConsumerContracts(requests, envVars, collectionVars);
|
|
4607
4660
|
case "provider":
|
|
4608
|
-
return snapshots.runProviderVerification(requests, envVars, specUrl, specPath, requestBaseUrl);
|
|
4661
|
+
return snapshots.runProviderVerification(requests, envVars, collectionVars, specUrl, specPath, requestBaseUrl);
|
|
4609
4662
|
case "provider-live":
|
|
4610
4663
|
return snapshots.runLiveProviderVerification(requests, envVars, collectionVars, providerBaseUrl, stateHandlerUrl);
|
|
4611
4664
|
case "bidirectional":
|
|
@@ -4628,7 +4681,7 @@ function registerContractHandlers(ipc) {
|
|
|
4628
4681
|
});
|
|
4629
4682
|
handle.handleIpc(ipc, handle.IPC.contract.captureSnapshot, async (_e, opts) => {
|
|
4630
4683
|
const dir = getWorkspaceDir();
|
|
4631
|
-
if (!dir) throw new Error("No workspace open
|
|
4684
|
+
if (!dir) throw new Error("No workspace open - cannot capture snapshot.");
|
|
4632
4685
|
const snapshot = await snapshots.captureSnapshot(dir, opts);
|
|
4633
4686
|
const relPath = snapshots.relPathOf(snapshot);
|
|
4634
4687
|
if (!relPath) throw new Error("Snapshot created but relPath was not attached.");
|
|
@@ -4649,6 +4702,24 @@ function registerContractHandlers(ipc) {
|
|
|
4649
4702
|
if (!dir) return;
|
|
4650
4703
|
await snapshots.deleteSnapshot(dir, relPath);
|
|
4651
4704
|
});
|
|
4705
|
+
handle.handleIpc(ipc, handle.IPC.contract.fuzz, async (_e, payload) => {
|
|
4706
|
+
let { specUrl, specPath } = payload;
|
|
4707
|
+
if (payload.specSnapshotRelPath) {
|
|
4708
|
+
const resolved = await resolveSnapshotSpec(payload.specSnapshotRelPath);
|
|
4709
|
+
specPath = resolved.specPath;
|
|
4710
|
+
specUrl = void 0;
|
|
4711
|
+
}
|
|
4712
|
+
return snapshots.runFuzz({ ...payload, specUrl, specPath });
|
|
4713
|
+
});
|
|
4714
|
+
handle.handleIpc(ipc, handle.IPC.contract.recordResult, async (_e, opts) => {
|
|
4715
|
+
const dir = getWorkspaceDir();
|
|
4716
|
+
if (!dir) throw new Error("No workspace open — cannot record result.");
|
|
4717
|
+
const pacticipant = opts.pacticipant?.trim();
|
|
4718
|
+
const version = opts.version?.trim();
|
|
4719
|
+
if (!pacticipant || !version) throw new Error("Pacticipant and version are required.");
|
|
4720
|
+
const file = await snapshots.recordResult(dir, pacticipant, version, opts.report, (/* @__PURE__ */ new Date()).toISOString());
|
|
4721
|
+
return { file };
|
|
4722
|
+
});
|
|
4652
4723
|
}
|
|
4653
4724
|
function git() {
|
|
4654
4725
|
const dir = getWorkspaceDir();
|
|
@@ -4930,10 +5001,10 @@ function createWindow() {
|
|
|
4930
5001
|
}
|
|
4931
5002
|
electron.app.whenReady().then(async () => {
|
|
4932
5003
|
if (process.platform !== "darwin") electron.Menu.setApplicationMenu(null);
|
|
4933
|
-
await
|
|
5004
|
+
await requestExec.initSecretStore(electron.app.getPath("userData"));
|
|
4934
5005
|
registerFileHandlers(electron.ipcMain);
|
|
4935
5006
|
registerRequestHandler(electron.ipcMain);
|
|
4936
|
-
|
|
5007
|
+
requestExec.registerSecretHandlers(electron.ipcMain);
|
|
4937
5008
|
registerImportHandlers(electron.ipcMain);
|
|
4938
5009
|
registerGenerateHandlers(electron.ipcMain);
|
|
4939
5010
|
registerRunnerHandler(electron.ipcMain);
|