Skip to content

Commit

Permalink
feat(runner): enable stop-on-error setting
Browse files Browse the repository at this point in the history
  • Loading branch information
ihexxa committed Sep 19, 2024
1 parent 5bf6efb commit 3a8030a
Show file tree
Hide file tree
Showing 2 changed files with 148 additions and 95 deletions.
2 changes: 1 addition & 1 deletion packages/insomnia/src/ui/routes/request.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ export interface RunnerContextForRequest {
requestId: string;
requestName: string;
requestUrl: string;
responseReason: string;
statusCode: number;
duration: number; // millisecond
size: number;
results: RequestTestResult[];
Expand Down
241 changes: 147 additions & 94 deletions packages/insomnia/src/ui/routes/runner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,44 +43,49 @@ const iterationInputStyle = 'placeholder:italic py-0.5 mr-1.5 px-1 w-16 rounded-
// TODO: improve the performance for a lot of logs
async function aggregateAllTimelines(errorMsg: string | null, testResult: RunnerTestResult) {
let timelines = new Array<ResponseTimelineEntry>();
const responsesInfo = testResult.responsesInfo;

for (let i = 0; i < responsesInfo.length; i++) {
const respInfo = responsesInfo[i];
const resp = await models.response.getById(respInfo.responseId);

if (resp) {
const timeline = models.response.getTimeline(resp, true) as unknown as ResponseTimelineEntry[];
timelines = [
...timelines,
{
value: `------ Start of request (${respInfo.originalRequestName}) ------`,
name: 'Text',
timestamp: Date.now(),
},
...timeline,
];
} else {
timelines = [
...timelines,
{
value: `------ Start of request (${respInfo.originalRequestName}) ------`,
name: 'Text',
timestamp: Date.now(),
},
{
value: `failed to read response for the request ${respInfo.originalRequestName}`,
name: 'Text',
timestamp: Date.now(),
},
];
}
}

if (errorMsg) {
timelines = [
...timelines,
{
value: errorMsg,
name: 'Text',
timestamp: Date.now(),
},
];
} else {
const responsesInfo = testResult.responsesInfo;

for (let i = 0; i < responsesInfo.length; i++) {
const respInfo = responsesInfo[i];
const resp = await models.response.getById(respInfo.responseId);

if (resp) {
const timeline = models.response.getTimeline(resp, true) as unknown as ResponseTimelineEntry[];
timelines = [
...timelines,
{
value: `------ Start of request (${respInfo.originalRequestName}) ------`,
name: 'Text',
timestamp: Date.now(),
},
...timeline,
];
} else {
timelines = [
...timelines,
{
value: `failed to read response for the request ${respInfo.originalRequestName}`,
name: 'Text',
timestamp: Date.now(),
},
];
}
}
}

return timelines;
Expand All @@ -91,6 +96,7 @@ interface RunnerSettings {
delay: number;
iterationData: UploadDataType[];
file: File | null;
bail: boolean;
}

// TODO: remove this when the suite management is introduced
Expand All @@ -99,6 +105,7 @@ let tempRunnerSettings: RunnerSettings = {
delay: 0,
iterationData: [],
file: null,
bail: true,
};

export const Runner: FC<{}> = () => {
Expand Down Expand Up @@ -128,6 +135,8 @@ export const Runner: FC<{}> = () => {
),
});
searchParams.delete('error');
} else {
setErrorMsg(null);
}

setSearchParams({});
Expand All @@ -137,6 +146,10 @@ export const Runner: FC<{}> = () => {
const [delay, setDelay] = useState(tempRunnerSettings?.delay || 0);
const [uploadData, setUploadData] = useState<UploadDataType[]>(tempRunnerSettings?.iterationData || []);
const [file, setFile] = useState<File | null>(tempRunnerSettings?.file || null);
const [runnerAdvancedSettings, setAdvancedRunnerSettings] = useState({
bail: tempRunnerSettings?.bail || true as boolean,
});
const toggleBail = () => setAdvancedRunnerSettings({ ...runnerAdvancedSettings, bail: !runnerAdvancedSettings.bail });

const { organizationId, projectId, workspaceId } = useParams() as {
organizationId: string;
Expand Down Expand Up @@ -252,6 +265,7 @@ export const Runner: FC<{}> = () => {
setIsRunning(true);

window.main.trackSegmentEvent({ event: SegmentEvent.collectionRunExecute, properties: { plan: currentPlan?.type || 'scratchpad', iterations: iterations } });

const selected = new Set(reqList.selectedKeys);
const requests = Array.from(reqList.items)
.filter(item => selected.has(item.id));
Expand All @@ -275,6 +289,7 @@ export const Runner: FC<{}> = () => {
iterations,
userUploadEnvs,
delay,
runnerAdvancedSettings,
},
{
method: 'post',
Expand Down Expand Up @@ -613,7 +628,6 @@ export const Runner: FC<{}> = () => {
onChange={() => { }}
type="checkbox"
disabled={true}
checked
/>
Persist responses for a session
<HelpTooltip className="space-left">Enabling this will impact performance while responses are saved for other purposes.</HelpTooltip>
Expand All @@ -634,11 +648,11 @@ export const Runner: FC<{}> = () => {
<div>
<label className="flex items-center gap-2">
<input
name='stop-on-error'
onChange={() => { }}
name='bail'
onChange={toggleBail}
type="checkbox"
disabled={true}
checked
disabled={isRunning}
checked={runnerAdvancedSettings.bail}
/>
Stop run if an error occurs
</label>
Expand Down Expand Up @@ -860,7 +874,7 @@ export const runCollectionAction: ActionFunction = async ({ request, params }) =
invariant(organizationId, 'Organization id is required');
invariant(projectId, 'Project id is required');
invariant(workspaceId, 'Workspace id is required');
const { requests, iterations, delay, userUploadEnvs } = await request.json();
const { requests, iterations, delay, userUploadEnvs, runnerAdvancedSettings } = await request.json();
const source: RunnerSource = 'runner';

let testCtx = {
Expand Down Expand Up @@ -898,77 +912,116 @@ export const runCollectionAction: ActionFunction = async ({ request, params }) =

for (let j = 0; j < requests.length; j++) {
const targetRequest = requests[j] as RequestType;
// TODO: we might find a better way to do runner cancellation
if (getExecution(workspaceId) === undefined) {
throw 'Runner has been stopped';
}

if (nextRequestIdOrName !== '') {
if (targetRequest.id === nextRequestIdOrName ||
// find the last request with matched name in case mulitple requests with same name in collection runner
(targetRequest.name.trim() === nextRequestIdOrName.trim() && j === requests.findLastIndex((req: RequestType) => req.name.trim() === nextRequestIdOrName.trim()))
) {
// reset nextRequestIdOrName when request name or id meets;
nextRequestIdOrName = '';
} else {
continue;
}
}
updateExecution(workspaceId, targetRequest.id);

window.main.updateLatestStepName({ requestId: workspaceId, stepName: `Iteration ${i + 1} - Executing ${j + 1} of ${requests.length} requests - "${targetRequest.name}"` });

const activeRequestMeta = await models.requestMeta.updateOrCreateByParentId(
targetRequest.id,
{ lastActive: Date.now() },
);
invariant(activeRequestMeta, 'Request meta not found');

await new Promise(resolve => setTimeout(resolve, delay));
const resultCollector = {
requestId: targetRequest.id,
requestName: targetRequest.name,
requestUrl: targetRequest.url,
responseReason: '',
statusCode: 0,
duration: 1,
size: 0,
results: new Array<RequestTestResult>(),
responseId: '',
};
const mutatedContext = await sendActionImp({
requestId: targetRequest.id,
workspaceId,
iteration: i + 1,
iterationCount: iterations,
userUploadEnv: wrapAroundIterationOverIterationData(userUploadEnvs, i),
shouldPromptForPathAfterResponse: false,
ignoreUndefinedEnvVariable: true,
testResultCollector: resultCollector,
}) as RequestContext | null;
if (mutatedContext?.execution?.nextRequestIdOrName) {
nextRequestIdOrName = mutatedContext.execution.nextRequestIdOrName || '';
};

const requestResults: RunnerResultPerRequest = {
requestName: targetRequest.name,
requestUrl: targetRequest.url,
responseCode: 0, // TODO: collect response
results: resultCollector.results,
};
// TODO: we might find a better way to do runner cancellation
if (getExecution(workspaceId) === undefined) {
throw 'Runner has been stopped';
}

try {
if (nextRequestIdOrName !== '') {
if (targetRequest.id === nextRequestIdOrName ||
// find the last request with matched name in case mulitple requests with same name in collection runner
(targetRequest.name.trim() === nextRequestIdOrName.trim() && j === requests.findLastIndex((req: RequestType) => req.name.trim() === nextRequestIdOrName.trim()))
) {
// reset nextRequestIdOrName when request name or id meets;
nextRequestIdOrName = '';
} else {
continue;
}
}
updateExecution(workspaceId, targetRequest.id);

window.main.updateLatestStepName({
requestId: workspaceId,
stepName: `Iteration ${i + 1} - Executing ${j + 1} of ${requests.length} requests - "${targetRequest.name}"`,
});

const activeRequestMeta = await models.requestMeta.updateOrCreateByParentId(
targetRequest.id,
{ lastActive: Date.now() },
);
invariant(activeRequestMeta, 'Request meta not found');

await new Promise(resolve => setTimeout(resolve, delay));

const mutatedContext = await sendActionImp({
requestId: targetRequest.id,
workspaceId,
iteration: i + 1,
iterationCount: iterations,
userUploadEnv: wrapAroundIterationOverIterationData(userUploadEnvs, i),
shouldPromptForPathAfterResponse: false,
ignoreUndefinedEnvVariable: true,
testResultCollector: resultCollector,
}) as RequestContext | null;
if (mutatedContext?.execution?.nextRequestIdOrName) {
nextRequestIdOrName = mutatedContext.execution.nextRequestIdOrName || '';
};

const requestResults: RunnerResultPerRequest = {
requestName: targetRequest.name,
requestUrl: targetRequest.url,
responseCode: resultCollector.statusCode,
results: resultCollector.results,
};

iterationResults = [...iterationResults, requestResults];
testCtx = {
...testCtx,
duration: testCtx.duration + resultCollector.duration,
responsesInfo: [
...testCtx.responsesInfo,
{
responseId: resultCollector.responseId,
originalRequestId: targetRequest.id,
originalRequestName: targetRequest.name,
},
],
};
} catch (e) {
const requestResults: RunnerResultPerRequest = {
requestName: targetRequest.name,
requestUrl: targetRequest.url,
responseCode: resultCollector.statusCode,
results: resultCollector.results,
};

iterationResults = [...iterationResults, requestResults];
testCtx = {
...testCtx,
responsesInfo: [
...testCtx.responsesInfo,
{
// this is ok and timeline will display an error
responseId: resultCollector.responseId || '',
originalRequestId: targetRequest.id,
originalRequestName: targetRequest.name,
},
],
};
if (runnerAdvancedSettings.bail) {
// save previous results in this iteration
testCtx = {
...testCtx,
iterationResults: [...testCtx.iterationResults, iterationResults],
};
throw e;
}
// or continue execution if needed
}

iterationResults = [...iterationResults, requestResults];
testCtx = {
...testCtx,
duration: testCtx.duration + resultCollector.duration,
responsesInfo: [
...testCtx.responsesInfo,
{
responseId: resultCollector.responseId,
originalRequestId: targetRequest.id,
originalRequestName: targetRequest.name,
},
],
};
}

testCtx = {
Expand Down

0 comments on commit 3a8030a

Please sign in to comment.