@testsmith/api-spector 0.0.3 → 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-CHCrooIl.js → index-KbYng3S3.js} +460 -99
- package/out/renderer/assets/{index-B_l1FCkO.css → index-f4ppKNTS.css} +52 -2
- package/out/renderer/index.html +14 -11
- 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,
|
|
@@ -9168,18 +9213,20 @@ function IconBtn({
|
|
|
9168
9213
|
danger = false,
|
|
9169
9214
|
alwaysVisible = false
|
|
9170
9215
|
}) {
|
|
9171
|
-
return /* @__PURE__ */ jsxRuntimeExports.
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9216
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "relative group/tip", children: [
|
|
9217
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9218
|
+
"button",
|
|
9219
|
+
{
|
|
9220
|
+
onClick: (e) => {
|
|
9221
|
+
e.stopPropagation();
|
|
9222
|
+
onClick(e);
|
|
9223
|
+
},
|
|
9224
|
+
className: `px-1 py-0.5 rounded transition-all ${alwaysVisible ? "" : "opacity-0 group-hover:opacity-100"} ${danger ? "hover:text-red-400" : "hover:text-blue-400"} text-surface-400 hover:scale-150`,
|
|
9225
|
+
children
|
|
9226
|
+
}
|
|
9227
|
+
),
|
|
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 }) })
|
|
9229
|
+
] });
|
|
9183
9230
|
}
|
|
9184
9231
|
function RunBtn({ onClick }) {
|
|
9185
9232
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -9190,7 +9237,7 @@ function RunBtn({ onClick }) {
|
|
|
9190
9237
|
e.stopPropagation();
|
|
9191
9238
|
onClick(e);
|
|
9192
9239
|
},
|
|
9193
|
-
className: "
|
|
9240
|
+
className: "px-1 py-0.5 rounded text-emerald-500 hover:text-emerald-400 transition-all",
|
|
9194
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" }) })
|
|
9195
9242
|
}
|
|
9196
9243
|
);
|
|
@@ -9217,19 +9264,22 @@ function CollectionTree() {
|
|
|
9217
9264
|
const updateRequestTags = useStore((s) => s.updateRequestTags);
|
|
9218
9265
|
const openRunner = useStore((s) => s.openRunner);
|
|
9219
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
|
+
}
|
|
9220
9274
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col flex-1 min-h-0 select-none", children: [
|
|
9221
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
children: "+"
|
|
9230
|
-
}
|
|
9231
|
-
)
|
|
9232
|
-
] }),
|
|
9275
|
+
pendingConfirm && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9276
|
+
ConfirmDialog,
|
|
9277
|
+
{
|
|
9278
|
+
message: pendingConfirm.message,
|
|
9279
|
+
onConfirm: pendingConfirm.onConfirm,
|
|
9280
|
+
onCancel: () => setPendingConfirm(null)
|
|
9281
|
+
}
|
|
9282
|
+
),
|
|
9233
9283
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex-1 overflow-y-auto", children: [
|
|
9234
9284
|
colList.map(({ data: col }) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9235
9285
|
CollectionNode,
|
|
@@ -9243,13 +9293,9 @@ function CollectionTree() {
|
|
|
9243
9293
|
onAddRequest: (folderId) => addRequest(col.id, folderId),
|
|
9244
9294
|
onAddFolder: (parentId, name2) => addFolder(col.id, parentId, name2),
|
|
9245
9295
|
onRenameCollection: (name2) => renameCollection(col.id, name2),
|
|
9246
|
-
onDeleteCollection: () => {
|
|
9247
|
-
if (confirm(`Delete collection "${col.name}"?`)) deleteCollection(col.id);
|
|
9248
|
-
},
|
|
9296
|
+
onDeleteCollection: () => confirmThen(`Delete collection "${col.name}"?`, () => deleteCollection(col.id)),
|
|
9249
9297
|
onRenameFolder: (folderId, name2) => renameFolder(col.id, folderId, name2),
|
|
9250
|
-
onDeleteFolder: (folderId) =>
|
|
9251
|
-
if (confirm("Delete this folder and all its requests?")) deleteFolder(col.id, folderId);
|
|
9252
|
-
},
|
|
9298
|
+
onDeleteFolder: (folderId) => confirmThen("Delete this folder and all its requests?", () => deleteFolder(col.id, folderId)),
|
|
9253
9299
|
onRenameRequest: renameRequest,
|
|
9254
9300
|
onDeleteRequest: (reqId) => deleteRequest(col.id, reqId),
|
|
9255
9301
|
onDuplicateRequest: (reqId) => duplicateRequest(col.id, reqId),
|
|
@@ -9291,6 +9337,13 @@ function CollectionNode({
|
|
|
9291
9337
|
}) {
|
|
9292
9338
|
const [expanded, setExpanded] = reactExports.useState(true);
|
|
9293
9339
|
const [renaming, setRenaming] = reactExports.useState(false);
|
|
9340
|
+
const [expandCtrl, setExpandCtrl] = reactExports.useState({ value: true, seq: 0 });
|
|
9341
|
+
function expandAll() {
|
|
9342
|
+
setExpandCtrl((c) => ({ value: true, seq: c.seq + 1 }));
|
|
9343
|
+
}
|
|
9344
|
+
function collapseAll() {
|
|
9345
|
+
setExpandCtrl((c) => ({ value: false, seq: c.seq + 1 }));
|
|
9346
|
+
}
|
|
9294
9347
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { children: [
|
|
9295
9348
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
9296
9349
|
"div",
|
|
@@ -9315,13 +9368,15 @@ function CollectionNode({
|
|
|
9315
9368
|
validate: (v2) => existingCollectionNames.filter((n2) => n2 !== col.name).includes(v2) ? `"${v2}" already exists` : null
|
|
9316
9369
|
}
|
|
9317
9370
|
) : /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs font-semibold truncate block", children: col.name }) }),
|
|
9318
|
-
/* @__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: [
|
|
9319
9372
|
/* @__PURE__ */ jsxRuntimeExports.jsx(RunBtn, { onClick: onRunCollection }),
|
|
9320
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "
|
|
9321
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "
|
|
9322
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "
|
|
9323
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "
|
|
9324
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "
|
|
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, {}) })
|
|
9325
9380
|
] })
|
|
9326
9381
|
]
|
|
9327
9382
|
}
|
|
@@ -9334,6 +9389,7 @@ function CollectionNode({
|
|
|
9334
9389
|
requests: col.requests,
|
|
9335
9390
|
activeRequestId,
|
|
9336
9391
|
depth: 0,
|
|
9392
|
+
expandCtrl,
|
|
9337
9393
|
onSelectRequest,
|
|
9338
9394
|
onAddRequest,
|
|
9339
9395
|
onAddFolder,
|
|
@@ -9353,6 +9409,7 @@ function FolderRow({
|
|
|
9353
9409
|
folder,
|
|
9354
9410
|
collectionId,
|
|
9355
9411
|
depth,
|
|
9412
|
+
expandCtrl,
|
|
9356
9413
|
onAddRequest,
|
|
9357
9414
|
onAddFolder,
|
|
9358
9415
|
onRename,
|
|
@@ -9362,6 +9419,9 @@ function FolderRow({
|
|
|
9362
9419
|
children
|
|
9363
9420
|
}) {
|
|
9364
9421
|
const [expanded, setExpanded] = reactExports.useState(true);
|
|
9422
|
+
reactExports.useEffect(() => {
|
|
9423
|
+
if (expandCtrl.seq > 0) setExpanded(expandCtrl.value);
|
|
9424
|
+
}, [expandCtrl.seq]);
|
|
9365
9425
|
const [renaming, setRenaming] = reactExports.useState(false);
|
|
9366
9426
|
const [showSettings, setShowSettings] = reactExports.useState(false);
|
|
9367
9427
|
const tags2 = folder.tags ?? [];
|
|
@@ -9399,16 +9459,16 @@ function FolderRow({
|
|
|
9399
9459
|
}
|
|
9400
9460
|
)
|
|
9401
9461
|
] }),
|
|
9402
|
-
/* @__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: [
|
|
9403
9463
|
/* @__PURE__ */ jsxRuntimeExports.jsx(RunBtn, { onClick: onRun }),
|
|
9404
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add request", onClick: onAddRequest, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs", children: "+" }) }),
|
|
9405
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add sub-folder", onClick: onAddFolder, children: /* @__PURE__ */ jsxRuntimeExports.jsx(FolderIcon, {}) }),
|
|
9406
|
-
/* @__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, {}) }),
|
|
9407
9467
|
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Add tag", onClick: () => {
|
|
9408
|
-
}, alwaysVisible:
|
|
9468
|
+
}, alwaysVisible: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(TagChips, { tags: [], onRemove: () => {
|
|
9409
9469
|
}, onAdd: (tag) => onUpdateTags([...tags2, tag]) }) }),
|
|
9410
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(IconBtn, { title: "Rename", onClick: () => setRenaming(true), children: /* @__PURE__ */ jsxRuntimeExports.jsx(PencilIcon, {}) }),
|
|
9411
|
-
/* @__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, {}) })
|
|
9412
9472
|
] })
|
|
9413
9473
|
]
|
|
9414
9474
|
}
|
|
@@ -9430,6 +9490,7 @@ function FolderContents({
|
|
|
9430
9490
|
requests,
|
|
9431
9491
|
activeRequestId,
|
|
9432
9492
|
depth,
|
|
9493
|
+
expandCtrl,
|
|
9433
9494
|
onSelectRequest,
|
|
9434
9495
|
onAddRequest,
|
|
9435
9496
|
onAddFolder,
|
|
@@ -9449,6 +9510,7 @@ function FolderContents({
|
|
|
9449
9510
|
folder: sub,
|
|
9450
9511
|
collectionId,
|
|
9451
9512
|
depth: depth + 1,
|
|
9513
|
+
expandCtrl,
|
|
9452
9514
|
onAddRequest: () => onAddRequest(sub.id),
|
|
9453
9515
|
onAddFolder: () => onAddFolder(sub.id, "New Folder"),
|
|
9454
9516
|
onRename: (name2) => onRenameFolder(sub.id, name2),
|
|
@@ -9463,6 +9525,7 @@ function FolderContents({
|
|
|
9463
9525
|
requests,
|
|
9464
9526
|
activeRequestId,
|
|
9465
9527
|
depth: depth + 1,
|
|
9528
|
+
expandCtrl,
|
|
9466
9529
|
onSelectRequest,
|
|
9467
9530
|
onAddRequest,
|
|
9468
9531
|
onAddFolder,
|
|
@@ -9576,6 +9639,12 @@ function CopyIcon() {
|
|
|
9576
9639
|
function KeyIcon() {
|
|
9577
9640
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M15 7a4 4 0 110 8 4 4 0 010-8zm-7 8l-1 1m0 0l-1 1m1-1l1 1M3 20l5-5" }) });
|
|
9578
9641
|
}
|
|
9642
|
+
function ExpandAllIcon() {
|
|
9643
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4 6h16M4 12h8M4 18h16M15 15l3 3 3-3" }) });
|
|
9644
|
+
}
|
|
9645
|
+
function CollapseAllIcon() {
|
|
9646
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("svg", { className: "w-3 h-3", fill: "none", stroke: "currentColor", strokeWidth: 2, viewBox: "0 0 24 24", children: /* @__PURE__ */ jsxRuntimeExports.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M4 6h16M4 12h8M4 18h16M21 12l-3-3-3 3" }) });
|
|
9647
|
+
}
|
|
9579
9648
|
function ParamsTab({ request, onChange }) {
|
|
9580
9649
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
9581
9650
|
KVTable,
|
|
@@ -36952,6 +37021,24 @@ sp.variables.set("field_value", json.field);`
|
|
|
36952
37021
|
label: "Save JSON field to environment",
|
|
36953
37022
|
code: `const json = sp.response.json();
|
|
36954
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") ?? '');`
|
|
36955
37042
|
}
|
|
36956
37043
|
]
|
|
36957
37044
|
}
|
|
@@ -36965,6 +37052,7 @@ function ScriptsTab({ request, onChange }) {
|
|
|
36965
37052
|
if (activeTabId) setTabScriptTab(activeTabId, t2);
|
|
36966
37053
|
};
|
|
36967
37054
|
const [expandedGroup, setExpandedGroup] = reactExports.useState(SNIPPET_GROUPS[0].group);
|
|
37055
|
+
const [snippetsOpen, setSnippetsOpen] = reactExports.useState(true);
|
|
36968
37056
|
const varNames = useVarNames();
|
|
36969
37057
|
const varValues = useVarValues();
|
|
36970
37058
|
const extensions = reactExports.useMemo(
|
|
@@ -37005,9 +37093,27 @@ function ScriptsTab({ request, onChange }) {
|
|
|
37005
37093
|
}
|
|
37006
37094
|
) })
|
|
37007
37095
|
] }),
|
|
37008
|
-
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className:
|
|
37009
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
37010
|
-
|
|
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: [
|
|
37011
37117
|
/* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
37012
37118
|
"button",
|
|
37013
37119
|
{
|
|
@@ -43782,36 +43888,82 @@ function makeJsonSnippet(path, value, mode) {
|
|
|
43782
43888
|
});`;
|
|
43783
43889
|
}
|
|
43784
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
|
+
}
|
|
43785
43923
|
function makeXmlSnippet(selector, value, mode) {
|
|
43786
|
-
const
|
|
43787
|
-
const query = `const el = doc.querySelector("${selector.replace(/"/g, '\\"')}");`;
|
|
43924
|
+
const sel = selector.replace(/"/g, '\\"');
|
|
43788
43925
|
switch (mode) {
|
|
43789
43926
|
case "equals":
|
|
43790
43927
|
return `sp.test('${selector} equals "${esc(value)}"', function() {
|
|
43791
|
-
${
|
|
43792
|
-
${query}
|
|
43793
|
-
sp.expect(el?.textContent?.trim()).to.equal("${esc(value)}");
|
|
43928
|
+
sp.expect(sp.response.xmlText("${sel}")).to.equal("${esc(value)}");
|
|
43794
43929
|
});`;
|
|
43795
43930
|
case "exists":
|
|
43796
43931
|
return `sp.test('${selector} exists', function() {
|
|
43797
|
-
${
|
|
43798
|
-
${query}
|
|
43799
|
-
sp.expect(el).to.not.equal(null);
|
|
43932
|
+
sp.expect(sp.response.xmlText("${sel}")).to.not.equal(null);
|
|
43800
43933
|
});`;
|
|
43801
43934
|
case "contains":
|
|
43802
43935
|
return `sp.test('${selector} contains "${esc(value)}"', function() {
|
|
43803
|
-
${
|
|
43804
|
-
${query}
|
|
43805
|
-
sp.expect(el?.textContent).to.include("${esc(value)}");
|
|
43936
|
+
sp.expect(sp.response.xmlText("${sel}")).to.include("${esc(value)}");
|
|
43806
43937
|
});`;
|
|
43807
43938
|
}
|
|
43808
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
|
+
}
|
|
43809
43958
|
function AssertMenu({
|
|
43810
43959
|
state,
|
|
43811
43960
|
onClose,
|
|
43812
43961
|
onConfirm
|
|
43813
43962
|
}) {
|
|
43814
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("");
|
|
43815
43967
|
reactExports.useEffect(() => {
|
|
43816
43968
|
function onMouse(e) {
|
|
43817
43969
|
if (ref2.current && !ref2.current.contains(e.target)) onClose();
|
|
@@ -43828,8 +43980,10 @@ function AssertMenu({
|
|
|
43828
43980
|
}, [onClose]);
|
|
43829
43981
|
let title2 = "";
|
|
43830
43982
|
let options = [];
|
|
43983
|
+
let jpSiblingKeys = [];
|
|
43984
|
+
let jpAvailable = false;
|
|
43831
43985
|
if (state.type === "json") {
|
|
43832
|
-
const { path, value } = state;
|
|
43986
|
+
const { path, value, root } = state;
|
|
43833
43987
|
const isStr = typeof value === "string";
|
|
43834
43988
|
const preview = isStr ? `"${value.length > 22 ? value.slice(0, 22) + "…" : value}"` : String(value);
|
|
43835
43989
|
title2 = jsonPathLabel(path);
|
|
@@ -43839,6 +43993,26 @@ function AssertMenu({
|
|
|
43839
43993
|
{ label: `is ${value === null ? "null" : typeof value}`, snippet: makeJsonSnippet(path, value, "type") },
|
|
43840
43994
|
...isStr ? [{ label: `contains ${preview}`, snippet: makeJsonSnippet(path, value, "contains") }] : []
|
|
43841
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
|
+
}
|
|
43842
44016
|
} else {
|
|
43843
44017
|
const { selector, value } = state;
|
|
43844
44018
|
const preview = `"${value.length > 22 ? value.slice(0, 22) + "…" : value}"`;
|
|
@@ -43849,14 +44023,14 @@ function AssertMenu({
|
|
|
43849
44023
|
{ label: `contains ${preview}`, snippet: makeXmlSnippet(selector, value, "contains") }
|
|
43850
44024
|
];
|
|
43851
44025
|
}
|
|
43852
|
-
const x2 = Math.min(state.x, window.innerWidth -
|
|
43853
|
-
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);
|
|
43854
44028
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
43855
44029
|
"div",
|
|
43856
44030
|
{
|
|
43857
44031
|
ref: ref2,
|
|
43858
44032
|
style: { top: y2, left: x2, position: "fixed" },
|
|
43859
|
-
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]",
|
|
43860
44034
|
children: [
|
|
43861
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 }),
|
|
43862
44036
|
options.map((opt) => /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -43870,7 +44044,141 @@ function AssertMenu({
|
|
|
43870
44044
|
children: opt.label
|
|
43871
44045
|
},
|
|
43872
44046
|
opt.label
|
|
43873
|
-
))
|
|
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
|
+
] })
|
|
43874
44182
|
]
|
|
43875
44183
|
}
|
|
43876
44184
|
);
|
|
@@ -43899,7 +44207,7 @@ function JsonNode({
|
|
|
43899
44207
|
onClick: (e) => onLeaf(e, path, value),
|
|
43900
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",
|
|
43901
44209
|
title: "Add assertion for this value",
|
|
43902
|
-
children: "+
|
|
44210
|
+
children: "+ insert"
|
|
43903
44211
|
}
|
|
43904
44212
|
)
|
|
43905
44213
|
] });
|
|
@@ -43970,7 +44278,7 @@ function XmlNode({
|
|
|
43970
44278
|
onClick: (e) => onLeaf(e, selector, text),
|
|
43971
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",
|
|
43972
44280
|
title: "Add assertion for this value",
|
|
43973
|
-
children: "+
|
|
44281
|
+
children: "+ insert"
|
|
43974
44282
|
}
|
|
43975
44283
|
)
|
|
43976
44284
|
] });
|
|
@@ -43999,22 +44307,26 @@ function InteractiveBody({ body, contentType, onAssert }) {
|
|
|
43999
44307
|
const [popover, setPopover] = reactExports.useState(null);
|
|
44000
44308
|
const isJson = contentType.includes("json");
|
|
44001
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
|
+
}
|
|
44002
44317
|
function handleJsonLeaf(e, path, value) {
|
|
44003
44318
|
e.stopPropagation();
|
|
44004
|
-
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 });
|
|
44005
44320
|
}
|
|
44006
44321
|
function handleXmlLeaf(e, selector, value) {
|
|
44007
44322
|
e.stopPropagation();
|
|
44008
44323
|
setPopover({ type: "xml", selector, value, x: e.clientX + 10, y: e.clientY + 10 });
|
|
44009
44324
|
}
|
|
44010
44325
|
const treeContent = isJson ? (() => {
|
|
44011
|
-
|
|
44012
|
-
try {
|
|
44013
|
-
parsed = JSON.parse(body);
|
|
44014
|
-
} catch {
|
|
44326
|
+
if (parsedJson === null) {
|
|
44015
44327
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "p-4 text-xs text-surface-600", children: "Unable to parse JSON response body" });
|
|
44016
44328
|
}
|
|
44017
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx(JsonNode, { nodeKey: null, value:
|
|
44329
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(JsonNode, { nodeKey: null, value: parsedJson, path: [], depth: 0, onLeaf: handleJsonLeaf });
|
|
44018
44330
|
})() : isXml ? (() => {
|
|
44019
44331
|
const doc2 = new DOMParser().parseFromString(body, "text/xml");
|
|
44020
44332
|
const root = doc2.documentElement;
|
|
@@ -44046,6 +44358,32 @@ function prettyJson(raw) {
|
|
|
44046
44358
|
return raw;
|
|
44047
44359
|
}
|
|
44048
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
|
+
}
|
|
44049
44387
|
function SaveAsMockModal({ onClose }) {
|
|
44050
44388
|
const mocks = useStore((s) => s.mocks);
|
|
44051
44389
|
const addMock = useStore((s) => s.addMock);
|
|
@@ -44381,7 +44719,7 @@ function ResponseViewer() {
|
|
|
44381
44719
|
const isJson = contentType.includes("json");
|
|
44382
44720
|
const isXml = !isJson && (contentType.includes("xml") || contentType.includes("html"));
|
|
44383
44721
|
const supportsTree = isJson || isXml;
|
|
44384
|
-
const displayBody = isJson ? prettyJson(response.body) : response.body;
|
|
44722
|
+
const displayBody = isJson ? prettyJson(response.body) : isXml ? prettyXml(response.body) : response.body;
|
|
44385
44723
|
const passedCount = scriptResult?.testResults.filter((t2) => t2.passed).length ?? 0;
|
|
44386
44724
|
const totalCount = scriptResult?.testResults.length ?? 0;
|
|
44387
44725
|
const consoleCount = scriptResult?.consoleOutput.length ?? 0;
|
|
@@ -44489,17 +44827,16 @@ function ResponseViewer() {
|
|
|
44489
44827
|
contentType,
|
|
44490
44828
|
onAssert: handleAssert
|
|
44491
44829
|
}
|
|
44492
|
-
) : tab === "body" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44830
|
+
) : tab === "body" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44493
44831
|
ReactCodeMirror,
|
|
44494
44832
|
{
|
|
44495
44833
|
value: displayBody,
|
|
44496
|
-
height: "100%",
|
|
44497
44834
|
theme: oneDark,
|
|
44498
|
-
extensions: isJson ? [json()] : [],
|
|
44835
|
+
extensions: isJson ? [json()] : isXml ? [xml()] : [],
|
|
44499
44836
|
readOnly: true,
|
|
44500
44837
|
basicSetup: { lineNumbers: true, foldGutter: true }
|
|
44501
44838
|
}
|
|
44502
|
-
)
|
|
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: [
|
|
44503
44840
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1.5 px-4 text-surface-400 font-mono w-56 align-top", children: k2 }),
|
|
44504
44841
|
/* @__PURE__ */ jsxRuntimeExports.jsx("td", { className: "py-1.5 px-4 text-white font-mono break-all", children: v2 })
|
|
44505
44842
|
] }, k2)) }) }) }) : tab === "tests" ? /* @__PURE__ */ jsxRuntimeExports.jsx(TestsPanel, { scriptResult }) : tab === "console" ? /* @__PURE__ */ jsxRuntimeExports.jsx(ConsolePanel, { scriptResult }) : tab === "request" ? /* @__PURE__ */ jsxRuntimeExports.jsx(RequestPanel, { sentRequest }) : null })
|
|
@@ -44909,7 +45246,7 @@ function MasterKeyModal({ onSuccess, onCancel }) {
|
|
|
44909
45246
|
const [password, setPassword] = reactExports.useState("");
|
|
44910
45247
|
const [error2, setError] = reactExports.useState("");
|
|
44911
45248
|
const [copied, setCopied] = reactExports.useState(null);
|
|
44912
|
-
async function
|
|
45249
|
+
async function confirm() {
|
|
44913
45250
|
if (!password.trim()) {
|
|
44914
45251
|
setError("Password cannot be empty.");
|
|
44915
45252
|
return;
|
|
@@ -44955,7 +45292,7 @@ function MasterKeyModal({ onSuccess, onCancel }) {
|
|
|
44955
45292
|
setPassword(e.target.value);
|
|
44956
45293
|
setError("");
|
|
44957
45294
|
},
|
|
44958
|
-
onKeyDown: (e) => e.key === "Enter" &&
|
|
45295
|
+
onKeyDown: (e) => e.key === "Enter" && confirm(),
|
|
44959
45296
|
placeholder: "Enter master password…",
|
|
44960
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"
|
|
44961
45298
|
}
|
|
@@ -44991,7 +45328,7 @@ function MasterKeyModal({ onSuccess, onCancel }) {
|
|
|
44991
45328
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
44992
45329
|
"button",
|
|
44993
45330
|
{
|
|
44994
|
-
onClick:
|
|
45331
|
+
onClick: confirm,
|
|
44995
45332
|
disabled: !password.trim(),
|
|
44996
45333
|
className: "px-3 py-1.5 text-xs bg-blue-600 hover:bg-blue-500 disabled:opacity-40 rounded transition-colors",
|
|
44997
45334
|
children: "Set Password & Continue"
|
|
@@ -45049,8 +45386,14 @@ function EnvironmentEditor({ onClose }) {
|
|
|
45049
45386
|
const [savedIdx, setSavedIdx] = reactExports.useState(null);
|
|
45050
45387
|
const [pendingEncryptIdx, setPendingEncryptIdx] = reactExports.useState(null);
|
|
45051
45388
|
const [nameError, setNameError] = reactExports.useState(null);
|
|
45389
|
+
const deleteEnvironment = useStore((s) => s.deleteEnvironment);
|
|
45052
45390
|
const envList = Object.values(environments);
|
|
45053
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
|
+
}
|
|
45054
45397
|
function updateVar(idx, patch) {
|
|
45055
45398
|
if (!env) return;
|
|
45056
45399
|
const vars = env.variables.map((v2, i) => i === idx ? { ...v2, ...patch } : v2);
|
|
@@ -45164,12 +45507,29 @@ function EnvironmentEditor({ onClose }) {
|
|
|
45164
45507
|
children: [
|
|
45165
45508
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "w-44 border-r border-surface-800 flex flex-col flex-shrink-0", children: [
|
|
45166
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" }),
|
|
45167
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto py-1", children: envList.map(({ data: e }) => /* @__PURE__ */ jsxRuntimeExports.
|
|
45168
|
-
"
|
|
45510
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 overflow-y-auto py-1", children: envList.map(({ data: e }) => /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
45511
|
+
"div",
|
|
45169
45512
|
{
|
|
45170
|
-
|
|
45171
|
-
|
|
45172
|
-
|
|
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
|
+
]
|
|
45173
45533
|
},
|
|
45174
45534
|
e.id
|
|
45175
45535
|
)) }),
|
|
@@ -45400,16 +45760,11 @@ function EnvironmentBar({ inline = false }) {
|
|
|
45400
45760
|
const environments = useStore((s) => s.environments);
|
|
45401
45761
|
const activeEnvironmentId = useStore((s) => s.activeEnvironmentId);
|
|
45402
45762
|
const setActiveEnvironment = useStore((s) => s.setActiveEnvironment);
|
|
45403
|
-
const addEnvironment = useStore((s) => s.addEnvironment);
|
|
45404
45763
|
const [showEditor, setShowEditor] = reactExports.useState(false);
|
|
45405
45764
|
const [pendingEnvId, setPendingEnvId] = reactExports.useState(null);
|
|
45406
45765
|
const envList = Object.values(environments);
|
|
45407
45766
|
const activeEnv = activeEnvironmentId ? environments[activeEnvironmentId]?.data : null;
|
|
45408
45767
|
const varCount = activeEnv?.variables.filter((v2) => v2.enabled).length ?? 0;
|
|
45409
|
-
function handleNew() {
|
|
45410
|
-
addEnvironment();
|
|
45411
|
-
setShowEditor(true);
|
|
45412
|
-
}
|
|
45413
45768
|
async function handleEnvChange(id2) {
|
|
45414
45769
|
if (id2) {
|
|
45415
45770
|
const hasSecrets = environments[id2]?.data.variables.some((v2) => v2.enabled && v2.secret);
|
|
@@ -45460,14 +45815,6 @@ function EnvironmentBar({ inline = false }) {
|
|
|
45460
45815
|
children: activeEnv ? "Edit" : "Manage"
|
|
45461
45816
|
}
|
|
45462
45817
|
),
|
|
45463
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
45464
|
-
"button",
|
|
45465
|
-
{
|
|
45466
|
-
onClick: handleNew,
|
|
45467
|
-
className: "text-surface-400 hover:text-white transition-colors text-xs",
|
|
45468
|
-
children: "+ New"
|
|
45469
|
-
}
|
|
45470
|
-
),
|
|
45471
45818
|
showEditor && /* @__PURE__ */ jsxRuntimeExports.jsx(EnvironmentEditor, { onClose: () => setShowEditor(false) })
|
|
45472
45819
|
] });
|
|
45473
45820
|
if (inline) return /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: controls });
|
|
@@ -48129,6 +48476,7 @@ function App() {
|
|
|
48129
48476
|
const sidebarTab = useStore((s) => s.sidebarTab);
|
|
48130
48477
|
const setSidebarTab = useStore((s) => s.setSidebarTab);
|
|
48131
48478
|
const historyCount = useStore((s) => s.history.length);
|
|
48479
|
+
const addCollection = useStore((s) => s.addCollection);
|
|
48132
48480
|
const addMockHit = useStore((s) => s.addMockHit);
|
|
48133
48481
|
const activeMockId = useStore((s) => s.activeMockId);
|
|
48134
48482
|
const theme2 = useStore((s) => s.theme);
|
|
@@ -48187,9 +48535,13 @@ function App() {
|
|
|
48187
48535
|
/* @__PURE__ */ jsxRuntimeExports.jsx(RunnerModal, {}),
|
|
48188
48536
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CommandPalette, {}),
|
|
48189
48537
|
docsModalOpen && /* @__PURE__ */ jsxRuntimeExports.jsx(DocsGeneratorModal, { onClose: () => setDocsModalOpen(false) }),
|
|
48190
|
-
/* @__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: [
|
|
48191
48539
|
"api ",
|
|
48192
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" })
|
|
48540
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { style: { color: "#6aa3c8" }, children: "Spector" }),
|
|
48541
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "ml-2 text-[10px] font-normal opacity-50", children: [
|
|
48542
|
+
"v",
|
|
48543
|
+
"0.0.5"
|
|
48544
|
+
] })
|
|
48193
48545
|
] }) }),
|
|
48194
48546
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Toolbar, { onOpenDocs: () => setDocsModalOpen(true) }),
|
|
48195
48547
|
workspace ? /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-1 min-h-0", children: [
|
|
@@ -48237,6 +48589,15 @@ function App() {
|
|
|
48237
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" }),
|
|
48238
48590
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1.5", children: [
|
|
48239
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
|
+
),
|
|
48240
48601
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
48241
48602
|
"button",
|
|
48242
48603
|
{
|