feat: updates

This commit is contained in:
lohxt1
2024-12-18 18:57:23 +05:30
parent f871bc0fa2
commit f72d643e02
9 changed files with 254 additions and 25 deletions

View File

@ -59,7 +59,7 @@ export default function RunnerResults({ collection }) {
pathname: info.pathname,
relativePath: getRelativePath(collection.pathname, info.pathname)
};
if (newItem.status !== 'error') {
if (newItem.status !== 'error' && newItem.status !== 'skipped') {
if (newItem.testResults) {
const failed = newItem.testResults.filter((result) => result.status === 'fail');
newItem.testStatus = failed.length ? 'fail' : 'pass';
@ -169,18 +169,18 @@ export default function RunnerResults({ collection }) {
<div className="item-path mt-2">
<div className="flex items-center">
<span>
{item.status !== 'error' && item.testStatus === 'pass' ? (
{item.status !== 'error' && item.testStatus === 'pass' && item.status !== 'skipped' ? (
<IconCircleCheck className="test-success" size={20} strokeWidth={1.5} />
) : (
<IconCircleX className="test-failure" size={20} strokeWidth={1.5} />
)}
</span>
<span
className={`mr-1 ml-2 ${item.status == 'error' || item.testStatus == 'fail' ? 'danger' : ''}`}
className={`mr-1 ml-2 ${item.status == 'error' || item.status == 'skipped' || item.testStatus == 'fail' ? 'danger' : ''}`}
>
{item.relativePath}
</span>
{item.status !== 'error' && item.status !== 'completed' ? (
{item.status !== 'error' && item.status !== 'skipped' && item.status !== 'completed' ? (
<IconRefresh className="animate-spin ml-1" size={18} strokeWidth={1.5} />
) : item.responseReceived?.status ? (
<span className="text-xs link cursor-pointer" onClick={() => setSelectedItem(item)}>

View File

@ -1712,6 +1712,12 @@ export const collectionsSlice = createSlice({
item.responseReceived = action.payload.responseReceived;
item.status = 'error';
}
if (type === 'request-skipped') {
const item = collection.runnerResult.items.findLast((i) => i.uid === request.uid);
item.status = 'skipped';
item.responseReceived = action.payload.responseReceived;
}
}
},
resetCollectionRunner: (state, action) => {

View File

@ -39,6 +39,7 @@ const Oauth2Store = require('../../store/oauth2');
const iconv = require('iconv-lite');
const FormData = require('form-data');
const { createFormData } = require('../../utils/form-data');
const { findItemInCollectionByPathname } = require('../../utils/collection');
const safeStringifyJSON = (data) => {
try {
@ -393,7 +394,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionUid,
runtimeVariables,
processEnvVars,
scriptingConfig
scriptingConfig,
runRequestByItemPathname
) => {
// run pre-request script
let scriptResult;
@ -408,7 +410,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionPath,
onConsoleLog,
processEnvVars,
scriptingConfig
scriptingConfig,
runRequestByItemPathname
);
mainWindow.webContents.send('main:script-environment-update', {
@ -458,7 +461,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionUid,
runtimeVariables,
processEnvVars,
scriptingConfig
scriptingConfig,
runRequestByItemPathname
) => {
// run post-response vars
const postResponseVars = get(request, 'vars.res', []);
@ -506,7 +510,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionPath,
onConsoleLog,
processEnvVars,
scriptingConfig
scriptingConfig,
runRequestByItemPathname
);
mainWindow.webContents.send('main:script-environment-update', {
@ -523,14 +528,24 @@ const registerNetworkIpc = (mainWindow) => {
return scriptResult;
};
// handler for sending http request
ipcMain.handle('send-http-request', async (event, item, collection, environment, runtimeVariables) => {
const runRequest = async ({ item, collection, environment, runtimeVariables, runInBackground = false }) => {
const collectionUid = collection.uid;
const collectionPath = collection.pathname;
const cancelTokenUid = uuid();
const requestUid = uuid();
mainWindow.webContents.send('main:run-request-event', {
const runRequestByItemPathname = async ({ itemPathname }) => {
return new Promise(async (resolve, reject) => {
const _item = findItemInCollectionByPathname(collection, itemPathname);
if(_item) {
const res = await runRequest({ item: _item, collection, environment, runtimeVariables, runInBackground: true });
resolve(res);
}
reject(`bru.runRequest: invalid request path - ${itemPathname}`);
});
}
!runInBackground && mainWindow.webContents.send('main:run-request-event', {
type: 'request-queued',
requestUid,
collectionUid,
@ -561,7 +576,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionUid,
runtimeVariables,
processEnvVars,
scriptingConfig
scriptingConfig,
runRequestByItemPathname
);
const axiosInstance = await configureRequest(
@ -573,7 +589,7 @@ const registerNetworkIpc = (mainWindow) => {
collectionPath
);
mainWindow.webContents.send('main:run-request-event', {
!runInBackground && mainWindow.webContents.send('main:run-request-event', {
type: 'request-sent',
requestSent: {
url: request.url,
@ -645,7 +661,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionUid,
runtimeVariables,
processEnvVars,
scriptingConfig
scriptingConfig,
runRequestByItemPathname
);
// run assertions
@ -661,7 +678,7 @@ const registerNetworkIpc = (mainWindow) => {
processEnvVars
);
mainWindow.webContents.send('main:run-request-event', {
!runInBackground && mainWindow.webContents.send('main:run-request-event', {
type: 'assertion-results',
results: results,
itemUid: item.uid,
@ -682,10 +699,11 @@ const registerNetworkIpc = (mainWindow) => {
collectionPath,
onConsoleLog,
processEnvVars,
scriptingConfig
scriptingConfig,
runRequestByItemPathname
);
mainWindow.webContents.send('main:run-request-event', {
!runInBackground && mainWindow.webContents.send('main:run-request-event', {
type: 'test-results',
results: testResults.results,
itemUid: item.uid,
@ -719,6 +737,11 @@ const registerNetworkIpc = (mainWindow) => {
return Promise.reject(error);
}
}
// handler for sending http request
ipcMain.handle('send-http-request', async (event, item, collection, environment, runtimeVariables) => {
return await runRequest({ item, collection, environment, runtimeVariables });
});
ipcMain.handle('send-collection-oauth2-request', async (event, collection, environment, runtimeVariables) => {
@ -996,6 +1019,41 @@ const registerNetworkIpc = (mainWindow) => {
nextRequestName = preRequestScriptResult.nextRequestName;
}
if (preRequestScriptResult?.stopExecution) {
deleteCancelToken(cancelTokenUid);
mainWindow.webContents.send('main:run-folder-event', {
type: 'response-received',
error: 'Request has been stopped from pre-request script',
responseReceived: {
status: 'terminated',
statusText: 'Request execution stopped!',
data: null
},
...eventData
});
mainWindow.webContents.send('main:run-folder-event', {
type: 'testrun-ended',
collectionUid,
folderUid
});
break;
}
if (preRequestScriptResult?.skipRequest) {
mainWindow.webContents.send('main:run-folder-event', {
type: 'request-skipped',
error: 'Request has been skipped from pre-request script',
responseReceived: {
status: 'skipped',
statusText: 'request skipped via pre-request script',
data: null
},
...eventData
});
currentRequestIndex++;
continue;
}
// todo:
// i have no clue why electron can't send the request object
// without safeParseJSON(safeStringifyJSON(request.data))
@ -1114,6 +1172,41 @@ const registerNetworkIpc = (mainWindow) => {
nextRequestName = postRequestScriptResult.nextRequestName;
}
if (postRequestScriptResult?.stopExecution) {
deleteCancelToken(cancelTokenUid);
mainWindow.webContents.send('main:run-folder-event', {
type: 'response-received',
error: 'Request has been stopped from post-response script',
responseReceived: {
status: 'terminated',
statusText: 'Request execution stopped!',
data: null
},
...eventData
});
mainWindow.webContents.send('main:run-folder-event', {
type: 'testrun-ended',
collectionUid,
folderUid
});
break;
}
if (postRequestScriptResult?.skipRequest) {
mainWindow.webContents.send('main:run-folder-event', {
type: 'request-skipped',
error: 'Request has been skipped from post-response script',
responseReceived: {
status: 'skipped',
statusText: 'request skipped via post-response script',
data: null
},
...eventData
});
currentRequestIndex++;
continue;
}
// run assertions
const assertions = get(item, 'request.assertions');
if (assertions) {

View File

@ -203,9 +203,31 @@ const getTreePathFromCollectionToItem = (collection, _item) => {
return path;
};
const slash = (path) => {
const isExtendedLengthPath = /^\\\\\?\\/.test(path);
if (isExtendedLengthPath) {
return path;
}
return path.replace(/\\/g, '/');
};
const findItemByPathname = (items = [], pathname) => {
return find(items, (i) => slash(i.pathname) === slash(pathname));
};
const findItemInCollectionByPathname = (collection, pathname) => {
let flattenedItems = flattenItems(collection.items);
return findItemByPathname(flattenedItems, pathname);
};
module.exports = {
mergeHeaders,
mergeVars,
mergeScripts,
getTreePathFromCollectionToItem
getTreePathFromCollectionToItem,
slash,
findItemByPathname,
findItemInCollectionByPathname
}

View File

@ -13,6 +13,14 @@ class Bru {
this.requestVariables = requestVariables || {};
this.globalEnvironmentVariables = globalEnvironmentVariables || {};
this.collectionPath = collectionPath;
this.runner = {
skipRequest: () => {
this.skipRequest = true;
},
stopExecution: () => {
this.stopExecution = true;
},
};
}
_interpolate = (str) => {

View File

@ -408,6 +408,8 @@ class AssertRuntime {
}
}
request.assertionResults = assertionResults;
return assertionResults;
}
}

View File

@ -45,7 +45,8 @@ class ScriptRuntime {
collectionPath,
onConsoleLog,
processEnvVars,
scriptingConfig
scriptingConfig,
runRequestByItemPathname
) {
const globalEnvironmentVariables = request?.globalEnvironmentVariables || {};
const collectionVariables = request?.collectionVariables || {};
@ -92,6 +93,10 @@ class ScriptRuntime {
};
}
if(runRequestByItemPathname) {
context.bru.runRequest = runRequestByItemPathname;
}
if (this.runtime === 'quickjs') {
await executeQuickJsVmAsync({
script: script,
@ -104,7 +109,9 @@ class ScriptRuntime {
envVariables: cleanJson(envVariables),
runtimeVariables: cleanJson(runtimeVariables),
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
nextRequestName: bru.nextRequest
nextRequestName: bru.nextRequest,
skipRequest: bru.skipRequest,
stopExecution: bru.stopExecution
};
}
@ -152,7 +159,9 @@ class ScriptRuntime {
envVariables: cleanJson(envVariables),
runtimeVariables: cleanJson(runtimeVariables),
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
nextRequestName: bru.nextRequest
nextRequestName: bru.nextRequest,
skipRequest: bru.skipRequest,
stopExecution: bru.stopExecution
};
}
@ -165,7 +174,8 @@ class ScriptRuntime {
collectionPath,
onConsoleLog,
processEnvVars,
scriptingConfig
scriptingConfig,
runRequestByItemPathname
) {
const globalEnvironmentVariables = request?.globalEnvironmentVariables || {};
const collectionVariables = request?.collectionVariables || {};
@ -209,6 +219,10 @@ class ScriptRuntime {
};
}
if(runRequestByItemPathname) {
context.bru.runRequest = runRequestByItemPathname;
}
if (this.runtime === 'quickjs') {
await executeQuickJsVmAsync({
script: script,
@ -221,7 +235,9 @@ class ScriptRuntime {
envVariables: cleanJson(envVariables),
runtimeVariables: cleanJson(runtimeVariables),
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
nextRequestName: bru.nextRequest
nextRequestName: bru.nextRequest,
skipRequest: bru.skipRequest,
stopExecution: bru.stopExecution
};
}
@ -269,7 +285,9 @@ class ScriptRuntime {
envVariables: cleanJson(envVariables),
runtimeVariables: cleanJson(runtimeVariables),
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
nextRequestName: bru.nextRequest
nextRequestName: bru.nextRequest,
skipRequest: bru.skipRequest,
stopExecution: bru.stopExecution
};
}
}

View File

@ -46,12 +46,14 @@ class TestRuntime {
collectionPath,
onConsoleLog,
processEnvVars,
scriptingConfig
scriptingConfig,
runRequestByItemPathname
) {
const globalEnvironmentVariables = request?.globalEnvironmentVariables || {};
const collectionVariables = request?.collectionVariables || {};
const folderVariables = request?.folderVariables || {};
const requestVariables = request?.requestVariables || {};
const assertionResults = request?.assertionResults || [];
const bru = new Bru(envVariables, runtimeVariables, processEnvVars, collectionPath, collectionVariables, folderVariables, requestVariables, globalEnvironmentVariables);
const req = new BrunoRequest(request);
const res = new BrunoResponse(response);
@ -75,6 +77,7 @@ class TestRuntime {
}
const __brunoTestResults = new TestResults();
const test = Test(__brunoTestResults, chai);
if (!testsFile || !testsFile.length) {
@ -88,6 +91,14 @@ class TestRuntime {
};
}
bru.getTestResults = async () => {
let results = __brunoTestResults.getResults();
return results;
}
bru.getAssertionResults = async () => {
return assertionResults
}
const context = {
test,
bru,
@ -113,6 +124,10 @@ class TestRuntime {
};
}
if(runRequestByItemPathname) {
context.bru.runRequest = runRequestByItemPathname;
}
if (this.runtime === 'quickjs') {
await executeQuickJsVmAsync({
script: testsFile,

View File

@ -1,3 +1,4 @@
const { cleanJson } = require('../../../utils');
const { marshallToVm } = require('../utils');
const addBruShimToContext = (vm, bru) => {
@ -123,6 +124,70 @@ const addBruShimToContext = (vm, bru) => {
vm.setProp(bruObject, 'getCollectionVar', getCollectionVar);
getCollectionVar.dispose();
let getTestResults = vm.newFunction('getTestResults', () => {
const promise = vm.newPromise();
bru.getTestResults()
.then((results) => {
promise.resolve(marshallToVm(cleanJson(results), vm));
})
.catch((err) => {
promise.resolve(
marshallToVm(
cleanJson({
message: err.message
}),
vm
)
);
});
promise.settled.then(vm.runtime.executePendingJobs);
return promise.handle;
});
getTestResults.consume((handle) => vm.setProp(bruObject, 'getTestResults', handle));
let getAssertionResults = vm.newFunction('getAssertionResults', () => {
const promise = vm.newPromise();
bru.getAssertionResults()
.then((results) => {
promise.resolve(marshallToVm(cleanJson(results), vm));
})
.catch((err) => {
promise.resolve(
marshallToVm(
cleanJson({
message: err.message
}),
vm
)
);
});
promise.settled.then(vm.runtime.executePendingJobs);
return promise.handle;
});
getAssertionResults.consume((handle) => vm.setProp(bruObject, 'getAssertionResults', handle));
let runRequestHandle = vm.newFunction('runRequest', (args) => {
const promise = vm.newPromise();
bru.runRequest(vm.dump(args))
.then((response) => {
const { status, headers, data, dataBuffer, size } = response || {};
promise.resolve(marshallToVm(cleanJson({ status, headers, data, dataBuffer, size }), vm));
})
.catch((err) => {
promise.resolve(
marshallToVm(
cleanJson({
message: err.message
}),
vm
)
);
});
promise.settled.then(vm.runtime.executePendingJobs);
return promise.handle;
});
runRequestHandle.consume((handle) => vm.setProp(bruObject, 'runRequest', handle));
const sleep = vm.newFunction('sleep', (timer) => {
const t = vm.getString(timer);
const promise = vm.newPromise();