norn-cli 1.5.4 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +38 -0
- package/README.md +35 -0
- package/dist/cli.js +1293 -1123
- package/package.json +72 -2
- package/.norn-cache/secret-keys.json +0 -9
- package/.norn-cache/swagger-body-intellisense.json +0 -409
- package/.norn-cache/validation-results.json +0 -47
package/dist/cli.js
CHANGED
|
@@ -28203,7 +28203,7 @@ function getValueByPath(response, path10) {
|
|
|
28203
28203
|
return void 0;
|
|
28204
28204
|
}
|
|
28205
28205
|
}
|
|
28206
|
-
async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, workingDir, fullDocumentText, onProgress, callStack, sequenceArgs, apiDefinitions, tagFilterOptions, sequenceSources, executionContext) {
|
|
28206
|
+
async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, workingDir, fullDocumentText, onProgress, callStack, sequenceArgs, apiDefinitions, tagFilterOptions, sequenceSources, executionContext, debugHooks) {
|
|
28207
28207
|
const startTime = Date.now();
|
|
28208
28208
|
const responses = [];
|
|
28209
28209
|
const scriptResults = [];
|
|
@@ -28246,566 +28246,987 @@ async function runSequenceWithJar(sequenceContent, fileVariables, cookieJar, wor
|
|
|
28246
28246
|
let requestIndex = 0;
|
|
28247
28247
|
const ifStack = [];
|
|
28248
28248
|
const shouldSkip = () => ifStack.length > 0 && ifStack.some((v) => !v);
|
|
28249
|
-
|
|
28250
|
-
const
|
|
28251
|
-
|
|
28252
|
-
|
|
28253
|
-
|
|
28254
|
-
|
|
28255
|
-
|
|
28256
|
-
|
|
28257
|
-
|
|
28258
|
-
|
|
28259
|
-
|
|
28260
|
-
|
|
28261
|
-
|
|
28262
|
-
|
|
28263
|
-
|
|
28264
|
-
|
|
28265
|
-
orderedSteps.push(stepResult);
|
|
28266
|
-
reportProgress(stepIdx, "if", `if ${step.content} \u2192 ${conditionMet}`, stepResult);
|
|
28267
|
-
}
|
|
28268
|
-
continue;
|
|
28269
|
-
}
|
|
28270
|
-
if (step.type === "endif") {
|
|
28271
|
-
if (ifStack.length > 0) {
|
|
28272
|
-
ifStack.pop();
|
|
28273
|
-
}
|
|
28274
|
-
continue;
|
|
28249
|
+
const resolveDebugStepLocation = (step) => {
|
|
28250
|
+
const sequenceName = executionContext?.sequenceName;
|
|
28251
|
+
const indexed = sequenceName ? executionContext?.sequenceLocationIndex?.get(sequenceName.toLowerCase()) : void 0;
|
|
28252
|
+
const startLine = executionContext?.sequenceStartLine ?? indexed?.startLine;
|
|
28253
|
+
return {
|
|
28254
|
+
filePath: executionContext?.filePath ?? indexed?.filePath,
|
|
28255
|
+
sequenceName,
|
|
28256
|
+
declarationLine: indexed?.declarationLine ?? executionContext?.sequenceStartLine,
|
|
28257
|
+
absoluteLine: startLine !== void 0 ? startLine + 1 + step.lineNumber : void 0,
|
|
28258
|
+
relativeLine: step.lineNumber,
|
|
28259
|
+
nestingDepth
|
|
28260
|
+
};
|
|
28261
|
+
};
|
|
28262
|
+
const emitBeforeStep = async (step, stepIdx) => {
|
|
28263
|
+
if (!debugHooks?.beforeStep) {
|
|
28264
|
+
return;
|
|
28275
28265
|
}
|
|
28276
|
-
|
|
28277
|
-
|
|
28266
|
+
await debugHooks.beforeStep({
|
|
28267
|
+
stepType: step.type,
|
|
28268
|
+
stepContent: step.content,
|
|
28269
|
+
stepIndex: stepIdx,
|
|
28270
|
+
totalSteps,
|
|
28271
|
+
location: resolveDebugStepLocation(step),
|
|
28272
|
+
runtimeVariables,
|
|
28273
|
+
responses
|
|
28274
|
+
});
|
|
28275
|
+
};
|
|
28276
|
+
const emitAfterStep = async (step, stepIdx) => {
|
|
28277
|
+
if (!debugHooks?.afterStep) {
|
|
28278
|
+
return;
|
|
28278
28279
|
}
|
|
28279
|
-
|
|
28280
|
-
|
|
28281
|
-
|
|
28282
|
-
|
|
28283
|
-
|
|
28284
|
-
|
|
28285
|
-
|
|
28286
|
-
|
|
28287
|
-
|
|
28288
|
-
|
|
28289
|
-
|
|
28290
|
-
|
|
28291
|
-
|
|
28292
|
-
};
|
|
28293
|
-
}
|
|
28294
|
-
const result = evaluateAssertion(parsed, responses, runtimeVariables, getValueByPath, responseIndexToVariable, workingDir);
|
|
28295
|
-
assertionResults.push(result);
|
|
28296
|
-
const stepResult = {
|
|
28297
|
-
type: "assertion",
|
|
28298
|
-
stepIndex: stepIdx,
|
|
28299
|
-
assertion: result,
|
|
28300
|
-
lineNumber: step.lineNumber
|
|
28301
|
-
};
|
|
28302
|
-
orderedSteps.push(stepResult);
|
|
28303
|
-
const statusIcon = result.passed ? "\u2713" : "\u2717";
|
|
28304
|
-
reportProgress(stepIdx, "assertion", `${statusIcon} assert ${result.expression}`, stepResult);
|
|
28305
|
-
if (!result.passed) {
|
|
28306
|
-
const failMessage = result.message ? `Assertion failed: ${result.message}` : `Assertion failed: ${result.expression}`;
|
|
28307
|
-
errors.push(failMessage);
|
|
28308
|
-
if (result.error) {
|
|
28309
|
-
errors.push(` Error: ${result.error}`);
|
|
28310
|
-
} else {
|
|
28311
|
-
errors.push(` Expected: ${result.rightExpression ?? result.operator}`);
|
|
28312
|
-
errors.push(` Actual: ${JSON.stringify(result.leftValue)}`);
|
|
28313
|
-
}
|
|
28314
|
-
return {
|
|
28315
|
-
name: "",
|
|
28316
|
-
success: false,
|
|
28317
|
-
responses,
|
|
28318
|
-
scriptResults,
|
|
28319
|
-
assertionResults,
|
|
28320
|
-
steps: orderedSteps,
|
|
28321
|
-
errors,
|
|
28322
|
-
duration: Date.now() - startTime
|
|
28323
|
-
};
|
|
28324
|
-
}
|
|
28325
|
-
continue;
|
|
28280
|
+
await debugHooks.afterStep({
|
|
28281
|
+
stepType: step.type,
|
|
28282
|
+
stepContent: step.content,
|
|
28283
|
+
stepIndex: stepIdx,
|
|
28284
|
+
totalSteps,
|
|
28285
|
+
location: resolveDebugStepLocation(step),
|
|
28286
|
+
runtimeVariables,
|
|
28287
|
+
responses
|
|
28288
|
+
});
|
|
28289
|
+
};
|
|
28290
|
+
const emitFailure = async (message, step, stepIdx) => {
|
|
28291
|
+
if (!debugHooks?.onFailure) {
|
|
28292
|
+
return;
|
|
28326
28293
|
}
|
|
28327
|
-
|
|
28328
|
-
|
|
28329
|
-
|
|
28330
|
-
|
|
28331
|
-
|
|
28332
|
-
|
|
28333
|
-
|
|
28334
|
-
|
|
28335
|
-
|
|
28336
|
-
|
|
28337
|
-
|
|
28338
|
-
|
|
28339
|
-
|
|
28294
|
+
await debugHooks.onFailure({
|
|
28295
|
+
message,
|
|
28296
|
+
stepIndex: stepIdx,
|
|
28297
|
+
location: step ? resolveDebugStepLocation(step) : void 0,
|
|
28298
|
+
runtimeVariables,
|
|
28299
|
+
responses
|
|
28300
|
+
});
|
|
28301
|
+
};
|
|
28302
|
+
if (debugHooks?.onSequenceEnter) {
|
|
28303
|
+
await debugHooks.onSequenceEnter({
|
|
28304
|
+
sequenceName: executionContext?.sequenceName,
|
|
28305
|
+
filePath: executionContext?.filePath,
|
|
28306
|
+
declarationLine: executionContext?.sequenceStartLine,
|
|
28307
|
+
nestingDepth,
|
|
28308
|
+
runtimeVariables,
|
|
28309
|
+
responses
|
|
28310
|
+
});
|
|
28311
|
+
}
|
|
28312
|
+
try {
|
|
28313
|
+
for (let stepIdx = 0; stepIdx < steps.length; stepIdx++) {
|
|
28314
|
+
const step = steps[stepIdx];
|
|
28315
|
+
await emitBeforeStep(step, stepIdx);
|
|
28316
|
+
try {
|
|
28317
|
+
if (step.type === "if") {
|
|
28318
|
+
if (shouldSkip()) {
|
|
28319
|
+
ifStack.push(false);
|
|
28320
|
+
} else {
|
|
28321
|
+
const conditionMet = evaluateIfCondition(step.content, responses, runtimeVariables, getValueByPath);
|
|
28322
|
+
ifStack.push(conditionMet);
|
|
28323
|
+
const stepResult = {
|
|
28324
|
+
type: "print",
|
|
28325
|
+
stepIndex: stepIdx,
|
|
28326
|
+
print: {
|
|
28327
|
+
title: `if ${step.content}: ${conditionMet ? "true" : "false"}`,
|
|
28328
|
+
timestamp: Date.now() - startTime
|
|
28329
|
+
}
|
|
28330
|
+
};
|
|
28331
|
+
orderedSteps.push(stepResult);
|
|
28332
|
+
reportProgress(stepIdx, "if", `if ${step.content} \u2192 ${conditionMet}`, stepResult);
|
|
28333
|
+
}
|
|
28334
|
+
continue;
|
|
28340
28335
|
}
|
|
28341
|
-
|
|
28342
|
-
|
|
28343
|
-
|
|
28344
|
-
|
|
28345
|
-
|
|
28346
|
-
if (step.type === "wait") {
|
|
28347
|
-
const durationMs = parseWaitCommand(step.content);
|
|
28348
|
-
reportProgress(stepIdx, "wait", `wait: ${durationMs}ms`);
|
|
28349
|
-
await sleep2(durationMs);
|
|
28350
|
-
const stepResult = {
|
|
28351
|
-
type: "print",
|
|
28352
|
-
stepIndex: stepIdx,
|
|
28353
|
-
print: {
|
|
28354
|
-
title: `Waited ${durationMs >= 1e3 ? durationMs / 1e3 + "s" : durationMs + "ms"}`,
|
|
28355
|
-
timestamp: Date.now() - startTime
|
|
28336
|
+
if (step.type === "endif") {
|
|
28337
|
+
if (ifStack.length > 0) {
|
|
28338
|
+
ifStack.pop();
|
|
28339
|
+
}
|
|
28340
|
+
continue;
|
|
28356
28341
|
}
|
|
28357
|
-
|
|
28358
|
-
|
|
28359
|
-
continue;
|
|
28360
|
-
}
|
|
28361
|
-
if (step.type === "json") {
|
|
28362
|
-
const bareResolved = resolveBareVariables(step.content, runtimeVariables);
|
|
28363
|
-
const parsed = parseJsonCommand(substituteVariables(bareResolved, runtimeVariables));
|
|
28364
|
-
if (!parsed) {
|
|
28365
|
-
errors.push(`Step ${stepIdx + 1}: Invalid json command: ${step.content}`);
|
|
28366
|
-
return {
|
|
28367
|
-
name: "",
|
|
28368
|
-
success: false,
|
|
28369
|
-
responses,
|
|
28370
|
-
scriptResults,
|
|
28371
|
-
assertionResults,
|
|
28372
|
-
steps: orderedSteps,
|
|
28373
|
-
errors,
|
|
28374
|
-
duration: Date.now() - startTime
|
|
28375
|
-
};
|
|
28376
|
-
}
|
|
28377
|
-
const result = readJsonFile(parsed.filePath, workingDir);
|
|
28378
|
-
const stepResult = {
|
|
28379
|
-
type: "json",
|
|
28380
|
-
stepIndex: stepIdx,
|
|
28381
|
-
json: {
|
|
28382
|
-
varName: parsed.varName,
|
|
28383
|
-
filePath: result.filePath,
|
|
28384
|
-
success: result.success,
|
|
28385
|
-
error: result.error,
|
|
28386
|
-
timestamp: Date.now() - startTime
|
|
28342
|
+
if (shouldSkip()) {
|
|
28343
|
+
continue;
|
|
28387
28344
|
}
|
|
28388
|
-
|
|
28389
|
-
|
|
28390
|
-
|
|
28391
|
-
|
|
28392
|
-
|
|
28393
|
-
|
|
28394
|
-
|
|
28395
|
-
|
|
28396
|
-
|
|
28397
|
-
|
|
28398
|
-
|
|
28399
|
-
|
|
28400
|
-
|
|
28401
|
-
|
|
28402
|
-
|
|
28403
|
-
|
|
28404
|
-
|
|
28405
|
-
|
|
28406
|
-
|
|
28407
|
-
|
|
28408
|
-
|
|
28409
|
-
|
|
28410
|
-
|
|
28411
|
-
|
|
28412
|
-
|
|
28413
|
-
|
|
28414
|
-
|
|
28415
|
-
|
|
28416
|
-
|
|
28417
|
-
|
|
28418
|
-
|
|
28419
|
-
|
|
28420
|
-
|
|
28421
|
-
|
|
28422
|
-
|
|
28423
|
-
|
|
28424
|
-
errors.push(`Step ${stepIdx + 1}: Variable '${parsed.varName}' is not defined`);
|
|
28425
|
-
return {
|
|
28426
|
-
name: "",
|
|
28427
|
-
success: false,
|
|
28428
|
-
responses,
|
|
28429
|
-
scriptResults,
|
|
28430
|
-
assertionResults,
|
|
28431
|
-
steps: orderedSteps,
|
|
28432
|
-
errors,
|
|
28433
|
-
duration: Date.now() - startTime
|
|
28434
|
-
};
|
|
28435
|
-
}
|
|
28436
|
-
let jsonObj;
|
|
28437
|
-
try {
|
|
28438
|
-
jsonObj = JSON.parse(runtimeVariables[parsed.varName]);
|
|
28439
|
-
} catch {
|
|
28440
|
-
errors.push(`Step ${stepIdx + 1}: Variable '${parsed.varName}' is not a valid JSON object`);
|
|
28441
|
-
return {
|
|
28442
|
-
name: "",
|
|
28443
|
-
success: false,
|
|
28444
|
-
responses,
|
|
28445
|
-
scriptResults,
|
|
28446
|
-
assertionResults,
|
|
28447
|
-
steps: orderedSteps,
|
|
28448
|
-
errors,
|
|
28449
|
-
duration: Date.now() - startTime
|
|
28450
|
-
};
|
|
28451
|
-
}
|
|
28452
|
-
const valueTrimmed = parsed.value.trim();
|
|
28453
|
-
const bareVarMatch = valueTrimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])*)$/);
|
|
28454
|
-
let newValue;
|
|
28455
|
-
if (bareVarMatch && bareVarMatch[1] in runtimeVariables) {
|
|
28456
|
-
const varName = bareVarMatch[1];
|
|
28457
|
-
const pathPart = bareVarMatch[2] || "";
|
|
28458
|
-
let value = runtimeVariables[varName];
|
|
28459
|
-
if (typeof value === "string") {
|
|
28460
|
-
try {
|
|
28461
|
-
const parsed2 = JSON.parse(value);
|
|
28462
|
-
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
28463
|
-
value = parsed2;
|
|
28345
|
+
if (step.type === "assertion") {
|
|
28346
|
+
const parsed = parseAssertCommand(substituteVariables(step.content, runtimeVariables));
|
|
28347
|
+
if (!parsed) {
|
|
28348
|
+
const invalidAssertMessage = `Step ${stepIdx + 1}: Invalid assert command: ${step.content}`;
|
|
28349
|
+
errors.push(invalidAssertMessage);
|
|
28350
|
+
await emitFailure(invalidAssertMessage, step, stepIdx);
|
|
28351
|
+
return {
|
|
28352
|
+
name: "",
|
|
28353
|
+
success: false,
|
|
28354
|
+
responses,
|
|
28355
|
+
scriptResults,
|
|
28356
|
+
assertionResults,
|
|
28357
|
+
steps: orderedSteps,
|
|
28358
|
+
errors,
|
|
28359
|
+
duration: Date.now() - startTime
|
|
28360
|
+
};
|
|
28361
|
+
}
|
|
28362
|
+
const result = evaluateAssertion(parsed, responses, runtimeVariables, getValueByPath, responseIndexToVariable, workingDir);
|
|
28363
|
+
assertionResults.push(result);
|
|
28364
|
+
const stepResult = {
|
|
28365
|
+
type: "assertion",
|
|
28366
|
+
stepIndex: stepIdx,
|
|
28367
|
+
assertion: result,
|
|
28368
|
+
lineNumber: step.lineNumber
|
|
28369
|
+
};
|
|
28370
|
+
orderedSteps.push(stepResult);
|
|
28371
|
+
const statusIcon = result.passed ? "\u2713" : "\u2717";
|
|
28372
|
+
reportProgress(stepIdx, "assertion", `${statusIcon} assert ${result.expression}`, stepResult);
|
|
28373
|
+
if (!result.passed) {
|
|
28374
|
+
const failMessage = result.message ? `Assertion failed: ${result.message}` : `Assertion failed: ${result.expression}`;
|
|
28375
|
+
errors.push(failMessage);
|
|
28376
|
+
if (result.error) {
|
|
28377
|
+
errors.push(` Error: ${result.error}`);
|
|
28378
|
+
} else {
|
|
28379
|
+
errors.push(` Expected: ${result.rightExpression ?? result.operator}`);
|
|
28380
|
+
errors.push(` Actual: ${JSON.stringify(result.leftValue)}`);
|
|
28464
28381
|
}
|
|
28465
|
-
|
|
28382
|
+
await emitFailure(failMessage, step, stepIdx);
|
|
28383
|
+
return {
|
|
28384
|
+
name: "",
|
|
28385
|
+
success: false,
|
|
28386
|
+
responses,
|
|
28387
|
+
scriptResults,
|
|
28388
|
+
assertionResults,
|
|
28389
|
+
steps: orderedSteps,
|
|
28390
|
+
errors,
|
|
28391
|
+
duration: Date.now() - startTime
|
|
28392
|
+
};
|
|
28466
28393
|
}
|
|
28394
|
+
continue;
|
|
28467
28395
|
}
|
|
28468
|
-
if (
|
|
28469
|
-
const
|
|
28470
|
-
|
|
28471
|
-
|
|
28472
|
-
|
|
28473
|
-
|
|
28474
|
-
|
|
28475
|
-
|
|
28476
|
-
|
|
28477
|
-
|
|
28478
|
-
|
|
28479
|
-
|
|
28480
|
-
|
|
28396
|
+
if (step.type === "print") {
|
|
28397
|
+
const parsed = parsePrintCommand(step.content);
|
|
28398
|
+
const titleResolved = resolveBareVariables(parsed.title, runtimeVariables);
|
|
28399
|
+
const title = substituteVariables(titleResolved, runtimeVariables);
|
|
28400
|
+
const bodyResolved = parsed.body ? resolveBareVariables(parsed.body, runtimeVariables) : void 0;
|
|
28401
|
+
const body = bodyResolved ? substituteVariables(bodyResolved, runtimeVariables) : void 0;
|
|
28402
|
+
const stepResult = {
|
|
28403
|
+
type: "print",
|
|
28404
|
+
stepIndex: stepIdx,
|
|
28405
|
+
print: {
|
|
28406
|
+
title,
|
|
28407
|
+
body,
|
|
28408
|
+
timestamp: Date.now() - startTime
|
|
28409
|
+
}
|
|
28410
|
+
};
|
|
28411
|
+
orderedSteps.push(stepResult);
|
|
28412
|
+
reportProgress(stepIdx, "print", `print: ${title}`, stepResult);
|
|
28413
|
+
continue;
|
|
28481
28414
|
}
|
|
28482
|
-
|
|
28483
|
-
|
|
28484
|
-
|
|
28485
|
-
|
|
28486
|
-
|
|
28487
|
-
|
|
28488
|
-
|
|
28489
|
-
|
|
28490
|
-
|
|
28491
|
-
|
|
28492
|
-
steps: orderedSteps,
|
|
28493
|
-
errors,
|
|
28494
|
-
duration: Date.now() - startTime
|
|
28495
|
-
};
|
|
28496
|
-
}
|
|
28497
|
-
runtimeVariables[parsed.varName] = JSON.stringify(jsonObj);
|
|
28498
|
-
reportProgress(stepIdx, "propAssign", `${parsed.varName}.${parsed.propertyPath} = ${parsed.value}`, void 0);
|
|
28499
|
-
continue;
|
|
28500
|
-
}
|
|
28501
|
-
if (step.type === "varAssign") {
|
|
28502
|
-
const parsed = parseVarAssignCommand(step.content);
|
|
28503
|
-
if (!parsed) {
|
|
28504
|
-
errors.push(`Step ${stepIdx + 1}: Invalid variable assignment: ${step.content}`);
|
|
28505
|
-
return {
|
|
28506
|
-
name: "",
|
|
28507
|
-
success: false,
|
|
28508
|
-
responses,
|
|
28509
|
-
scriptResults,
|
|
28510
|
-
assertionResults,
|
|
28511
|
-
steps: orderedSteps,
|
|
28512
|
-
errors,
|
|
28513
|
-
duration: Date.now() - startTime
|
|
28514
|
-
};
|
|
28515
|
-
}
|
|
28516
|
-
const result = evaluateValueExpression(parsed.valueExpr, runtimeVariables);
|
|
28517
|
-
if (result.error) {
|
|
28518
|
-
errors.push(`Step ${stepIdx + 1}: ${result.error}`);
|
|
28519
|
-
return {
|
|
28520
|
-
name: "",
|
|
28521
|
-
success: false,
|
|
28522
|
-
responses,
|
|
28523
|
-
scriptResults,
|
|
28524
|
-
assertionResults,
|
|
28525
|
-
steps: orderedSteps,
|
|
28526
|
-
errors,
|
|
28527
|
-
duration: Date.now() - startTime
|
|
28528
|
-
};
|
|
28529
|
-
}
|
|
28530
|
-
runtimeVariables[parsed.varName] = result.value;
|
|
28531
|
-
reportProgress(stepIdx, "propAssign", `var ${parsed.varName} = ${result.value}`, void 0);
|
|
28532
|
-
continue;
|
|
28533
|
-
}
|
|
28534
|
-
if (step.type === "varRequest") {
|
|
28535
|
-
const commandLine = step.content.split("\n")[0];
|
|
28536
|
-
const parsed = parseVarRequestCommand(commandLine);
|
|
28537
|
-
if (!parsed) {
|
|
28538
|
-
errors.push(`Step ${stepIdx + 1}: Invalid variable request: ${commandLine}`);
|
|
28539
|
-
return {
|
|
28540
|
-
name: "",
|
|
28541
|
-
success: false,
|
|
28542
|
-
responses,
|
|
28543
|
-
scriptResults,
|
|
28544
|
-
assertionResults,
|
|
28545
|
-
steps: orderedSteps,
|
|
28546
|
-
errors,
|
|
28547
|
-
duration: Date.now() - startTime
|
|
28548
|
-
};
|
|
28549
|
-
}
|
|
28550
|
-
requestIndex++;
|
|
28551
|
-
let resolvedUrl = "";
|
|
28552
|
-
let resolvedHeaders = {};
|
|
28553
|
-
let requestDescription = `${parsed.method} ${parsed.url}`;
|
|
28554
|
-
try {
|
|
28555
|
-
const endpointMatch = parsed.url.match(/^([a-zA-Z_][a-zA-Z0-9_]*)(?:\(([^)]*)\))?(\s+.*)?$/);
|
|
28556
|
-
const potentialEndpointName = endpointMatch ? endpointMatch[1] : null;
|
|
28557
|
-
const endpoint = apiDefinitions && potentialEndpointName ? getEndpoint({ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints }, potentialEndpointName) : void 0;
|
|
28558
|
-
if (endpoint && apiDefinitions) {
|
|
28559
|
-
const paramsStr = endpointMatch[2] || "";
|
|
28560
|
-
const headerGroupsStr = endpointMatch[3]?.trim() || "";
|
|
28561
|
-
const paramTokens = paramsStr ? paramsStr.split(",").map((p) => p.trim()) : [];
|
|
28562
|
-
const params = {};
|
|
28563
|
-
const isNamedSyntax = paramTokens.length > 0 && paramTokens[0].includes(":");
|
|
28564
|
-
if (isNamedSyntax) {
|
|
28565
|
-
for (const token of paramTokens) {
|
|
28566
|
-
const kvMatch = token.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.+)$/);
|
|
28567
|
-
if (kvMatch) {
|
|
28568
|
-
const paramName = kvMatch[1];
|
|
28569
|
-
let paramValue = kvMatch[2].trim().replace(/^["']|["']$/g, "");
|
|
28570
|
-
const bareResolved = resolveBareVariables(paramValue, runtimeVariables);
|
|
28571
|
-
params[paramName] = substituteVariables(bareResolved, runtimeVariables);
|
|
28572
|
-
}
|
|
28415
|
+
if (step.type === "wait") {
|
|
28416
|
+
const durationMs = parseWaitCommand(step.content);
|
|
28417
|
+
reportProgress(stepIdx, "wait", `wait: ${durationMs}ms`);
|
|
28418
|
+
await sleep2(durationMs);
|
|
28419
|
+
const stepResult = {
|
|
28420
|
+
type: "print",
|
|
28421
|
+
stepIndex: stepIdx,
|
|
28422
|
+
print: {
|
|
28423
|
+
title: `Waited ${durationMs >= 1e3 ? durationMs / 1e3 + "s" : durationMs + "ms"}`,
|
|
28424
|
+
timestamp: Date.now() - startTime
|
|
28573
28425
|
}
|
|
28574
|
-
}
|
|
28575
|
-
|
|
28576
|
-
|
|
28577
|
-
|
|
28578
|
-
|
|
28579
|
-
|
|
28580
|
-
|
|
28581
|
-
|
|
28426
|
+
};
|
|
28427
|
+
orderedSteps.push(stepResult);
|
|
28428
|
+
continue;
|
|
28429
|
+
}
|
|
28430
|
+
if (step.type === "json") {
|
|
28431
|
+
const bareResolved = resolveBareVariables(step.content, runtimeVariables);
|
|
28432
|
+
const parsed = parseJsonCommand(substituteVariables(bareResolved, runtimeVariables));
|
|
28433
|
+
if (!parsed) {
|
|
28434
|
+
errors.push(`Step ${stepIdx + 1}: Invalid json command: ${step.content}`);
|
|
28435
|
+
return {
|
|
28436
|
+
name: "",
|
|
28437
|
+
success: false,
|
|
28438
|
+
responses,
|
|
28439
|
+
scriptResults,
|
|
28440
|
+
assertionResults,
|
|
28441
|
+
steps: orderedSteps,
|
|
28442
|
+
errors,
|
|
28443
|
+
duration: Date.now() - startTime
|
|
28444
|
+
};
|
|
28582
28445
|
}
|
|
28583
|
-
|
|
28584
|
-
|
|
28585
|
-
|
|
28586
|
-
|
|
28587
|
-
|
|
28446
|
+
const result = readJsonFile(parsed.filePath, workingDir);
|
|
28447
|
+
const stepResult = {
|
|
28448
|
+
type: "json",
|
|
28449
|
+
stepIndex: stepIdx,
|
|
28450
|
+
json: {
|
|
28451
|
+
varName: parsed.varName,
|
|
28452
|
+
filePath: result.filePath,
|
|
28453
|
+
success: result.success,
|
|
28454
|
+
error: result.error,
|
|
28455
|
+
timestamp: Date.now() - startTime
|
|
28588
28456
|
}
|
|
28457
|
+
};
|
|
28458
|
+
orderedSteps.push(stepResult);
|
|
28459
|
+
if (!result.success) {
|
|
28460
|
+
errors.push(`Failed to load JSON file: ${result.error}`);
|
|
28461
|
+
reportProgress(stepIdx, "json", `json: ${parsed.varName} \u2717 ${result.error}`, stepResult);
|
|
28462
|
+
return {
|
|
28463
|
+
name: "",
|
|
28464
|
+
success: false,
|
|
28465
|
+
responses,
|
|
28466
|
+
scriptResults,
|
|
28467
|
+
assertionResults,
|
|
28468
|
+
steps: orderedSteps,
|
|
28469
|
+
errors,
|
|
28470
|
+
duration: Date.now() - startTime
|
|
28471
|
+
};
|
|
28589
28472
|
}
|
|
28590
|
-
|
|
28591
|
-
|
|
28592
|
-
|
|
28593
|
-
|
|
28594
|
-
|
|
28595
|
-
|
|
28596
|
-
|
|
28597
|
-
|
|
28598
|
-
|
|
28599
|
-
|
|
28600
|
-
|
|
28473
|
+
runtimeVariables[parsed.varName] = JSON.stringify(result.data);
|
|
28474
|
+
reportProgress(stepIdx, "json", `json: ${parsed.varName} = ${parsed.filePath}`, stepResult);
|
|
28475
|
+
continue;
|
|
28476
|
+
}
|
|
28477
|
+
if (step.type === "propAssign") {
|
|
28478
|
+
const parsed = parsePropertyAssignment(step.content);
|
|
28479
|
+
if (!parsed) {
|
|
28480
|
+
errors.push(`Step ${stepIdx + 1}: Invalid property assignment: ${step.content}`);
|
|
28481
|
+
return {
|
|
28482
|
+
name: "",
|
|
28483
|
+
success: false,
|
|
28484
|
+
responses,
|
|
28485
|
+
scriptResults,
|
|
28486
|
+
assertionResults,
|
|
28487
|
+
steps: orderedSteps,
|
|
28488
|
+
errors,
|
|
28489
|
+
duration: Date.now() - startTime
|
|
28490
|
+
};
|
|
28491
|
+
}
|
|
28492
|
+
if (!(parsed.varName in runtimeVariables)) {
|
|
28493
|
+
errors.push(`Step ${stepIdx + 1}: Variable '${parsed.varName}' is not defined`);
|
|
28494
|
+
return {
|
|
28495
|
+
name: "",
|
|
28496
|
+
success: false,
|
|
28497
|
+
responses,
|
|
28498
|
+
scriptResults,
|
|
28499
|
+
assertionResults,
|
|
28500
|
+
steps: orderedSteps,
|
|
28501
|
+
errors,
|
|
28502
|
+
duration: Date.now() - startTime
|
|
28503
|
+
};
|
|
28504
|
+
}
|
|
28505
|
+
let jsonObj;
|
|
28506
|
+
try {
|
|
28507
|
+
jsonObj = JSON.parse(runtimeVariables[parsed.varName]);
|
|
28508
|
+
} catch {
|
|
28509
|
+
errors.push(`Step ${stepIdx + 1}: Variable '${parsed.varName}' is not a valid JSON object`);
|
|
28510
|
+
return {
|
|
28511
|
+
name: "",
|
|
28512
|
+
success: false,
|
|
28513
|
+
responses,
|
|
28514
|
+
scriptResults,
|
|
28515
|
+
assertionResults,
|
|
28516
|
+
steps: orderedSteps,
|
|
28517
|
+
errors,
|
|
28518
|
+
duration: Date.now() - startTime
|
|
28519
|
+
};
|
|
28520
|
+
}
|
|
28521
|
+
const valueTrimmed = parsed.value.trim();
|
|
28522
|
+
const bareVarMatch = valueTrimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)((?:\.[a-zA-Z_][a-zA-Z0-9_]*|\[\d+\])*)$/);
|
|
28523
|
+
let newValue;
|
|
28524
|
+
if (bareVarMatch && bareVarMatch[1] in runtimeVariables) {
|
|
28525
|
+
const varName = bareVarMatch[1];
|
|
28526
|
+
const pathPart = bareVarMatch[2] || "";
|
|
28527
|
+
let value = runtimeVariables[varName];
|
|
28528
|
+
if (typeof value === "string") {
|
|
28529
|
+
try {
|
|
28530
|
+
const parsed2 = JSON.parse(value);
|
|
28531
|
+
if (typeof parsed2 === "object" && parsed2 !== null) {
|
|
28532
|
+
value = parsed2;
|
|
28533
|
+
}
|
|
28534
|
+
} catch {
|
|
28601
28535
|
}
|
|
28602
28536
|
}
|
|
28603
|
-
|
|
28604
|
-
|
|
28605
|
-
|
|
28606
|
-
|
|
28607
|
-
|
|
28608
|
-
|
|
28609
|
-
let requestText = `${parsed.method} ${resolvedUrl}`;
|
|
28610
|
-
if (Object.keys(resolvedHeaders).length > 0) {
|
|
28611
|
-
const headerLines = Object.entries(resolvedHeaders).map(([k, v]) => `${k}: ${v}`);
|
|
28612
|
-
requestText += "\n" + headerLines.join("\n");
|
|
28613
|
-
}
|
|
28614
|
-
const contentLines = step.content.split("\n");
|
|
28615
|
-
if (contentLines.length > 1) {
|
|
28616
|
-
const extraLines = contentLines.slice(1).map((line2) => substituteVariables(line2, runtimeVariables));
|
|
28617
|
-
const hasExplicitBlank = extraLines.some((line2) => line2.trim() === "");
|
|
28618
|
-
const firstNonEmpty = extraLines.find((line2) => line2.trim() !== "");
|
|
28619
|
-
const startsWithHeader = firstNonEmpty ? /^[A-Za-z0-9\-_]+\s*:/.test(firstNonEmpty.trim()) : false;
|
|
28620
|
-
if (hasExplicitBlank || startsWithHeader) {
|
|
28621
|
-
requestText += "\n" + extraLines.join("\n");
|
|
28537
|
+
if (pathPart) {
|
|
28538
|
+
const path10 = pathPart.replace(/^\./, "");
|
|
28539
|
+
newValue = getNestedValueFromObject(value, path10);
|
|
28540
|
+
} else {
|
|
28541
|
+
newValue = value;
|
|
28542
|
+
}
|
|
28622
28543
|
} else {
|
|
28623
|
-
|
|
28544
|
+
const bareResolvedValue = resolveBareVariables(parsed.value, runtimeVariables);
|
|
28545
|
+
const resolvedValue = substituteVariables(bareResolvedValue, runtimeVariables);
|
|
28546
|
+
newValue = resolvedValue;
|
|
28547
|
+
try {
|
|
28548
|
+
newValue = JSON.parse(resolvedValue);
|
|
28549
|
+
} catch {
|
|
28550
|
+
}
|
|
28551
|
+
}
|
|
28552
|
+
const success = setNestedValue(jsonObj, parsed.propertyPath, newValue);
|
|
28553
|
+
if (!success) {
|
|
28554
|
+
errors.push(`Step ${stepIdx + 1}: Failed to set property '${parsed.propertyPath}' on '${parsed.varName}'`);
|
|
28555
|
+
return {
|
|
28556
|
+
name: "",
|
|
28557
|
+
success: false,
|
|
28558
|
+
responses,
|
|
28559
|
+
scriptResults,
|
|
28560
|
+
assertionResults,
|
|
28561
|
+
steps: orderedSteps,
|
|
28562
|
+
errors,
|
|
28563
|
+
duration: Date.now() - startTime
|
|
28564
|
+
};
|
|
28624
28565
|
}
|
|
28566
|
+
runtimeVariables[parsed.varName] = JSON.stringify(jsonObj);
|
|
28567
|
+
reportProgress(stepIdx, "propAssign", `${parsed.varName}.${parsed.propertyPath} = ${parsed.value}`, void 0);
|
|
28568
|
+
continue;
|
|
28625
28569
|
}
|
|
28626
|
-
|
|
28627
|
-
|
|
28628
|
-
|
|
28629
|
-
|
|
28630
|
-
|
|
28631
|
-
|
|
28632
|
-
|
|
28633
|
-
|
|
28634
|
-
|
|
28635
|
-
|
|
28570
|
+
if (step.type === "varAssign") {
|
|
28571
|
+
const parsed = parseVarAssignCommand(step.content);
|
|
28572
|
+
if (!parsed) {
|
|
28573
|
+
errors.push(`Step ${stepIdx + 1}: Invalid variable assignment: ${step.content}`);
|
|
28574
|
+
return {
|
|
28575
|
+
name: "",
|
|
28576
|
+
success: false,
|
|
28577
|
+
responses,
|
|
28578
|
+
scriptResults,
|
|
28579
|
+
assertionResults,
|
|
28580
|
+
steps: orderedSteps,
|
|
28581
|
+
errors,
|
|
28582
|
+
duration: Date.now() - startTime
|
|
28583
|
+
};
|
|
28636
28584
|
}
|
|
28637
|
-
|
|
28638
|
-
|
|
28639
|
-
|
|
28640
|
-
|
|
28641
|
-
|
|
28642
|
-
|
|
28643
|
-
|
|
28644
|
-
|
|
28645
|
-
|
|
28646
|
-
|
|
28647
|
-
|
|
28648
|
-
|
|
28649
|
-
|
|
28650
|
-
orderedSteps.push(stepResult);
|
|
28651
|
-
reportProgress(stepIdx, "request", `var ${parsed.varName} = ${requestDescription}`, stepResult);
|
|
28652
|
-
const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
|
|
28653
|
-
for (const capture of relevantCaptures) {
|
|
28654
|
-
const value = getValueByPath(response, capture.path);
|
|
28655
|
-
if (value !== void 0) {
|
|
28656
|
-
runtimeVariables[capture.varName] = value;
|
|
28657
|
-
} else {
|
|
28658
|
-
errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
|
|
28585
|
+
const result = evaluateValueExpression(parsed.valueExpr, runtimeVariables);
|
|
28586
|
+
if (result.error) {
|
|
28587
|
+
errors.push(`Step ${stepIdx + 1}: ${result.error}`);
|
|
28588
|
+
return {
|
|
28589
|
+
name: "",
|
|
28590
|
+
success: false,
|
|
28591
|
+
responses,
|
|
28592
|
+
scriptResults,
|
|
28593
|
+
assertionResults,
|
|
28594
|
+
steps: orderedSteps,
|
|
28595
|
+
errors,
|
|
28596
|
+
duration: Date.now() - startTime
|
|
28597
|
+
};
|
|
28659
28598
|
}
|
|
28599
|
+
runtimeVariables[parsed.varName] = result.value;
|
|
28600
|
+
reportProgress(stepIdx, "propAssign", `var ${parsed.varName} = ${result.value}`, void 0);
|
|
28601
|
+
continue;
|
|
28660
28602
|
}
|
|
28661
|
-
|
|
28662
|
-
|
|
28663
|
-
|
|
28664
|
-
|
|
28665
|
-
|
|
28666
|
-
|
|
28603
|
+
if (step.type === "varRequest") {
|
|
28604
|
+
const commandLine = step.content.split("\n")[0];
|
|
28605
|
+
const parsed = parseVarRequestCommand(commandLine);
|
|
28606
|
+
if (!parsed) {
|
|
28607
|
+
errors.push(`Step ${stepIdx + 1}: Invalid variable request: ${commandLine}`);
|
|
28608
|
+
return {
|
|
28609
|
+
name: "",
|
|
28610
|
+
success: false,
|
|
28611
|
+
responses,
|
|
28612
|
+
scriptResults,
|
|
28613
|
+
assertionResults,
|
|
28614
|
+
steps: orderedSteps,
|
|
28615
|
+
errors,
|
|
28616
|
+
duration: Date.now() - startTime
|
|
28617
|
+
};
|
|
28618
|
+
}
|
|
28619
|
+
requestIndex++;
|
|
28620
|
+
let resolvedUrl = "";
|
|
28621
|
+
let resolvedHeaders = {};
|
|
28622
|
+
let requestDescription = `${parsed.method} ${parsed.url}`;
|
|
28623
|
+
try {
|
|
28624
|
+
const endpointMatch = parsed.url.match(/^([a-zA-Z_][a-zA-Z0-9_]*)(?:\(([^)]*)\))?(\s+.*)?$/);
|
|
28625
|
+
const potentialEndpointName = endpointMatch ? endpointMatch[1] : null;
|
|
28626
|
+
const endpoint = apiDefinitions && potentialEndpointName ? getEndpoint({ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints }, potentialEndpointName) : void 0;
|
|
28627
|
+
if (endpoint && apiDefinitions) {
|
|
28628
|
+
const paramsStr = endpointMatch[2] || "";
|
|
28629
|
+
const headerGroupsStr = endpointMatch[3]?.trim() || "";
|
|
28630
|
+
const paramTokens = paramsStr ? paramsStr.split(",").map((p) => p.trim()) : [];
|
|
28631
|
+
const params = {};
|
|
28632
|
+
const isNamedSyntax = paramTokens.length > 0 && paramTokens[0].includes(":");
|
|
28633
|
+
if (isNamedSyntax) {
|
|
28634
|
+
for (const token of paramTokens) {
|
|
28635
|
+
const kvMatch = token.match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.+)$/);
|
|
28636
|
+
if (kvMatch) {
|
|
28637
|
+
const paramName = kvMatch[1];
|
|
28638
|
+
let paramValue = kvMatch[2].trim().replace(/^["']|["']$/g, "");
|
|
28639
|
+
const bareResolved = resolveBareVariables(paramValue, runtimeVariables);
|
|
28640
|
+
params[paramName] = substituteVariables(bareResolved, runtimeVariables);
|
|
28641
|
+
}
|
|
28642
|
+
}
|
|
28643
|
+
} else {
|
|
28644
|
+
endpoint.parameters.forEach((paramName, idx) => {
|
|
28645
|
+
if (paramTokens[idx] !== void 0) {
|
|
28646
|
+
let paramValue = paramTokens[idx].replace(/^["']|["']$/g, "");
|
|
28647
|
+
const bareResolved = resolveBareVariables(paramValue, runtimeVariables);
|
|
28648
|
+
params[paramName] = substituteVariables(bareResolved, runtimeVariables);
|
|
28649
|
+
}
|
|
28650
|
+
});
|
|
28651
|
+
}
|
|
28652
|
+
let pathWithVars = substituteVariables(endpoint.path, runtimeVariables);
|
|
28653
|
+
for (const paramName of endpoint.parameters) {
|
|
28654
|
+
const value = params[paramName];
|
|
28655
|
+
if (value !== void 0) {
|
|
28656
|
+
pathWithVars = pathWithVars.replace(`{${paramName}}`, value);
|
|
28657
|
+
}
|
|
28658
|
+
}
|
|
28659
|
+
resolvedUrl = pathWithVars;
|
|
28660
|
+
if (headerGroupsStr) {
|
|
28661
|
+
const headerGroupNames = headerGroupsStr.split(/\s+/).filter((n) => n);
|
|
28662
|
+
for (const groupName of headerGroupNames) {
|
|
28663
|
+
const group = getHeaderGroup(
|
|
28664
|
+
{ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
|
|
28665
|
+
groupName
|
|
28666
|
+
);
|
|
28667
|
+
if (group) {
|
|
28668
|
+
const groupHeaders = resolveHeaderValues(group, runtimeVariables);
|
|
28669
|
+
Object.assign(resolvedHeaders, groupHeaders);
|
|
28670
|
+
}
|
|
28671
|
+
}
|
|
28672
|
+
}
|
|
28673
|
+
requestDescription = `${potentialEndpointName}(${paramTokens.join(", ")}) \u2192 ${parsed.method} ${resolvedUrl}`;
|
|
28674
|
+
} else {
|
|
28675
|
+
resolvedUrl = substituteVariables(parsed.url, runtimeVariables);
|
|
28676
|
+
requestDescription = `${parsed.method} ${resolvedUrl}`;
|
|
28677
|
+
}
|
|
28678
|
+
let requestText = `${parsed.method} ${resolvedUrl}`;
|
|
28679
|
+
if (Object.keys(resolvedHeaders).length > 0) {
|
|
28680
|
+
const headerLines = Object.entries(resolvedHeaders).map(([k, v]) => `${k}: ${v}`);
|
|
28681
|
+
requestText += "\n" + headerLines.join("\n");
|
|
28682
|
+
}
|
|
28683
|
+
const contentLines = step.content.split("\n");
|
|
28684
|
+
if (contentLines.length > 1) {
|
|
28685
|
+
const extraLines = contentLines.slice(1).map((line2) => substituteVariables(line2, runtimeVariables));
|
|
28686
|
+
const hasExplicitBlank = extraLines.some((line2) => line2.trim() === "");
|
|
28687
|
+
const firstNonEmpty = extraLines.find((line2) => line2.trim() !== "");
|
|
28688
|
+
const startsWithHeader = firstNonEmpty ? /^[A-Za-z0-9\-_]+\s*:/.test(firstNonEmpty.trim()) : false;
|
|
28689
|
+
if (hasExplicitBlank || startsWithHeader) {
|
|
28690
|
+
requestText += "\n" + extraLines.join("\n");
|
|
28691
|
+
} else {
|
|
28692
|
+
requestText += "\n\n" + extraLines.join("\n");
|
|
28693
|
+
}
|
|
28694
|
+
}
|
|
28695
|
+
const requestParsed = parserHttpRequest(requestText, runtimeVariables);
|
|
28696
|
+
validatePreparedRequest(
|
|
28697
|
+
requestParsed,
|
|
28698
|
+
buildRequestValidationContext(stepIdx + 1, requestParsed.method, requestParsed.url, `var ${parsed.varName}`)
|
|
28699
|
+
);
|
|
28700
|
+
const retryOpts = parsed.retryCount ? {
|
|
28701
|
+
retryCount: parsed.retryCount,
|
|
28702
|
+
backoffMs: parsed.backoffMs || 1e3,
|
|
28703
|
+
onRetry: (attempt, total, error, waitMs) => {
|
|
28704
|
+
reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
|
|
28705
|
+
}
|
|
28706
|
+
} : void 0;
|
|
28707
|
+
const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
|
|
28708
|
+
addResponse(response);
|
|
28709
|
+
responseIndexToVariable.set(responses.length, parsed.varName);
|
|
28710
|
+
runtimeVariables[parsed.varName] = response;
|
|
28711
|
+
const stepResult = {
|
|
28712
|
+
type: "request",
|
|
28713
|
+
stepIndex: stepIdx,
|
|
28714
|
+
response,
|
|
28715
|
+
requestMethod: parsed.method,
|
|
28716
|
+
requestUrl: resolvedUrl,
|
|
28717
|
+
variableName: parsed.varName
|
|
28718
|
+
};
|
|
28719
|
+
orderedSteps.push(stepResult);
|
|
28720
|
+
reportProgress(stepIdx, "request", `var ${parsed.varName} = ${requestDescription}`, stepResult);
|
|
28721
|
+
const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
|
|
28722
|
+
for (const capture of relevantCaptures) {
|
|
28723
|
+
const value = getValueByPath(response, capture.path);
|
|
28724
|
+
if (value !== void 0) {
|
|
28725
|
+
runtimeVariables[capture.varName] = value;
|
|
28726
|
+
} else {
|
|
28727
|
+
errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
|
|
28728
|
+
}
|
|
28729
|
+
}
|
|
28730
|
+
} catch (error) {
|
|
28731
|
+
const userMessage = formatUserFacingError(
|
|
28732
|
+
error,
|
|
28733
|
+
buildRequestValidationContext(stepIdx + 1, void 0, void 0, `var ${parsed.varName}`)
|
|
28734
|
+
);
|
|
28735
|
+
let errorDetails = `Request failed for var ${parsed.varName}:
|
|
28667
28736
|
${indentMultiline(userMessage)}`;
|
|
28668
|
-
|
|
28669
|
-
|
|
28737
|
+
if (requestDescription && !/\nRequest:\s/.test(userMessage)) {
|
|
28738
|
+
errorDetails += `
|
|
28670
28739
|
Request: ${requestDescription}`;
|
|
28740
|
+
}
|
|
28741
|
+
errors.push(errorDetails);
|
|
28742
|
+
await emitFailure(errorDetails, step, stepIdx);
|
|
28743
|
+
return {
|
|
28744
|
+
name: "",
|
|
28745
|
+
success: false,
|
|
28746
|
+
responses,
|
|
28747
|
+
scriptResults,
|
|
28748
|
+
assertionResults,
|
|
28749
|
+
steps: orderedSteps,
|
|
28750
|
+
errors,
|
|
28751
|
+
duration: Date.now() - startTime
|
|
28752
|
+
};
|
|
28753
|
+
}
|
|
28754
|
+
continue;
|
|
28671
28755
|
}
|
|
28672
|
-
|
|
28673
|
-
|
|
28674
|
-
|
|
28675
|
-
|
|
28676
|
-
|
|
28677
|
-
|
|
28678
|
-
|
|
28679
|
-
|
|
28680
|
-
|
|
28681
|
-
|
|
28682
|
-
|
|
28683
|
-
|
|
28684
|
-
|
|
28685
|
-
|
|
28686
|
-
|
|
28687
|
-
|
|
28688
|
-
|
|
28689
|
-
|
|
28690
|
-
|
|
28691
|
-
|
|
28692
|
-
success: false,
|
|
28693
|
-
responses,
|
|
28694
|
-
scriptResults,
|
|
28695
|
-
assertionResults,
|
|
28696
|
-
steps: orderedSteps,
|
|
28697
|
-
errors,
|
|
28698
|
-
duration: Date.now() - startTime
|
|
28699
|
-
};
|
|
28700
|
-
}
|
|
28701
|
-
const substitutedArgs = parsed.args.map((arg) => {
|
|
28702
|
-
const bareResolved = resolveBareVariables(arg, runtimeVariables);
|
|
28703
|
-
return substituteVariables(bareResolved, runtimeVariables);
|
|
28704
|
-
});
|
|
28705
|
-
const substitutedPath = substituteVariables(parsed.scriptPath, runtimeVariables);
|
|
28706
|
-
try {
|
|
28707
|
-
const result = await runScript(
|
|
28708
|
-
parsed.type,
|
|
28709
|
-
substitutedPath,
|
|
28710
|
-
substitutedArgs,
|
|
28711
|
-
workingDir || process.cwd(),
|
|
28712
|
-
runtimeVariables,
|
|
28713
|
-
parsed.captureAs
|
|
28714
|
-
);
|
|
28715
|
-
scriptResults.push(result);
|
|
28716
|
-
const stepResult = {
|
|
28717
|
-
type: "script",
|
|
28718
|
-
stepIndex: stepIdx,
|
|
28719
|
-
script: result
|
|
28720
|
-
};
|
|
28721
|
-
orderedSteps.push(stepResult);
|
|
28722
|
-
reportProgress(stepIdx, "script", `run ${parsed.type} ${substitutedPath}`, stepResult);
|
|
28723
|
-
if (!result.success) {
|
|
28724
|
-
errors.push(`Script failed (exit ${result.exitCode}): ${result.error || result.output}`);
|
|
28725
|
-
return {
|
|
28726
|
-
name: "",
|
|
28727
|
-
success: false,
|
|
28728
|
-
responses,
|
|
28729
|
-
scriptResults,
|
|
28730
|
-
assertionResults,
|
|
28731
|
-
steps: orderedSteps,
|
|
28732
|
-
errors,
|
|
28733
|
-
duration: Date.now() - startTime
|
|
28734
|
-
};
|
|
28735
|
-
}
|
|
28736
|
-
if (parsed.captureAs) {
|
|
28737
|
-
let outputValue = result.output;
|
|
28756
|
+
if (step.type === "script") {
|
|
28757
|
+
const parsed = parseRunCommand(substituteVariables(step.content, runtimeVariables));
|
|
28758
|
+
if (!parsed) {
|
|
28759
|
+
errors.push(`Step ${stepIdx + 1}: Invalid run command: ${step.content}`);
|
|
28760
|
+
return {
|
|
28761
|
+
name: "",
|
|
28762
|
+
success: false,
|
|
28763
|
+
responses,
|
|
28764
|
+
scriptResults,
|
|
28765
|
+
assertionResults,
|
|
28766
|
+
steps: orderedSteps,
|
|
28767
|
+
errors,
|
|
28768
|
+
duration: Date.now() - startTime
|
|
28769
|
+
};
|
|
28770
|
+
}
|
|
28771
|
+
const substitutedArgs = parsed.args.map((arg) => {
|
|
28772
|
+
const bareResolved = resolveBareVariables(arg, runtimeVariables);
|
|
28773
|
+
return substituteVariables(bareResolved, runtimeVariables);
|
|
28774
|
+
});
|
|
28775
|
+
const substitutedPath = substituteVariables(parsed.scriptPath, runtimeVariables);
|
|
28738
28776
|
try {
|
|
28739
|
-
const
|
|
28740
|
-
|
|
28741
|
-
|
|
28777
|
+
const result = await runScript(
|
|
28778
|
+
parsed.type,
|
|
28779
|
+
substitutedPath,
|
|
28780
|
+
substitutedArgs,
|
|
28781
|
+
workingDir || process.cwd(),
|
|
28782
|
+
runtimeVariables,
|
|
28783
|
+
parsed.captureAs
|
|
28784
|
+
);
|
|
28785
|
+
scriptResults.push(result);
|
|
28786
|
+
const stepResult = {
|
|
28787
|
+
type: "script",
|
|
28788
|
+
stepIndex: stepIdx,
|
|
28789
|
+
script: result
|
|
28790
|
+
};
|
|
28791
|
+
orderedSteps.push(stepResult);
|
|
28792
|
+
reportProgress(stepIdx, "script", `run ${parsed.type} ${substitutedPath}`, stepResult);
|
|
28793
|
+
if (!result.success) {
|
|
28794
|
+
errors.push(`Script failed (exit ${result.exitCode}): ${result.error || result.output}`);
|
|
28795
|
+
return {
|
|
28796
|
+
name: "",
|
|
28797
|
+
success: false,
|
|
28798
|
+
responses,
|
|
28799
|
+
scriptResults,
|
|
28800
|
+
assertionResults,
|
|
28801
|
+
steps: orderedSteps,
|
|
28802
|
+
errors,
|
|
28803
|
+
duration: Date.now() - startTime
|
|
28804
|
+
};
|
|
28805
|
+
}
|
|
28806
|
+
if (parsed.captureAs) {
|
|
28807
|
+
let outputValue = result.output;
|
|
28808
|
+
try {
|
|
28809
|
+
const jsonParsed = JSON.parse(result.output);
|
|
28810
|
+
outputValue = jsonParsed;
|
|
28811
|
+
} catch {
|
|
28812
|
+
}
|
|
28813
|
+
runtimeVariables[parsed.captureAs] = outputValue;
|
|
28814
|
+
}
|
|
28815
|
+
} catch (error) {
|
|
28816
|
+
errors.push(`Script execution error: ${error.message}`);
|
|
28817
|
+
return {
|
|
28818
|
+
name: "",
|
|
28819
|
+
success: false,
|
|
28820
|
+
responses,
|
|
28821
|
+
scriptResults,
|
|
28822
|
+
assertionResults,
|
|
28823
|
+
steps: orderedSteps,
|
|
28824
|
+
errors,
|
|
28825
|
+
duration: Date.now() - startTime
|
|
28826
|
+
};
|
|
28742
28827
|
}
|
|
28743
|
-
|
|
28744
|
-
|
|
28745
|
-
|
|
28746
|
-
|
|
28747
|
-
|
|
28748
|
-
|
|
28749
|
-
|
|
28750
|
-
|
|
28751
|
-
|
|
28752
|
-
|
|
28753
|
-
|
|
28754
|
-
|
|
28755
|
-
|
|
28756
|
-
|
|
28757
|
-
|
|
28758
|
-
|
|
28759
|
-
|
|
28760
|
-
|
|
28761
|
-
|
|
28762
|
-
|
|
28763
|
-
|
|
28764
|
-
|
|
28765
|
-
|
|
28766
|
-
|
|
28767
|
-
|
|
28768
|
-
|
|
28769
|
-
|
|
28770
|
-
|
|
28771
|
-
|
|
28772
|
-
|
|
28773
|
-
|
|
28774
|
-
|
|
28775
|
-
|
|
28776
|
-
|
|
28777
|
-
|
|
28778
|
-
|
|
28779
|
-
|
|
28780
|
-
|
|
28781
|
-
|
|
28782
|
-
|
|
28783
|
-
|
|
28784
|
-
|
|
28785
|
-
|
|
28786
|
-
|
|
28787
|
-
|
|
28788
|
-
|
|
28789
|
-
|
|
28790
|
-
|
|
28791
|
-
|
|
28792
|
-
|
|
28793
|
-
|
|
28794
|
-
|
|
28795
|
-
|
|
28796
|
-
|
|
28797
|
-
|
|
28798
|
-
|
|
28799
|
-
|
|
28800
|
-
|
|
28801
|
-
|
|
28802
|
-
|
|
28803
|
-
|
|
28804
|
-
|
|
28805
|
-
|
|
28806
|
-
|
|
28807
|
-
|
|
28808
|
-
|
|
28828
|
+
} else if (step.type === "namedRequest") {
|
|
28829
|
+
const parsed = parseRunNamedRequestCommand(step.content);
|
|
28830
|
+
if (!parsed) {
|
|
28831
|
+
errors.push(`Invalid run command: ${step.content}`);
|
|
28832
|
+
return {
|
|
28833
|
+
name: "",
|
|
28834
|
+
success: false,
|
|
28835
|
+
responses,
|
|
28836
|
+
scriptResults,
|
|
28837
|
+
assertionResults,
|
|
28838
|
+
steps: orderedSteps,
|
|
28839
|
+
errors,
|
|
28840
|
+
duration: Date.now() - startTime
|
|
28841
|
+
};
|
|
28842
|
+
}
|
|
28843
|
+
const { name: targetName, args: runArgs } = parsed;
|
|
28844
|
+
if (!fullDocumentText) {
|
|
28845
|
+
errors.push(`Cannot run "${targetName}": full document text not provided`);
|
|
28846
|
+
return {
|
|
28847
|
+
name: "",
|
|
28848
|
+
success: false,
|
|
28849
|
+
responses,
|
|
28850
|
+
scriptResults,
|
|
28851
|
+
assertionResults,
|
|
28852
|
+
steps: orderedSteps,
|
|
28853
|
+
errors,
|
|
28854
|
+
duration: Date.now() - startTime
|
|
28855
|
+
};
|
|
28856
|
+
}
|
|
28857
|
+
const allSequences = extractSequences(fullDocumentText);
|
|
28858
|
+
const targetSequence = allSequences.find((s) => s.name === targetName);
|
|
28859
|
+
if (targetSequence) {
|
|
28860
|
+
const currentCallStack = callStack || [];
|
|
28861
|
+
if (currentCallStack.includes(targetName)) {
|
|
28862
|
+
errors.push(`Circular reference detected: ${[...currentCallStack, targetName].join(" \u2192 ")}`);
|
|
28863
|
+
return {
|
|
28864
|
+
name: "",
|
|
28865
|
+
success: false,
|
|
28866
|
+
responses,
|
|
28867
|
+
scriptResults,
|
|
28868
|
+
assertionResults,
|
|
28869
|
+
steps: orderedSteps,
|
|
28870
|
+
errors,
|
|
28871
|
+
duration: Date.now() - startTime
|
|
28872
|
+
};
|
|
28873
|
+
}
|
|
28874
|
+
let sequenceArgs2 = {};
|
|
28875
|
+
if (targetSequence.parameters.length > 0 || runArgs.length > 0) {
|
|
28876
|
+
const bindResult = bindSequenceArguments(targetSequence.parameters, runArgs, runtimeVariables);
|
|
28877
|
+
if ("error" in bindResult) {
|
|
28878
|
+
errors.push(`Error calling sequence "${targetName}": ${bindResult.error}`);
|
|
28879
|
+
return {
|
|
28880
|
+
name: "",
|
|
28881
|
+
success: false,
|
|
28882
|
+
responses,
|
|
28883
|
+
scriptResults,
|
|
28884
|
+
assertionResults,
|
|
28885
|
+
steps: orderedSteps,
|
|
28886
|
+
errors,
|
|
28887
|
+
duration: Date.now() - startTime
|
|
28888
|
+
};
|
|
28889
|
+
}
|
|
28890
|
+
sequenceArgs2 = bindResult.variables;
|
|
28891
|
+
}
|
|
28892
|
+
if (tagFilterOptions && tagFilterOptions.filters.length > 0) {
|
|
28893
|
+
if (!sequenceMatchesTags(targetSequence, tagFilterOptions)) {
|
|
28894
|
+
continue;
|
|
28895
|
+
}
|
|
28896
|
+
}
|
|
28897
|
+
reportProgress(stepIdx, "sequenceStart", `Starting sequence ${targetName}`, void 0, targetName);
|
|
28898
|
+
let subWorkingDir = workingDir;
|
|
28899
|
+
if (sequenceSources) {
|
|
28900
|
+
const sourceFile = sequenceSources.get(targetName.toLowerCase());
|
|
28901
|
+
if (sourceFile) {
|
|
28902
|
+
const path10 = await import("path");
|
|
28903
|
+
subWorkingDir = path10.dirname(sourceFile);
|
|
28904
|
+
}
|
|
28905
|
+
}
|
|
28906
|
+
const targetLocation = executionContext?.sequenceLocationIndex?.get(targetName.toLowerCase());
|
|
28907
|
+
const childExecutionContext = {
|
|
28908
|
+
filePath: targetLocation?.filePath ?? executionContext?.filePath,
|
|
28909
|
+
sequenceName: targetName,
|
|
28910
|
+
environment: executionContext?.environment,
|
|
28911
|
+
sequenceStartLine: targetLocation?.startLine ?? targetSequence.startLine,
|
|
28912
|
+
sequenceLocationIndex: executionContext?.sequenceLocationIndex
|
|
28913
|
+
};
|
|
28914
|
+
const subResult = await runSequenceWithJar(
|
|
28915
|
+
targetSequence.content,
|
|
28916
|
+
runtimeVariables,
|
|
28917
|
+
// Pass current runtime variables (includes file vars + any captured)
|
|
28918
|
+
cookieJar,
|
|
28919
|
+
subWorkingDir,
|
|
28920
|
+
fullDocumentText,
|
|
28921
|
+
onProgress,
|
|
28922
|
+
// Pass through progress callback
|
|
28923
|
+
[...currentCallStack, targetName],
|
|
28924
|
+
// Add to call stack for circular detection
|
|
28925
|
+
sequenceArgs2,
|
|
28926
|
+
// Pass bound arguments as initial scope variables
|
|
28927
|
+
apiDefinitions,
|
|
28928
|
+
// Pass API definitions for endpoint resolution
|
|
28929
|
+
tagFilterOptions,
|
|
28930
|
+
// Pass tag filter options for nested sequence filtering
|
|
28931
|
+
sequenceSources,
|
|
28932
|
+
// Pass sequence sources for nested sequences
|
|
28933
|
+
childExecutionContext,
|
|
28934
|
+
debugHooks
|
|
28935
|
+
);
|
|
28936
|
+
reportProgress(stepIdx, "sequenceEnd", `Finished sequence ${targetName}`, void 0, targetName);
|
|
28937
|
+
for (const subResponse of subResult.responses) {
|
|
28938
|
+
addResponse(subResponse);
|
|
28939
|
+
}
|
|
28940
|
+
scriptResults.push(...subResult.scriptResults);
|
|
28941
|
+
assertionResults.push(...subResult.assertionResults);
|
|
28942
|
+
for (const subStep of subResult.steps) {
|
|
28943
|
+
orderedSteps.push({
|
|
28944
|
+
...subStep,
|
|
28945
|
+
stepIndex: orderedSteps.length
|
|
28946
|
+
});
|
|
28947
|
+
}
|
|
28948
|
+
requestIndex += subResult.responses.length;
|
|
28949
|
+
if (!subResult.success) {
|
|
28950
|
+
errors.push(`Sequence "${targetName}" failed`);
|
|
28951
|
+
errors.push(...subResult.errors);
|
|
28952
|
+
return {
|
|
28953
|
+
name: "",
|
|
28954
|
+
success: false,
|
|
28955
|
+
responses,
|
|
28956
|
+
scriptResults,
|
|
28957
|
+
assertionResults,
|
|
28958
|
+
steps: orderedSteps,
|
|
28959
|
+
errors,
|
|
28960
|
+
duration: Date.now() - startTime,
|
|
28961
|
+
variables: runtimeVariables
|
|
28962
|
+
};
|
|
28963
|
+
}
|
|
28964
|
+
continue;
|
|
28965
|
+
}
|
|
28966
|
+
const namedRequest = getNamedRequest(fullDocumentText, targetName);
|
|
28967
|
+
if (!namedRequest) {
|
|
28968
|
+
errors.push(`"${targetName}" is not a named request or sequence`);
|
|
28969
|
+
return {
|
|
28970
|
+
name: "",
|
|
28971
|
+
success: false,
|
|
28972
|
+
responses,
|
|
28973
|
+
scriptResults,
|
|
28974
|
+
assertionResults,
|
|
28975
|
+
steps: orderedSteps,
|
|
28976
|
+
errors,
|
|
28977
|
+
duration: Date.now() - startTime
|
|
28978
|
+
};
|
|
28979
|
+
}
|
|
28980
|
+
requestIndex++;
|
|
28981
|
+
let requestUrl = "";
|
|
28982
|
+
let requestMethod = "";
|
|
28983
|
+
try {
|
|
28984
|
+
let requestParsed;
|
|
28985
|
+
if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(namedRequest.content, apiDefinitions.endpoints)) {
|
|
28986
|
+
const apiRequest = parseApiRequest(
|
|
28987
|
+
namedRequest.content,
|
|
28988
|
+
apiDefinitions.endpoints,
|
|
28989
|
+
apiDefinitions.headerGroups
|
|
28990
|
+
);
|
|
28991
|
+
if (apiRequest) {
|
|
28992
|
+
const endpoint = getEndpoint(
|
|
28993
|
+
{ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
|
|
28994
|
+
apiRequest.endpointName
|
|
28995
|
+
);
|
|
28996
|
+
if (endpoint) {
|
|
28997
|
+
let resolvedPath = endpoint.path;
|
|
28998
|
+
resolvedPath = resolvedPath.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g, (_, varName) => {
|
|
28999
|
+
return runtimeVariables[varName] !== void 0 ? String(runtimeVariables[varName]) : `{{${varName}}}`;
|
|
29000
|
+
});
|
|
29001
|
+
for (const [paramName, paramValue] of Object.entries(apiRequest.params)) {
|
|
29002
|
+
let resolvedValue = paramValue;
|
|
29003
|
+
if (runtimeVariables[paramValue] !== void 0) {
|
|
29004
|
+
resolvedValue = String(runtimeVariables[paramValue]);
|
|
29005
|
+
} else if (paramValue.startsWith("{{") && paramValue.endsWith("}}")) {
|
|
29006
|
+
const varName = paramValue.slice(2, -2);
|
|
29007
|
+
if (runtimeVariables[varName] !== void 0) {
|
|
29008
|
+
resolvedValue = String(runtimeVariables[varName]);
|
|
29009
|
+
}
|
|
29010
|
+
}
|
|
29011
|
+
resolvedPath = resolvedPath.replace(`{${paramName}}`, resolvedValue);
|
|
29012
|
+
}
|
|
29013
|
+
const combinedHeaders = {};
|
|
29014
|
+
for (const groupName of apiRequest.headerGroupNames) {
|
|
29015
|
+
const group = getHeaderGroup(apiDefinitions, groupName);
|
|
29016
|
+
if (group) {
|
|
29017
|
+
const resolvedHeaders = resolveHeaderValues(group, runtimeVariables);
|
|
29018
|
+
Object.assign(combinedHeaders, resolvedHeaders);
|
|
29019
|
+
}
|
|
29020
|
+
}
|
|
29021
|
+
for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
|
|
29022
|
+
let resolved = headerValue;
|
|
29023
|
+
resolved = resolved.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g, (_, varName) => {
|
|
29024
|
+
return runtimeVariables[varName] !== void 0 ? String(runtimeVariables[varName]) : `{{${varName}}}`;
|
|
29025
|
+
});
|
|
29026
|
+
combinedHeaders[headerName] = resolved;
|
|
29027
|
+
}
|
|
29028
|
+
const bodyParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
|
|
29029
|
+
requestParsed = {
|
|
29030
|
+
method: apiRequest.method,
|
|
29031
|
+
url: resolvedPath,
|
|
29032
|
+
headers: combinedHeaders,
|
|
29033
|
+
body: bodyParsed.body
|
|
29034
|
+
};
|
|
29035
|
+
} else {
|
|
29036
|
+
throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
|
|
29037
|
+
}
|
|
29038
|
+
} else {
|
|
29039
|
+
requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
|
|
29040
|
+
}
|
|
29041
|
+
} else {
|
|
29042
|
+
requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
|
|
29043
|
+
if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
|
|
29044
|
+
requestParsed = applyHeaderGroupsToRequest(requestParsed, namedRequest.content, apiDefinitions.headerGroups, runtimeVariables);
|
|
29045
|
+
}
|
|
29046
|
+
}
|
|
29047
|
+
requestUrl = requestParsed.url;
|
|
29048
|
+
requestMethod = requestParsed.method;
|
|
29049
|
+
validatePreparedRequest(
|
|
29050
|
+
requestParsed,
|
|
29051
|
+
buildRequestValidationContext(stepIdx + 1, requestMethod, requestUrl, targetName)
|
|
29052
|
+
);
|
|
29053
|
+
const effectiveRetryCount = parsed.retryCount ?? requestParsed.retryCount;
|
|
29054
|
+
const effectiveBackoffMs = parsed.backoffMs ?? requestParsed.backoffMs ?? 1e3;
|
|
29055
|
+
const retryOpts = effectiveRetryCount ? {
|
|
29056
|
+
retryCount: effectiveRetryCount,
|
|
29057
|
+
backoffMs: effectiveBackoffMs,
|
|
29058
|
+
onRetry: (attempt, total, error, waitMs) => {
|
|
29059
|
+
reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] run ${targetName} - waiting ${waitMs}ms`, void 0);
|
|
29060
|
+
}
|
|
29061
|
+
} : void 0;
|
|
29062
|
+
const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
|
|
29063
|
+
addResponse(response);
|
|
29064
|
+
const stepResult = {
|
|
29065
|
+
type: "request",
|
|
29066
|
+
stepIndex: stepIdx,
|
|
29067
|
+
response,
|
|
29068
|
+
requestMethod: requestParsed.method,
|
|
29069
|
+
requestUrl: requestParsed.url
|
|
29070
|
+
};
|
|
29071
|
+
orderedSteps.push(stepResult);
|
|
29072
|
+
reportProgress(stepIdx, "namedRequest", `run ${targetName} \u2192 ${requestParsed.method} ${requestParsed.url}`, stepResult);
|
|
29073
|
+
const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
|
|
29074
|
+
for (const capture of relevantCaptures) {
|
|
29075
|
+
const value = getValueByPath(response, capture.path);
|
|
29076
|
+
if (value !== void 0) {
|
|
29077
|
+
runtimeVariables[capture.varName] = value;
|
|
29078
|
+
} else {
|
|
29079
|
+
errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
|
|
29080
|
+
}
|
|
29081
|
+
}
|
|
29082
|
+
} catch (error) {
|
|
29083
|
+
const userMessage = formatUserFacingError(
|
|
29084
|
+
error,
|
|
29085
|
+
buildRequestValidationContext(stepIdx + 1, requestMethod || void 0, requestUrl || void 0, targetName)
|
|
29086
|
+
);
|
|
29087
|
+
let errorDetails = `Named request "${targetName}" failed:
|
|
29088
|
+
${indentMultiline(userMessage)}`;
|
|
29089
|
+
if (requestUrl && !/\nRequest:\s/.test(userMessage)) {
|
|
29090
|
+
errorDetails += `
|
|
29091
|
+
Request: ${requestMethod} ${requestUrl}`;
|
|
29092
|
+
}
|
|
29093
|
+
errors.push(errorDetails);
|
|
29094
|
+
await emitFailure(errorDetails, step, stepIdx);
|
|
29095
|
+
return {
|
|
29096
|
+
name: "",
|
|
29097
|
+
success: false,
|
|
29098
|
+
responses,
|
|
29099
|
+
scriptResults,
|
|
29100
|
+
assertionResults,
|
|
29101
|
+
steps: orderedSteps,
|
|
29102
|
+
errors,
|
|
29103
|
+
duration: Date.now() - startTime
|
|
29104
|
+
};
|
|
29105
|
+
}
|
|
29106
|
+
} else if (step.type === "varRunSequence") {
|
|
29107
|
+
const parsed = parseVarRunSequenceCommand(step.content);
|
|
29108
|
+
if (!parsed) {
|
|
29109
|
+
errors.push(`Invalid var run sequence command: ${step.content}`);
|
|
29110
|
+
return {
|
|
29111
|
+
name: "",
|
|
29112
|
+
success: false,
|
|
29113
|
+
responses,
|
|
29114
|
+
scriptResults,
|
|
29115
|
+
assertionResults,
|
|
29116
|
+
steps: orderedSteps,
|
|
29117
|
+
errors,
|
|
29118
|
+
duration: Date.now() - startTime
|
|
29119
|
+
};
|
|
29120
|
+
}
|
|
29121
|
+
const { varName, sequenceName, args: runArgs } = parsed;
|
|
29122
|
+
if (!fullDocumentText) {
|
|
29123
|
+
errors.push(`Cannot run sequence "${sequenceName}": full document text not provided`);
|
|
29124
|
+
return {
|
|
29125
|
+
name: "",
|
|
29126
|
+
success: false,
|
|
29127
|
+
responses,
|
|
29128
|
+
scriptResults,
|
|
29129
|
+
assertionResults,
|
|
29130
|
+
steps: orderedSteps,
|
|
29131
|
+
errors,
|
|
29132
|
+
duration: Date.now() - startTime
|
|
29133
|
+
};
|
|
29134
|
+
}
|
|
29135
|
+
const allSequences = extractSequences(fullDocumentText);
|
|
29136
|
+
const targetSequence = allSequences.find((s) => s.name === sequenceName);
|
|
29137
|
+
if (!targetSequence) {
|
|
29138
|
+
const namedRequest = getNamedRequest(fullDocumentText, sequenceName);
|
|
29139
|
+
if (!namedRequest) {
|
|
29140
|
+
errors.push(`"${sequenceName}" is not a named request or sequence`);
|
|
29141
|
+
return {
|
|
29142
|
+
name: "",
|
|
29143
|
+
success: false,
|
|
29144
|
+
responses,
|
|
29145
|
+
scriptResults,
|
|
29146
|
+
assertionResults,
|
|
29147
|
+
steps: orderedSteps,
|
|
29148
|
+
errors,
|
|
29149
|
+
duration: Date.now() - startTime
|
|
29150
|
+
};
|
|
29151
|
+
}
|
|
29152
|
+
requestIndex++;
|
|
29153
|
+
let requestUrl = "";
|
|
29154
|
+
let requestMethod = "";
|
|
29155
|
+
try {
|
|
29156
|
+
const requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
|
|
29157
|
+
requestUrl = requestParsed.url;
|
|
29158
|
+
requestMethod = requestParsed.method;
|
|
29159
|
+
validatePreparedRequest(
|
|
29160
|
+
requestParsed,
|
|
29161
|
+
buildRequestValidationContext(stepIdx + 1, requestMethod, requestUrl, `${varName} = run ${sequenceName}`)
|
|
29162
|
+
);
|
|
29163
|
+
const effectiveRetryCount = parsed.retryCount ?? requestParsed.retryCount;
|
|
29164
|
+
const effectiveBackoffMs = parsed.backoffMs ?? requestParsed.backoffMs ?? 1e3;
|
|
29165
|
+
const retryOpts = effectiveRetryCount ? {
|
|
29166
|
+
retryCount: effectiveRetryCount,
|
|
29167
|
+
backoffMs: effectiveBackoffMs,
|
|
29168
|
+
onRetry: (attempt, total, error, waitMs) => {
|
|
29169
|
+
reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] var ${varName} = run ${sequenceName} - waiting ${waitMs}ms`, void 0);
|
|
29170
|
+
}
|
|
29171
|
+
} : void 0;
|
|
29172
|
+
const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
|
|
29173
|
+
addResponse(response);
|
|
29174
|
+
responseIndexToVariable.set(responses.length, varName);
|
|
29175
|
+
const stepResult = {
|
|
29176
|
+
type: "request",
|
|
29177
|
+
stepIndex: stepIdx,
|
|
29178
|
+
response,
|
|
29179
|
+
requestMethod: requestParsed.method,
|
|
29180
|
+
requestUrl: requestParsed.url,
|
|
29181
|
+
variableName: varName
|
|
29182
|
+
};
|
|
29183
|
+
orderedSteps.push(stepResult);
|
|
29184
|
+
reportProgress(stepIdx, "namedRequest", `var ${varName} = run ${sequenceName} \u2192 ${requestParsed.method} ${requestParsed.url}`, stepResult);
|
|
29185
|
+
const capturedResponse = {
|
|
29186
|
+
status: response.status,
|
|
29187
|
+
statusText: response.statusText,
|
|
29188
|
+
body: response.body,
|
|
29189
|
+
headers: response.headers
|
|
29190
|
+
};
|
|
29191
|
+
runtimeVariables[varName] = JSON.stringify(capturedResponse);
|
|
29192
|
+
const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
|
|
29193
|
+
for (const capture of relevantCaptures) {
|
|
29194
|
+
const value = getValueByPath(response, capture.path);
|
|
29195
|
+
if (value !== void 0) {
|
|
29196
|
+
runtimeVariables[capture.varName] = value;
|
|
29197
|
+
} else {
|
|
29198
|
+
errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
|
|
29199
|
+
}
|
|
29200
|
+
}
|
|
29201
|
+
} catch (error) {
|
|
29202
|
+
const userMessage = formatUserFacingError(
|
|
29203
|
+
error,
|
|
29204
|
+
buildRequestValidationContext(stepIdx + 1, requestMethod || void 0, requestUrl || void 0, `${varName} = run ${sequenceName}`)
|
|
29205
|
+
);
|
|
29206
|
+
let errorDetails = `Named request "${sequenceName}" failed:
|
|
29207
|
+
${indentMultiline(userMessage)}`;
|
|
29208
|
+
if (requestUrl && !/\nRequest:\s/.test(userMessage)) {
|
|
29209
|
+
errorDetails += `
|
|
29210
|
+
Request: ${requestMethod} ${requestUrl}`;
|
|
29211
|
+
}
|
|
29212
|
+
errors.push(errorDetails);
|
|
29213
|
+
await emitFailure(errorDetails, step, stepIdx);
|
|
29214
|
+
return {
|
|
29215
|
+
name: "",
|
|
29216
|
+
success: false,
|
|
29217
|
+
responses,
|
|
29218
|
+
scriptResults,
|
|
29219
|
+
assertionResults,
|
|
29220
|
+
steps: orderedSteps,
|
|
29221
|
+
errors,
|
|
29222
|
+
duration: Date.now() - startTime
|
|
29223
|
+
};
|
|
29224
|
+
}
|
|
29225
|
+
continue;
|
|
29226
|
+
}
|
|
29227
|
+
const currentCallStack = callStack || [];
|
|
29228
|
+
if (currentCallStack.includes(sequenceName)) {
|
|
29229
|
+
errors.push(`Circular reference detected: ${[...currentCallStack, sequenceName].join(" \u2192 ")}`);
|
|
28809
29230
|
return {
|
|
28810
29231
|
name: "",
|
|
28811
29232
|
success: false,
|
|
@@ -28817,600 +29238,285 @@ ${indentMultiline(userMessage)}`;
|
|
|
28817
29238
|
duration: Date.now() - startTime
|
|
28818
29239
|
};
|
|
28819
29240
|
}
|
|
28820
|
-
sequenceArgs2 =
|
|
28821
|
-
|
|
28822
|
-
|
|
28823
|
-
|
|
28824
|
-
|
|
28825
|
-
|
|
28826
|
-
|
|
28827
|
-
|
|
28828
|
-
|
|
28829
|
-
|
|
28830
|
-
|
|
28831
|
-
|
|
28832
|
-
|
|
28833
|
-
|
|
28834
|
-
}
|
|
28835
|
-
}
|
|
28836
|
-
const subResult = await runSequenceWithJar(
|
|
28837
|
-
targetSequence.content,
|
|
28838
|
-
runtimeVariables,
|
|
28839
|
-
// Pass current runtime variables (includes file vars + any captured)
|
|
28840
|
-
cookieJar,
|
|
28841
|
-
subWorkingDir,
|
|
28842
|
-
fullDocumentText,
|
|
28843
|
-
onProgress,
|
|
28844
|
-
// Pass through progress callback
|
|
28845
|
-
[...currentCallStack, targetName],
|
|
28846
|
-
// Add to call stack for circular detection
|
|
28847
|
-
sequenceArgs2,
|
|
28848
|
-
// Pass bound arguments as initial scope variables
|
|
28849
|
-
apiDefinitions,
|
|
28850
|
-
// Pass API definitions for endpoint resolution
|
|
28851
|
-
tagFilterOptions,
|
|
28852
|
-
// Pass tag filter options for nested sequence filtering
|
|
28853
|
-
sequenceSources,
|
|
28854
|
-
// Pass sequence sources for nested sequences
|
|
28855
|
-
executionContext
|
|
28856
|
-
);
|
|
28857
|
-
reportProgress(stepIdx, "sequenceEnd", `Finished sequence ${targetName}`, void 0, targetName);
|
|
28858
|
-
for (const subResponse of subResult.responses) {
|
|
28859
|
-
addResponse(subResponse);
|
|
28860
|
-
}
|
|
28861
|
-
scriptResults.push(...subResult.scriptResults);
|
|
28862
|
-
assertionResults.push(...subResult.assertionResults);
|
|
28863
|
-
for (const subStep of subResult.steps) {
|
|
28864
|
-
orderedSteps.push({
|
|
28865
|
-
...subStep,
|
|
28866
|
-
stepIndex: orderedSteps.length
|
|
28867
|
-
});
|
|
28868
|
-
}
|
|
28869
|
-
requestIndex += subResult.responses.length;
|
|
28870
|
-
if (!subResult.success) {
|
|
28871
|
-
errors.push(`Sequence "${targetName}" failed`);
|
|
28872
|
-
errors.push(...subResult.errors);
|
|
28873
|
-
return {
|
|
28874
|
-
name: "",
|
|
28875
|
-
success: false,
|
|
28876
|
-
responses,
|
|
28877
|
-
scriptResults,
|
|
28878
|
-
assertionResults,
|
|
28879
|
-
steps: orderedSteps,
|
|
28880
|
-
errors,
|
|
28881
|
-
duration: Date.now() - startTime,
|
|
28882
|
-
variables: runtimeVariables
|
|
28883
|
-
};
|
|
28884
|
-
}
|
|
28885
|
-
continue;
|
|
28886
|
-
}
|
|
28887
|
-
const namedRequest = getNamedRequest(fullDocumentText, targetName);
|
|
28888
|
-
if (!namedRequest) {
|
|
28889
|
-
errors.push(`"${targetName}" is not a named request or sequence`);
|
|
28890
|
-
return {
|
|
28891
|
-
name: "",
|
|
28892
|
-
success: false,
|
|
28893
|
-
responses,
|
|
28894
|
-
scriptResults,
|
|
28895
|
-
assertionResults,
|
|
28896
|
-
steps: orderedSteps,
|
|
28897
|
-
errors,
|
|
28898
|
-
duration: Date.now() - startTime
|
|
28899
|
-
};
|
|
28900
|
-
}
|
|
28901
|
-
requestIndex++;
|
|
28902
|
-
let requestUrl = "";
|
|
28903
|
-
let requestMethod = "";
|
|
28904
|
-
try {
|
|
28905
|
-
let requestParsed;
|
|
28906
|
-
if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(namedRequest.content, apiDefinitions.endpoints)) {
|
|
28907
|
-
const apiRequest = parseApiRequest(
|
|
28908
|
-
namedRequest.content,
|
|
28909
|
-
apiDefinitions.endpoints,
|
|
28910
|
-
apiDefinitions.headerGroups
|
|
28911
|
-
);
|
|
28912
|
-
if (apiRequest) {
|
|
28913
|
-
const endpoint = getEndpoint(
|
|
28914
|
-
{ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
|
|
28915
|
-
apiRequest.endpointName
|
|
28916
|
-
);
|
|
28917
|
-
if (endpoint) {
|
|
28918
|
-
let resolvedPath = endpoint.path;
|
|
28919
|
-
resolvedPath = resolvedPath.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g, (_, varName) => {
|
|
28920
|
-
return runtimeVariables[varName] !== void 0 ? String(runtimeVariables[varName]) : `{{${varName}}}`;
|
|
28921
|
-
});
|
|
28922
|
-
for (const [paramName, paramValue] of Object.entries(apiRequest.params)) {
|
|
28923
|
-
let resolvedValue = paramValue;
|
|
28924
|
-
if (runtimeVariables[paramValue] !== void 0) {
|
|
28925
|
-
resolvedValue = String(runtimeVariables[paramValue]);
|
|
28926
|
-
} else if (paramValue.startsWith("{{") && paramValue.endsWith("}}")) {
|
|
28927
|
-
const varName = paramValue.slice(2, -2);
|
|
28928
|
-
if (runtimeVariables[varName] !== void 0) {
|
|
28929
|
-
resolvedValue = String(runtimeVariables[varName]);
|
|
28930
|
-
}
|
|
28931
|
-
}
|
|
28932
|
-
resolvedPath = resolvedPath.replace(`{${paramName}}`, resolvedValue);
|
|
28933
|
-
}
|
|
28934
|
-
const combinedHeaders = {};
|
|
28935
|
-
for (const groupName of apiRequest.headerGroupNames) {
|
|
28936
|
-
const group = getHeaderGroup(apiDefinitions, groupName);
|
|
28937
|
-
if (group) {
|
|
28938
|
-
const resolvedHeaders = resolveHeaderValues(group, runtimeVariables);
|
|
28939
|
-
Object.assign(combinedHeaders, resolvedHeaders);
|
|
28940
|
-
}
|
|
28941
|
-
}
|
|
28942
|
-
for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
|
|
28943
|
-
let resolved = headerValue;
|
|
28944
|
-
resolved = resolved.replace(/\{\{([a-zA-Z_][a-zA-Z0-9_]*)\}\}/g, (_, varName) => {
|
|
28945
|
-
return runtimeVariables[varName] !== void 0 ? String(runtimeVariables[varName]) : `{{${varName}}}`;
|
|
28946
|
-
});
|
|
28947
|
-
combinedHeaders[headerName] = resolved;
|
|
28948
|
-
}
|
|
28949
|
-
const bodyParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
|
|
28950
|
-
requestParsed = {
|
|
28951
|
-
method: apiRequest.method,
|
|
28952
|
-
url: resolvedPath,
|
|
28953
|
-
headers: combinedHeaders,
|
|
28954
|
-
body: bodyParsed.body
|
|
29241
|
+
let sequenceArgs2 = {};
|
|
29242
|
+
if (targetSequence.parameters.length > 0 || runArgs.length > 0) {
|
|
29243
|
+
const bindResult = bindSequenceArguments(targetSequence.parameters, runArgs, runtimeVariables);
|
|
29244
|
+
if ("error" in bindResult) {
|
|
29245
|
+
errors.push(`Error calling sequence "${sequenceName}": ${bindResult.error}`);
|
|
29246
|
+
return {
|
|
29247
|
+
name: "",
|
|
29248
|
+
success: false,
|
|
29249
|
+
responses,
|
|
29250
|
+
scriptResults,
|
|
29251
|
+
assertionResults,
|
|
29252
|
+
steps: orderedSteps,
|
|
29253
|
+
errors,
|
|
29254
|
+
duration: Date.now() - startTime
|
|
28955
29255
|
};
|
|
28956
|
-
} else {
|
|
28957
|
-
throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
|
|
28958
29256
|
}
|
|
28959
|
-
|
|
28960
|
-
requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
|
|
28961
|
-
}
|
|
28962
|
-
} else {
|
|
28963
|
-
requestParsed = parserHttpRequest(namedRequest.content, runtimeVariables);
|
|
28964
|
-
if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
|
|
28965
|
-
requestParsed = applyHeaderGroupsToRequest(requestParsed, namedRequest.content, apiDefinitions.headerGroups, runtimeVariables);
|
|
29257
|
+
sequenceArgs2 = bindResult.variables;
|
|
28966
29258
|
}
|
|
28967
|
-
|
|
28968
|
-
|
|
28969
|
-
|
|
28970
|
-
|
|
28971
|
-
requestParsed,
|
|
28972
|
-
buildRequestValidationContext(stepIdx + 1, requestMethod, requestUrl, targetName)
|
|
28973
|
-
);
|
|
28974
|
-
const effectiveRetryCount = parsed.retryCount ?? requestParsed.retryCount;
|
|
28975
|
-
const effectiveBackoffMs = parsed.backoffMs ?? requestParsed.backoffMs ?? 1e3;
|
|
28976
|
-
const retryOpts = effectiveRetryCount ? {
|
|
28977
|
-
retryCount: effectiveRetryCount,
|
|
28978
|
-
backoffMs: effectiveBackoffMs,
|
|
28979
|
-
onRetry: (attempt, total, error, waitMs) => {
|
|
28980
|
-
reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] run ${targetName} - waiting ${waitMs}ms`, void 0);
|
|
29259
|
+
if (tagFilterOptions && tagFilterOptions.filters.length > 0) {
|
|
29260
|
+
if (!sequenceMatchesTags(targetSequence, tagFilterOptions)) {
|
|
29261
|
+
continue;
|
|
29262
|
+
}
|
|
28981
29263
|
}
|
|
28982
|
-
|
|
28983
|
-
|
|
28984
|
-
|
|
28985
|
-
|
|
28986
|
-
|
|
28987
|
-
|
|
28988
|
-
|
|
28989
|
-
|
|
28990
|
-
requestUrl: requestParsed.url
|
|
28991
|
-
};
|
|
28992
|
-
orderedSteps.push(stepResult);
|
|
28993
|
-
reportProgress(stepIdx, "namedRequest", `run ${targetName} \u2192 ${requestParsed.method} ${requestParsed.url}`, stepResult);
|
|
28994
|
-
const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
|
|
28995
|
-
for (const capture of relevantCaptures) {
|
|
28996
|
-
const value = getValueByPath(response, capture.path);
|
|
28997
|
-
if (value !== void 0) {
|
|
28998
|
-
runtimeVariables[capture.varName] = value;
|
|
28999
|
-
} else {
|
|
29000
|
-
errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
|
|
29264
|
+
reportProgress(stepIdx, "sequenceStart", `Starting sequence ${sequenceName}`, void 0, sequenceName);
|
|
29265
|
+
let subWorkingDir = workingDir;
|
|
29266
|
+
if (sequenceSources) {
|
|
29267
|
+
const sourceFile = sequenceSources.get(sequenceName.toLowerCase());
|
|
29268
|
+
if (sourceFile) {
|
|
29269
|
+
const path10 = await import("path");
|
|
29270
|
+
subWorkingDir = path10.dirname(sourceFile);
|
|
29271
|
+
}
|
|
29001
29272
|
}
|
|
29002
|
-
|
|
29003
|
-
|
|
29004
|
-
|
|
29005
|
-
|
|
29006
|
-
|
|
29007
|
-
|
|
29008
|
-
|
|
29009
|
-
${indentMultiline(userMessage)}`;
|
|
29010
|
-
if (requestUrl && !/\nRequest:\s/.test(userMessage)) {
|
|
29011
|
-
errorDetails += `
|
|
29012
|
-
Request: ${requestMethod} ${requestUrl}`;
|
|
29013
|
-
}
|
|
29014
|
-
errors.push(errorDetails);
|
|
29015
|
-
return {
|
|
29016
|
-
name: "",
|
|
29017
|
-
success: false,
|
|
29018
|
-
responses,
|
|
29019
|
-
scriptResults,
|
|
29020
|
-
assertionResults,
|
|
29021
|
-
steps: orderedSteps,
|
|
29022
|
-
errors,
|
|
29023
|
-
duration: Date.now() - startTime
|
|
29024
|
-
};
|
|
29025
|
-
}
|
|
29026
|
-
} else if (step.type === "varRunSequence") {
|
|
29027
|
-
const parsed = parseVarRunSequenceCommand(step.content);
|
|
29028
|
-
if (!parsed) {
|
|
29029
|
-
errors.push(`Invalid var run sequence command: ${step.content}`);
|
|
29030
|
-
return {
|
|
29031
|
-
name: "",
|
|
29032
|
-
success: false,
|
|
29033
|
-
responses,
|
|
29034
|
-
scriptResults,
|
|
29035
|
-
assertionResults,
|
|
29036
|
-
steps: orderedSteps,
|
|
29037
|
-
errors,
|
|
29038
|
-
duration: Date.now() - startTime
|
|
29039
|
-
};
|
|
29040
|
-
}
|
|
29041
|
-
const { varName, sequenceName, args: runArgs } = parsed;
|
|
29042
|
-
if (!fullDocumentText) {
|
|
29043
|
-
errors.push(`Cannot run sequence "${sequenceName}": full document text not provided`);
|
|
29044
|
-
return {
|
|
29045
|
-
name: "",
|
|
29046
|
-
success: false,
|
|
29047
|
-
responses,
|
|
29048
|
-
scriptResults,
|
|
29049
|
-
assertionResults,
|
|
29050
|
-
steps: orderedSteps,
|
|
29051
|
-
errors,
|
|
29052
|
-
duration: Date.now() - startTime
|
|
29053
|
-
};
|
|
29054
|
-
}
|
|
29055
|
-
const allSequences = extractSequences(fullDocumentText);
|
|
29056
|
-
const targetSequence = allSequences.find((s) => s.name === sequenceName);
|
|
29057
|
-
if (!targetSequence) {
|
|
29058
|
-
const namedRequest = getNamedRequest(fullDocumentText, sequenceName);
|
|
29059
|
-
if (!namedRequest) {
|
|
29060
|
-
errors.push(`"${sequenceName}" is not a named request or sequence`);
|
|
29061
|
-
return {
|
|
29062
|
-
name: "",
|
|
29063
|
-
success: false,
|
|
29064
|
-
responses,
|
|
29065
|
-
scriptResults,
|
|
29066
|
-
assertionResults,
|
|
29067
|
-
steps: orderedSteps,
|
|
29068
|
-
errors,
|
|
29069
|
-
duration: Date.now() - startTime
|
|
29273
|
+
const targetLocation = executionContext?.sequenceLocationIndex?.get(sequenceName.toLowerCase());
|
|
29274
|
+
const childExecutionContext = {
|
|
29275
|
+
filePath: targetLocation?.filePath ?? executionContext?.filePath,
|
|
29276
|
+
sequenceName,
|
|
29277
|
+
environment: executionContext?.environment,
|
|
29278
|
+
sequenceStartLine: targetLocation?.startLine ?? targetSequence.startLine,
|
|
29279
|
+
sequenceLocationIndex: executionContext?.sequenceLocationIndex
|
|
29070
29280
|
};
|
|
29071
|
-
|
|
29072
|
-
|
|
29073
|
-
|
|
29074
|
-
|
|
29075
|
-
|
|
29076
|
-
|
|
29077
|
-
|
|
29078
|
-
|
|
29079
|
-
|
|
29080
|
-
|
|
29081
|
-
|
|
29281
|
+
const subResult = await runSequenceWithJar(
|
|
29282
|
+
targetSequence.content,
|
|
29283
|
+
runtimeVariables,
|
|
29284
|
+
cookieJar,
|
|
29285
|
+
subWorkingDir,
|
|
29286
|
+
fullDocumentText,
|
|
29287
|
+
onProgress,
|
|
29288
|
+
[...currentCallStack, sequenceName],
|
|
29289
|
+
sequenceArgs2,
|
|
29290
|
+
// Pass bound arguments
|
|
29291
|
+
apiDefinitions,
|
|
29292
|
+
// Pass API definitions for endpoint resolution
|
|
29293
|
+
tagFilterOptions,
|
|
29294
|
+
// Pass tag filter options for nested sequence filtering
|
|
29295
|
+
sequenceSources,
|
|
29296
|
+
// Pass sequence sources for nested sequences
|
|
29297
|
+
childExecutionContext,
|
|
29298
|
+
debugHooks
|
|
29082
29299
|
);
|
|
29083
|
-
|
|
29084
|
-
const
|
|
29085
|
-
|
|
29086
|
-
retryCount: effectiveRetryCount,
|
|
29087
|
-
backoffMs: effectiveBackoffMs,
|
|
29088
|
-
onRetry: (attempt, total, error, waitMs) => {
|
|
29089
|
-
reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] var ${varName} = run ${sequenceName} - waiting ${waitMs}ms`, void 0);
|
|
29090
|
-
}
|
|
29091
|
-
} : void 0;
|
|
29092
|
-
const response = cookieJar ? await sendRequestWithJar(requestParsed, cookieJar, retryOpts) : await sendRequest(requestParsed, retryOpts);
|
|
29093
|
-
addResponse(response);
|
|
29094
|
-
responseIndexToVariable.set(responses.length, varName);
|
|
29095
|
-
const stepResult = {
|
|
29096
|
-
type: "request",
|
|
29097
|
-
stepIndex: stepIdx,
|
|
29098
|
-
response,
|
|
29099
|
-
requestMethod: requestParsed.method,
|
|
29100
|
-
requestUrl: requestParsed.url,
|
|
29101
|
-
variableName: varName
|
|
29102
|
-
};
|
|
29103
|
-
orderedSteps.push(stepResult);
|
|
29104
|
-
reportProgress(stepIdx, "namedRequest", `var ${varName} = run ${sequenceName} \u2192 ${requestParsed.method} ${requestParsed.url}`, stepResult);
|
|
29105
|
-
const capturedResponse = {
|
|
29106
|
-
status: response.status,
|
|
29107
|
-
statusText: response.statusText,
|
|
29108
|
-
body: response.body,
|
|
29109
|
-
headers: response.headers
|
|
29110
|
-
};
|
|
29111
|
-
runtimeVariables[varName] = JSON.stringify(capturedResponse);
|
|
29112
|
-
const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
|
|
29113
|
-
for (const capture of relevantCaptures) {
|
|
29114
|
-
const value = getValueByPath(response, capture.path);
|
|
29115
|
-
if (value !== void 0) {
|
|
29116
|
-
runtimeVariables[capture.varName] = value;
|
|
29117
|
-
} else {
|
|
29118
|
-
errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
|
|
29119
|
-
}
|
|
29300
|
+
reportProgress(stepIdx, "sequenceEnd", `Finished sequence ${sequenceName}`, void 0, sequenceName);
|
|
29301
|
+
for (const subResponse of subResult.responses) {
|
|
29302
|
+
addResponse(subResponse);
|
|
29120
29303
|
}
|
|
29121
|
-
|
|
29122
|
-
|
|
29123
|
-
|
|
29124
|
-
|
|
29125
|
-
|
|
29126
|
-
|
|
29127
|
-
|
|
29128
|
-
if (requestUrl && !/\nRequest:\s/.test(userMessage)) {
|
|
29129
|
-
errorDetails += `
|
|
29130
|
-
Request: ${requestMethod} ${requestUrl}`;
|
|
29304
|
+
scriptResults.push(...subResult.scriptResults);
|
|
29305
|
+
assertionResults.push(...subResult.assertionResults);
|
|
29306
|
+
for (const subStep of subResult.steps) {
|
|
29307
|
+
orderedSteps.push({
|
|
29308
|
+
...subStep,
|
|
29309
|
+
stepIndex: orderedSteps.length
|
|
29310
|
+
});
|
|
29131
29311
|
}
|
|
29132
|
-
|
|
29133
|
-
|
|
29134
|
-
|
|
29135
|
-
|
|
29136
|
-
|
|
29137
|
-
|
|
29138
|
-
|
|
29139
|
-
|
|
29140
|
-
|
|
29141
|
-
|
|
29142
|
-
|
|
29143
|
-
|
|
29144
|
-
|
|
29145
|
-
|
|
29146
|
-
|
|
29147
|
-
|
|
29148
|
-
|
|
29149
|
-
|
|
29150
|
-
|
|
29151
|
-
success: false,
|
|
29152
|
-
responses,
|
|
29153
|
-
scriptResults,
|
|
29154
|
-
assertionResults,
|
|
29155
|
-
steps: orderedSteps,
|
|
29156
|
-
errors,
|
|
29157
|
-
duration: Date.now() - startTime
|
|
29158
|
-
};
|
|
29159
|
-
}
|
|
29160
|
-
let sequenceArgs2 = {};
|
|
29161
|
-
if (targetSequence.parameters.length > 0 || runArgs.length > 0) {
|
|
29162
|
-
const bindResult = bindSequenceArguments(targetSequence.parameters, runArgs, runtimeVariables);
|
|
29163
|
-
if ("error" in bindResult) {
|
|
29164
|
-
errors.push(`Error calling sequence "${sequenceName}": ${bindResult.error}`);
|
|
29165
|
-
return {
|
|
29166
|
-
name: "",
|
|
29167
|
-
success: false,
|
|
29168
|
-
responses,
|
|
29169
|
-
scriptResults,
|
|
29170
|
-
assertionResults,
|
|
29171
|
-
steps: orderedSteps,
|
|
29172
|
-
errors,
|
|
29173
|
-
duration: Date.now() - startTime
|
|
29174
|
-
};
|
|
29175
|
-
}
|
|
29176
|
-
sequenceArgs2 = bindResult.variables;
|
|
29177
|
-
}
|
|
29178
|
-
if (tagFilterOptions && tagFilterOptions.filters.length > 0) {
|
|
29179
|
-
if (!sequenceMatchesTags(targetSequence, tagFilterOptions)) {
|
|
29180
|
-
continue;
|
|
29181
|
-
}
|
|
29182
|
-
}
|
|
29183
|
-
reportProgress(stepIdx, "sequenceStart", `Starting sequence ${sequenceName}`, void 0, sequenceName);
|
|
29184
|
-
let subWorkingDir = workingDir;
|
|
29185
|
-
if (sequenceSources) {
|
|
29186
|
-
const sourceFile = sequenceSources.get(sequenceName.toLowerCase());
|
|
29187
|
-
if (sourceFile) {
|
|
29188
|
-
const path10 = await import("path");
|
|
29189
|
-
subWorkingDir = path10.dirname(sourceFile);
|
|
29190
|
-
}
|
|
29191
|
-
}
|
|
29192
|
-
const subResult = await runSequenceWithJar(
|
|
29193
|
-
targetSequence.content,
|
|
29194
|
-
runtimeVariables,
|
|
29195
|
-
cookieJar,
|
|
29196
|
-
subWorkingDir,
|
|
29197
|
-
fullDocumentText,
|
|
29198
|
-
onProgress,
|
|
29199
|
-
[...currentCallStack, sequenceName],
|
|
29200
|
-
sequenceArgs2,
|
|
29201
|
-
// Pass bound arguments
|
|
29202
|
-
apiDefinitions,
|
|
29203
|
-
// Pass API definitions for endpoint resolution
|
|
29204
|
-
tagFilterOptions,
|
|
29205
|
-
// Pass tag filter options for nested sequence filtering
|
|
29206
|
-
sequenceSources,
|
|
29207
|
-
// Pass sequence sources for nested sequences
|
|
29208
|
-
executionContext
|
|
29209
|
-
);
|
|
29210
|
-
reportProgress(stepIdx, "sequenceEnd", `Finished sequence ${sequenceName}`, void 0, sequenceName);
|
|
29211
|
-
for (const subResponse of subResult.responses) {
|
|
29212
|
-
addResponse(subResponse);
|
|
29213
|
-
}
|
|
29214
|
-
scriptResults.push(...subResult.scriptResults);
|
|
29215
|
-
assertionResults.push(...subResult.assertionResults);
|
|
29216
|
-
for (const subStep of subResult.steps) {
|
|
29217
|
-
orderedSteps.push({
|
|
29218
|
-
...subStep,
|
|
29219
|
-
stepIndex: orderedSteps.length
|
|
29220
|
-
});
|
|
29221
|
-
}
|
|
29222
|
-
const returnExpressions = extractReturnVariables(targetSequence.content);
|
|
29223
|
-
if (returnExpressions && returnExpressions.length > 0 && subResult.variables) {
|
|
29224
|
-
if (returnExpressions.length === 1) {
|
|
29225
|
-
const resolved = resolveReturnExpression(returnExpressions[0], subResult.variables);
|
|
29226
|
-
if (resolved) {
|
|
29227
|
-
runtimeVariables[varName] = resolved.value;
|
|
29312
|
+
const returnExpressions = extractReturnVariables(targetSequence.content);
|
|
29313
|
+
if (returnExpressions && returnExpressions.length > 0 && subResult.variables) {
|
|
29314
|
+
if (returnExpressions.length === 1) {
|
|
29315
|
+
const resolved = resolveReturnExpression(returnExpressions[0], subResult.variables);
|
|
29316
|
+
if (resolved) {
|
|
29317
|
+
runtimeVariables[varName] = resolved.value;
|
|
29318
|
+
} else {
|
|
29319
|
+
runtimeVariables[varName] = "";
|
|
29320
|
+
}
|
|
29321
|
+
} else {
|
|
29322
|
+
const returnedObj = {};
|
|
29323
|
+
for (const expr of returnExpressions) {
|
|
29324
|
+
const resolved = resolveReturnExpression(expr, subResult.variables);
|
|
29325
|
+
if (resolved) {
|
|
29326
|
+
returnedObj[resolved.key] = resolved.value;
|
|
29327
|
+
}
|
|
29328
|
+
}
|
|
29329
|
+
runtimeVariables[varName] = JSON.stringify(returnedObj);
|
|
29330
|
+
}
|
|
29228
29331
|
} else {
|
|
29229
29332
|
runtimeVariables[varName] = "";
|
|
29230
29333
|
}
|
|
29231
|
-
|
|
29232
|
-
|
|
29233
|
-
|
|
29234
|
-
|
|
29235
|
-
|
|
29236
|
-
|
|
29237
|
-
|
|
29334
|
+
requestIndex += subResult.responses.length;
|
|
29335
|
+
if (!subResult.success) {
|
|
29336
|
+
errors.push(`Sequence "${sequenceName}" failed`);
|
|
29337
|
+
errors.push(...subResult.errors);
|
|
29338
|
+
return {
|
|
29339
|
+
name: "",
|
|
29340
|
+
success: false,
|
|
29341
|
+
responses,
|
|
29342
|
+
scriptResults,
|
|
29343
|
+
assertionResults,
|
|
29344
|
+
steps: orderedSteps,
|
|
29345
|
+
errors,
|
|
29346
|
+
duration: Date.now() - startTime,
|
|
29347
|
+
variables: runtimeVariables
|
|
29348
|
+
};
|
|
29238
29349
|
}
|
|
29239
|
-
|
|
29240
|
-
}
|
|
29241
|
-
|
|
29242
|
-
|
|
29243
|
-
|
|
29244
|
-
|
|
29245
|
-
|
|
29246
|
-
|
|
29247
|
-
|
|
29248
|
-
|
|
29249
|
-
|
|
29250
|
-
success: false,
|
|
29251
|
-
responses,
|
|
29252
|
-
scriptResults,
|
|
29253
|
-
assertionResults,
|
|
29254
|
-
steps: orderedSteps,
|
|
29255
|
-
errors,
|
|
29256
|
-
duration: Date.now() - startTime,
|
|
29257
|
-
variables: runtimeVariables
|
|
29258
|
-
};
|
|
29259
|
-
}
|
|
29260
|
-
continue;
|
|
29261
|
-
} else {
|
|
29262
|
-
requestIndex++;
|
|
29263
|
-
let parsed;
|
|
29264
|
-
let requestDescription = "";
|
|
29265
|
-
try {
|
|
29266
|
-
if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(step.content, apiDefinitions.endpoints)) {
|
|
29267
|
-
const apiRequest = parseApiRequest(
|
|
29268
|
-
step.content,
|
|
29269
|
-
apiDefinitions.endpoints,
|
|
29270
|
-
apiDefinitions.headerGroups
|
|
29271
|
-
);
|
|
29272
|
-
if (apiRequest) {
|
|
29273
|
-
const endpoint = getEndpoint(
|
|
29274
|
-
{ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
|
|
29275
|
-
apiRequest.endpointName
|
|
29276
|
-
);
|
|
29277
|
-
if (!endpoint) {
|
|
29278
|
-
throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
|
|
29279
|
-
}
|
|
29280
|
-
const pathWithVars = substituteVariables(endpoint.path, runtimeVariables);
|
|
29281
|
-
const resolvedParams = {};
|
|
29282
|
-
for (const [key, value] of Object.entries(apiRequest.params)) {
|
|
29283
|
-
const bareResolved = resolveBareVariables(value, runtimeVariables);
|
|
29284
|
-
resolvedParams[key] = substituteVariables(bareResolved, runtimeVariables);
|
|
29285
|
-
}
|
|
29286
|
-
let resolvedPath = pathWithVars;
|
|
29287
|
-
for (const paramName of endpoint.parameters) {
|
|
29288
|
-
const value = resolvedParams[paramName];
|
|
29289
|
-
if (value !== void 0) {
|
|
29290
|
-
resolvedPath = resolvedPath.replace(`{${paramName}}`, value);
|
|
29291
|
-
}
|
|
29292
|
-
}
|
|
29293
|
-
const combinedHeaders = {};
|
|
29294
|
-
for (const groupName of apiRequest.headerGroupNames) {
|
|
29295
|
-
const group = getHeaderGroup(
|
|
29296
|
-
{ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
|
|
29297
|
-
groupName
|
|
29350
|
+
continue;
|
|
29351
|
+
} else {
|
|
29352
|
+
requestIndex++;
|
|
29353
|
+
let parsed;
|
|
29354
|
+
let requestDescription = "";
|
|
29355
|
+
try {
|
|
29356
|
+
if (apiDefinitions && apiDefinitions.endpoints.length > 0 && isApiRequestLine(step.content, apiDefinitions.endpoints)) {
|
|
29357
|
+
const apiRequest = parseApiRequest(
|
|
29358
|
+
step.content,
|
|
29359
|
+
apiDefinitions.endpoints,
|
|
29360
|
+
apiDefinitions.headerGroups
|
|
29298
29361
|
);
|
|
29299
|
-
if (
|
|
29300
|
-
const
|
|
29301
|
-
|
|
29362
|
+
if (apiRequest) {
|
|
29363
|
+
const endpoint = getEndpoint(
|
|
29364
|
+
{ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
|
|
29365
|
+
apiRequest.endpointName
|
|
29366
|
+
);
|
|
29367
|
+
if (!endpoint) {
|
|
29368
|
+
throw new Error(`Unknown endpoint: ${apiRequest.endpointName}`);
|
|
29369
|
+
}
|
|
29370
|
+
const pathWithVars = substituteVariables(endpoint.path, runtimeVariables);
|
|
29371
|
+
const resolvedParams = {};
|
|
29372
|
+
for (const [key, value] of Object.entries(apiRequest.params)) {
|
|
29373
|
+
const bareResolved = resolveBareVariables(value, runtimeVariables);
|
|
29374
|
+
resolvedParams[key] = substituteVariables(bareResolved, runtimeVariables);
|
|
29375
|
+
}
|
|
29376
|
+
let resolvedPath = pathWithVars;
|
|
29377
|
+
for (const paramName of endpoint.parameters) {
|
|
29378
|
+
const value = resolvedParams[paramName];
|
|
29379
|
+
if (value !== void 0) {
|
|
29380
|
+
resolvedPath = resolvedPath.replace(`{${paramName}}`, value);
|
|
29381
|
+
}
|
|
29382
|
+
}
|
|
29383
|
+
const combinedHeaders = {};
|
|
29384
|
+
for (const groupName of apiRequest.headerGroupNames) {
|
|
29385
|
+
const group = getHeaderGroup(
|
|
29386
|
+
{ headerGroups: apiDefinitions.headerGroups, endpoints: apiDefinitions.endpoints },
|
|
29387
|
+
groupName
|
|
29388
|
+
);
|
|
29389
|
+
if (group) {
|
|
29390
|
+
const resolvedHeaders = resolveHeaderValues(group, runtimeVariables);
|
|
29391
|
+
Object.assign(combinedHeaders, resolvedHeaders);
|
|
29392
|
+
}
|
|
29393
|
+
}
|
|
29394
|
+
for (const [headerName, headerValue] of Object.entries(apiRequest.inlineHeaders)) {
|
|
29395
|
+
combinedHeaders[headerName] = substituteVariables(headerValue, runtimeVariables);
|
|
29396
|
+
}
|
|
29397
|
+
let resolvedBody;
|
|
29398
|
+
if (apiRequest.body) {
|
|
29399
|
+
resolvedBody = substituteVariables(apiRequest.body, runtimeVariables);
|
|
29400
|
+
}
|
|
29401
|
+
parsed = {
|
|
29402
|
+
method: apiRequest.method,
|
|
29403
|
+
url: resolvedPath,
|
|
29404
|
+
headers: combinedHeaders,
|
|
29405
|
+
body: resolvedBody
|
|
29406
|
+
};
|
|
29407
|
+
requestDescription = `${apiRequest.endpointName}(${Object.values(resolvedParams).join(", ")}) \u2192 ${parsed.method} ${parsed.url}`;
|
|
29408
|
+
} else {
|
|
29409
|
+
parsed = parserHttpRequest(step.content, runtimeVariables);
|
|
29410
|
+
requestDescription = `${parsed.method} ${parsed.url}`;
|
|
29411
|
+
if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
|
|
29412
|
+
parsed = applyHeaderGroupsToRequest(parsed, step.content, apiDefinitions.headerGroups, runtimeVariables);
|
|
29413
|
+
}
|
|
29414
|
+
}
|
|
29415
|
+
} else {
|
|
29416
|
+
parsed = parserHttpRequest(step.content, runtimeVariables);
|
|
29417
|
+
requestDescription = `${parsed.method} ${parsed.url}`;
|
|
29418
|
+
if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
|
|
29419
|
+
parsed = applyHeaderGroupsToRequest(parsed, step.content, apiDefinitions.headerGroups, runtimeVariables);
|
|
29420
|
+
}
|
|
29421
|
+
if (parsed.body) {
|
|
29422
|
+
const bodyTrimmed = parsed.body.trim();
|
|
29423
|
+
const bareVarMatch = bodyTrimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)$/);
|
|
29424
|
+
if (bareVarMatch && bareVarMatch[1] in runtimeVariables) {
|
|
29425
|
+
const varValue = runtimeVariables[bareVarMatch[1]];
|
|
29426
|
+
if (typeof varValue === "object" && varValue !== null) {
|
|
29427
|
+
parsed.body = JSON.stringify(varValue);
|
|
29428
|
+
} else if (typeof varValue === "string") {
|
|
29429
|
+
parsed.body = varValue;
|
|
29430
|
+
} else {
|
|
29431
|
+
parsed.body = String(varValue);
|
|
29432
|
+
}
|
|
29433
|
+
}
|
|
29302
29434
|
}
|
|
29303
29435
|
}
|
|
29304
|
-
|
|
29305
|
-
|
|
29306
|
-
|
|
29307
|
-
|
|
29308
|
-
|
|
29309
|
-
|
|
29310
|
-
|
|
29311
|
-
|
|
29312
|
-
|
|
29313
|
-
|
|
29314
|
-
|
|
29315
|
-
|
|
29436
|
+
const { retryCount, backoffMs } = extractRetryOptions(step.content);
|
|
29437
|
+
validatePreparedRequest(
|
|
29438
|
+
parsed,
|
|
29439
|
+
buildRequestValidationContext(stepIdx + 1, parsed.method, parsed.url)
|
|
29440
|
+
);
|
|
29441
|
+
const retryOpts = retryCount ?? parsed.retryCount ? {
|
|
29442
|
+
retryCount: retryCount ?? parsed.retryCount ?? 0,
|
|
29443
|
+
backoffMs: backoffMs ?? parsed.backoffMs ?? 1e3,
|
|
29444
|
+
onRetry: (attempt, total, error, waitMs) => {
|
|
29445
|
+
reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
|
|
29446
|
+
}
|
|
29447
|
+
} : void 0;
|
|
29448
|
+
const response = cookieJar ? await sendRequestWithJar(parsed, cookieJar, retryOpts) : await sendRequest(parsed, retryOpts);
|
|
29449
|
+
addResponse(response);
|
|
29450
|
+
const stepResult = {
|
|
29451
|
+
type: "request",
|
|
29452
|
+
stepIndex: stepIdx,
|
|
29453
|
+
response,
|
|
29454
|
+
requestMethod: parsed.method,
|
|
29455
|
+
requestUrl: parsed.url
|
|
29316
29456
|
};
|
|
29317
|
-
|
|
29318
|
-
|
|
29319
|
-
|
|
29320
|
-
|
|
29321
|
-
|
|
29322
|
-
|
|
29323
|
-
|
|
29324
|
-
}
|
|
29325
|
-
} else {
|
|
29326
|
-
parsed = parserHttpRequest(step.content, runtimeVariables);
|
|
29327
|
-
requestDescription = `${parsed.method} ${parsed.url}`;
|
|
29328
|
-
if (apiDefinitions && apiDefinitions.headerGroups.length > 0) {
|
|
29329
|
-
parsed = applyHeaderGroupsToRequest(parsed, step.content, apiDefinitions.headerGroups, runtimeVariables);
|
|
29330
|
-
}
|
|
29331
|
-
if (parsed.body) {
|
|
29332
|
-
const bodyTrimmed = parsed.body.trim();
|
|
29333
|
-
const bareVarMatch = bodyTrimmed.match(/^([a-zA-Z_][a-zA-Z0-9_]*)$/);
|
|
29334
|
-
if (bareVarMatch && bareVarMatch[1] in runtimeVariables) {
|
|
29335
|
-
const varValue = runtimeVariables[bareVarMatch[1]];
|
|
29336
|
-
if (typeof varValue === "object" && varValue !== null) {
|
|
29337
|
-
parsed.body = JSON.stringify(varValue);
|
|
29338
|
-
} else if (typeof varValue === "string") {
|
|
29339
|
-
parsed.body = varValue;
|
|
29457
|
+
orderedSteps.push(stepResult);
|
|
29458
|
+
reportProgress(stepIdx, "request", requestDescription, stepResult);
|
|
29459
|
+
const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
|
|
29460
|
+
for (const capture of relevantCaptures) {
|
|
29461
|
+
const value = getValueByPath(response, capture.path);
|
|
29462
|
+
if (value !== void 0) {
|
|
29463
|
+
runtimeVariables[capture.varName] = value;
|
|
29340
29464
|
} else {
|
|
29341
|
-
|
|
29465
|
+
errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
|
|
29342
29466
|
}
|
|
29343
29467
|
}
|
|
29344
|
-
}
|
|
29345
|
-
|
|
29346
|
-
|
|
29347
|
-
|
|
29348
|
-
|
|
29349
|
-
|
|
29350
|
-
);
|
|
29351
|
-
const retryOpts = retryCount ?? parsed.retryCount ? {
|
|
29352
|
-
retryCount: retryCount ?? parsed.retryCount ?? 0,
|
|
29353
|
-
backoffMs: backoffMs ?? parsed.backoffMs ?? 1e3,
|
|
29354
|
-
onRetry: (attempt, total, error, waitMs) => {
|
|
29355
|
-
reportProgress(stepIdx, "request", `[Retry ${attempt}/${total}] ${requestDescription} - waiting ${waitMs}ms`, void 0);
|
|
29356
|
-
}
|
|
29357
|
-
} : void 0;
|
|
29358
|
-
const response = cookieJar ? await sendRequestWithJar(parsed, cookieJar, retryOpts) : await sendRequest(parsed, retryOpts);
|
|
29359
|
-
addResponse(response);
|
|
29360
|
-
const stepResult = {
|
|
29361
|
-
type: "request",
|
|
29362
|
-
stepIndex: stepIdx,
|
|
29363
|
-
response,
|
|
29364
|
-
requestMethod: parsed.method,
|
|
29365
|
-
requestUrl: parsed.url
|
|
29366
|
-
};
|
|
29367
|
-
orderedSteps.push(stepResult);
|
|
29368
|
-
reportProgress(stepIdx, "request", requestDescription, stepResult);
|
|
29369
|
-
const relevantCaptures = captures.filter((c) => c.afterRequest === requestIndex);
|
|
29370
|
-
for (const capture of relevantCaptures) {
|
|
29371
|
-
const value = getValueByPath(response, capture.path);
|
|
29372
|
-
if (value !== void 0) {
|
|
29373
|
-
runtimeVariables[capture.varName] = value;
|
|
29374
|
-
} else {
|
|
29375
|
-
errors.push(`Warning: Could not capture ${capture.varName} from $${requestIndex}.${capture.path}`);
|
|
29376
|
-
}
|
|
29377
|
-
}
|
|
29378
|
-
} catch (error) {
|
|
29379
|
-
const userMessage = formatUserFacingError(
|
|
29380
|
-
error,
|
|
29381
|
-
buildRequestValidationContext(stepIdx + 1)
|
|
29382
|
-
);
|
|
29383
|
-
let errorDetails = `Request ${requestIndex} failed:
|
|
29468
|
+
} catch (error) {
|
|
29469
|
+
const userMessage = formatUserFacingError(
|
|
29470
|
+
error,
|
|
29471
|
+
buildRequestValidationContext(stepIdx + 1)
|
|
29472
|
+
);
|
|
29473
|
+
let errorDetails = `Request ${requestIndex} failed:
|
|
29384
29474
|
${indentMultiline(userMessage)}`;
|
|
29385
|
-
|
|
29386
|
-
|
|
29475
|
+
if (requestDescription && !/\nRequest:\s/.test(userMessage)) {
|
|
29476
|
+
errorDetails += `
|
|
29387
29477
|
Request: ${requestDescription}`;
|
|
29478
|
+
}
|
|
29479
|
+
errors.push(errorDetails);
|
|
29480
|
+
await emitFailure(errorDetails, step, stepIdx);
|
|
29481
|
+
return {
|
|
29482
|
+
name: "",
|
|
29483
|
+
success: false,
|
|
29484
|
+
responses,
|
|
29485
|
+
scriptResults,
|
|
29486
|
+
assertionResults,
|
|
29487
|
+
steps: orderedSteps,
|
|
29488
|
+
errors,
|
|
29489
|
+
duration: Date.now() - startTime
|
|
29490
|
+
};
|
|
29491
|
+
}
|
|
29388
29492
|
}
|
|
29389
|
-
|
|
29390
|
-
|
|
29391
|
-
name: "",
|
|
29392
|
-
success: false,
|
|
29393
|
-
responses,
|
|
29394
|
-
scriptResults,
|
|
29395
|
-
assertionResults,
|
|
29396
|
-
steps: orderedSteps,
|
|
29397
|
-
errors,
|
|
29398
|
-
duration: Date.now() - startTime
|
|
29399
|
-
};
|
|
29493
|
+
} finally {
|
|
29494
|
+
await emitAfterStep(step, stepIdx);
|
|
29400
29495
|
}
|
|
29401
29496
|
}
|
|
29497
|
+
return {
|
|
29498
|
+
name: "",
|
|
29499
|
+
success: errors.filter((e) => !e.startsWith("Warning")).length === 0,
|
|
29500
|
+
responses,
|
|
29501
|
+
scriptResults,
|
|
29502
|
+
assertionResults,
|
|
29503
|
+
steps: orderedSteps,
|
|
29504
|
+
errors,
|
|
29505
|
+
duration: Date.now() - startTime,
|
|
29506
|
+
variables: runtimeVariables
|
|
29507
|
+
};
|
|
29508
|
+
} finally {
|
|
29509
|
+
if (debugHooks?.onSequenceExit) {
|
|
29510
|
+
await debugHooks.onSequenceExit({
|
|
29511
|
+
sequenceName: executionContext?.sequenceName,
|
|
29512
|
+
filePath: executionContext?.filePath,
|
|
29513
|
+
declarationLine: executionContext?.sequenceStartLine,
|
|
29514
|
+
nestingDepth,
|
|
29515
|
+
runtimeVariables,
|
|
29516
|
+
responses
|
|
29517
|
+
});
|
|
29518
|
+
}
|
|
29402
29519
|
}
|
|
29403
|
-
return {
|
|
29404
|
-
name: "",
|
|
29405
|
-
success: errors.filter((e) => !e.startsWith("Warning")).length === 0,
|
|
29406
|
-
responses,
|
|
29407
|
-
scriptResults,
|
|
29408
|
-
assertionResults,
|
|
29409
|
-
steps: orderedSteps,
|
|
29410
|
-
errors,
|
|
29411
|
-
duration: Date.now() - startTime,
|
|
29412
|
-
variables: runtimeVariables
|
|
29413
|
-
};
|
|
29414
29520
|
}
|
|
29415
29521
|
|
|
29416
29522
|
// src/cli/colors.ts
|
|
@@ -32270,34 +32376,98 @@ ${fileContent}` : fileContent;
|
|
|
32270
32376
|
}
|
|
32271
32377
|
}
|
|
32272
32378
|
}
|
|
32273
|
-
|
|
32274
|
-
|
|
32275
|
-
|
|
32276
|
-
|
|
32277
|
-
|
|
32278
|
-
|
|
32279
|
-
|
|
32280
|
-
|
|
32281
|
-
|
|
32282
|
-
|
|
32283
|
-
|
|
32284
|
-
|
|
32285
|
-
|
|
32286
|
-
|
|
32287
|
-
|
|
32379
|
+
let theoryData = targetSeq.theoryData;
|
|
32380
|
+
if (theoryData?.source && theoryData.cases.length === 0) {
|
|
32381
|
+
try {
|
|
32382
|
+
const cases = await loadTheoryFile(theoryData.source, workingDir);
|
|
32383
|
+
theoryData = { ...theoryData, cases };
|
|
32384
|
+
} catch (error) {
|
|
32385
|
+
const message = `Failed to load theory file '${theoryData.source}': ${error instanceof Error ? error.message : String(error)}`;
|
|
32386
|
+
const failedResult = {
|
|
32387
|
+
name: targetSeq.name,
|
|
32388
|
+
success: false,
|
|
32389
|
+
responses: [],
|
|
32390
|
+
scriptResults: [],
|
|
32391
|
+
assertionResults: [],
|
|
32392
|
+
steps: [],
|
|
32393
|
+
errors: [message],
|
|
32394
|
+
duration: 0
|
|
32395
|
+
};
|
|
32396
|
+
if (options.output === "json") {
|
|
32397
|
+
console.log(JSON.stringify({ success: false, results: [failedResult] }, null, 2));
|
|
32398
|
+
} else {
|
|
32399
|
+
const lines = formatSequenceResult(failedResult, { colors, verbose: options.verbose, redaction: redaction2 });
|
|
32400
|
+
for (const line2 of lines) {
|
|
32401
|
+
console.log(line2);
|
|
32402
|
+
}
|
|
32403
|
+
const summaryLines = formatRunSummary([failedResult], Date.now() - startTime, colors);
|
|
32404
|
+
for (const line2 of summaryLines) {
|
|
32405
|
+
console.log(line2);
|
|
32406
|
+
}
|
|
32407
|
+
}
|
|
32408
|
+
process.exit(1);
|
|
32409
|
+
}
|
|
32410
|
+
}
|
|
32411
|
+
const sequenceResults = [];
|
|
32412
|
+
if (theoryData && theoryData.cases.length > 0) {
|
|
32413
|
+
for (let caseIdx = 0; caseIdx < theoryData.cases.length; caseIdx++) {
|
|
32414
|
+
const caseParams = theoryData.cases[caseIdx];
|
|
32415
|
+
const caseLabel = formatCaseLabel(caseParams);
|
|
32416
|
+
const caseArgs = { ...defaultArgs };
|
|
32417
|
+
for (const [key, value] of Object.entries(caseParams)) {
|
|
32418
|
+
caseArgs[key] = String(value);
|
|
32419
|
+
}
|
|
32420
|
+
const caseResult = await runSequenceWithJar(
|
|
32421
|
+
targetSeq.content,
|
|
32422
|
+
variables,
|
|
32423
|
+
cookieJar,
|
|
32424
|
+
workingDir,
|
|
32425
|
+
fileContentWithImports,
|
|
32426
|
+
void 0,
|
|
32427
|
+
void 0,
|
|
32428
|
+
caseArgs,
|
|
32429
|
+
apiDefinitions,
|
|
32430
|
+
tagFilterOptions,
|
|
32431
|
+
importResult.sequenceSources,
|
|
32432
|
+
{ filePath, sequenceName: targetSeq.name, environment: envValidationContext }
|
|
32433
|
+
);
|
|
32434
|
+
caseResult.name = `${targetSeq.name}${caseLabel}`;
|
|
32435
|
+
sequenceResults.push(caseResult);
|
|
32436
|
+
}
|
|
32437
|
+
} else {
|
|
32438
|
+
const seqResult = await runSequenceWithJar(
|
|
32439
|
+
targetSeq.content,
|
|
32440
|
+
variables,
|
|
32441
|
+
cookieJar,
|
|
32442
|
+
workingDir,
|
|
32443
|
+
fileContentWithImports,
|
|
32444
|
+
void 0,
|
|
32445
|
+
void 0,
|
|
32446
|
+
defaultArgs,
|
|
32447
|
+
apiDefinitions,
|
|
32448
|
+
tagFilterOptions,
|
|
32449
|
+
importResult.sequenceSources,
|
|
32450
|
+
{ filePath, sequenceName: targetSeq.name, environment: envValidationContext }
|
|
32451
|
+
);
|
|
32452
|
+
seqResult.name = targetSeq.name;
|
|
32453
|
+
sequenceResults.push(seqResult);
|
|
32454
|
+
}
|
|
32288
32455
|
if (options.output === "json") {
|
|
32289
|
-
|
|
32456
|
+
const success = sequenceResults.every((result2) => result2.success);
|
|
32457
|
+
console.log(JSON.stringify({ success, results: sequenceResults }, null, 2));
|
|
32290
32458
|
} else {
|
|
32291
|
-
const
|
|
32292
|
-
|
|
32293
|
-
|
|
32459
|
+
for (const result2 of sequenceResults) {
|
|
32460
|
+
const lines = formatSequenceResult(result2, { colors, verbose: options.verbose, redaction: redaction2 });
|
|
32461
|
+
for (const line2 of lines) {
|
|
32462
|
+
console.log(line2);
|
|
32463
|
+
}
|
|
32294
32464
|
}
|
|
32295
|
-
const summaryLines = formatRunSummary(
|
|
32465
|
+
const summaryLines = formatRunSummary(sequenceResults, Date.now() - startTime, colors);
|
|
32296
32466
|
for (const line2 of summaryLines) {
|
|
32297
32467
|
console.log(line2);
|
|
32298
32468
|
}
|
|
32299
32469
|
}
|
|
32300
|
-
process.exit(
|
|
32470
|
+
process.exit(sequenceResults.every((result2) => result2.success) ? 0 : 1);
|
|
32301
32471
|
}
|
|
32302
32472
|
}
|
|
32303
32473
|
let totalTestCount = 0;
|