mirror of
https://github.com/usebruno/bruno.git
synced 2025-02-08 14:02:09 +01:00
[Feature] Stop button for runner execution (#1580)
* First attempts * Sending cancel token in run-folder-event * Remove logs, update lock * cancelTokenUid with default value * Indentation * Generating token in the main process side * Removing uuid import
This commit is contained in:
parent
eab50f01d7
commit
942a895ae0
24606
package-lock.json
generated
24606
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { useDispatch } from 'react-redux';
|
import { useDispatch } from 'react-redux';
|
||||||
import { get, cloneDeep } from 'lodash';
|
import { get, cloneDeep } from 'lodash';
|
||||||
import { runCollectionFolder } from 'providers/ReduxStore/slices/collections/actions';
|
import { runCollectionFolder, cancelRunnerExecution } from 'providers/ReduxStore/slices/collections/actions';
|
||||||
import { resetCollectionRunner } from 'providers/ReduxStore/slices/collections';
|
import { resetCollectionRunner } from 'providers/ReduxStore/slices/collections';
|
||||||
import { findItemInCollection, getTotalRequestCountInCollection } from 'utils/collections';
|
import { findItemInCollection, getTotalRequestCountInCollection } from 'utils/collections';
|
||||||
import { IconRefresh, IconCircleCheck, IconCircleX, IconCheck, IconX, IconRun } from '@tabler/icons';
|
import { IconRefresh, IconCircleCheck, IconCircleX, IconCheck, IconX, IconRun } from '@tabler/icons';
|
||||||
@ -32,6 +32,7 @@ export default function RunnerResults({ collection }) {
|
|||||||
|
|
||||||
const collectionCopy = cloneDeep(collection);
|
const collectionCopy = cloneDeep(collection);
|
||||||
const runnerInfo = get(collection, 'runnerResult.info', {});
|
const runnerInfo = get(collection, 'runnerResult.info', {});
|
||||||
|
|
||||||
const items = cloneDeep(get(collection, 'runnerResult.items', []))
|
const items = cloneDeep(get(collection, 'runnerResult.items', []))
|
||||||
.map((item) => {
|
.map((item) => {
|
||||||
const info = findItemInCollection(collectionCopy, item.uid);
|
const info = findItemInCollection(collectionCopy, item.uid);
|
||||||
@ -81,6 +82,10 @@ export default function RunnerResults({ collection }) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const cancelExecution = () => {
|
||||||
|
dispatch(cancelRunnerExecution(runnerInfo.cancelTokenUid));
|
||||||
|
};
|
||||||
|
|
||||||
const totalRequestsInCollection = getTotalRequestCountInCollection(collectionCopy);
|
const totalRequestsInCollection = getTotalRequestCountInCollection(collectionCopy);
|
||||||
const passedRequests = items.filter((item) => {
|
const passedRequests = items.filter((item) => {
|
||||||
return item.status !== 'error' && item.testStatus === 'pass' && item.assertionStatus === 'pass';
|
return item.status !== 'error' && item.testStatus === 'pass' && item.assertionStatus === 'pass';
|
||||||
@ -114,10 +119,17 @@ export default function RunnerResults({ collection }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledWrapper className="px-4 pb-4 flex flex-grow flex-col relative">
|
<StyledWrapper className="px-4 pb-4 flex flex-grow flex-col relative">
|
||||||
<div className="font-medium mt-6 mb-4 title flex items-center">
|
<div className="flex flex-row mt-6 mb-4">
|
||||||
|
<div className="font-medium title flex items-center">
|
||||||
Runner
|
Runner
|
||||||
<IconRun size={20} strokeWidth={1.5} className="ml-2" />
|
<IconRun size={20} strokeWidth={1.5} className="ml-2" />
|
||||||
</div>
|
</div>
|
||||||
|
{runnerInfo.status !== 'ended' && runnerInfo.cancelTokenUid && (
|
||||||
|
<button className="btn ml-6 btn-sm btn-danger" onClick={cancelExecution}>
|
||||||
|
Cancel Execution
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<div className="flex flex-1">
|
<div className="flex flex-1">
|
||||||
<div className="flex flex-col flex-1">
|
<div className="flex flex-col flex-1">
|
||||||
<div className="py-2 font-medium test-summary">
|
<div className="py-2 font-medium test-summary">
|
||||||
@ -195,7 +207,6 @@ export default function RunnerResults({ collection }) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{runnerInfo.status === 'ended' ? (
|
{runnerInfo.status === 'ended' ? (
|
||||||
<div className="mt-2 mb-4">
|
<div className="mt-2 mb-4">
|
||||||
<button type="submit" className="submit btn btn-sm btn-secondary mt-6" onClick={runAgain}>
|
<button type="submit" className="submit btn btn-sm btn-secondary mt-6" onClick={runAgain}>
|
||||||
|
@ -208,6 +208,10 @@ export const cancelRequest = (cancelTokenUid, item, collection) => (dispatch) =>
|
|||||||
.catch((err) => console.log(err));
|
.catch((err) => console.log(err));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const cancelRunnerExecution = (cancelTokenUid) => (dispatch) => {
|
||||||
|
cancelNetworkRequest(cancelTokenUid).catch((err) => console.log(err));
|
||||||
|
};
|
||||||
|
|
||||||
export const runCollectionFolder = (collectionUid, folderUid, recursive) => (dispatch, getState) => {
|
export const runCollectionFolder = (collectionUid, folderUid, recursive) => (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const collection = findCollectionByUid(state.collections.collections, collectionUid);
|
const collection = findCollectionByUid(state.collections.collections, collectionUid);
|
||||||
|
@ -1290,7 +1290,7 @@ export const collectionsSlice = createSlice({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
runFolderEvent: (state, action) => {
|
runFolderEvent: (state, action) => {
|
||||||
const { collectionUid, folderUid, itemUid, type, isRecursive, error } = action.payload;
|
const { collectionUid, folderUid, itemUid, type, isRecursive, error, cancelTokenUid } = action.payload;
|
||||||
const collection = findCollectionByUid(state.collections, collectionUid);
|
const collection = findCollectionByUid(state.collections, collectionUid);
|
||||||
|
|
||||||
if (collection) {
|
if (collection) {
|
||||||
@ -1306,6 +1306,7 @@ export const collectionsSlice = createSlice({
|
|||||||
info.collectionUid = collectionUid;
|
info.collectionUid = collectionUid;
|
||||||
info.folderUid = folderUid;
|
info.folderUid = folderUid;
|
||||||
info.isRecursive = isRecursive;
|
info.isRecursive = isRecursive;
|
||||||
|
info.cancelTokenUid = cancelTokenUid;
|
||||||
info.status = 'started';
|
info.status = 'started';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,9 +400,9 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
const scriptingConfig = get(brunoConfig, 'scripts', {});
|
const scriptingConfig = get(brunoConfig, 'scripts', {});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const cancelToken = axios.CancelToken.source();
|
const controller = new AbortController();
|
||||||
request.cancelToken = cancelToken.token;
|
request.signal = controller.signal;
|
||||||
saveCancelToken(cancelTokenUid, cancelToken);
|
saveCancelToken(cancelTokenUid, controller);
|
||||||
|
|
||||||
await runPreRequest(
|
await runPreRequest(
|
||||||
request,
|
request,
|
||||||
@ -586,7 +586,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
ipcMain.handle('cancel-http-request', async (event, cancelTokenUid) => {
|
ipcMain.handle('cancel-http-request', async (event, cancelTokenUid) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (cancelTokenUid && cancelTokens[cancelTokenUid]) {
|
if (cancelTokenUid && cancelTokens[cancelTokenUid]) {
|
||||||
cancelTokens[cancelTokenUid].cancel();
|
cancelTokens[cancelTokenUid].abort();
|
||||||
deleteCancelToken(cancelTokenUid);
|
deleteCancelToken(cancelTokenUid);
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
@ -679,10 +679,14 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
const collectionUid = collection.uid;
|
const collectionUid = collection.uid;
|
||||||
const collectionPath = collection.pathname;
|
const collectionPath = collection.pathname;
|
||||||
const folderUid = folder ? folder.uid : null;
|
const folderUid = folder ? folder.uid : null;
|
||||||
|
const cancelTokenUid = uuid();
|
||||||
const brunoConfig = getBrunoConfig(collectionUid);
|
const brunoConfig = getBrunoConfig(collectionUid);
|
||||||
const scriptingConfig = get(brunoConfig, 'scripts', {});
|
const scriptingConfig = get(brunoConfig, 'scripts', {});
|
||||||
const collectionRoot = get(collection, 'root', {});
|
const collectionRoot = get(collection, 'root', {});
|
||||||
|
|
||||||
|
const abortController = new AbortController();
|
||||||
|
saveCancelToken(cancelTokenUid, abortController);
|
||||||
|
|
||||||
if (!folder) {
|
if (!folder) {
|
||||||
folder = collection;
|
folder = collection;
|
||||||
}
|
}
|
||||||
@ -691,7 +695,8 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
type: 'testrun-started',
|
type: 'testrun-started',
|
||||||
isRecursive: recursive,
|
isRecursive: recursive,
|
||||||
collectionUid,
|
collectionUid,
|
||||||
folderUid
|
folderUid,
|
||||||
|
cancelTokenUid
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -717,6 +722,13 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
let currentRequestIndex = 0;
|
let currentRequestIndex = 0;
|
||||||
let nJumps = 0; // count the number of jumps to avoid infinite loops
|
let nJumps = 0; // count the number of jumps to avoid infinite loops
|
||||||
while (currentRequestIndex < folderRequests.length) {
|
while (currentRequestIndex < folderRequests.length) {
|
||||||
|
// user requested to cancel runner
|
||||||
|
if (abortController.signal.aborted) {
|
||||||
|
let error = new Error('Runner execution cancelled');
|
||||||
|
error.isCancel = true;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
const item = folderRequests[currentRequestIndex];
|
const item = folderRequests[currentRequestIndex];
|
||||||
let nextRequestName;
|
let nextRequestName;
|
||||||
const itemUid = item.uid;
|
const itemUid = item.uid;
|
||||||
@ -770,6 +782,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
...eventData
|
...eventData
|
||||||
});
|
});
|
||||||
|
|
||||||
|
request.signal = abortController.signal;
|
||||||
const axiosInstance = await configureRequest(
|
const axiosInstance = await configureRequest(
|
||||||
collectionUid,
|
collectionUid,
|
||||||
request,
|
request,
|
||||||
@ -803,7 +816,7 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
...eventData
|
...eventData
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error?.response) {
|
if (error?.response && !axios.isCancel(error)) {
|
||||||
const { data, dataBuffer } = parseDataFromResponse(error.response);
|
const { data, dataBuffer } = parseDataFromResponse(error.response);
|
||||||
error.response.data = data;
|
error.response.data = data;
|
||||||
|
|
||||||
@ -928,15 +941,19 @@ const registerNetworkIpc = (mainWindow) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteCancelToken(cancelTokenUid);
|
||||||
mainWindow.webContents.send('main:run-folder-event', {
|
mainWindow.webContents.send('main:run-folder-event', {
|
||||||
type: 'testrun-ended',
|
type: 'testrun-ended',
|
||||||
collectionUid,
|
collectionUid,
|
||||||
folderUid
|
folderUid
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
deleteCancelToken(cancelTokenUid);
|
||||||
mainWindow.webContents.send('main:run-folder-event', {
|
mainWindow.webContents.send('main:run-folder-event', {
|
||||||
type: 'error',
|
type: 'testrun-ended',
|
||||||
error
|
collectionUid,
|
||||||
|
folderUid,
|
||||||
|
error: error && !error.isCancel ? error : null
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const cancelTokens = {};
|
const cancelTokens = {};
|
||||||
|
|
||||||
const saveCancelToken = (uid, axiosRequest) => {
|
const saveCancelToken = (uid, abortController) => {
|
||||||
cancelTokens[uid] = axiosRequest;
|
cancelTokens[uid] = abortController;
|
||||||
};
|
};
|
||||||
|
|
||||||
const deleteCancelToken = (uid) => {
|
const deleteCancelToken = (uid) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user