mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-15 03:41:28 +00:00
@@ -6,22 +6,48 @@ import StyledWrapper from './StyledWrapper';
|
||||
const ScriptError = ({ item, onClose }) => {
|
||||
const preRequestError = item?.preRequestScriptErrorMessage;
|
||||
const postResponseError = item?.postResponseScriptErrorMessage;
|
||||
const testScriptError = item?.testScriptErrorMessage;
|
||||
|
||||
if (!preRequestError && !postResponseError) return null;
|
||||
if (!preRequestError && !postResponseError && !testScriptError) return null;
|
||||
|
||||
const errorMessage = preRequestError || postResponseError;
|
||||
const errorTitle = preRequestError ? 'Pre-Request Script Error' : 'Post-Response Script Error';
|
||||
const errors = [];
|
||||
|
||||
if (preRequestError) {
|
||||
errors.push({
|
||||
title: 'Pre-Request Script Error',
|
||||
message: preRequestError
|
||||
});
|
||||
}
|
||||
|
||||
if (postResponseError) {
|
||||
errors.push({
|
||||
title: 'Post-Response Script Error',
|
||||
message: postResponseError
|
||||
});
|
||||
}
|
||||
|
||||
if (testScriptError) {
|
||||
errors.push({
|
||||
title: 'Test Script Error',
|
||||
message: testScriptError
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledWrapper className="mt-4 mb-2">
|
||||
<div className="flex items-start gap-3 px-4 py-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="error-title">
|
||||
{errorTitle}
|
||||
</div>
|
||||
<div className="error-message">
|
||||
{errorMessage}
|
||||
</div>
|
||||
{errors.map((error, index) => (
|
||||
<div key={index}>
|
||||
{index > 0 && <div className="border-t border-gray-300 my-3 dark:border-gray-600"></div>}
|
||||
<div className="error-title">
|
||||
{error.title}
|
||||
</div>
|
||||
<div className="error-message">
|
||||
{error.message}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
className="close-button flex-shrink-0 cursor-pointer"
|
||||
|
||||
@@ -33,10 +33,10 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (item?.preRequestScriptErrorMessage || item?.postResponseScriptErrorMessage) {
|
||||
if (item?.preRequestScriptErrorMessage || item?.postResponseScriptErrorMessage || item?.testScriptErrorMessage) {
|
||||
setShowScriptErrorCard(true);
|
||||
}
|
||||
}, [item?.preRequestScriptErrorMessage, item?.postResponseScriptErrorMessage]);
|
||||
}, [item?.preRequestScriptErrorMessage, item?.postResponseScriptErrorMessage, item?.testScriptErrorMessage]);
|
||||
|
||||
const selectTab = (tab) => {
|
||||
dispatch(
|
||||
@@ -128,7 +128,7 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
|
||||
const responseHeadersCount = typeof response.headers === 'object' ? Object.entries(response.headers).length : 0;
|
||||
|
||||
const hasScriptError = item?.preRequestScriptErrorMessage || item?.postResponseScriptErrorMessage;
|
||||
const hasScriptError = item?.preRequestScriptErrorMessage || item?.postResponseScriptErrorMessage || item?.testScriptErrorMessage;
|
||||
|
||||
return (
|
||||
<StyledWrapper className="flex flex-col h-full relative">
|
||||
@@ -174,7 +174,11 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
) : null}
|
||||
</div>
|
||||
<section
|
||||
className={`flex flex-col flex-grow relative pl-3 pr-4 ${focusedTab.responsePaneTab === 'response' ? '' : 'mt-4'}`}
|
||||
className={`flex flex-col min-h-0 relative pl-3 pr-4 auto`}
|
||||
style={{
|
||||
flex: '1 1 0',
|
||||
height: hasScriptError && showScriptErrorCard ? 'auto' : '100%'
|
||||
}}
|
||||
>
|
||||
{isLoading ? <Overlay item={item} collection={collection} /> : null}
|
||||
{hasScriptError && showScriptErrorCard && (
|
||||
@@ -183,17 +187,19 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
onClose={() => setShowScriptErrorCard(false)}
|
||||
/>
|
||||
)}
|
||||
{!item?.response ? (
|
||||
focusedTab?.responsePaneTab === "timeline" && requestTimeline?.length ? (
|
||||
<Timeline
|
||||
collection={collection}
|
||||
item={item}
|
||||
width={rightPaneWidth}
|
||||
/>
|
||||
) : null
|
||||
) : (
|
||||
<>{getTabPanel(focusedTab.responsePaneTab)}</>
|
||||
)}
|
||||
<div className='flex-1 overflow-hidden min-h-[200px]'>
|
||||
{!item?.response ? (
|
||||
focusedTab?.responsePaneTab === "timeline" && requestTimeline?.length ? (
|
||||
<Timeline
|
||||
collection={collection}
|
||||
item={item}
|
||||
width={rightPaneWidth}
|
||||
/>
|
||||
) : null
|
||||
) : (
|
||||
<>{getTabPanel(focusedTab.responsePaneTab)}</>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</StyledWrapper>
|
||||
);
|
||||
|
||||
@@ -1987,6 +1987,10 @@ export const collectionsSlice = createSlice({
|
||||
item.postResponseScriptErrorMessage = action.payload.errorMessage;
|
||||
}
|
||||
|
||||
if(type === 'test-script-execution') {
|
||||
item.testScriptErrorMessage = action.payload.errorMessage;
|
||||
}
|
||||
|
||||
if (type === 'request-queued') {
|
||||
const { cancelTokenUid } = action.payload;
|
||||
// ignore if request is already in progress or completed
|
||||
|
||||
@@ -805,19 +805,39 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
const collectionName = collection?.name
|
||||
if (typeof testFile === 'string') {
|
||||
const testRuntime = new TestRuntime({ runtime: scriptingConfig?.runtime });
|
||||
const testResults = await testRuntime.runTests(
|
||||
decomment(testFile),
|
||||
request,
|
||||
response,
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
collectionPath,
|
||||
onConsoleLog,
|
||||
processEnvVars,
|
||||
scriptingConfig,
|
||||
runRequestByItemPathname,
|
||||
collectionName
|
||||
);
|
||||
let testResults = null;
|
||||
let testError = null;
|
||||
|
||||
try {
|
||||
testResults = await testRuntime.runTests(
|
||||
decomment(testFile),
|
||||
request,
|
||||
response,
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
collectionPath,
|
||||
onConsoleLog,
|
||||
processEnvVars,
|
||||
scriptingConfig,
|
||||
runRequestByItemPathname,
|
||||
collectionName
|
||||
);
|
||||
} catch (error) {
|
||||
testError = error;
|
||||
|
||||
if (error.partialResults) {
|
||||
testResults = error.partialResults;
|
||||
} else {
|
||||
testResults = {
|
||||
request,
|
||||
envVariables: envVars,
|
||||
runtimeVariables,
|
||||
globalEnvironmentVariables: request?.globalEnvironmentVariables || {},
|
||||
results: [],
|
||||
nextRequestName: null
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
!runInBackground && mainWindow.webContents.send('main:run-request-event', {
|
||||
type: 'test-results',
|
||||
@@ -839,6 +859,20 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
});
|
||||
|
||||
collection.globalEnvironmentVariables = testResults.globalEnvironmentVariables;
|
||||
|
||||
const testScriptExecutionEvent = {
|
||||
type: 'test-script-execution',
|
||||
requestUid,
|
||||
collectionUid,
|
||||
itemUid: item.uid,
|
||||
errorMessage: null,
|
||||
}
|
||||
|
||||
if (testError) {
|
||||
const errorMessage = testError?.message || 'An error occurred in test script';
|
||||
testScriptExecutionEvent.errorMessage = errorMessage;
|
||||
}
|
||||
!runInBackground && mainWindow.webContents.send('main:run-request-event', testScriptExecutionEvent);
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -1213,42 +1247,67 @@ const registerNetworkIpc = (mainWindow) => {
|
||||
const testFile = get(request, 'tests');
|
||||
const collectionName = collection?.name
|
||||
if (typeof testFile === 'string') {
|
||||
const testRuntime = new TestRuntime({ runtime: scriptingConfig?.runtime });
|
||||
const testResults = await testRuntime.runTests(
|
||||
decomment(testFile),
|
||||
request,
|
||||
response,
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
collectionPath,
|
||||
onConsoleLog,
|
||||
processEnvVars,
|
||||
scriptingConfig,
|
||||
runRequestByItemPathname,
|
||||
collectionName
|
||||
);
|
||||
try {
|
||||
const testRuntime = new TestRuntime({ runtime: scriptingConfig?.runtime });
|
||||
const testResults = await testRuntime.runTests(
|
||||
decomment(testFile),
|
||||
request,
|
||||
response,
|
||||
envVars,
|
||||
runtimeVariables,
|
||||
collectionPath,
|
||||
onConsoleLog,
|
||||
processEnvVars,
|
||||
scriptingConfig,
|
||||
runRequestByItemPathname,
|
||||
collectionName
|
||||
);
|
||||
|
||||
if (testResults?.nextRequestName !== undefined) {
|
||||
nextRequestName = testResults.nextRequestName;
|
||||
if (testResults?.nextRequestName !== undefined) {
|
||||
nextRequestName = testResults.nextRequestName;
|
||||
}
|
||||
|
||||
mainWindow.webContents.send('main:run-folder-event', {
|
||||
type: 'test-results',
|
||||
testResults: testResults.results,
|
||||
...eventData
|
||||
});
|
||||
|
||||
mainWindow.webContents.send('main:script-environment-update', {
|
||||
envVariables: testResults.envVariables,
|
||||
runtimeVariables: testResults.runtimeVariables,
|
||||
collectionUid
|
||||
});
|
||||
|
||||
mainWindow.webContents.send('main:global-environment-variables-update', {
|
||||
globalEnvironmentVariables: testResults.globalEnvironmentVariables
|
||||
});
|
||||
|
||||
collection.globalEnvironmentVariables = testResults.globalEnvironmentVariables;
|
||||
} catch (testError) {
|
||||
|
||||
if (testError.partialResults && testError.partialResults.results.length > 0) {
|
||||
|
||||
// Send the partial test results
|
||||
mainWindow.webContents.send('main:run-folder-event', {
|
||||
type: 'test-results',
|
||||
testResults: testError.partialResults.results,
|
||||
...eventData
|
||||
});
|
||||
|
||||
mainWindow.webContents.send('main:script-environment-update', {
|
||||
envVariables: testError.partialResults.envVariables,
|
||||
runtimeVariables: testError.partialResults.runtimeVariables,
|
||||
collectionUid
|
||||
});
|
||||
|
||||
mainWindow.webContents.send('main:global-environment-variables-update', {
|
||||
globalEnvironmentVariables: testError.partialResults.globalEnvironmentVariables
|
||||
});
|
||||
|
||||
collection.globalEnvironmentVariables = testError.partialResults.globalEnvironmentVariables;
|
||||
}
|
||||
}
|
||||
|
||||
mainWindow.webContents.send('main:run-folder-event', {
|
||||
type: 'test-results',
|
||||
testResults: testResults.results,
|
||||
...eventData
|
||||
});
|
||||
|
||||
mainWindow.webContents.send('main:script-environment-update', {
|
||||
envVariables: testResults.envVariables,
|
||||
runtimeVariables: testResults.runtimeVariables,
|
||||
collectionUid
|
||||
});
|
||||
|
||||
mainWindow.webContents.send('main:global-environment-variables-update', {
|
||||
globalEnvironmentVariables: testResults.globalEnvironmentVariables
|
||||
});
|
||||
|
||||
collection.globalEnvironmentVariables = testResults.globalEnvironmentVariables;
|
||||
}
|
||||
} catch (error) {
|
||||
mainWindow.webContents.send('main:run-folder-event', {
|
||||
|
||||
@@ -124,56 +124,63 @@ class TestRuntime {
|
||||
context.bru.runRequest = runRequestByItemPathname;
|
||||
}
|
||||
|
||||
if (this.runtime === 'quickjs') {
|
||||
await executeQuickJsVmAsync({
|
||||
script: testsFile,
|
||||
context: context
|
||||
});
|
||||
} else {
|
||||
// default runtime is vm2
|
||||
const vm = new NodeVM({
|
||||
sandbox: context,
|
||||
require: {
|
||||
context: 'sandbox',
|
||||
external: true,
|
||||
root: [collectionPath, ...additionalContextRootsAbsolute],
|
||||
mock: {
|
||||
// node libs
|
||||
path,
|
||||
stream,
|
||||
util,
|
||||
url,
|
||||
http,
|
||||
https,
|
||||
punycode,
|
||||
zlib,
|
||||
// 3rd party libs
|
||||
ajv,
|
||||
'ajv-formats': addFormats,
|
||||
btoa,
|
||||
atob,
|
||||
lodash,
|
||||
moment,
|
||||
uuid,
|
||||
nanoid,
|
||||
axios,
|
||||
chai,
|
||||
'node-fetch': fetch,
|
||||
'crypto-js': CryptoJS,
|
||||
'xml2js': xml2js,
|
||||
cheerio,
|
||||
tv4,
|
||||
...whitelistedModules,
|
||||
fs: allowScriptFilesystemAccess ? fs : undefined,
|
||||
'node-vault': NodeVault
|
||||
let scriptError = null;
|
||||
|
||||
try {
|
||||
if (this.runtime === 'quickjs') {
|
||||
await executeQuickJsVmAsync({
|
||||
script: testsFile,
|
||||
context: context
|
||||
});
|
||||
} else {
|
||||
// default runtime is vm2
|
||||
const vm = new NodeVM({
|
||||
sandbox: context,
|
||||
require: {
|
||||
context: 'sandbox',
|
||||
external: true,
|
||||
root: [collectionPath, ...additionalContextRootsAbsolute],
|
||||
mock: {
|
||||
// node libs
|
||||
path,
|
||||
stream,
|
||||
util,
|
||||
url,
|
||||
http,
|
||||
https,
|
||||
punycode,
|
||||
zlib,
|
||||
// 3rd party libs
|
||||
ajv,
|
||||
'ajv-formats': addFormats,
|
||||
btoa,
|
||||
atob,
|
||||
lodash,
|
||||
moment,
|
||||
uuid,
|
||||
nanoid,
|
||||
axios,
|
||||
chai,
|
||||
'node-fetch': fetch,
|
||||
'crypto-js': CryptoJS,
|
||||
'xml2js': xml2js,
|
||||
cheerio,
|
||||
tv4,
|
||||
...whitelistedModules,
|
||||
fs: allowScriptFilesystemAccess ? fs : undefined,
|
||||
'node-vault': NodeVault
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
const asyncVM = vm.run(`module.exports = async () => { ${testsFile}}`, path.join(collectionPath, 'vm.js'));
|
||||
await asyncVM();
|
||||
});
|
||||
const asyncVM = vm.run(`module.exports = async () => { ${testsFile}}`, path.join(collectionPath, 'vm.js'));
|
||||
await asyncVM();
|
||||
}
|
||||
} catch (error) {
|
||||
scriptError = error;
|
||||
console.error('Test script execution error:', error);
|
||||
}
|
||||
|
||||
return {
|
||||
const result = {
|
||||
request,
|
||||
envVariables: cleanJson(envVariables),
|
||||
runtimeVariables: cleanJson(runtimeVariables),
|
||||
@@ -181,6 +188,13 @@ class TestRuntime {
|
||||
results: cleanJson(__brunoTestResults.getResults()),
|
||||
nextRequestName: bru.nextRequest
|
||||
};
|
||||
|
||||
if (scriptError) {
|
||||
scriptError.partialResults = result;
|
||||
throw scriptError;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user