diff --git a/packages/insomnia/src/ui/routes/request.tsx b/packages/insomnia/src/ui/routes/request.tsx index 3cdd77689317..3d59dd1e01df 100644 --- a/packages/insomnia/src/ui/routes/request.tsx +++ b/packages/insomnia/src/ui/routes/request.tsx @@ -418,7 +418,7 @@ export interface RunnerContextForRequest { requestId: string; requestName: string; requestUrl: string; - responseReason: string; + statusCode: number; duration: number; // millisecond size: number; results: RequestTestResult[]; diff --git a/packages/insomnia/src/ui/routes/runner.tsx b/packages/insomnia/src/ui/routes/runner.tsx index 04fdb034f677..ca90e911c49a 100644 --- a/packages/insomnia/src/ui/routes/runner.tsx +++ b/packages/insomnia/src/ui/routes/runner.tsx @@ -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(); + 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; @@ -91,6 +96,7 @@ interface RunnerSettings { delay: number; iterationData: UploadDataType[]; file: File | null; + bail: boolean; } // TODO: remove this when the suite management is introduced @@ -99,6 +105,7 @@ let tempRunnerSettings: RunnerSettings = { delay: 0, iterationData: [], file: null, + bail: true, }; export const Runner: FC<{}> = () => { @@ -128,6 +135,8 @@ export const Runner: FC<{}> = () => { ), }); searchParams.delete('error'); + } else { + setErrorMsg(null); } setSearchParams({}); @@ -137,6 +146,10 @@ export const Runner: FC<{}> = () => { const [delay, setDelay] = useState(tempRunnerSettings?.delay || 0); const [uploadData, setUploadData] = useState(tempRunnerSettings?.iterationData || []); const [file, setFile] = useState(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; @@ -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)); @@ -275,6 +289,7 @@ export const Runner: FC<{}> = () => { iterations, userUploadEnvs, delay, + runnerAdvancedSettings, }, { method: 'post', @@ -613,7 +628,6 @@ export const Runner: FC<{}> = () => { onChange={() => { }} type="checkbox" disabled={true} - checked /> Persist responses for a session Enabling this will impact performance while responses are saved for other purposes. @@ -634,11 +648,11 @@ export const Runner: FC<{}> = () => {
@@ -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 = { @@ -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(), 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 = {