@testsmith/api-spector 0.7.7 → 0.7.8
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 +4 -0
- package/out/main/chunks/{broker-client-Df4PpJqB.js → broker-client-BYpWeOWT.js} +4 -1
- package/out/main/chunks/{flow-engine-BVzjz4lJ.js → flow-engine-BxwjEy37.js} +66 -1
- package/out/main/chunks/{handle-Bz0C56Eq.js → handle-Crv1i9hb.js} +60 -0
- package/out/main/chunks/{import-BhGBiW7j.js → import-DTcZMRNu.js} +2 -2
- package/out/main/chunks/perf-report-ChaO3K0w.js +124 -0
- package/out/main/chunks/perf-run-uZBqnG3S.js +578 -0
- package/out/main/chunks/{request-collection-KGPuvN7o.js → request-collection-gkOYlWml.js} +68 -11
- package/out/main/chunks/{request-exec-B-F0K-pN.js → request-exec-Cv2jkrO1.js} +439 -12
- package/out/main/chunks/run-concurrent-468tH0AX.js +26 -0
- package/out/main/chunks/{soap-handler-DqIPcIQL.js → soap-handler-Brr7BPWO.js} +2 -2
- package/out/main/contract.js +6 -3
- package/out/main/flow.js +8 -5
- package/out/main/index.js +1338 -179
- package/out/main/lib.js +6 -3
- package/out/main/perf-worker.js +45 -0
- package/out/main/perf.js +253 -0
- package/out/main/runner.js +181 -7
- package/out/main/socketio.js +172 -0
- package/out/main/wsdl.js +3 -3
- package/out/preload/index.js +123 -0
- package/out/renderer/assets/{GraphQLEditor-BXpdmpn6.js → GraphQLEditor-Dv2K4wdd.js} +59 -14
- package/out/renderer/assets/{SoapEditor-De6WlP6E.js → SoapEditor-Cnz3M4Dz.js} +3 -3
- package/out/renderer/assets/ajv-CJPwFjiP.js +6718 -0
- package/out/renderer/assets/{codemirror-F6m3Hm1M.js → codemirror-CdIFBPs2.js} +1214 -1207
- package/out/renderer/assets/{index-Gcd_t6pX.js → index-CtCdFiRx.js} +15213 -12165
- package/out/renderer/assets/index-DkhdKzQJ.css +627 -0
- package/out/renderer/index.html +4 -4
- package/package.json +12 -6
- package/out/renderer/assets/ajv-2MAg6kGF.js +0 -6153
- package/out/renderer/assets/index-CYArpqm5.css +0 -627
|
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
const undici = require("undici");
|
|
25
25
|
const promises$1 = require("fs/promises");
|
|
26
26
|
const nodeTls = require("tls");
|
|
27
|
-
const handle = require("./handle-
|
|
27
|
+
const handle = require("./handle-Crv1i9hb.js");
|
|
28
28
|
const fs = require("node:fs");
|
|
29
29
|
const os = require("node:os");
|
|
30
30
|
const path = require("node:path");
|
|
@@ -35,6 +35,9 @@ const vm = require("node:vm");
|
|
|
35
35
|
const tv4 = require("tv4");
|
|
36
36
|
const jsonpathPlus = require("jsonpath-plus");
|
|
37
37
|
const xmldom = require("@xmldom/xmldom");
|
|
38
|
+
const mqtt = require("mqtt");
|
|
39
|
+
const WebSocket = require("ws");
|
|
40
|
+
const socket_ioClient = require("socket.io-client");
|
|
38
41
|
const http = require("node:http");
|
|
39
42
|
const Ajv = require("ajv");
|
|
40
43
|
function _interopNamespaceDefault(e) {
|
|
@@ -905,9 +908,9 @@ async function buildEnvVars(environment) {
|
|
|
905
908
|
}
|
|
906
909
|
return vars;
|
|
907
910
|
}
|
|
908
|
-
function mergeVars(envVars, collectionVars, globals2, localVars = {}, dynamicVars = {}) {
|
|
911
|
+
function mergeVars(envVars, collectionVars, globals2, localVars = {}, dynamicVars = {}, folderVars = {}) {
|
|
909
912
|
const out = {};
|
|
910
|
-
for (const src of [dynamicVars, globals2, collectionVars, envVars, localVars]) {
|
|
913
|
+
for (const src of [dynamicVars, globals2, collectionVars, folderVars, envVars, localVars]) {
|
|
911
914
|
for (const [k, v] of Object.entries(src ?? {})) {
|
|
912
915
|
if (v == null) continue;
|
|
913
916
|
out[k] = v;
|
|
@@ -1091,6 +1094,7 @@ class AssertionError extends Error {
|
|
|
1091
1094
|
}
|
|
1092
1095
|
function buildAt(ctx, testResults, consoleOutput) {
|
|
1093
1096
|
const { envVars, collectionVars, globals: globals2, localVars, piiMaskPatterns = [] } = ctx;
|
|
1097
|
+
const folderVars = ctx.folderVars ?? {};
|
|
1094
1098
|
const isSensitiveKey = (key) => piiMaskPatterns.some((p) => key.toLowerCase().includes(p.toLowerCase()));
|
|
1095
1099
|
function makeVarScope(store, scopeName) {
|
|
1096
1100
|
return {
|
|
@@ -1113,9 +1117,10 @@ function buildAt(ctx, testResults, consoleOutput) {
|
|
|
1113
1117
|
variables: makeVarScope(localVars, "variables"),
|
|
1114
1118
|
environment: makeVarScope(envVars, "environment"),
|
|
1115
1119
|
collectionVariables: makeVarScope(collectionVars, "collectionVariables"),
|
|
1120
|
+
folder: makeVarScope(folderVars, "folder"),
|
|
1116
1121
|
globals: makeVarScope(globals2, "globals"),
|
|
1117
1122
|
// Convenience: get/set across all scopes (local wins)
|
|
1118
|
-
variables_get: (key) => localVars[key] ?? envVars[key] ?? collectionVars[key] ?? globals2[key] ?? null,
|
|
1123
|
+
variables_get: (key) => localVars[key] ?? envVars[key] ?? folderVars[key] ?? collectionVars[key] ?? globals2[key] ?? null,
|
|
1119
1124
|
variables_set: (key, value) => {
|
|
1120
1125
|
localVars[key] = String(value);
|
|
1121
1126
|
},
|
|
@@ -1174,6 +1179,14 @@ function buildAt(ctx, testResults, consoleOutput) {
|
|
|
1174
1179
|
return String(binCode % 10 ** digits).padStart(digits, "0");
|
|
1175
1180
|
}
|
|
1176
1181
|
};
|
|
1182
|
+
const collected = ctx.messages ?? ctx.mqttMessages;
|
|
1183
|
+
if (collected) {
|
|
1184
|
+
sp.messages = collected.map((m) => ({
|
|
1185
|
+
...m,
|
|
1186
|
+
json: () => JSON.parse(m.payload),
|
|
1187
|
+
text: () => m.payload
|
|
1188
|
+
}));
|
|
1189
|
+
}
|
|
1177
1190
|
if (ctx.response) {
|
|
1178
1191
|
const resp = ctx.response;
|
|
1179
1192
|
let parsedJson = void 0;
|
|
@@ -1249,12 +1262,17 @@ async function runScript(code, ctx, timeoutMs = 5e3) {
|
|
|
1249
1262
|
const collectionCopy = { ...ctx.collectionVars };
|
|
1250
1263
|
const globalsCopy = { ...ctx.globals };
|
|
1251
1264
|
const localVarsCopy = { ...ctx.localVars };
|
|
1265
|
+
const folderCopy = { ...ctx.folderVars ?? {} };
|
|
1252
1266
|
const scriptCtx = {
|
|
1253
1267
|
envVars: envVarsCopy,
|
|
1254
1268
|
collectionVars: collectionCopy,
|
|
1255
1269
|
globals: globalsCopy,
|
|
1256
1270
|
localVars: localVarsCopy,
|
|
1271
|
+
folderVars: folderCopy,
|
|
1257
1272
|
response: ctx.response,
|
|
1273
|
+
data: ctx.data,
|
|
1274
|
+
mqttMessages: ctx.mqttMessages,
|
|
1275
|
+
messages: ctx.messages,
|
|
1258
1276
|
piiMaskPatterns: ctx.piiMaskPatterns
|
|
1259
1277
|
};
|
|
1260
1278
|
const sandbox = buildSandbox(scriptCtx, testResults, consoleOutput, faker);
|
|
@@ -1269,6 +1287,7 @@ async function runScript(code, ctx, timeoutMs = 5e3) {
|
|
|
1269
1287
|
updatedCollectionVars: collectionCopy,
|
|
1270
1288
|
updatedGlobals: globalsCopy,
|
|
1271
1289
|
updatedLocalVars: localVarsCopy,
|
|
1290
|
+
updatedFolderVars: folderCopy,
|
|
1272
1291
|
error: message
|
|
1273
1292
|
};
|
|
1274
1293
|
}
|
|
@@ -1278,7 +1297,8 @@ async function runScript(code, ctx, timeoutMs = 5e3) {
|
|
|
1278
1297
|
updatedEnvVars: envVarsCopy,
|
|
1279
1298
|
updatedCollectionVars: collectionCopy,
|
|
1280
1299
|
updatedGlobals: globalsCopy,
|
|
1281
|
-
updatedLocalVars: localVarsCopy
|
|
1300
|
+
updatedLocalVars: localVarsCopy,
|
|
1301
|
+
updatedFolderVars: folderCopy
|
|
1282
1302
|
};
|
|
1283
1303
|
}
|
|
1284
1304
|
async function evaluateExpression(code, ctx, timeoutMs = 5e3) {
|
|
@@ -1357,6 +1377,7 @@ class StreamFramer {
|
|
|
1357
1377
|
constructor(mode) {
|
|
1358
1378
|
this.mode = mode;
|
|
1359
1379
|
}
|
|
1380
|
+
mode;
|
|
1360
1381
|
carry = "";
|
|
1361
1382
|
/** Feed a decoded text chunk; returns the frames that completed. */
|
|
1362
1383
|
push(text) {
|
|
@@ -1519,6 +1540,314 @@ async function readStream(body, kind, startMs, opts = {}) {
|
|
|
1519
1540
|
}
|
|
1520
1541
|
return { events, text, close, firstEventMs: Math.max(firstEventMs, 0) };
|
|
1521
1542
|
}
|
|
1543
|
+
async function runMqttTest(cfg) {
|
|
1544
|
+
const messages = [];
|
|
1545
|
+
const collectMs = Math.max(0, Math.min(6e4, Math.floor(cfg.collectMs ?? 2e3)));
|
|
1546
|
+
const clientId = cfg.clientId?.trim() || `api-spector-test-${Date.now().toString(36)}-${Math.random().toString(16).slice(2, 10)}`;
|
|
1547
|
+
return new Promise((resolve2) => {
|
|
1548
|
+
let settled = false;
|
|
1549
|
+
let connected = false;
|
|
1550
|
+
let lastError = "";
|
|
1551
|
+
let client;
|
|
1552
|
+
let timer;
|
|
1553
|
+
const runAssertions = async (connectError) => {
|
|
1554
|
+
if (settled) return;
|
|
1555
|
+
settled = true;
|
|
1556
|
+
if (timer) clearTimeout(timer);
|
|
1557
|
+
try {
|
|
1558
|
+
client?.end(true);
|
|
1559
|
+
} catch {
|
|
1560
|
+
}
|
|
1561
|
+
if (connectError && messages.length === 0) {
|
|
1562
|
+
resolve2({ testResults: [], consoleOutput: [], error: connectError, messages, clientId });
|
|
1563
|
+
return;
|
|
1564
|
+
}
|
|
1565
|
+
const out = await runScript(cfg.script ?? "", {
|
|
1566
|
+
envVars: cfg.envVars ?? {},
|
|
1567
|
+
collectionVars: cfg.collectionVars ?? {},
|
|
1568
|
+
globals: cfg.globals ?? {},
|
|
1569
|
+
localVars: cfg.localVars ?? {},
|
|
1570
|
+
mqttMessages: messages
|
|
1571
|
+
});
|
|
1572
|
+
resolve2({ testResults: out.testResults, consoleOutput: out.consoleOutput, error: out.error, messages, clientId });
|
|
1573
|
+
};
|
|
1574
|
+
try {
|
|
1575
|
+
client = mqtt.connect(cfg.url, {
|
|
1576
|
+
clientId,
|
|
1577
|
+
username: cfg.username?.trim() || void 0,
|
|
1578
|
+
password: cfg.password ? cfg.password : void 0,
|
|
1579
|
+
clean: cfg.cleanSession !== false,
|
|
1580
|
+
reconnectPeriod: 0,
|
|
1581
|
+
connectTimeout: 15e3
|
|
1582
|
+
});
|
|
1583
|
+
} catch (err) {
|
|
1584
|
+
resolve2({ testResults: [], consoleOutput: [], error: err instanceof Error ? err.message : String(err), messages, clientId });
|
|
1585
|
+
return;
|
|
1586
|
+
}
|
|
1587
|
+
client.on("connect", () => {
|
|
1588
|
+
connected = true;
|
|
1589
|
+
const publish = () => {
|
|
1590
|
+
const topic = cfg.publishTopic?.trim();
|
|
1591
|
+
if (!topic) return;
|
|
1592
|
+
if (/[+#]/.test(topic)) return;
|
|
1593
|
+
client.publish(topic, cfg.publishPayload ?? "", { qos: cfg.qos ?? 0, retain: Boolean(cfg.retain) });
|
|
1594
|
+
};
|
|
1595
|
+
const sub = cfg.subscribeTopic?.trim();
|
|
1596
|
+
if (sub) {
|
|
1597
|
+
client.subscribe(sub, { qos: cfg.qos ?? 0 }, () => setTimeout(publish, 100));
|
|
1598
|
+
} else {
|
|
1599
|
+
publish();
|
|
1600
|
+
}
|
|
1601
|
+
timer = setTimeout(() => void runAssertions(), collectMs);
|
|
1602
|
+
});
|
|
1603
|
+
client.on("message", (topic, payload, packet) => {
|
|
1604
|
+
messages.push({ topic, payload: payload.toString("utf8"), qos: packet?.qos, retain: Boolean(packet?.retain), timestamp: Date.now() });
|
|
1605
|
+
});
|
|
1606
|
+
client.on("error", (err) => {
|
|
1607
|
+
const code = err?.code;
|
|
1608
|
+
lastError = [err?.message, code].filter(Boolean).join(" ");
|
|
1609
|
+
void runAssertions(err?.message || "Could not connect to the broker");
|
|
1610
|
+
});
|
|
1611
|
+
client.on("close", () => {
|
|
1612
|
+
if (settled) return;
|
|
1613
|
+
void runAssertions(connected ? `The test connection was dropped by the broker before the window finished${lastError ? ` [${lastError}]` : ""}. Common causes: publishing to a topic that contains a wildcard (+ or #), or another connection using the same client id. Check the broker's log.` : `Could not connect to the broker${lastError ? ` [${lastError}]` : ""}`);
|
|
1614
|
+
});
|
|
1615
|
+
});
|
|
1616
|
+
}
|
|
1617
|
+
function clampWindow(ms) {
|
|
1618
|
+
return Math.max(0, Math.min(6e4, Math.floor(ms ?? 3e3)));
|
|
1619
|
+
}
|
|
1620
|
+
async function assertOver(cfg, messages) {
|
|
1621
|
+
const out = await runScript(cfg.script ?? "", {
|
|
1622
|
+
envVars: cfg.envVars ?? {},
|
|
1623
|
+
collectionVars: cfg.collectionVars ?? {},
|
|
1624
|
+
globals: cfg.globals ?? {},
|
|
1625
|
+
localVars: cfg.localVars ?? {},
|
|
1626
|
+
messages
|
|
1627
|
+
});
|
|
1628
|
+
return { testResults: out.testResults, consoleOutput: out.consoleOutput, error: out.error, messages };
|
|
1629
|
+
}
|
|
1630
|
+
function errorResult(error, messages) {
|
|
1631
|
+
return { testResults: [], consoleOutput: [], error, messages };
|
|
1632
|
+
}
|
|
1633
|
+
function parseJsonObject(text, label) {
|
|
1634
|
+
if (!text || !text.trim()) return void 0;
|
|
1635
|
+
let parsed;
|
|
1636
|
+
try {
|
|
1637
|
+
parsed = JSON.parse(text);
|
|
1638
|
+
} catch {
|
|
1639
|
+
throw new Error(`${label} is not valid JSON`);
|
|
1640
|
+
}
|
|
1641
|
+
return parsed && typeof parsed === "object" ? parsed : void 0;
|
|
1642
|
+
}
|
|
1643
|
+
async function runWsTest(cfg) {
|
|
1644
|
+
const messages = [];
|
|
1645
|
+
const collectMs = clampWindow(cfg.collectMs);
|
|
1646
|
+
return new Promise((resolve2) => {
|
|
1647
|
+
let settled = false;
|
|
1648
|
+
let connected = false;
|
|
1649
|
+
let lastError = "";
|
|
1650
|
+
let ws;
|
|
1651
|
+
let timer;
|
|
1652
|
+
const finish = async (connectError) => {
|
|
1653
|
+
if (settled) return;
|
|
1654
|
+
settled = true;
|
|
1655
|
+
if (timer) clearTimeout(timer);
|
|
1656
|
+
try {
|
|
1657
|
+
ws?.close();
|
|
1658
|
+
} catch {
|
|
1659
|
+
}
|
|
1660
|
+
if (connectError && messages.length === 0) {
|
|
1661
|
+
resolve2(errorResult(connectError, messages));
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
resolve2(await assertOver(cfg, messages));
|
|
1665
|
+
};
|
|
1666
|
+
try {
|
|
1667
|
+
ws = new WebSocket(cfg.url, { headers: cfg.headers });
|
|
1668
|
+
} catch (err) {
|
|
1669
|
+
resolve2(errorResult(err instanceof Error ? err.message : String(err), messages));
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1672
|
+
ws.on("open", () => {
|
|
1673
|
+
connected = true;
|
|
1674
|
+
if (cfg.sendMessage) {
|
|
1675
|
+
try {
|
|
1676
|
+
ws.send(cfg.sendMessage);
|
|
1677
|
+
} catch {
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
timer = setTimeout(() => void finish(), collectMs);
|
|
1681
|
+
});
|
|
1682
|
+
ws.on("message", (data) => {
|
|
1683
|
+
const text = data.toString();
|
|
1684
|
+
messages.push({ payload: text, data: text, timestamp: Date.now() });
|
|
1685
|
+
});
|
|
1686
|
+
ws.on("error", (err) => {
|
|
1687
|
+
lastError = err.message;
|
|
1688
|
+
void finish(err.message || "Could not connect to the server");
|
|
1689
|
+
});
|
|
1690
|
+
ws.on("close", () => {
|
|
1691
|
+
if (settled) return;
|
|
1692
|
+
void finish(connected ? `The WebSocket was closed before the window finished${lastError ? ` [${lastError}]` : ""}.` : `Could not connect to the server${lastError ? ` [${lastError}]` : ""}`);
|
|
1693
|
+
});
|
|
1694
|
+
});
|
|
1695
|
+
}
|
|
1696
|
+
function targetUrl(url, namespace) {
|
|
1697
|
+
const ns = (namespace ?? "").trim();
|
|
1698
|
+
if (!ns || ns === "/") return url;
|
|
1699
|
+
return url.replace(/\/+$/, "") + (ns.startsWith("/") ? ns : `/${ns}`);
|
|
1700
|
+
}
|
|
1701
|
+
async function runSocketIoTest(cfg) {
|
|
1702
|
+
const messages = [];
|
|
1703
|
+
const collectMs = clampWindow(cfg.collectMs);
|
|
1704
|
+
let auth;
|
|
1705
|
+
try {
|
|
1706
|
+
auth = parseJsonObject(cfg.auth, "Auth");
|
|
1707
|
+
} catch (e) {
|
|
1708
|
+
return errorResult(e instanceof Error ? e.message : String(e), messages);
|
|
1709
|
+
}
|
|
1710
|
+
let emitArgs = [];
|
|
1711
|
+
if (cfg.emitArgs && cfg.emitArgs.trim()) {
|
|
1712
|
+
try {
|
|
1713
|
+
const parsed = JSON.parse(cfg.emitArgs);
|
|
1714
|
+
emitArgs = Array.isArray(parsed) ? parsed : [parsed];
|
|
1715
|
+
} catch {
|
|
1716
|
+
return errorResult("Emit arguments are not valid JSON", messages);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
return new Promise((resolve2) => {
|
|
1720
|
+
let settled = false;
|
|
1721
|
+
let socket;
|
|
1722
|
+
let timer;
|
|
1723
|
+
const finish = async (connectError) => {
|
|
1724
|
+
if (settled) return;
|
|
1725
|
+
settled = true;
|
|
1726
|
+
if (timer) clearTimeout(timer);
|
|
1727
|
+
try {
|
|
1728
|
+
socket?.disconnect();
|
|
1729
|
+
} catch {
|
|
1730
|
+
}
|
|
1731
|
+
if (connectError && messages.length === 0) {
|
|
1732
|
+
resolve2(errorResult(connectError, messages));
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
resolve2(await assertOver(cfg, messages));
|
|
1736
|
+
};
|
|
1737
|
+
try {
|
|
1738
|
+
socket = socket_ioClient.io(targetUrl(cfg.url, cfg.namespace), {
|
|
1739
|
+
path: cfg.path?.trim() || "/socket.io",
|
|
1740
|
+
transports: ["websocket", "polling"],
|
|
1741
|
+
extraHeaders: cfg.headers,
|
|
1742
|
+
auth,
|
|
1743
|
+
forceNew: true,
|
|
1744
|
+
reconnection: false
|
|
1745
|
+
});
|
|
1746
|
+
} catch (err) {
|
|
1747
|
+
resolve2(errorResult(err instanceof Error ? err.message : String(err), messages));
|
|
1748
|
+
return;
|
|
1749
|
+
}
|
|
1750
|
+
socket.on("connect", () => {
|
|
1751
|
+
const ev = cfg.emitEvent?.trim();
|
|
1752
|
+
if (ev) setTimeout(() => {
|
|
1753
|
+
try {
|
|
1754
|
+
socket.emit(ev, ...emitArgs);
|
|
1755
|
+
} catch {
|
|
1756
|
+
}
|
|
1757
|
+
}, 100);
|
|
1758
|
+
timer = setTimeout(() => void finish(), collectMs);
|
|
1759
|
+
});
|
|
1760
|
+
socket.on("connect_error", (err) => {
|
|
1761
|
+
void finish(`Could not connect to the server${err.message ? ` [${err.message}]` : ""}`);
|
|
1762
|
+
});
|
|
1763
|
+
socket.onAny((event, ...args) => {
|
|
1764
|
+
const payload = JSON.stringify(args.length <= 1 ? args[0] ?? null : args);
|
|
1765
|
+
messages.push({ payload, event, args, timestamp: Date.now() });
|
|
1766
|
+
});
|
|
1767
|
+
});
|
|
1768
|
+
}
|
|
1769
|
+
const GQL_SUB_ID = "1";
|
|
1770
|
+
async function runGraphqlWsTest(cfg) {
|
|
1771
|
+
const messages = [];
|
|
1772
|
+
const collectMs = clampWindow(cfg.collectMs);
|
|
1773
|
+
let variables;
|
|
1774
|
+
let connectionParams;
|
|
1775
|
+
try {
|
|
1776
|
+
variables = parseJsonObject(cfg.variables, "Variables");
|
|
1777
|
+
connectionParams = parseJsonObject(cfg.connectionParams, "Connection params");
|
|
1778
|
+
} catch (e) {
|
|
1779
|
+
return errorResult(e instanceof Error ? e.message : String(e), messages);
|
|
1780
|
+
}
|
|
1781
|
+
return new Promise((resolve2) => {
|
|
1782
|
+
let settled = false;
|
|
1783
|
+
let connected = false;
|
|
1784
|
+
let lastError = "";
|
|
1785
|
+
let ws;
|
|
1786
|
+
let timer;
|
|
1787
|
+
const finish = async (connectError) => {
|
|
1788
|
+
if (settled) return;
|
|
1789
|
+
settled = true;
|
|
1790
|
+
if (timer) clearTimeout(timer);
|
|
1791
|
+
try {
|
|
1792
|
+
ws?.close();
|
|
1793
|
+
} catch {
|
|
1794
|
+
}
|
|
1795
|
+
if (connectError && messages.length === 0) {
|
|
1796
|
+
resolve2(errorResult(connectError, messages));
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
resolve2(await assertOver(cfg, messages));
|
|
1800
|
+
};
|
|
1801
|
+
try {
|
|
1802
|
+
ws = new WebSocket(cfg.url, "graphql-transport-ws", { headers: cfg.headers });
|
|
1803
|
+
} catch (err) {
|
|
1804
|
+
resolve2(errorResult(err instanceof Error ? err.message : String(err), messages));
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
ws.on("open", () => {
|
|
1808
|
+
connected = true;
|
|
1809
|
+
ws.send(JSON.stringify({ type: "connection_init", payload: connectionParams }));
|
|
1810
|
+
timer = setTimeout(() => void finish(), collectMs);
|
|
1811
|
+
});
|
|
1812
|
+
ws.on("message", (raw) => {
|
|
1813
|
+
let msg;
|
|
1814
|
+
try {
|
|
1815
|
+
msg = JSON.parse(raw.toString());
|
|
1816
|
+
} catch {
|
|
1817
|
+
return;
|
|
1818
|
+
}
|
|
1819
|
+
switch (msg.type) {
|
|
1820
|
+
case "connection_ack":
|
|
1821
|
+
ws.send(JSON.stringify({ id: GQL_SUB_ID, type: "subscribe", payload: { query: cfg.query, variables, operationName: cfg.operationName } }));
|
|
1822
|
+
break;
|
|
1823
|
+
case "next": {
|
|
1824
|
+
const payload = msg.payload;
|
|
1825
|
+
const data = payload?.data;
|
|
1826
|
+
messages.push({ payload: JSON.stringify(data ?? payload ?? null), data, errors: payload?.errors, timestamp: Date.now() });
|
|
1827
|
+
break;
|
|
1828
|
+
}
|
|
1829
|
+
case "error":
|
|
1830
|
+
lastError = JSON.stringify(msg.payload);
|
|
1831
|
+
void finish(`The subscription returned an error${lastError ? ` [${lastError}]` : ""}`);
|
|
1832
|
+
break;
|
|
1833
|
+
case "complete":
|
|
1834
|
+
void finish();
|
|
1835
|
+
break;
|
|
1836
|
+
case "ping":
|
|
1837
|
+
ws.send(JSON.stringify({ type: "pong" }));
|
|
1838
|
+
break;
|
|
1839
|
+
}
|
|
1840
|
+
});
|
|
1841
|
+
ws.on("error", (err) => {
|
|
1842
|
+
lastError = err.message;
|
|
1843
|
+
void finish(err.message || "Could not connect to the server");
|
|
1844
|
+
});
|
|
1845
|
+
ws.on("close", () => {
|
|
1846
|
+
if (settled) return;
|
|
1847
|
+
void finish(connected ? `The subscription connection was closed before the window finished${lastError ? ` [${lastError}]` : ""}.` : `Could not connect to the server${lastError ? ` [${lastError}]` : ""}`);
|
|
1848
|
+
});
|
|
1849
|
+
});
|
|
1850
|
+
}
|
|
1522
1851
|
const SIGNATURE = Buffer.from("NTLMSSP\0", "latin1");
|
|
1523
1852
|
const F_UNICODE = 1;
|
|
1524
1853
|
const F_OEM = 2;
|
|
@@ -1695,7 +2024,7 @@ async function buildAuthHeaders(auth, vars) {
|
|
|
1695
2024
|
const username = interpolate(auth.username ?? "", vars);
|
|
1696
2025
|
headers["Authorization"] = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
|
|
1697
2026
|
}
|
|
1698
|
-
if (auth.type === "apikey" && auth.apiKeyIn === "header") {
|
|
2027
|
+
if (auth.type === "apikey" && (auth.apiKeyIn ?? "header") === "header") {
|
|
1699
2028
|
let value = auth.apiKeyValue ?? "";
|
|
1700
2029
|
if (!value && auth.apiKeySecretRef) value = await getSecret(auth.apiKeySecretRef) ?? "";
|
|
1701
2030
|
value = interpolate(value, vars);
|
|
@@ -2286,6 +2615,7 @@ async function resolveInlineSecrets(req) {
|
|
|
2286
2615
|
async function executeRunnerRequest(opts) {
|
|
2287
2616
|
const { req, collectionVars, envVars, globals: globals2, dispatcher, piiMaskPatterns, proxy, tls, onScriptOutput } = opts;
|
|
2288
2617
|
let { localVars } = opts;
|
|
2618
|
+
const folderVars = opts.folderVars ?? {};
|
|
2289
2619
|
applyRequestDefaults(req);
|
|
2290
2620
|
const base = {
|
|
2291
2621
|
requestId: req.id,
|
|
@@ -2296,13 +2626,99 @@ async function executeRunnerRequest(opts) {
|
|
|
2296
2626
|
};
|
|
2297
2627
|
const dynamicVars = await buildDynamicVars();
|
|
2298
2628
|
if (localVars && Object.keys(localVars).length > 0) {
|
|
2299
|
-
localVars = resolveDataRow(localVars, mergeVars(envVars, collectionVars, globals2, {}, dynamicVars));
|
|
2629
|
+
localVars = resolveDataRow(localVars, mergeVars(envVars, collectionVars, globals2, {}, dynamicVars, folderVars));
|
|
2300
2630
|
}
|
|
2301
|
-
let vars = mergeVars(envVars, collectionVars, globals2, localVars, dynamicVars);
|
|
2631
|
+
let vars = mergeVars(envVars, collectionVars, globals2, localVars, dynamicVars, folderVars);
|
|
2302
2632
|
let updatedEnvVars = { ...envVars };
|
|
2303
2633
|
let updatedCollectionVars = { ...collectionVars };
|
|
2304
2634
|
let updatedGlobals = { ...globals2 };
|
|
2635
|
+
let updatedFolderVars = { ...folderVars };
|
|
2305
2636
|
let preScriptError;
|
|
2637
|
+
if (req.protocol === "mqtt") {
|
|
2638
|
+
const m = req.mqtt ?? {};
|
|
2639
|
+
const istr = (s) => s ? interpolate(s, vars) : void 0;
|
|
2640
|
+
const mqttStart = Date.now();
|
|
2641
|
+
const testRes = await runMqttTest({
|
|
2642
|
+
url: interpolate(req.url, vars),
|
|
2643
|
+
clientId: istr(m.clientId),
|
|
2644
|
+
username: istr(m.username),
|
|
2645
|
+
password: m.password ? interpolate(m.password, vars) : void 0,
|
|
2646
|
+
cleanSession: m.cleanSession !== false,
|
|
2647
|
+
subscribeTopic: istr(m.subscribeTopic),
|
|
2648
|
+
qos: m.qos,
|
|
2649
|
+
publishTopic: istr(m.publishTopic),
|
|
2650
|
+
publishPayload: istr(m.publishPayload),
|
|
2651
|
+
retain: m.retain,
|
|
2652
|
+
collectMs: m.collectMs ?? 2e3,
|
|
2653
|
+
script: m.testScript ?? "",
|
|
2654
|
+
envVars,
|
|
2655
|
+
collectionVars,
|
|
2656
|
+
globals: globals2,
|
|
2657
|
+
localVars
|
|
2658
|
+
});
|
|
2659
|
+
const status = testRes.error ? "error" : testRes.testResults.length === 0 ? "skipped" : testRes.testResults.every((t) => t.passed) ? "passed" : "failed";
|
|
2660
|
+
return {
|
|
2661
|
+
result: {
|
|
2662
|
+
...base,
|
|
2663
|
+
status,
|
|
2664
|
+
durationMs: Date.now() - mqttStart,
|
|
2665
|
+
testResults: testRes.testResults,
|
|
2666
|
+
consoleOutput: testRes.consoleOutput,
|
|
2667
|
+
error: testRes.error
|
|
2668
|
+
},
|
|
2669
|
+
updatedEnvVars,
|
|
2670
|
+
updatedCollectionVars,
|
|
2671
|
+
updatedGlobals,
|
|
2672
|
+
updatedLocalVars: localVars,
|
|
2673
|
+
updatedFolderVars
|
|
2674
|
+
};
|
|
2675
|
+
}
|
|
2676
|
+
if (req.protocol === "websocket" || req.protocol === "socketio") {
|
|
2677
|
+
const rtHeaders = {};
|
|
2678
|
+
for (const h of req.headers ?? []) if (h.enabled && h.key) rtHeaders[interpolate(h.key, vars)] = interpolate(h.value, vars);
|
|
2679
|
+
const istr = (s) => s ? interpolate(s, vars) : void 0;
|
|
2680
|
+
const rtStart = Date.now();
|
|
2681
|
+
const testRes = req.protocol === "websocket" ? await runWsTest({
|
|
2682
|
+
url: interpolate(req.url, vars),
|
|
2683
|
+
headers: rtHeaders,
|
|
2684
|
+
sendMessage: istr(req.websocket?.sendMessage),
|
|
2685
|
+
collectMs: req.websocket?.collectMs ?? 3e3,
|
|
2686
|
+
script: req.websocket?.testScript ?? "",
|
|
2687
|
+
envVars,
|
|
2688
|
+
collectionVars,
|
|
2689
|
+
globals: globals2,
|
|
2690
|
+
localVars
|
|
2691
|
+
}) : await runSocketIoTest({
|
|
2692
|
+
url: interpolate(req.url, vars),
|
|
2693
|
+
namespace: req.socketio?.namespace,
|
|
2694
|
+
auth: istr(req.socketio?.auth),
|
|
2695
|
+
headers: rtHeaders,
|
|
2696
|
+
emitEvent: istr(req.socketio?.emitEvent),
|
|
2697
|
+
emitArgs: istr(req.socketio?.emitArgs),
|
|
2698
|
+
collectMs: req.socketio?.collectMs ?? 3e3,
|
|
2699
|
+
script: req.socketio?.testScript ?? "",
|
|
2700
|
+
envVars,
|
|
2701
|
+
collectionVars,
|
|
2702
|
+
globals: globals2,
|
|
2703
|
+
localVars
|
|
2704
|
+
});
|
|
2705
|
+
const status = testRes.error ? "error" : testRes.testResults.length === 0 ? "skipped" : testRes.testResults.every((t) => t.passed) ? "passed" : "failed";
|
|
2706
|
+
return {
|
|
2707
|
+
result: {
|
|
2708
|
+
...base,
|
|
2709
|
+
status,
|
|
2710
|
+
durationMs: Date.now() - rtStart,
|
|
2711
|
+
testResults: testRes.testResults,
|
|
2712
|
+
consoleOutput: testRes.consoleOutput,
|
|
2713
|
+
error: testRes.error
|
|
2714
|
+
},
|
|
2715
|
+
updatedEnvVars,
|
|
2716
|
+
updatedCollectionVars,
|
|
2717
|
+
updatedGlobals,
|
|
2718
|
+
updatedLocalVars: localVars,
|
|
2719
|
+
updatedFolderVars
|
|
2720
|
+
};
|
|
2721
|
+
}
|
|
2306
2722
|
let inlineSecrets = {};
|
|
2307
2723
|
try {
|
|
2308
2724
|
inlineSecrets = await resolveInlineSecrets(req);
|
|
@@ -2317,7 +2733,8 @@ async function executeRunnerRequest(opts) {
|
|
|
2317
2733
|
updatedEnvVars,
|
|
2318
2734
|
updatedCollectionVars,
|
|
2319
2735
|
updatedGlobals,
|
|
2320
|
-
updatedLocalVars: localVars
|
|
2736
|
+
updatedLocalVars: localVars,
|
|
2737
|
+
updatedFolderVars
|
|
2321
2738
|
};
|
|
2322
2739
|
}
|
|
2323
2740
|
if (Object.keys(inlineSecrets).length) vars = { ...vars, ...inlineSecrets };
|
|
@@ -2327,6 +2744,7 @@ async function executeRunnerRequest(opts) {
|
|
|
2327
2744
|
collectionVars: { ...collectionVars },
|
|
2328
2745
|
globals: { ...globals2 },
|
|
2329
2746
|
localVars: { ...localVars },
|
|
2747
|
+
folderVars: { ...updatedFolderVars },
|
|
2330
2748
|
piiMaskPatterns
|
|
2331
2749
|
});
|
|
2332
2750
|
preScriptError = r.error;
|
|
@@ -2334,10 +2752,11 @@ async function executeRunnerRequest(opts) {
|
|
|
2334
2752
|
updatedEnvVars = r.updatedEnvVars;
|
|
2335
2753
|
updatedCollectionVars = r.updatedCollectionVars;
|
|
2336
2754
|
updatedGlobals = r.updatedGlobals;
|
|
2755
|
+
updatedFolderVars = r.updatedFolderVars;
|
|
2337
2756
|
patchGlobals(r.updatedGlobals);
|
|
2338
2757
|
await persistGlobals();
|
|
2339
2758
|
onScriptOutput?.("pre", r.consoleOutput, r.error);
|
|
2340
|
-
vars = mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars);
|
|
2759
|
+
vars = mergeVars(updatedEnvVars, updatedCollectionVars, updatedGlobals, localVars, dynamicVars, updatedFolderVars);
|
|
2341
2760
|
if (Object.keys(inlineSecrets).length) vars = { ...vars, ...inlineSecrets };
|
|
2342
2761
|
}
|
|
2343
2762
|
const resolvedUrl = buildUrl(req.url, req.params, vars);
|
|
@@ -2366,6 +2785,7 @@ async function executeRunnerRequest(opts) {
|
|
|
2366
2785
|
collectionVars: updatedCollectionVars,
|
|
2367
2786
|
globals: updatedGlobals,
|
|
2368
2787
|
localVars,
|
|
2788
|
+
folderVars: updatedFolderVars,
|
|
2369
2789
|
response: scriptResponse,
|
|
2370
2790
|
piiMaskPatterns
|
|
2371
2791
|
});
|
|
@@ -2375,6 +2795,7 @@ async function executeRunnerRequest(opts) {
|
|
|
2375
2795
|
updatedEnvVars = r.updatedEnvVars;
|
|
2376
2796
|
updatedCollectionVars = r.updatedCollectionVars;
|
|
2377
2797
|
updatedGlobals = r.updatedGlobals;
|
|
2798
|
+
updatedFolderVars = r.updatedFolderVars;
|
|
2378
2799
|
localVars = r.updatedLocalVars;
|
|
2379
2800
|
patchGlobals(r.updatedGlobals);
|
|
2380
2801
|
await persistGlobals();
|
|
@@ -2414,7 +2835,8 @@ async function executeRunnerRequest(opts) {
|
|
|
2414
2835
|
updatedEnvVars,
|
|
2415
2836
|
updatedCollectionVars,
|
|
2416
2837
|
updatedGlobals,
|
|
2417
|
-
updatedLocalVars: localVars
|
|
2838
|
+
updatedLocalVars: localVars,
|
|
2839
|
+
updatedFolderVars
|
|
2418
2840
|
};
|
|
2419
2841
|
} catch (err) {
|
|
2420
2842
|
return {
|
|
@@ -2428,7 +2850,8 @@ async function executeRunnerRequest(opts) {
|
|
|
2428
2850
|
updatedEnvVars,
|
|
2429
2851
|
updatedCollectionVars,
|
|
2430
2852
|
updatedGlobals,
|
|
2431
|
-
updatedLocalVars: localVars
|
|
2853
|
+
updatedLocalVars: localVars,
|
|
2854
|
+
updatedFolderVars
|
|
2432
2855
|
};
|
|
2433
2856
|
}
|
|
2434
2857
|
}
|
|
@@ -2510,7 +2933,11 @@ exports.registerSecretProvider = registerSecretProvider;
|
|
|
2510
2933
|
exports.registeredSchemes = registeredSchemes;
|
|
2511
2934
|
exports.resolveDataRow = resolveDataRow;
|
|
2512
2935
|
exports.resolveExternalSecret = resolveExternalSecret;
|
|
2936
|
+
exports.runGraphqlWsTest = runGraphqlWsTest;
|
|
2937
|
+
exports.runMqttTest = runMqttTest;
|
|
2513
2938
|
exports.runScript = runScript;
|
|
2939
|
+
exports.runSocketIoTest = runSocketIoTest;
|
|
2940
|
+
exports.runWsTest = runWsTest;
|
|
2514
2941
|
exports.setGlobals = setGlobals;
|
|
2515
2942
|
exports.setSecretsConfig = setSecretsConfig;
|
|
2516
2943
|
exports.syntheticHttpFailure = syntheticHttpFailure;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
async function runPlanConcurrent(concurrency, items, isBarrier, run) {
|
|
3
|
+
const n = Math.max(1, Math.floor(concurrency));
|
|
4
|
+
const pool = [];
|
|
5
|
+
const settleOne = async () => {
|
|
6
|
+
const done = await Promise.race(pool.map((x) => x.p));
|
|
7
|
+
const pi = pool.findIndex((x) => x.i === done);
|
|
8
|
+
if (pi >= 0) pool.splice(pi, 1);
|
|
9
|
+
};
|
|
10
|
+
const drain = async () => {
|
|
11
|
+
while (pool.length) await settleOne();
|
|
12
|
+
};
|
|
13
|
+
for (let i = 0; i < items.length; i++) {
|
|
14
|
+
const item = items[i];
|
|
15
|
+
if (isBarrier(item)) {
|
|
16
|
+
await drain();
|
|
17
|
+
await run(item, i);
|
|
18
|
+
} else {
|
|
19
|
+
const idx = i;
|
|
20
|
+
pool.push({ p: run(item, idx).then(() => idx), i: idx });
|
|
21
|
+
if (pool.length >= n) await settleOne();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
await drain();
|
|
25
|
+
}
|
|
26
|
+
exports.runPlanConcurrent = runPlanConcurrent;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const handle = require("./handle-
|
|
2
|
+
const handle = require("./handle-Crv1i9hb.js");
|
|
3
3
|
const https = require("node:https");
|
|
4
4
|
const http = require("node:http");
|
|
5
5
|
const xmldom = require("@xmldom/xmldom");
|
|
@@ -313,7 +313,7 @@ function registerSoapHandlers(ipc) {
|
|
|
313
313
|
handle.handleIpc(ipc, handle.IPC.wsdl.import, async (_event, opts) => {
|
|
314
314
|
const { validateWsdlImport } = await Promise.resolve().then(() => require("./ipc-validate-sqUFqRmE.js"));
|
|
315
315
|
validateWsdlImport(opts);
|
|
316
|
-
const { importWsdl } = await Promise.resolve().then(() => require("./import-
|
|
316
|
+
const { importWsdl } = await Promise.resolve().then(() => require("./import-DTcZMRNu.js"));
|
|
317
317
|
const wsdlText = opts.xml ?? (opts.url ? await fetchUrl(opts.url) : "");
|
|
318
318
|
if (!wsdlText) throw new Error("wsdl:import requires either `url` or `xml`");
|
|
319
319
|
return importWsdl(wsdlText, { name: opts.name, existingMockPorts: opts.existingMockPorts });
|
package/out/main/contract.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"use strict";
|
|
3
3
|
const promises = require("node:fs/promises");
|
|
4
4
|
const path = require("node:path");
|
|
5
|
-
const brokerClient = require("./chunks/broker-client-
|
|
5
|
+
const brokerClient = require("./chunks/broker-client-BYpWeOWT.js");
|
|
6
6
|
const escape = require("./chunks/escape-DDVAlOvZ.js");
|
|
7
7
|
const crypto = require("node:crypto");
|
|
8
8
|
const undici = require("undici");
|
|
@@ -11,16 +11,19 @@ const cliCommon = require("./chunks/cli-common-GnihVSjU.js");
|
|
|
11
11
|
const node_child_process = require("node:child_process");
|
|
12
12
|
const jsYaml = require("js-yaml");
|
|
13
13
|
const environments = require("./chunks/environments-iM3SUM-4.js");
|
|
14
|
-
require("./chunks/request-exec-
|
|
14
|
+
require("./chunks/request-exec-Cv2jkrO1.js");
|
|
15
15
|
require("fs/promises");
|
|
16
16
|
require("tls");
|
|
17
|
-
require("./chunks/handle-
|
|
17
|
+
require("./chunks/handle-Crv1i9hb.js");
|
|
18
18
|
require("node:fs");
|
|
19
19
|
require("dayjs");
|
|
20
20
|
require("node:vm");
|
|
21
21
|
require("tv4");
|
|
22
22
|
require("jsonpath-plus");
|
|
23
23
|
require("@xmldom/xmldom");
|
|
24
|
+
require("mqtt");
|
|
25
|
+
require("ws");
|
|
26
|
+
require("socket.io-client");
|
|
24
27
|
require("node:http");
|
|
25
28
|
require("ajv");
|
|
26
29
|
require("./chunks/data-files-BYqhsoPe.js");
|