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

View File

@ -39,6 +39,7 @@ const Oauth2Store = require('../../store/oauth2');
const iconv = require('iconv-lite'); const iconv = require('iconv-lite');
const FormData = require('form-data'); const FormData = require('form-data');
const { createFormData } = require('../../utils/form-data'); const { createFormData } = require('../../utils/form-data');
const { findItemInCollectionByPathname } = require('../../utils/collection');
const safeStringifyJSON = (data) => { const safeStringifyJSON = (data) => {
try { try {
@ -393,7 +394,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionUid, collectionUid,
runtimeVariables, runtimeVariables,
processEnvVars, processEnvVars,
scriptingConfig scriptingConfig,
runRequestByItemPathname
) => { ) => {
// run pre-request script // run pre-request script
let scriptResult; let scriptResult;
@ -408,7 +410,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionPath, collectionPath,
onConsoleLog, onConsoleLog,
processEnvVars, processEnvVars,
scriptingConfig scriptingConfig,
runRequestByItemPathname
); );
mainWindow.webContents.send('main:script-environment-update', { mainWindow.webContents.send('main:script-environment-update', {
@ -458,7 +461,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionUid, collectionUid,
runtimeVariables, runtimeVariables,
processEnvVars, processEnvVars,
scriptingConfig scriptingConfig,
runRequestByItemPathname
) => { ) => {
// run post-response vars // run post-response vars
const postResponseVars = get(request, 'vars.res', []); const postResponseVars = get(request, 'vars.res', []);
@ -506,7 +510,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionPath, collectionPath,
onConsoleLog, onConsoleLog,
processEnvVars, processEnvVars,
scriptingConfig scriptingConfig,
runRequestByItemPathname
); );
mainWindow.webContents.send('main:script-environment-update', { mainWindow.webContents.send('main:script-environment-update', {
@ -523,14 +528,24 @@ const registerNetworkIpc = (mainWindow) => {
return scriptResult; return scriptResult;
}; };
// handler for sending http request const runRequest = async ({ item, collection, environment, runtimeVariables, runInBackground = false }) => {
ipcMain.handle('send-http-request', async (event, item, collection, environment, runtimeVariables) => {
const collectionUid = collection.uid; const collectionUid = collection.uid;
const collectionPath = collection.pathname; const collectionPath = collection.pathname;
const cancelTokenUid = uuid(); const cancelTokenUid = uuid();
const requestUid = 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', type: 'request-queued',
requestUid, requestUid,
collectionUid, collectionUid,
@ -561,7 +576,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionUid, collectionUid,
runtimeVariables, runtimeVariables,
processEnvVars, processEnvVars,
scriptingConfig scriptingConfig,
runRequestByItemPathname
); );
const axiosInstance = await configureRequest( const axiosInstance = await configureRequest(
@ -573,7 +589,7 @@ const registerNetworkIpc = (mainWindow) => {
collectionPath collectionPath
); );
mainWindow.webContents.send('main:run-request-event', { !runInBackground && mainWindow.webContents.send('main:run-request-event', {
type: 'request-sent', type: 'request-sent',
requestSent: { requestSent: {
url: request.url, url: request.url,
@ -645,7 +661,8 @@ const registerNetworkIpc = (mainWindow) => {
collectionUid, collectionUid,
runtimeVariables, runtimeVariables,
processEnvVars, processEnvVars,
scriptingConfig scriptingConfig,
runRequestByItemPathname
); );
// run assertions // run assertions
@ -661,7 +678,7 @@ const registerNetworkIpc = (mainWindow) => {
processEnvVars processEnvVars
); );
mainWindow.webContents.send('main:run-request-event', { !runInBackground && mainWindow.webContents.send('main:run-request-event', {
type: 'assertion-results', type: 'assertion-results',
results: results, results: results,
itemUid: item.uid, itemUid: item.uid,
@ -682,10 +699,11 @@ const registerNetworkIpc = (mainWindow) => {
collectionPath, collectionPath,
onConsoleLog, onConsoleLog,
processEnvVars, processEnvVars,
scriptingConfig scriptingConfig,
runRequestByItemPathname
); );
mainWindow.webContents.send('main:run-request-event', { !runInBackground && mainWindow.webContents.send('main:run-request-event', {
type: 'test-results', type: 'test-results',
results: testResults.results, results: testResults.results,
itemUid: item.uid, itemUid: item.uid,
@ -719,6 +737,11 @@ const registerNetworkIpc = (mainWindow) => {
return Promise.reject(error); 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) => { ipcMain.handle('send-collection-oauth2-request', async (event, collection, environment, runtimeVariables) => {
@ -996,6 +1019,41 @@ const registerNetworkIpc = (mainWindow) => {
nextRequestName = preRequestScriptResult.nextRequestName; 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: // todo:
// i have no clue why electron can't send the request object // i have no clue why electron can't send the request object
// without safeParseJSON(safeStringifyJSON(request.data)) // without safeParseJSON(safeStringifyJSON(request.data))
@ -1114,6 +1172,41 @@ const registerNetworkIpc = (mainWindow) => {
nextRequestName = postRequestScriptResult.nextRequestName; 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 // run assertions
const assertions = get(item, 'request.assertions'); const assertions = get(item, 'request.assertions');
if (assertions) { if (assertions) {

View File

@ -203,9 +203,31 @@ const getTreePathFromCollectionToItem = (collection, _item) => {
return path; 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 = { module.exports = {
mergeHeaders, mergeHeaders,
mergeVars, mergeVars,
mergeScripts, mergeScripts,
getTreePathFromCollectionToItem getTreePathFromCollectionToItem,
slash,
findItemByPathname,
findItemInCollectionByPathname
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,4 @@
const { cleanJson } = require('../../../utils');
const { marshallToVm } = require('../utils'); const { marshallToVm } = require('../utils');
const addBruShimToContext = (vm, bru) => { const addBruShimToContext = (vm, bru) => {
@ -123,6 +124,70 @@ const addBruShimToContext = (vm, bru) => {
vm.setProp(bruObject, 'getCollectionVar', getCollectionVar); vm.setProp(bruObject, 'getCollectionVar', getCollectionVar);
getCollectionVar.dispose(); 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 sleep = vm.newFunction('sleep', (timer) => {
const t = vm.getString(timer); const t = vm.getString(timer);
const promise = vm.newPromise(); const promise = vm.newPromise();