diff --git a/packages/bruno-app/src/components/RunnerResults/index.jsx b/packages/bruno-app/src/components/RunnerResults/index.jsx
index 4b0b68cba..f411404a4 100644
--- a/packages/bruno-app/src/components/RunnerResults/index.jsx
+++ b/packages/bruno-app/src/components/RunnerResults/index.jsx
@@ -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 }) {
- {item.status !== 'error' && item.testStatus === 'pass' ? (
+ {item.status !== 'error' && item.testStatus === 'pass' && item.status !== 'skipped' ? (
) : (
)}
{item.relativePath}
- {item.status !== 'error' && item.status !== 'completed' ? (
+ {item.status !== 'error' && item.status !== 'skipped' && item.status !== 'completed' ? (
) : item.responseReceived?.status ? (
setSelectedItem(item)}>
diff --git a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
index 2b5a532c3..dc0dd50f6 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
@@ -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) => {
diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js
index 9e37c8289..f3bf6b583 100644
--- a/packages/bruno-electron/src/ipc/network/index.js
+++ b/packages/bruno-electron/src/ipc/network/index.js
@@ -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) {
diff --git a/packages/bruno-electron/src/utils/collection.js b/packages/bruno-electron/src/utils/collection.js
index 5b9c3452e..872321b50 100644
--- a/packages/bruno-electron/src/utils/collection.js
+++ b/packages/bruno-electron/src/utils/collection.js
@@ -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
}
\ No newline at end of file
diff --git a/packages/bruno-js/src/bru.js b/packages/bruno-js/src/bru.js
index 5a4d69426..946b62480 100644
--- a/packages/bruno-js/src/bru.js
+++ b/packages/bruno-js/src/bru.js
@@ -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) => {
diff --git a/packages/bruno-js/src/runtime/assert-runtime.js b/packages/bruno-js/src/runtime/assert-runtime.js
index 4132fea9c..558dcaba1 100644
--- a/packages/bruno-js/src/runtime/assert-runtime.js
+++ b/packages/bruno-js/src/runtime/assert-runtime.js
@@ -408,6 +408,8 @@ class AssertRuntime {
}
}
+ request.assertionResults = assertionResults;
+
return assertionResults;
}
}
diff --git a/packages/bruno-js/src/runtime/script-runtime.js b/packages/bruno-js/src/runtime/script-runtime.js
index 0f82114b2..dcde3f27c 100644
--- a/packages/bruno-js/src/runtime/script-runtime.js
+++ b/packages/bruno-js/src/runtime/script-runtime.js
@@ -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
};
}
}
diff --git a/packages/bruno-js/src/runtime/test-runtime.js b/packages/bruno-js/src/runtime/test-runtime.js
index 5391f7e10..cb68eb5c4 100644
--- a/packages/bruno-js/src/runtime/test-runtime.js
+++ b/packages/bruno-js/src/runtime/test-runtime.js
@@ -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,
diff --git a/packages/bruno-js/src/sandbox/quickjs/shims/bru.js b/packages/bruno-js/src/sandbox/quickjs/shims/bru.js
index 297add66a..6797104f3 100644
--- a/packages/bruno-js/src/sandbox/quickjs/shims/bru.js
+++ b/packages/bruno-js/src/sandbox/quickjs/shims/bru.js
@@ -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();