@testsmith/api-spector 0.0.4 → 0.0.5
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/LICENSE +674 -0
- package/out/main/chunks/{script-runner-DIevRMmJ.js → script-runner-CmdLWmKN.js} +70 -1
- package/out/main/index.js +8 -1
- package/out/main/runner.js +3 -1
- package/out/preload/index.js +3 -1
- package/out/renderer/assets/{index-BbhJgjM1.js → index-KbYng3S3.js} +422 -90
- package/out/renderer/assets/{index-C1Q1TfC-.css → index-f4ppKNTS.css} +17 -5
- package/out/renderer/index.html +2 -2
- package/package.json +5 -3
|
@@ -8339,6 +8339,14 @@ const useStore = create()(
|
|
|
8339
8339
|
s.activeEnvironmentId = env.id;
|
|
8340
8340
|
if (s.workspace) s.workspace.environments.push(relPath);
|
|
8341
8341
|
}),
|
|
8342
|
+
deleteEnvironment: (id2) => set2((s) => {
|
|
8343
|
+
const relPath = s.environments[id2]?.relPath;
|
|
8344
|
+
delete s.environments[id2];
|
|
8345
|
+
if (s.activeEnvironmentId === id2) s.activeEnvironmentId = null;
|
|
8346
|
+
if (relPath && s.workspace) {
|
|
8347
|
+
s.workspace.environments = s.workspace.environments.filter((p2) => p2 !== relPath);
|
|
8348
|
+
}
|
|
8349
|
+
}),
|
|
8342
8350
|
// ── Globals ───────────────────────────────────────────────────────────────
|
|
8343
8351
|
setGlobals: (globals) => set2((s) => {
|
|
8344
8352
|
s.globals = globals;
|
|
@@ -9161,6 +9169,43 @@ function TagChips({
|
|
|
9161
9169
|
)
|
|
9162
9170
|
] });
|
|
9163
9171
|
}
|
|
9172
|
+
function ConfirmDialog({ message, onConfirm, onCancel }) {
|
|
9173
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9174
|
+
"div",
|
|
9175
|
+
{
|
|
9176
|
+
className: "fixed inset-0 bg-black/50 z-[300] flex items-center justify-center",
|
|
9177
|
+
onClick: onCancel,
|
|
9178
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
9179
|
+
"div",
|
|
9180
|
+
{
|
|
9181
|
+
className: "bg-surface-900 border border-surface-700 rounded-lg shadow-2xl p-4 w-72 flex flex-col gap-4",
|
|
9182
|
+
onClick: (e) => e.stopPropagation(),
|
|
9183
|
+
children: [
|
|
9184
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-white", children: message }),
|
|
9185
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex justify-end gap-2", children: [
|
|
9186
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9187
|
+
"button",
|
|
9188
|
+
{
|
|
9189
|
+
onClick: onCancel,
|
|
9190
|
+
className: "px-3 py-1.5 text-xs text-surface-400 hover:text-white transition-colors",
|
|
9191
|
+
children: "Cancel"
|
|
9192
|
+
}
|
|
9193
|
+
),
|
|
9194
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9195
|
+
"button",
|
|
9196
|
+
{
|
|
9197
|
+
onClick: onConfirm,
|
|
9198
|
+
className: "px-3 py-1.5 text-xs bg-red-700 hover:bg-red-600 rounded transition-colors",
|
|
9199
|
+
children: "Delete"
|
|
9200
|
+
}
|
|
9201
|
+
)
|
|
9202
|
+
] })
|
|
9203
|
+
]
|
|
9204
|
+
}
|
|
9205
|
+
)
|
|
9206
|
+
}
|
|
9207
|
+
);
|
|
9208
|
+
}
|
|
9164
9209
|
function IconBtn({
|
|
9165
9210
|
title: title2,
|
|
9166
9211
|
onClick,
|
|
@@ -9180,7 +9225,7 @@ function IconBtn({
|
|
|
9180
9225
|
children
|
|
9181
9226
|
}
|
|
9182
9227
|
),
|
|
9183
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pointer-events-none absolute
|
|
9228
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pointer-events-none absolute top-full left-1/2 -translate-x-1/2 mt-1 z-50 hidden group-hover/tip:block", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "whitespace-nowrap rounded px-1.5 py-0.5 text-[10px] bg-[#1e1b2e] text-gray-300 border border-white/10 shadow-lg", children: title2 }) })
|
|
9184
9229
|
] });
|
|
9185
9230
|
}
|
|
9186
9231
|
function RunBtn({ onClick }) {
|
|
@@ -9192,7 +9237,7 @@ function RunBtn({ onClick }) {
|
|
|
9192
9237
|
e.stopPropagation();
|
|
9193
9238
|
onClick(e);
|
|
9194
9239
|
},
|
|
9195
|
-
className: "
|
|
9240
|
+
className: "px-1 py-0.5 rounded text-emerald-500 hover:text-emerald-400 transition-all",
|
|
9196
9241
|
children: /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-3 h-3", viewBox: "0 0 20 20", fill: "currentColor", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z", clipRule: "evenodd" }) })
|
|
9197
9242
|
}
|
|
9198
9243
|
);
|
|
@@ -9219,19 +9264,22 @@ function CollectionTree() {
|
|
|
9219
9264
|
const updateRequestTags = useStore((s) => s.updateRequestTags);
|
|
9220
9265
|
const openRunner = useStore((s) => s.openRunner);
|
|
9221
9266
|
const colList = Object.values(collections);
|
|
9267
|
+
const [pendingConfirm, setPendingConfirm] = reactExports.useState(null);
|
|
9268
|
+
function confirmThen(message, action) {
|
|
9269
|
+
setPendingConfirm({ message, onConfirm: () => {
|
|
9270
|
+
action();
|
|
9271
|
+
setPendingConfirm(null);
|
|
9272
|
+
} });
|
|
9273
|
+
}
|
|
9222
9274
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col flex-1 min-h-0 select-none", children: [
|
|
9223
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
children: "+"
|
|
9232
|
-
}
|
|
9233
|
-
)
|
|
9234
|
-
] }),
|
|
9275
|
+
pendingConfirm && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9276
|
+
ConfirmDialog,
|
|
9277
|
+
{
|
|
9278
|
+
message: pendingConfirm.message,
|
|
9279
|
+
onConfirm: pendingConfirm.onConfirm,
|
|
9280
|
+
onCancel: () => setPendingConfirm(null)
|
|
9281
|
+
}
|
|
9282
|
+
),
|
|
9235
9283
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
9236
9284
|
colList.map(({ data: col }) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9237
9285
|
CollectionNode,
|
|
@@ -9245,13 +9293,9 @@ function CollectionTree() {
|
|
|
9245
9293
|
onAddRequest: (folderId) => addRequest(col.id, folderId),
|
|
9246
9294
|
onAddFolder: (parentId, name2) => addFolder(col.id, parentId, name2),
|
|
9247
9295
|
onRenameCollection: (name2) => renameCollection(col.id, name2),
|
|
9248
|
-
onDeleteCollection: () => {
|
|
9249
|
-
if (confirm(`Delete collection "${col.name}"?`)) deleteCollection(col.id);
|
|
9250
|
-
},
|
|
9296
|
+
onDeleteCollection: () => confirmThen(`Delete collection "${col.name}"?`, () => deleteCollection(col.id)),
|
|
9251
9297
|
onRenameFolder: (folderId, name2) => renameFolder(col.id, folderId, name2),
|
|
9252
|
-
onDeleteFolder: (folderId) =>
|
|
9253
|
-
if (confirm("Delete this folder and all its requests?")) deleteFolder(col.id, folderId);
|
|
9254
|
-
},
|
|
9298
|
+
onDeleteFolder: (folderId) => confirmThen("Delete this folder and all its requests?", () => deleteFolder(col.id, folderId)),
|
|
9255
9299
|
onRenameRequest: renameRequest,
|
|
9256
9300
|
onDeleteRequest: (reqId) => deleteRequest(col.id, reqId),
|
|
9257
9301
|
onDuplicateRequest: (reqId) => duplicateRequest(col.id, reqId),
|
|
@@ -9324,15 +9368,15 @@ function CollectionNode({
|
|
|
9324
9368
|
validate: (v2) => existingCollectionNames.filter((n2) => n2 !== col.name).includes(v2) ? `"${v2}" already exists` : null
|
|
9325
9369
|
}
|
|
9326
9370
|
) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-semibold truncate block", children: col.name }) }),
|
|
9327
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center shrink-0 gap-0", children: [
|
|
9371
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "hidden group-hover:flex items-center shrink-0 gap-0", children: [
|
|
9328
9372
|
/* @__PURE__ */ jsxRuntimeExports.jsx(RunBtn, { onClick: onRunCollection }),
|
|
9329
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Expand all folders", onClick: expandAll, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandAllIcon, {}) }),
|
|
9330
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Collapse all folders", onClick: collapseAll, children: /* @__PURE__ */ jsxRuntimeExports.jsx(CollapseAllIcon, {}) }),
|
|
9331
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Collection data (iterations)", onClick: onSelectCollection, children: /* @__PURE__ */ jsxRuntimeExports.jsx(TableIcon, {}) }),
|
|
9332
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add request", onClick: () => onAddRequest(col.rootFolder.id), children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "+" }) }),
|
|
9333
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add folder", onClick: () => onAddFolder(col.rootFolder.id, "New Folder"), children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderIcon, {}) }),
|
|
9334
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Rename", onClick: () => setRenaming(true), children: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}) }),
|
|
9335
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Delete collection", onClick: onDeleteCollection, danger: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(TrashIcon, {}) })
|
|
9373
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Expand all folders", onClick: expandAll, alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(ExpandAllIcon, {}) }),
|
|
9374
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Collapse all folders", onClick: collapseAll, alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(CollapseAllIcon, {}) }),
|
|
9375
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Collection data (iterations)", onClick: onSelectCollection, alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(TableIcon, {}) }),
|
|
9376
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add request", onClick: () => onAddRequest(col.rootFolder.id), alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "+" }) }),
|
|
9377
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add folder", onClick: () => onAddFolder(col.rootFolder.id, "New Folder"), alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderIcon, {}) }),
|
|
9378
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Rename", onClick: () => setRenaming(true), alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}) }),
|
|
9379
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Delete collection", onClick: onDeleteCollection, danger: true, alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(TrashIcon, {}) })
|
|
9336
9380
|
] })
|
|
9337
9381
|
]
|
|
9338
9382
|
}
|
|
@@ -9415,16 +9459,16 @@ function FolderRow({
|
|
|
9415
9459
|
}
|
|
9416
9460
|
)
|
|
9417
9461
|
] }),
|
|
9418
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center shrink-0 gap-0", children: [
|
|
9462
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "hidden group-hover:flex items-center shrink-0 gap-0", children: [
|
|
9419
9463
|
/* @__PURE__ */ jsxRuntimeExports.jsx(RunBtn, { onClick: onRun }),
|
|
9420
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add request", onClick: onAddRequest, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "+" }) }),
|
|
9421
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add sub-folder", onClick: onAddFolder, children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderIcon, {}) }),
|
|
9422
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Folder auth & headers", onClick: () => setShowSettings(true), children: /* @__PURE__ */ jsxRuntimeExports.jsx(KeyIcon, {}) }),
|
|
9464
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add request", onClick: onAddRequest, alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "+" }) }),
|
|
9465
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add sub-folder", onClick: onAddFolder, alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderIcon, {}) }),
|
|
9466
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Folder auth & headers", onClick: () => setShowSettings(true), alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(KeyIcon, {}) }),
|
|
9423
9467
|
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add tag", onClick: () => {
|
|
9424
|
-
}, alwaysVisible:
|
|
9468
|
+
}, alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(TagChips, { tags: [], onRemove: () => {
|
|
9425
9469
|
}, onAdd: (tag) => onUpdateTags([...tags2, tag]) }) }),
|
|
9426
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Rename", onClick: () => setRenaming(true), children: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}) }),
|
|
9427
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Delete folder", onClick: onDelete, danger: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(TrashIcon, {}) })
|
|
9470
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Rename", onClick: () => setRenaming(true), alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}) }),
|
|
9471
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Delete folder", onClick: onDelete, danger: true, alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(TrashIcon, {}) })
|
|
9428
9472
|
] })
|
|
9429
9473
|
]
|
|
9430
9474
|
}
|
|
@@ -36977,6 +37021,24 @@ sp.variables.set("field_value", json.field);`
|
|
|
36977
37021
|
label: "Save JSON field to environment",
|
|
36978
37022
|
code: `const json = sp.response.json();
|
|
36979
37023
|
sp.environment.set("token", json.token);`
|
|
37024
|
+
},
|
|
37025
|
+
{
|
|
37026
|
+
label: "Extract via JSONPath to variable",
|
|
37027
|
+
code: `const matches = sp.jsonPath(sp.response.json(), '$.data[0].id');
|
|
37028
|
+
sp.variables.set("extracted_value", String(matches[0] ?? ''));`
|
|
37029
|
+
},
|
|
37030
|
+
{
|
|
37031
|
+
label: "Extract via JSONPath to environment",
|
|
37032
|
+
code: `const matches = sp.jsonPath(sp.response.json(), '$.data[0].id');
|
|
37033
|
+
sp.environment.set("extracted_value", String(matches[0] ?? ''));`
|
|
37034
|
+
},
|
|
37035
|
+
{
|
|
37036
|
+
label: "Extract from XML to variable",
|
|
37037
|
+
code: `sp.variables.set("extracted_value", sp.response.xmlText("ElementName") ?? '');`
|
|
37038
|
+
},
|
|
37039
|
+
{
|
|
37040
|
+
label: "Extract from XML to environment",
|
|
37041
|
+
code: `sp.environment.set("extracted_value", sp.response.xmlText("ElementName") ?? '');`
|
|
36980
37042
|
}
|
|
36981
37043
|
]
|
|
36982
37044
|
}
|
|
@@ -36990,6 +37052,7 @@ function ScriptsTab({ request, onChange }) {
|
|
|
36990
37052
|
if (activeTabId) setTabScriptTab(activeTabId, t2);
|
|
36991
37053
|
};
|
|
36992
37054
|
const [expandedGroup, setExpandedGroup] = reactExports.useState(SNIPPET_GROUPS[0].group);
|
|
37055
|
+
const [snippetsOpen, setSnippetsOpen] = reactExports.useState(true);
|
|
36993
37056
|
const varNames = useVarNames();
|
|
36994
37057
|
const varValues = useVarValues();
|
|
36995
37058
|
const extensions = reactExports.useMemo(
|
|
@@ -37030,9 +37093,27 @@ function ScriptsTab({ request, onChange }) {
|
|
|
37030
37093
|
}
|
|
37031
37094
|
) })
|
|
37032
37095
|
] }),
|
|
37033
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className:
|
|
37034
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
37035
|
-
|
|
37096
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `flex-shrink-0 flex flex-col border-l border-surface-700 transition-all ${snippetsOpen ? "w-52 pl-2 overflow-y-auto" : "w-7"}`, children: [
|
|
37097
|
+
snippetsOpen ? /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
37098
|
+
"button",
|
|
37099
|
+
{
|
|
37100
|
+
onClick: () => setSnippetsOpen(false),
|
|
37101
|
+
className: "flex items-center gap-1 text-[10px] font-semibold text-surface-400 uppercase tracking-wider mb-2 hover:text-white transition-colors w-full",
|
|
37102
|
+
children: [
|
|
37103
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "▾" }),
|
|
37104
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Quick inserts" })
|
|
37105
|
+
]
|
|
37106
|
+
}
|
|
37107
|
+
) : /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
37108
|
+
"button",
|
|
37109
|
+
{
|
|
37110
|
+
onClick: () => setSnippetsOpen(true),
|
|
37111
|
+
className: "flex-1 flex items-center justify-center hover:bg-surface-800 transition-colors rounded-sm",
|
|
37112
|
+
title: "Expand quick inserts",
|
|
37113
|
+
children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-semibold text-surface-400 uppercase tracking-wider [writing-mode:vertical-rl] rotate-180", children: "Quick inserts" })
|
|
37114
|
+
}
|
|
37115
|
+
),
|
|
37116
|
+
snippetsOpen && SNIPPET_GROUPS.map((group) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-1", children: [
|
|
37036
37117
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
37037
37118
|
"button",
|
|
37038
37119
|
{
|
|
@@ -43807,36 +43888,82 @@ function makeJsonSnippet(path, value, mode) {
|
|
|
43807
43888
|
});`;
|
|
43808
43889
|
}
|
|
43809
43890
|
}
|
|
43891
|
+
function getAtPath(root, path) {
|
|
43892
|
+
let cur2 = root;
|
|
43893
|
+
for (const key of path) {
|
|
43894
|
+
if (cur2 == null || typeof cur2 !== "object") return void 0;
|
|
43895
|
+
cur2 = cur2[key];
|
|
43896
|
+
}
|
|
43897
|
+
return cur2;
|
|
43898
|
+
}
|
|
43899
|
+
function toJsonPathExpr(path, filterKey, filterValue) {
|
|
43900
|
+
let arrayIdx = -1;
|
|
43901
|
+
for (let i = path.length - 1; i >= 0; i--) {
|
|
43902
|
+
if (typeof path[i] === "number") {
|
|
43903
|
+
arrayIdx = i;
|
|
43904
|
+
break;
|
|
43905
|
+
}
|
|
43906
|
+
}
|
|
43907
|
+
if (arrayIdx < 0) return "";
|
|
43908
|
+
const arrayPart = "$." + path.slice(0, arrayIdx).join(".");
|
|
43909
|
+
const leafPart = path.slice(arrayIdx + 1).join(".");
|
|
43910
|
+
const filterVal = isNaN(Number(filterValue)) ? `"${filterValue.replace(/"/g, '\\"')}"` : filterValue;
|
|
43911
|
+
const expr = leafPart ? `${arrayPart}[?(@.${filterKey}==${filterVal})].${leafPart}` : `${arrayPart}[?(@.${filterKey}==${filterVal})]`;
|
|
43912
|
+
return expr;
|
|
43913
|
+
}
|
|
43914
|
+
function makeJsonPathSnippet(path, value, filterKey, filterValue) {
|
|
43915
|
+
const expr = toJsonPathExpr(path, filterKey, filterValue);
|
|
43916
|
+
const lit = toLit(value);
|
|
43917
|
+
return `sp.test('${expr} equals ${lit}', function() {
|
|
43918
|
+
const matches = sp.jsonPath(sp.response.json(), '${expr}');
|
|
43919
|
+
sp.expect(matches.length).to.be.above(0);
|
|
43920
|
+
sp.expect(matches[0]).to.equal(${lit});
|
|
43921
|
+
});`;
|
|
43922
|
+
}
|
|
43810
43923
|
function makeXmlSnippet(selector, value, mode) {
|
|
43811
|
-
const
|
|
43812
|
-
const query = `const el = doc.querySelector("${selector.replace(/"/g, '\\"')}");`;
|
|
43924
|
+
const sel = selector.replace(/"/g, '\\"');
|
|
43813
43925
|
switch (mode) {
|
|
43814
43926
|
case "equals":
|
|
43815
43927
|
return `sp.test('${selector} equals "${esc(value)}"', function() {
|
|
43816
|
-
${
|
|
43817
|
-
${query}
|
|
43818
|
-
sp.expect(el?.textContent?.trim()).to.equal("${esc(value)}");
|
|
43928
|
+
sp.expect(sp.response.xmlText("${sel}")).to.equal("${esc(value)}");
|
|
43819
43929
|
});`;
|
|
43820
43930
|
case "exists":
|
|
43821
43931
|
return `sp.test('${selector} exists', function() {
|
|
43822
|
-
${
|
|
43823
|
-
${query}
|
|
43824
|
-
sp.expect(el).to.not.equal(null);
|
|
43932
|
+
sp.expect(sp.response.xmlText("${sel}")).to.not.equal(null);
|
|
43825
43933
|
});`;
|
|
43826
43934
|
case "contains":
|
|
43827
43935
|
return `sp.test('${selector} contains "${esc(value)}"', function() {
|
|
43828
|
-
${
|
|
43829
|
-
${query}
|
|
43830
|
-
sp.expect(el?.textContent).to.include("${esc(value)}");
|
|
43936
|
+
sp.expect(sp.response.xmlText("${sel}")).to.include("${esc(value)}");
|
|
43831
43937
|
});`;
|
|
43832
43938
|
}
|
|
43833
43939
|
}
|
|
43940
|
+
function varNameFromPath(path) {
|
|
43941
|
+
return path.filter((k2) => typeof k2 === "string").at(-1) ?? "extracted_value";
|
|
43942
|
+
}
|
|
43943
|
+
function makeJsonExtractSnippet(path, target) {
|
|
43944
|
+
const acc = jsonAccessor(path);
|
|
43945
|
+
const varName = varNameFromPath(path);
|
|
43946
|
+
return `const json = sp.response.json();
|
|
43947
|
+
sp.${target}.set("${varName}", String(${acc}));`;
|
|
43948
|
+
}
|
|
43949
|
+
function makeJsonPathExtractSnippet(path, filterKey, filterValue, target) {
|
|
43950
|
+
const expr = toJsonPathExpr(path, filterKey, filterValue);
|
|
43951
|
+
const varName = varNameFromPath(path);
|
|
43952
|
+
return `const matches = sp.jsonPath(sp.response.json(), '${expr}');
|
|
43953
|
+
sp.${target}.set("${varName}", String(matches[0] ?? ''));`;
|
|
43954
|
+
}
|
|
43955
|
+
function makeXmlExtractSnippet(selector, target) {
|
|
43956
|
+
return `sp.${target}.set("extracted_value", sp.response.xmlText("${selector.replace(/"/g, '\\"')}") ?? '');`;
|
|
43957
|
+
}
|
|
43834
43958
|
function AssertMenu({
|
|
43835
43959
|
state,
|
|
43836
43960
|
onClose,
|
|
43837
43961
|
onConfirm
|
|
43838
43962
|
}) {
|
|
43839
43963
|
const ref2 = reactExports.useRef(null);
|
|
43964
|
+
const [jpOpen, setJpOpen] = reactExports.useState(false);
|
|
43965
|
+
const [filterKey, setFilterKey] = reactExports.useState("");
|
|
43966
|
+
const [filterVal, setFilterVal] = reactExports.useState("");
|
|
43840
43967
|
reactExports.useEffect(() => {
|
|
43841
43968
|
function onMouse(e) {
|
|
43842
43969
|
if (ref2.current && !ref2.current.contains(e.target)) onClose();
|
|
@@ -43853,8 +43980,10 @@ function AssertMenu({
|
|
|
43853
43980
|
}, [onClose]);
|
|
43854
43981
|
let title2 = "";
|
|
43855
43982
|
let options = [];
|
|
43983
|
+
let jpSiblingKeys = [];
|
|
43984
|
+
let jpAvailable = false;
|
|
43856
43985
|
if (state.type === "json") {
|
|
43857
|
-
const { path, value } = state;
|
|
43986
|
+
const { path, value, root } = state;
|
|
43858
43987
|
const isStr = typeof value === "string";
|
|
43859
43988
|
const preview = isStr ? `"${value.length > 22 ? value.slice(0, 22) + "…" : value}"` : String(value);
|
|
43860
43989
|
title2 = jsonPathLabel(path);
|
|
@@ -43864,6 +43993,26 @@ function AssertMenu({
|
|
|
43864
43993
|
{ label: `is ${value === null ? "null" : typeof value}`, snippet: makeJsonSnippet(path, value, "type") },
|
|
43865
43994
|
...isStr ? [{ label: `contains ${preview}`, snippet: makeJsonSnippet(path, value, "contains") }] : []
|
|
43866
43995
|
];
|
|
43996
|
+
const arrayIdx = [...path].reverse().findIndex((k2) => typeof k2 === "number");
|
|
43997
|
+
if (arrayIdx >= 0) {
|
|
43998
|
+
jpAvailable = true;
|
|
43999
|
+
const realIdx = path.length - 1 - arrayIdx;
|
|
44000
|
+
const itemObj = getAtPath(root, path.slice(0, realIdx + 1));
|
|
44001
|
+
if (itemObj != null && typeof itemObj === "object" && !Array.isArray(itemObj)) {
|
|
44002
|
+
jpSiblingKeys = Object.keys(itemObj).filter((k2) => {
|
|
44003
|
+
const v2 = itemObj[k2];
|
|
44004
|
+
return typeof v2 !== "object" || v2 === null;
|
|
44005
|
+
});
|
|
44006
|
+
}
|
|
44007
|
+
if (!filterKey && jpSiblingKeys.length > 0) {
|
|
44008
|
+
const defaultKey = jpSiblingKeys.find((k2) => k2 === "name" || k2 === "id") ?? jpSiblingKeys[0];
|
|
44009
|
+
setTimeout(() => {
|
|
44010
|
+
setFilterKey(defaultKey);
|
|
44011
|
+
const seed = getAtPath(root, [...path.slice(0, realIdx + 1), defaultKey]);
|
|
44012
|
+
setFilterVal(seed != null ? String(seed) : "");
|
|
44013
|
+
}, 0);
|
|
44014
|
+
}
|
|
44015
|
+
}
|
|
43867
44016
|
} else {
|
|
43868
44017
|
const { selector, value } = state;
|
|
43869
44018
|
const preview = `"${value.length > 22 ? value.slice(0, 22) + "…" : value}"`;
|
|
@@ -43874,14 +44023,14 @@ function AssertMenu({
|
|
|
43874
44023
|
{ label: `contains ${preview}`, snippet: makeXmlSnippet(selector, value, "contains") }
|
|
43875
44024
|
];
|
|
43876
44025
|
}
|
|
43877
|
-
const x2 = Math.min(state.x, window.innerWidth -
|
|
43878
|
-
const y2 = Math.min(state.y, window.innerHeight -
|
|
44026
|
+
const x2 = Math.min(state.x, window.innerWidth - 280);
|
|
44027
|
+
const y2 = Math.min(state.y, window.innerHeight - 280);
|
|
43879
44028
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
43880
44029
|
"div",
|
|
43881
44030
|
{
|
|
43882
44031
|
ref: ref2,
|
|
43883
44032
|
style: { top: y2, left: x2, position: "fixed" },
|
|
43884
|
-
className: "z-[200] bg-surface-900 border border-surface-700 rounded-lg shadow-2xl p-2 min-w-[
|
|
44033
|
+
className: "z-[200] bg-surface-900 border border-surface-700 rounded-lg shadow-2xl p-2 min-w-[260px]",
|
|
43885
44034
|
children: [
|
|
43886
44035
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-surface-500 font-mono px-1.5 pb-1.5 mb-1.5 border-b border-surface-800 truncate", children: title2 }),
|
|
43887
44036
|
options.map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -43895,7 +44044,141 @@ function AssertMenu({
|
|
|
43895
44044
|
children: opt.label
|
|
43896
44045
|
},
|
|
43897
44046
|
opt.label
|
|
43898
|
-
))
|
|
44047
|
+
)),
|
|
44048
|
+
jpAvailable && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 border-t border-surface-800 pt-1", children: [
|
|
44049
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
44050
|
+
"button",
|
|
44051
|
+
{
|
|
44052
|
+
onClick: () => setJpOpen((o) => !o),
|
|
44053
|
+
className: "w-full text-left text-xs text-blue-400 hover:text-blue-300 hover:bg-surface-800 rounded px-2 py-1.5 transition-colors flex items-center gap-1",
|
|
44054
|
+
children: [
|
|
44055
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: jpOpen ? "▾" : "▸" }),
|
|
44056
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "JSONPath assert (with filter)" })
|
|
44057
|
+
]
|
|
44058
|
+
}
|
|
44059
|
+
),
|
|
44060
|
+
jpOpen && state.type === "json" && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 px-2 flex flex-col gap-1.5", children: [
|
|
44061
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
44062
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-surface-400 w-16 shrink-0", children: "filter by" }),
|
|
44063
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44064
|
+
"select",
|
|
44065
|
+
{
|
|
44066
|
+
value: filterKey,
|
|
44067
|
+
onChange: (e) => {
|
|
44068
|
+
const k2 = e.target.value;
|
|
44069
|
+
setFilterKey(k2);
|
|
44070
|
+
const arrayIdx2 = [...state.path].reverse().findIndex((seg) => typeof seg === "number");
|
|
44071
|
+
const realIdx2 = state.path.length - 1 - arrayIdx2;
|
|
44072
|
+
const itemObj2 = getAtPath(state.root, state.path.slice(0, realIdx2 + 1));
|
|
44073
|
+
const seed = itemObj2 != null ? itemObj2[k2] : void 0;
|
|
44074
|
+
setFilterVal(seed != null ? String(seed) : "");
|
|
44075
|
+
},
|
|
44076
|
+
className: "flex-1 bg-surface-800 border border-surface-700 rounded px-1 py-0.5 text-xs focus:outline-none",
|
|
44077
|
+
children: jpSiblingKeys.map((k2) => /* @__PURE__ */ jsxRuntimeExports.jsx("option", { value: k2, children: k2 }, k2))
|
|
44078
|
+
}
|
|
44079
|
+
)
|
|
44080
|
+
] }),
|
|
44081
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1", children: [
|
|
44082
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] text-surface-400 w-16 shrink-0", children: "equals" }),
|
|
44083
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44084
|
+
"input",
|
|
44085
|
+
{
|
|
44086
|
+
value: filterVal,
|
|
44087
|
+
onChange: (e) => setFilterVal(e.target.value),
|
|
44088
|
+
className: "flex-1 bg-surface-800 border border-surface-700 rounded px-1 py-0.5 text-xs font-mono focus:outline-none focus:border-blue-500"
|
|
44089
|
+
}
|
|
44090
|
+
)
|
|
44091
|
+
] }),
|
|
44092
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-1 self-end", children: [
|
|
44093
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44094
|
+
"button",
|
|
44095
|
+
{
|
|
44096
|
+
disabled: !filterKey || !filterVal,
|
|
44097
|
+
onClick: () => {
|
|
44098
|
+
onConfirm(makeJsonPathSnippet(state.path, state.value, filterKey, filterVal));
|
|
44099
|
+
onClose();
|
|
44100
|
+
},
|
|
44101
|
+
className: "text-xs px-2 py-1 bg-blue-700 hover:bg-blue-600 disabled:opacity-40 rounded transition-colors",
|
|
44102
|
+
children: "Assert"
|
|
44103
|
+
}
|
|
44104
|
+
),
|
|
44105
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44106
|
+
"button",
|
|
44107
|
+
{
|
|
44108
|
+
disabled: !filterKey || !filterVal,
|
|
44109
|
+
onClick: () => {
|
|
44110
|
+
onConfirm(makeJsonPathExtractSnippet(state.path, filterKey, filterVal, "variables"));
|
|
44111
|
+
onClose();
|
|
44112
|
+
},
|
|
44113
|
+
className: "text-xs px-2 py-1 bg-surface-700 hover:bg-surface-600 disabled:opacity-40 rounded transition-colors",
|
|
44114
|
+
children: "→ variable"
|
|
44115
|
+
}
|
|
44116
|
+
),
|
|
44117
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44118
|
+
"button",
|
|
44119
|
+
{
|
|
44120
|
+
disabled: !filterKey || !filterVal,
|
|
44121
|
+
onClick: () => {
|
|
44122
|
+
onConfirm(makeJsonPathExtractSnippet(state.path, filterKey, filterVal, "environment"));
|
|
44123
|
+
onClose();
|
|
44124
|
+
},
|
|
44125
|
+
className: "text-xs px-2 py-1 bg-surface-700 hover:bg-surface-600 disabled:opacity-40 rounded transition-colors",
|
|
44126
|
+
children: "→ env"
|
|
44127
|
+
}
|
|
44128
|
+
)
|
|
44129
|
+
] })
|
|
44130
|
+
] })
|
|
44131
|
+
] }),
|
|
44132
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mt-1 border-t border-surface-800 pt-1", children: [
|
|
44133
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-[10px] text-surface-500 uppercase tracking-wider px-2 py-1", children: "Extract" }),
|
|
44134
|
+
state.type === "json" ? /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
44135
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44136
|
+
"button",
|
|
44137
|
+
{
|
|
44138
|
+
onClick: () => {
|
|
44139
|
+
onConfirm(makeJsonExtractSnippet(state.path, "variables"));
|
|
44140
|
+
onClose();
|
|
44141
|
+
},
|
|
44142
|
+
className: "w-full text-left text-xs text-surface-300 hover:text-white hover:bg-surface-800 rounded px-2 py-1.5 transition-colors",
|
|
44143
|
+
children: "Save to variable"
|
|
44144
|
+
}
|
|
44145
|
+
),
|
|
44146
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44147
|
+
"button",
|
|
44148
|
+
{
|
|
44149
|
+
onClick: () => {
|
|
44150
|
+
onConfirm(makeJsonExtractSnippet(state.path, "environment"));
|
|
44151
|
+
onClose();
|
|
44152
|
+
},
|
|
44153
|
+
className: "w-full text-left text-xs text-surface-300 hover:text-white hover:bg-surface-800 rounded px-2 py-1.5 transition-colors",
|
|
44154
|
+
children: "Save to environment"
|
|
44155
|
+
}
|
|
44156
|
+
)
|
|
44157
|
+
] }) : /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
44158
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44159
|
+
"button",
|
|
44160
|
+
{
|
|
44161
|
+
onClick: () => {
|
|
44162
|
+
onConfirm(makeXmlExtractSnippet(state.selector, "variables"));
|
|
44163
|
+
onClose();
|
|
44164
|
+
},
|
|
44165
|
+
className: "w-full text-left text-xs text-surface-300 hover:text-white hover:bg-surface-800 rounded px-2 py-1.5 transition-colors",
|
|
44166
|
+
children: "Save to variable"
|
|
44167
|
+
}
|
|
44168
|
+
),
|
|
44169
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44170
|
+
"button",
|
|
44171
|
+
{
|
|
44172
|
+
onClick: () => {
|
|
44173
|
+
onConfirm(makeXmlExtractSnippet(state.selector, "environment"));
|
|
44174
|
+
onClose();
|
|
44175
|
+
},
|
|
44176
|
+
className: "w-full text-left text-xs text-surface-300 hover:text-white hover:bg-surface-800 rounded px-2 py-1.5 transition-colors",
|
|
44177
|
+
children: "Save to environment"
|
|
44178
|
+
}
|
|
44179
|
+
)
|
|
44180
|
+
] })
|
|
44181
|
+
] })
|
|
43899
44182
|
]
|
|
43900
44183
|
}
|
|
43901
44184
|
);
|
|
@@ -43924,7 +44207,7 @@ function JsonNode({
|
|
|
43924
44207
|
onClick: (e) => onLeaf(e, path, value),
|
|
43925
44208
|
className: "ml-auto opacity-0 group-hover:opacity-100 shrink-0 text-[10px] px-1.5 leading-4 py-0.5 text-blue-400 border border-blue-800 hover:border-blue-500 hover:text-blue-300 rounded transition-all",
|
|
43926
44209
|
title: "Add assertion for this value",
|
|
43927
|
-
children: "+
|
|
44210
|
+
children: "+ insert"
|
|
43928
44211
|
}
|
|
43929
44212
|
)
|
|
43930
44213
|
] });
|
|
@@ -43995,7 +44278,7 @@ function XmlNode({
|
|
|
43995
44278
|
onClick: (e) => onLeaf(e, selector, text),
|
|
43996
44279
|
className: "ml-auto opacity-0 group-hover:opacity-100 shrink-0 text-[10px] px-1.5 leading-4 py-0.5 text-blue-400 border border-blue-800 hover:border-blue-500 hover:text-blue-300 rounded transition-all",
|
|
43997
44280
|
title: "Add assertion for this value",
|
|
43998
|
-
children: "+
|
|
44281
|
+
children: "+ insert"
|
|
43999
44282
|
}
|
|
44000
44283
|
)
|
|
44001
44284
|
] });
|
|
@@ -44024,22 +44307,26 @@ function InteractiveBody({ body, contentType, onAssert }) {
|
|
|
44024
44307
|
const [popover, setPopover] = reactExports.useState(null);
|
|
44025
44308
|
const isJson = contentType.includes("json");
|
|
44026
44309
|
const isXml = !isJson && (contentType.includes("xml") || contentType.includes("html"));
|
|
44310
|
+
let parsedJson = null;
|
|
44311
|
+
if (isJson) {
|
|
44312
|
+
try {
|
|
44313
|
+
parsedJson = JSON.parse(body);
|
|
44314
|
+
} catch {
|
|
44315
|
+
}
|
|
44316
|
+
}
|
|
44027
44317
|
function handleJsonLeaf(e, path, value) {
|
|
44028
44318
|
e.stopPropagation();
|
|
44029
|
-
setPopover({ type: "json", path, value, x: e.clientX + 10, y: e.clientY + 10 });
|
|
44319
|
+
setPopover({ type: "json", path, value, root: parsedJson, x: e.clientX + 10, y: e.clientY + 10 });
|
|
44030
44320
|
}
|
|
44031
44321
|
function handleXmlLeaf(e, selector, value) {
|
|
44032
44322
|
e.stopPropagation();
|
|
44033
44323
|
setPopover({ type: "xml", selector, value, x: e.clientX + 10, y: e.clientY + 10 });
|
|
44034
44324
|
}
|
|
44035
44325
|
const treeContent = isJson ? (() => {
|
|
44036
|
-
|
|
44037
|
-
try {
|
|
44038
|
-
parsed = JSON.parse(body);
|
|
44039
|
-
} catch {
|
|
44326
|
+
if (parsedJson === null) {
|
|
44040
44327
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 text-xs text-surface-600", children: "Unable to parse JSON response body" });
|
|
44041
44328
|
}
|
|
44042
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(JsonNode, { nodeKey: null, value:
|
|
44329
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(JsonNode, { nodeKey: null, value: parsedJson, path: [], depth: 0, onLeaf: handleJsonLeaf });
|
|
44043
44330
|
})() : isXml ? (() => {
|
|
44044
44331
|
const doc2 = new DOMParser().parseFromString(body, "text/xml");
|
|
44045
44332
|
const root = doc2.documentElement;
|
|
@@ -44071,6 +44358,32 @@ function prettyJson(raw) {
|
|
|
44071
44358
|
return raw;
|
|
44072
44359
|
}
|
|
44073
44360
|
}
|
|
44361
|
+
function prettyXml(raw) {
|
|
44362
|
+
try {
|
|
44363
|
+
const indent = " ";
|
|
44364
|
+
let result = "";
|
|
44365
|
+
let depth = 0;
|
|
44366
|
+
const tokens = raw.match(/<[^>]+>|[^<]+/g) ?? [];
|
|
44367
|
+
for (const token of tokens) {
|
|
44368
|
+
const text = token.trim();
|
|
44369
|
+
if (!text) continue;
|
|
44370
|
+
if (text.startsWith("<?") || text.startsWith("<!")) {
|
|
44371
|
+
result += indent.repeat(depth) + text + "\n";
|
|
44372
|
+
} else if (token.startsWith("</")) {
|
|
44373
|
+
depth = Math.max(0, depth - 1);
|
|
44374
|
+
result += indent.repeat(depth) + text + "\n";
|
|
44375
|
+
} else if (token.startsWith("<") && !token.endsWith("/>") && !token.includes("</")) {
|
|
44376
|
+
result += indent.repeat(depth) + text + "\n";
|
|
44377
|
+
depth++;
|
|
44378
|
+
} else {
|
|
44379
|
+
result += indent.repeat(depth) + text + "\n";
|
|
44380
|
+
}
|
|
44381
|
+
}
|
|
44382
|
+
return result.trimEnd();
|
|
44383
|
+
} catch {
|
|
44384
|
+
return raw;
|
|
44385
|
+
}
|
|
44386
|
+
}
|
|
44074
44387
|
function SaveAsMockModal({ onClose }) {
|
|
44075
44388
|
const mocks = useStore((s) => s.mocks);
|
|
44076
44389
|
const addMock = useStore((s) => s.addMock);
|
|
@@ -44406,7 +44719,7 @@ function ResponseViewer() {
|
|
|
44406
44719
|
const isJson = contentType.includes("json");
|
|
44407
44720
|
const isXml = !isJson && (contentType.includes("xml") || contentType.includes("html"));
|
|
44408
44721
|
const supportsTree = isJson || isXml;
|
|
44409
|
-
const displayBody = isJson ? prettyJson(response.body) : response.body;
|
|
44722
|
+
const displayBody = isJson ? prettyJson(response.body) : isXml ? prettyXml(response.body) : response.body;
|
|
44410
44723
|
const passedCount = scriptResult?.testResults.filter((t2) => t2.passed).length ?? 0;
|
|
44411
44724
|
const totalCount = scriptResult?.testResults.length ?? 0;
|
|
44412
44725
|
const consoleCount = scriptResult?.consoleOutput.length ?? 0;
|
|
@@ -44514,17 +44827,16 @@ function ResponseViewer() {
|
|
|
44514
44827
|
contentType,
|
|
44515
44828
|
onAssert: handleAssert
|
|
44516
44829
|
}
|
|
44517
|
-
) : tab === "body" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44830
|
+
) : tab === "body" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44518
44831
|
ReactCodeMirror,
|
|
44519
44832
|
{
|
|
44520
44833
|
value: displayBody,
|
|
44521
|
-
height: "100%",
|
|
44522
44834
|
theme: oneDark,
|
|
44523
|
-
extensions: isJson ? [json()] : [],
|
|
44835
|
+
extensions: isJson ? [json()] : isXml ? [xml()] : [],
|
|
44524
44836
|
readOnly: true,
|
|
44525
44837
|
basicSetup: { lineNumbers: true, foldGutter: true }
|
|
44526
44838
|
}
|
|
44527
|
-
)
|
|
44839
|
+
) : tab === "headers" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-h-0 overflow-y-auto", children: /* @__PURE__ */ jsxRuntimeExports.jsx("table", { className: "w-full text-xs px-4 py-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx("tbody", { children: Object.entries(response.headers).map(([k2, v2]) => /* @__PURE__ */ jsxRuntimeExports.jsxs("tr", { className: "border-b border-surface-800", children: [
|
|
44528
44840
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1.5 px-4 text-surface-400 font-mono w-56 align-top", children: k2 }),
|
|
44529
44841
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1.5 px-4 text-white font-mono break-all", children: v2 })
|
|
44530
44842
|
] }, k2)) }) }) }) : tab === "tests" ? /* @__PURE__ */ jsxRuntimeExports.jsx(TestsPanel, { scriptResult }) : tab === "console" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ConsolePanel, { scriptResult }) : tab === "request" ? /* @__PURE__ */ jsxRuntimeExports.jsx(RequestPanel, { sentRequest }) : null })
|
|
@@ -44934,7 +45246,7 @@ function MasterKeyModal({ onSuccess, onCancel }) {
|
|
|
44934
45246
|
const [password, setPassword] = reactExports.useState("");
|
|
44935
45247
|
const [error2, setError] = reactExports.useState("");
|
|
44936
45248
|
const [copied, setCopied] = reactExports.useState(null);
|
|
44937
|
-
async function
|
|
45249
|
+
async function confirm() {
|
|
44938
45250
|
if (!password.trim()) {
|
|
44939
45251
|
setError("Password cannot be empty.");
|
|
44940
45252
|
return;
|
|
@@ -44980,7 +45292,7 @@ function MasterKeyModal({ onSuccess, onCancel }) {
|
|
|
44980
45292
|
setPassword(e.target.value);
|
|
44981
45293
|
setError("");
|
|
44982
45294
|
},
|
|
44983
|
-
onKeyDown: (e) => e.key === "Enter" &&
|
|
45295
|
+
onKeyDown: (e) => e.key === "Enter" && confirm(),
|
|
44984
45296
|
placeholder: "Enter master password…",
|
|
44985
45297
|
className: "bg-surface-800 border border-surface-700 rounded px-3 py-1.5 text-sm font-mono focus:outline-none focus:border-blue-500"
|
|
44986
45298
|
}
|
|
@@ -45016,7 +45328,7 @@ function MasterKeyModal({ onSuccess, onCancel }) {
|
|
|
45016
45328
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45017
45329
|
"button",
|
|
45018
45330
|
{
|
|
45019
|
-
onClick:
|
|
45331
|
+
onClick: confirm,
|
|
45020
45332
|
disabled: !password.trim(),
|
|
45021
45333
|
className: "px-3 py-1.5 text-xs bg-blue-600 hover:bg-blue-500 disabled:opacity-40 rounded transition-colors",
|
|
45022
45334
|
children: "Set Password & Continue"
|
|
@@ -45074,8 +45386,14 @@ function EnvironmentEditor({ onClose }) {
|
|
|
45074
45386
|
const [savedIdx, setSavedIdx] = reactExports.useState(null);
|
|
45075
45387
|
const [pendingEncryptIdx, setPendingEncryptIdx] = reactExports.useState(null);
|
|
45076
45388
|
const [nameError, setNameError] = reactExports.useState(null);
|
|
45389
|
+
const deleteEnvironment = useStore((s) => s.deleteEnvironment);
|
|
45077
45390
|
const envList = Object.values(environments);
|
|
45078
45391
|
const env = selectedId ? environments[selectedId]?.data ?? null : null;
|
|
45392
|
+
function handleDelete(id2) {
|
|
45393
|
+
deleteEnvironment(id2);
|
|
45394
|
+
const remaining = Object.keys(environments).filter((k2) => k2 !== id2);
|
|
45395
|
+
setSelectedId(remaining[0] ?? "");
|
|
45396
|
+
}
|
|
45079
45397
|
function updateVar(idx, patch) {
|
|
45080
45398
|
if (!env) return;
|
|
45081
45399
|
const vars = env.variables.map((v2, i) => i === idx ? { ...v2, ...patch } : v2);
|
|
@@ -45189,12 +45507,29 @@ function EnvironmentEditor({ onClose }) {
|
|
|
45189
45507
|
children: [
|
|
45190
45508
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-44 border-r border-surface-800 flex flex-col flex-shrink-0", children: [
|
|
45191
45509
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "px-3 py-2 text-xs font-semibold text-surface-400 uppercase tracking-wider border-b border-surface-800", children: "Environments" }),
|
|
45192
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto py-1", children: envList.map(({ data: e }) => /* @__PURE__ */ jsxRuntimeExports.
|
|
45193
|
-
"
|
|
45510
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto py-1", children: envList.map(({ data: e }) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
45511
|
+
"div",
|
|
45194
45512
|
{
|
|
45195
|
-
|
|
45196
|
-
|
|
45197
|
-
|
|
45513
|
+
className: `group flex items-center pr-1 transition-colors ${selectedId === e.id ? "bg-surface-800" : "hover:bg-surface-800"}`,
|
|
45514
|
+
children: [
|
|
45515
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45516
|
+
"button",
|
|
45517
|
+
{
|
|
45518
|
+
onClick: () => selectEnv(e.id),
|
|
45519
|
+
className: `flex-1 text-left px-3 py-1.5 text-xs truncate ${selectedId === e.id ? "text-white" : "text-surface-200"}`,
|
|
45520
|
+
children: e.name
|
|
45521
|
+
}
|
|
45522
|
+
),
|
|
45523
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45524
|
+
"button",
|
|
45525
|
+
{
|
|
45526
|
+
onClick: () => handleDelete(e.id),
|
|
45527
|
+
className: "opacity-0 group-hover:opacity-100 text-surface-400 hover:text-red-400 transition-all px-1 text-sm leading-none shrink-0",
|
|
45528
|
+
title: "Delete environment",
|
|
45529
|
+
children: "×"
|
|
45530
|
+
}
|
|
45531
|
+
)
|
|
45532
|
+
]
|
|
45198
45533
|
},
|
|
45199
45534
|
e.id
|
|
45200
45535
|
)) }),
|
|
@@ -45425,16 +45760,11 @@ function EnvironmentBar({ inline = false }) {
|
|
|
45425
45760
|
const environments = useStore((s) => s.environments);
|
|
45426
45761
|
const activeEnvironmentId = useStore((s) => s.activeEnvironmentId);
|
|
45427
45762
|
const setActiveEnvironment = useStore((s) => s.setActiveEnvironment);
|
|
45428
|
-
const addEnvironment = useStore((s) => s.addEnvironment);
|
|
45429
45763
|
const [showEditor, setShowEditor] = reactExports.useState(false);
|
|
45430
45764
|
const [pendingEnvId, setPendingEnvId] = reactExports.useState(null);
|
|
45431
45765
|
const envList = Object.values(environments);
|
|
45432
45766
|
const activeEnv = activeEnvironmentId ? environments[activeEnvironmentId]?.data : null;
|
|
45433
45767
|
const varCount = activeEnv?.variables.filter((v2) => v2.enabled).length ?? 0;
|
|
45434
|
-
function handleNew() {
|
|
45435
|
-
addEnvironment();
|
|
45436
|
-
setShowEditor(true);
|
|
45437
|
-
}
|
|
45438
45768
|
async function handleEnvChange(id2) {
|
|
45439
45769
|
if (id2) {
|
|
45440
45770
|
const hasSecrets = environments[id2]?.data.variables.some((v2) => v2.enabled && v2.secret);
|
|
@@ -45485,14 +45815,6 @@ function EnvironmentBar({ inline = false }) {
|
|
|
45485
45815
|
children: activeEnv ? "Edit" : "Manage"
|
|
45486
45816
|
}
|
|
45487
45817
|
),
|
|
45488
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45489
|
-
"button",
|
|
45490
|
-
{
|
|
45491
|
-
onClick: handleNew,
|
|
45492
|
-
className: "text-surface-400 hover:text-white transition-colors text-xs",
|
|
45493
|
-
children: "+ New"
|
|
45494
|
-
}
|
|
45495
|
-
),
|
|
45496
45818
|
showEditor && /* @__PURE__ */ jsxRuntimeExports.jsx(EnvironmentEditor, { onClose: () => setShowEditor(false) })
|
|
45497
45819
|
] });
|
|
45498
45820
|
if (inline) return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: controls });
|
|
@@ -48154,6 +48476,7 @@ function App() {
|
|
|
48154
48476
|
const sidebarTab = useStore((s) => s.sidebarTab);
|
|
48155
48477
|
const setSidebarTab = useStore((s) => s.setSidebarTab);
|
|
48156
48478
|
const historyCount = useStore((s) => s.history.length);
|
|
48479
|
+
const addCollection = useStore((s) => s.addCollection);
|
|
48157
48480
|
const addMockHit = useStore((s) => s.addMockHit);
|
|
48158
48481
|
const activeMockId = useStore((s) => s.activeMockId);
|
|
48159
48482
|
const theme2 = useStore((s) => s.theme);
|
|
@@ -48212,12 +48535,12 @@ function App() {
|
|
|
48212
48535
|
/* @__PURE__ */ jsxRuntimeExports.jsx(RunnerModal, {}),
|
|
48213
48536
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CommandPalette, {}),
|
|
48214
48537
|
docsModalOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(DocsGeneratorModal, { onClose: () => setDocsModalOpen(false) }),
|
|
48215
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "drag-region flex-shrink-0 bg-surface-950 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "no-drag text-[11px] font-medium tracking-widest select-none", style: { color: "var(--text-muted)" }, children: [
|
|
48538
|
+
window.electron.platform !== "win32" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "drag-region flex-shrink-0 bg-surface-950 flex items-center justify-center", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "no-drag text-[11px] font-medium tracking-widest select-none", style: { color: "var(--text-muted)" }, children: [
|
|
48216
48539
|
"api ",
|
|
48217
48540
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" }),
|
|
48218
48541
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2 text-[10px] font-normal opacity-50", children: [
|
|
48219
48542
|
"v",
|
|
48220
|
-
"0.0.
|
|
48543
|
+
"0.0.5"
|
|
48221
48544
|
] })
|
|
48222
48545
|
] }) }),
|
|
48223
48546
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Toolbar, { onOpenDocs: () => setDocsModalOpen(true) }),
|
|
@@ -48266,6 +48589,15 @@ function App() {
|
|
|
48266
48589
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] font-semibold uppercase tracking-widest text-surface-600", children: sidebarTab === "collections" ? "Collections" : sidebarTab === "history" ? "History" : sidebarTab === "mocks" ? "Mocks" : "Contracts" }),
|
|
48267
48590
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
48268
48591
|
sidebarTab === "history" && historyCount > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-[10px] bg-surface-700 text-surface-400 rounded px-1.5 py-0.5", children: historyCount }),
|
|
48592
|
+
sidebarTab === "collections" && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
48593
|
+
"button",
|
|
48594
|
+
{
|
|
48595
|
+
onClick: () => addCollection("New Collection"),
|
|
48596
|
+
title: "New collection",
|
|
48597
|
+
className: "text-surface-600 hover:text-surface-300 transition-colors text-sm leading-none px-0.5",
|
|
48598
|
+
children: "+"
|
|
48599
|
+
}
|
|
48600
|
+
),
|
|
48269
48601
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
48270
48602
|
"button",
|
|
48271
48603
|
{
|