diff --git a/packages/bruno-app/src/components/ResponsePane/TestResults/StyledWrapper.js b/packages/bruno-app/src/components/ResponsePane/TestResults/StyledWrapper.js
index 001b4dc29..5b029386e 100644
--- a/packages/bruno-app/src/components/ResponsePane/TestResults/StyledWrapper.js
+++ b/packages/bruno-app/src/components/ResponsePane/TestResults/StyledWrapper.js
@@ -1,6 +1,18 @@
import styled from 'styled-components';
const StyledWrapper = styled.div`
+ color: ${(props) => props.theme.text};
+
+ .test-summary {
+ transition: background-color 0.2s;
+ border-bottom: 1px solid ${(props) => props.theme.sidebar.collection.item.indentBorder};
+ color: ${(props) => props.theme.text};
+
+ &:hover {
+ background-color: ${(props) => props.theme.sidebar.collection.item.hoverBg};
+ }
+ }
+
.test-success {
color: ${(props) => props.theme.colors.text.green};
}
@@ -9,12 +21,24 @@ const StyledWrapper = styled.div`
color: ${(props) => props.theme.colors.text.danger};
}
+ .test-success-count {
+ color: ${(props) => props.theme.colors.text.green};
+ }
+
+ .test-failure-count {
+ color: ${(props) => props.theme.colors.text.danger};
+ }
+
.error-message {
color: ${(props) => props.theme.colors.text.muted};
}
- .skipped-request {
- color: ${(props) => props.theme.colors.text.muted};
+ .test-results-list {
+ transition: all 0.3s ease;
+ }
+
+ .dropdown-icon {
+ color: ${(props) => props.theme.sidebar.dropdownIcon.color};
}
`;
diff --git a/packages/bruno-app/src/components/ResponsePane/TestResults/index.js b/packages/bruno-app/src/components/ResponsePane/TestResults/index.js
index 074fac9e1..8157df2ee 100644
--- a/packages/bruno-app/src/components/ResponsePane/TestResults/index.js
+++ b/packages/bruno-app/src/components/ResponsePane/TestResults/index.js
@@ -1,63 +1,151 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import StyledWrapper from './StyledWrapper';
+import {
+ IconChevronDown,
+ IconChevronRight,
+ IconCircleCheck,
+ IconCircleX
+} from '@tabler/icons';
-const TestResults = ({ results, assertionResults }) => {
+const ResultIcon = ({ status }) => (
+
+ {status === 'pass' ? (
+
+ ) : (
+
+ )}
+
+);
+
+const ErrorMessage = ({ error }) => error && (
+ <>
+
+
+ {error}
+
+ >
+);
+
+const ResultItem = ({ result, type }) => (
+
-
- Tests ({results.length}/{results.length}), Passed: {passedTests.length}, Failed: {failedTests.length}
-
-
- {results.map((result) => (
- -
- {result.status === 'pass' ? (
- ✔ {result.description}
- ) : (
- <>
- ✘ {result.description}
-
- {result.error}
- >
- )}
-
- ))}
-
+
+ toggleSection('preRequest')}
+ type="test"
+ />
-
- Assertions ({assertionResults.length}/{assertionResults.length}), Passed: {passedAssertions.length}, Failed:{' '}
- {failedAssertions.length}
-
-
- {assertionResults.map((result) => (
- -
- {result.status === 'pass' ? (
-
- ✔ {result.lhsExpr}: {result.rhsExpr}
-
- ) : (
- <>
-
- ✘ {result.lhsExpr}: {result.rhsExpr}
-
-
- {result.error}
- >
- )}
-
- ))}
-
+ toggleSection('postResponse')}
+ type="test"
+ />
+
+ toggleSection('tests')}
+ type="test"
+ />
+
+ toggleSection('assertions')}
+ type="assertion"
+ />
);
};
diff --git a/packages/bruno-app/src/components/ResponsePane/TestResultsLabel/index.js b/packages/bruno-app/src/components/ResponsePane/TestResultsLabel/index.js
index f894d1f76..51d6f94cc 100644
--- a/packages/bruno-app/src/components/ResponsePane/TestResultsLabel/index.js
+++ b/packages/bruno-app/src/components/ResponsePane/TestResultsLabel/index.js
@@ -1,9 +1,13 @@
import React from 'react';
+import { IconCircleCheck, IconCircleX } from '@tabler/icons';
-const TestResultsLabel = ({ results, assertionResults }) => {
+const TestResultsLabel = ({ results, assertionResults, preRequestTestResults, postResponseTestResults }) => {
results = results || [];
assertionResults = assertionResults || [];
- if (!results.length && !assertionResults.length) {
+ preRequestTestResults = preRequestTestResults || [];
+ postResponseTestResults = postResponseTestResults || [];
+
+ if (!results.length && !assertionResults.length && !preRequestTestResults.length && !postResponseTestResults.length) {
return 'Tests';
}
@@ -13,8 +17,14 @@ const TestResultsLabel = ({ results, assertionResults }) => {
const numberOfAssertions = assertionResults.length;
const numberOfFailedAssertions = assertionResults.filter((result) => result.status === 'fail').length;
- const totalNumberOfTests = numberOfTests + numberOfAssertions;
- const totalNumberOfFailedTests = numberOfFailedTests + numberOfFailedAssertions;
+ const numberOfPreRequestTests = preRequestTestResults.length;
+ const numberOfFailedPreRequestTests = preRequestTestResults.filter((result) => result.status === 'fail').length;
+
+ const numberOfPostResponseTests = postResponseTestResults.length;
+ const numberOfFailedPostResponseTests = postResponseTestResults.filter((result) => result.status === 'fail').length;
+
+ const totalNumberOfTests = numberOfTests + numberOfAssertions + numberOfPreRequestTests + numberOfPostResponseTests;
+ const totalNumberOfFailedTests = numberOfFailedTests + numberOfFailedAssertions + numberOfFailedPreRequestTests + numberOfFailedPostResponseTests;
return (
diff --git a/packages/bruno-app/src/components/ResponsePane/index.js b/packages/bruno-app/src/components/ResponsePane/index.js
index 1fb120ae9..71e55cdd5 100644
--- a/packages/bruno-app/src/components/ResponsePane/index.js
+++ b/packages/bruno-app/src/components/ResponsePane/index.js
@@ -73,7 +73,12 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
return ;
}
case 'tests': {
- return ;
+ return ;
}
default: {
@@ -122,7 +127,7 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
};
const responseHeadersCount = typeof response.headers === 'object' ? Object.entries(response.headers).length : 0;
-
+
const hasScriptError = item?.preRequestScriptErrorMessage || item?.postResponseScriptErrorMessage;
return (
@@ -139,14 +144,19 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
Timeline
selectTab('tests')}>
-
+
{!isLoading ? (
{hasScriptError && !showScriptErrorCard && (
- setShowScriptErrorCard(true)}
+ setShowScriptErrorCard(true)}
/>
)}
{focusedTab?.responsePaneTab === "timeline" ? (
@@ -168,9 +178,9 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
>
{isLoading ? : null}
{hasScriptError && showScriptErrorCard && (
- setShowScriptErrorCard(false)}
+ setShowScriptErrorCard(false)}
/>
)}
{!item?.response ? (
diff --git a/packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js b/packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js
index 5591dbfea..21f02406e 100644
--- a/packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js
+++ b/packages/bruno-app/src/components/RunnerResults/ResponsePane/index.js
@@ -16,7 +16,7 @@ import RunnerTimeline from 'components/ResponsePane/RunnerTimeline';
const ResponsePane = ({ rightPaneWidth, item, collection }) => {
const [selectedTab, setSelectedTab] = useState('response');
- const { requestSent, responseReceived, testResults, assertionResults, error } = item;
+ const { requestSent, responseReceived, testResults, assertionResults, preRequestTestResults, postResponseTestResults, error } = item;
const headers = get(item, 'responseReceived.headers', []);
const status = get(item, 'responseReceived.status', 0);
@@ -49,7 +49,12 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
return ;
}
case 'tests': {
- return ;
+ return ;
}
default: {
@@ -86,7 +91,12 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
Timeline
selectTab('tests')}>
-
+
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 67c099428..15ce34f62 100644
--- a/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
+++ b/packages/bruno-app/src/providers/ReduxStore/slices/collections/index.js
@@ -2015,6 +2015,16 @@ export const collectionsSlice = createSlice({
const { results } = action.payload;
item.testResults = results;
}
+
+ if (type === 'test-results-pre-request') {
+ const { results } = action.payload;
+ item.preRequestTestResults = results;
+ }
+
+ if (type === 'test-results-post-response') {
+ const { results } = action.payload;
+ item.postResponseTestResults = results;
+ }
}
}
},
diff --git a/packages/bruno-cli/src/commands/run.js b/packages/bruno-cli/src/commands/run.js
index af4cf4ae3..5cf4f67f6 100644
--- a/packages/bruno-cli/src/commands/run.js
+++ b/packages/bruno-cli/src/commands/run.js
@@ -16,6 +16,20 @@ const { findItemInCollection, getAllRequestsInFolder, createCollectionJsonFromPa
const command = 'run [filename]';
const desc = 'Run a request';
+const formatTestSummary = (label, maxLength, passed, failed, total, errorCount = 0, skippedCount = 0) => {
+ const parts = [
+ `${rpad(label, maxLength)} ${chalk.green(`${passed} passed`)}`
+ ];
+
+ if (failed > 0) parts.push(chalk.red(`${failed} failed`));
+ if (errorCount > 0) parts.push(chalk.red(`${errorCount} error`));
+ if (skippedCount > 0) parts.push(chalk.magenta(`${skippedCount} skipped`));
+
+ parts.push(`${total} total`);
+
+ return parts.join(', ');
+};
+
const printRunSummary = (results) => {
const {
totalRequests,
@@ -28,38 +42,40 @@ const printRunSummary = (results) => {
failedAssertions,
totalTests,
passedTests,
- failedTests
+ failedTests,
+ totalPreRequestTests,
+ passedPreRequestTests,
+ failedPreRequestTests,
+ totalPostResponseTests,
+ passedPostResponseTests,
+ failedPostResponseTests
} = getRunnerSummary(results);
const maxLength = 12;
- let requestSummary = `${rpad('Requests:', maxLength)} ${chalk.green(`${passedRequests} passed`)}`;
- if (failedRequests > 0) {
- requestSummary += `, ${chalk.red(`${failedRequests} failed`)}`;
- }
- if (errorRequests > 0) {
- requestSummary += `, ${chalk.red(`${errorRequests} error`)}`;
- }
- if (skippedRequests > 0) {
- requestSummary += `, ${chalk.magenta(`${skippedRequests} skipped`)}`;
- }
- requestSummary += `, ${totalRequests} total`;
+ const requestSummary = formatTestSummary('Requests:', maxLength, passedRequests, failedRequests, totalRequests, errorRequests, skippedRequests);
+ const testSummary = formatTestSummary('Tests:', maxLength, passedTests, failedTests, totalTests);
+ const assertSummary = formatTestSummary('Assertions:', maxLength, passedAssertions, failedAssertions, totalAssertions);
- let assertSummary = `${rpad('Tests:', maxLength)} ${chalk.green(`${passedTests} passed`)}`;
- if (failedTests > 0) {
- assertSummary += `, ${chalk.red(`${failedTests} failed`)}`;
+ let preRequestTestSummary = '';
+ if (totalPreRequestTests > 0) {
+ preRequestTestSummary = formatTestSummary('Pre-Request Tests:', maxLength, passedPreRequestTests, failedPreRequestTests, totalPreRequestTests);
}
- assertSummary += `, ${totalTests} total`;
- let testSummary = `${rpad('Assertions:', maxLength)} ${chalk.green(`${passedAssertions} passed`)}`;
- if (failedAssertions > 0) {
- testSummary += `, ${chalk.red(`${failedAssertions} failed`)}`;
+ let postResponseTestSummary = '';
+ if (totalPostResponseTests > 0) {
+ postResponseTestSummary = formatTestSummary('Post-Response Tests:', maxLength, passedPostResponseTests, failedPostResponseTests, totalPostResponseTests);
}
- testSummary += `, ${totalAssertions} total`;
console.log('\n' + chalk.bold(requestSummary));
- console.log(chalk.bold(assertSummary));
+ if (preRequestTestSummary) {
+ console.log(chalk.bold(preRequestTestSummary));
+ }
+ if (postResponseTestSummary) {
+ console.log(chalk.bold(postResponseTestSummary));
+ }
console.log(chalk.bold(testSummary));
+ console.log(chalk.bold(assertSummary));
return {
totalRequests,
@@ -72,7 +88,13 @@ const printRunSummary = (results) => {
failedAssertions,
totalTests,
passedTests,
- failedTests
+ failedTests,
+ totalPreRequestTests,
+ passedPreRequestTests,
+ failedPreRequestTests,
+ totalPostResponseTests,
+ passedPostResponseTests,
+ failedPostResponseTests
}
};
@@ -498,7 +520,7 @@ const handler = async function (argv) {
if(Number.isNaN(delay) && !isLastRun){
console.log(chalk.red(`Ignoring delay because it's not a valid number.`));
}
-
+
results.push({
...result,
runtime: process.hrtime(start)[0] + process.hrtime(start)[1] / 1e9,
@@ -539,7 +561,9 @@ const handler = async function (argv) {
const requestFailure = result?.error && !result?.skipped;
const testFailure = result?.testResults?.find((iter) => iter.status === 'fail');
const assertionFailure = result?.assertionResults?.find((iter) => iter.status === 'fail');
- if (requestFailure || testFailure || assertionFailure) {
+ const preRequestTestFailure = result?.preRequestTestResults?.find((iter) => iter.status === 'fail');
+ const postResponseTestFailure = result?.postResponseTestResults?.find((iter) => iter.status === 'fail');
+ if (requestFailure || testFailure || assertionFailure || preRequestTestFailure || postResponseTestFailure) {
break;
}
}
@@ -550,7 +574,7 @@ const handler = async function (argv) {
if (result?.shouldStopRunnerExecution) {
break;
}
-
+
if (nextRequestName !== undefined) {
nJumps++;
if (nJumps > 10000) {
@@ -617,7 +641,7 @@ const handler = async function (argv) {
}
}
- if ((summary.failedAssertions + summary.failedTests + summary.failedRequests > 0) || (summary?.errorRequests > 0)) {
+ if ((summary.failedAssertions + summary.failedTests + summary.failedPreRequestTests + summary.failedPostResponseTests + summary.failedRequests > 0) || (summary?.errorRequests > 0)) {
process.exit(constants.EXIT_STATUS.ERROR_FAILED_COLLECTION);
}
} catch (err) {
diff --git a/packages/bruno-cli/src/runner/run-single-request.js b/packages/bruno-cli/src/runner/run-single-request.js
index c660367e8..6fd575f90 100644
--- a/packages/bruno-cli/src/runner/run-single-request.js
+++ b/packages/bruno-cli/src/runner/run-single-request.js
@@ -45,10 +45,33 @@ const runSingleRequest = async function (
) {
const { pathname: itemPathname } = item;
const relativeItemPathname = path.relative(collectionPath, itemPathname);
+
+ const logResults = (results, title) => {
+ if (results?.length) {
+ if (title) {
+ console.log(chalk.dim(title));
+ }
+ each(results, (r) => {
+ const message = r.description || `${r.lhsExpr}: ${r.rhsExpr}`;
+ if (r.status === 'pass') {
+ console.log(chalk.green(` ✓ `) + chalk.dim(message));
+ } else {
+ console.log(chalk.red(` ✕ `) + chalk.red(message));
+ if (r.error) {
+ console.log(chalk.red(` ${r.error}`));
+ }
+ }
+ });
+ }
+ };
+
try {
let request;
let nextRequestName;
let shouldStopRunnerExecution = false;
+ let preRequestTestResults = [];
+ let postResponseTestResults = [];
+
request = prepareRequest(item, collection);
request.__bruno__executionMode = 'cli';
@@ -103,9 +126,13 @@ const runSingleRequest = async function (
skipped: true,
assertionResults: [],
testResults: [],
+ preRequestTestResults: result?.results || [],
+ postResponseTestResults: [],
shouldStopRunnerExecution
};
}
+
+ preRequestTestResults = result?.results || [];
}
// interpolate variables inside request
@@ -428,6 +455,8 @@ const runSingleRequest = async function (
status: 'error',
assertionResults: [],
testResults: [],
+ preRequestTestResults,
+ postResponseTestResults,
nextRequestName: nextRequestName,
shouldStopRunnerExecution
};
@@ -441,6 +470,9 @@ const runSingleRequest = async function (
chalk.dim(` (${response.status} ${response.statusText}) - ${responseTime} ms`)
);
+ // Log pre-request test results
+ logResults(preRequestTestResults, 'Pre-Request Tests');
+
// run post-response vars
const postResponseVars = get(item, 'request.vars.res');
if (postResponseVars?.length) {
@@ -480,9 +512,11 @@ const runSingleRequest = async function (
if (result?.stopExecution) {
shouldStopRunnerExecution = true;
}
+
+ postResponseTestResults = result?.results || [];
+ logResults(postResponseTestResults, 'Post-Response Tests');
}
- // run assertions
let assertionResults = [];
const assertions = get(item, 'request.assertions');
if (assertions) {
@@ -495,15 +529,6 @@ const runSingleRequest = async function (
runtimeVariables,
processEnvVars
);
-
- each(assertionResults, (r) => {
- if (r.status === 'pass') {
- console.log(chalk.green(` ✓ `) + chalk.dim(`assert: ${r.lhsExpr}: ${r.rhsExpr}`));
- } else {
- console.log(chalk.red(` ✕ `) + chalk.red(`assert: ${r.lhsExpr}: ${r.rhsExpr}`));
- console.log(chalk.red(` ${r.error}`));
- }
- });
}
// run tests
@@ -533,17 +558,12 @@ const runSingleRequest = async function (
if (result?.stopExecution) {
shouldStopRunnerExecution = true;
}
+
+ logResults(testResults, 'Tests');
}
- if (testResults?.length) {
- each(testResults, (testResult) => {
- if (testResult.status === 'pass') {
- console.log(chalk.green(` ✓ `) + chalk.dim(testResult.description));
- } else {
- console.log(chalk.red(` ✕ `) + chalk.red(testResult.description));
- }
- });
- }
+
+ logResults(assertionResults, 'Assertions');
return {
test: {
@@ -566,6 +586,8 @@ const runSingleRequest = async function (
status: 'pass',
assertionResults,
testResults,
+ preRequestTestResults,
+ postResponseTestResults,
nextRequestName: nextRequestName,
shouldStopRunnerExecution
};
@@ -591,7 +613,9 @@ const runSingleRequest = async function (
status: 'error',
error: err.message,
assertionResults: [],
- testResults: []
+ testResults: [],
+ preRequestTestResults: [],
+ postResponseTestResults: []
};
}
};
diff --git a/packages/bruno-common/src/runner/runner-summary.ts b/packages/bruno-common/src/runner/runner-summary.ts
index 5862729bb..4fb276a29 100644
--- a/packages/bruno-common/src/runner/runner-summary.ts
+++ b/packages/bruno-common/src/runner/runner-summary.ts
@@ -13,12 +13,20 @@ export const getRunnerSummary = (results: T_RunnerRequestExecutionResult[]): T_R
let totalTests = 0;
let passedTests = 0;
let failedTests = 0;
+ let totalPreRequestTests = 0;
+ let passedPreRequestTests = 0;
+ let failedPreRequestTests = 0;
+ let totalPostResponseTests = 0;
+ let passedPostResponseTests = 0;
+ let failedPostResponseTests = 0;
for (const result of results || []) {
- const { status, testResults, assertionResults } = result;
+ const { status, testResults, assertionResults, preRequestTestResults, postResponseTestResults } = result;
totalRequests += 1;
totalTests += Number(testResults?.length) || 0;
totalAssertions += Number(assertionResults?.length) || 0;
+ totalPreRequestTests += Number(preRequestTestResults?.length) || 0;
+ totalPostResponseTests += Number(postResponseTestResults?.length) || 0;
if (status === 'skipped') {
skippedRequests += 1;
@@ -42,6 +50,22 @@ export const getRunnerSummary = (results: T_RunnerRequestExecutionResult[]): T_R
failedAssertions += 1;
}
}
+ for (const preRequestTestResult of preRequestTestResults || []) {
+ if (preRequestTestResult.status === "pass") {
+ passedPreRequestTests += 1;
+ } else {
+ anyFailed = true;
+ failedPreRequestTests += 1;
+ }
+ }
+ for (const postResponseTestResult of postResponseTestResults || []) {
+ if (postResponseTestResult.status === "pass") {
+ passedPostResponseTests += 1;
+ } else {
+ anyFailed = true;
+ failedPostResponseTests += 1;
+ }
+ }
if (!anyFailed && status !== "error") {
passedRequests += 1;
@@ -64,5 +88,11 @@ export const getRunnerSummary = (results: T_RunnerRequestExecutionResult[]): T_R
totalTests,
passedTests,
failedTests,
+ totalPreRequestTests,
+ passedPreRequestTests,
+ failedPreRequestTests,
+ totalPostResponseTests,
+ passedPostResponseTests,
+ failedPostResponseTests,
};
};
\ No newline at end of file
diff --git a/packages/bruno-common/src/runner/types/index.ts b/packages/bruno-common/src/runner/types/index.ts
index 33840f0ad..3565930f2 100644
--- a/packages/bruno-common/src/runner/types/index.ts
+++ b/packages/bruno-common/src/runner/types/index.ts
@@ -89,6 +89,8 @@ export type T_RunnerRequestExecutionResult = {
error: null | undefined | string;
assertionResults?: T_AssertionResult[];
testResults?: T_TestResult[];
+ preRequestTestResults?: T_TestResult[];
+ postResponseTestResults?: T_TestResult[];
runDuration: number;
}
@@ -112,4 +114,10 @@ export type T_RunSummary = {
totalTests: number;
passedTests: number;
failedTests: number;
+ totalPreRequestTests: number;
+ passedPreRequestTests: number;
+ failedPreRequestTests: number;
+ totalPostResponseTests: number;
+ passedPostResponseTests: number;
+ failedPostResponseTests: number;
}
\ No newline at end of file
diff --git a/packages/bruno-converters/src/postman/postman-to-bruno.js b/packages/bruno-converters/src/postman/postman-to-bruno.js
index 8ae8e195e..bef21b46e 100644
--- a/packages/bruno-converters/src/postman/postman-to-bruno.js
+++ b/packages/bruno-converters/src/postman/postman-to-bruno.js
@@ -103,14 +103,14 @@ const importScriptsFromEvents = (events, requestObject) => {
}
if (event.listen === 'test') {
- if (!requestObject.tests) {
- requestObject.tests = {};
+ if (!requestObject.script) {
+ requestObject.script = {};
}
if (event.script.exec && event.script.exec.length > 0) {
- requestObject.tests = postmanTranslation(event.script.exec)
+ requestObject.script.res = postmanTranslation(event.script.exec)
} else {
- requestObject.tests = '';
+ requestObject.script.res = '';
console.warn('Unexpected event.script.exec type', typeof event.script.exec);
}
}
@@ -376,16 +376,17 @@ const importPostmanV2CollectionItem = (brunoParent, item, parentAuth, { useWorke
}
}
if (event.listen === 'test' && event.script && event.script.exec) {
- if (!brunoRequestItem.request?.tests) {
- brunoRequestItem.request.tests = {};
+ if (!brunoRequestItem.request?.script) {
+ brunoRequestItem.request.script = {};
}
if (event.script.exec && event.script.exec.length > 0) {
- brunoRequestItem.request.tests = postmanTranslation(event.script.exec)
+ brunoRequestItem.request.script.res = postmanTranslation(event.script.exec)
} else {
- brunoRequestItem.request.tests = '';
+ brunoRequestItem.request.script.res = '';
console.warn('Unexpected event.script.exec type', typeof event.script.exec);
}
}
+
});
}
}
@@ -581,15 +582,12 @@ const importPostmanV2Collection = async (collection, { useWorkers = false }) =>
if (!item.root.request.script) {
item.root.request.script = {};
}
- if (!item.root.request.tests) {
- item.root.request.tests = '';
- }
const script = translatedScripts.get(item.uid).request?.script?.req;
- const tests = translatedScripts.get(item.uid).request?.tests;
+ const tests = translatedScripts.get(item.uid).request?.script?.res;
item.root.request.script.req = script && script.length > 0 ? script : '';
- item.root.request.tests = tests && tests.length > 0 ? tests : '';
+ item.root.request.script.res = tests && tests.length > 0 ? tests : '';
}
// Recursively apply to nested items
@@ -601,15 +599,12 @@ const importPostmanV2Collection = async (collection, { useWorkers = false }) =>
if (!item.request.script) {
item.request.script = {};
}
- if (!item.request.tests) {
- item.request.tests = '';
- }
const script = translatedScripts.get(item.uid).request?.script?.req;
- const tests = translatedScripts.get(item.uid).request?.tests;
+ const tests = translatedScripts.get(item.uid).request?.script?.res;
item.request.script.req = script && script.length > 0 ? script : '';
- item.request.tests = tests && tests.length > 0 ? tests : '';
+ item.request.script.res = tests && tests.length > 0 ? tests : '';
}
}
});
diff --git a/packages/bruno-converters/src/workers/scripts/translate-postman-scripts.js b/packages/bruno-converters/src/workers/scripts/translate-postman-scripts.js
index 31b7d9008..816a08f03 100644
--- a/packages/bruno-converters/src/workers/scripts/translate-postman-scripts.js
+++ b/packages/bruno-converters/src/workers/scripts/translate-postman-scripts.js
@@ -6,8 +6,7 @@ parentPort.on('message', (workerData) => {
const { scripts } = workerData;
const modScripts = scripts.map(([uid, { events }]) => {
const requestObject = {
- script: {},
- tests: {}
+ script: {}
}
if (events && Array.isArray(events)) {
@@ -23,9 +22,9 @@ parentPort.on('message', (workerData) => {
if(event.listen === 'test') {
if(event.script.exec && event.script.exec.length > 0) {
- requestObject.tests = postmanTranslation(event.script.exec);
+ requestObject.script.res = postmanTranslation(event.script.exec);
} else {
- requestObject.tests = '';
+ requestObject.script.res = '';
}
}
}
diff --git a/packages/bruno-electron/src/ipc/network/index.js b/packages/bruno-electron/src/ipc/network/index.js
index f5a49ca1d..674cb5754 100644
--- a/packages/bruno-electron/src/ipc/network/index.js
+++ b/packages/bruno-electron/src/ipc/network/index.js
@@ -596,7 +596,7 @@ const registerNetworkIpc = (mainWindow) => {
try {
- await runPreRequest(
+ const preRequestScriptResult = await runPreRequest(
request,
requestUid,
envVars,
@@ -609,6 +609,16 @@ const registerNetworkIpc = (mainWindow) => {
runRequestByItemPathname
);
+ if (preRequestScriptResult?.results) {
+ mainWindow.webContents.send('main:run-request-event', {
+ type: 'test-results-pre-request',
+ results: preRequestScriptResult.results,
+ itemUid: item.uid,
+ requestUid,
+ collectionUid
+ });
+ }
+
!runInBackground && mainWindow.webContents.send('main:run-request-event', {
type: 'pre-request-script-execution',
requestUid,
@@ -724,7 +734,7 @@ const registerNetworkIpc = (mainWindow) => {
mainWindow.webContents.send('main:cookies-update', safeParseJSON(safeStringifyJSON(domainsWithCookies)));
try {
- await runPostResponse(
+ const postResponseScriptResult = await runPostResponse(
request,
response,
requestUid,
@@ -737,6 +747,16 @@ const registerNetworkIpc = (mainWindow) => {
scriptingConfig,
runRequestByItemPathname
);
+
+ if (postResponseScriptResult?.results) {
+ mainWindow.webContents.send('main:run-request-event', {
+ type: 'test-results-post-response',
+ results: postResponseScriptResult.results,
+ itemUid: item.uid,
+ requestUid,
+ collectionUid
+ });
+ }
!runInBackground && mainWindow.webContents.send('main:run-request-event', {
type: 'post-response-script-execution',
requestUid,
diff --git a/packages/bruno-js/src/runtime/script-runtime.js b/packages/bruno-js/src/runtime/script-runtime.js
index a8f2abbee..a18c94917 100644
--- a/packages/bruno-js/src/runtime/script-runtime.js
+++ b/packages/bruno-js/src/runtime/script-runtime.js
@@ -12,7 +12,10 @@ const { get } = require('lodash');
const Bru = require('../bru');
const BrunoRequest = require('../bruno-request');
const BrunoResponse = require('../bruno-response');
+const Test = require('../test');
+const TestResults = require('../test-results');
const { cleanJson } = require('../utils');
+const { createBruTestResultMethods } = require('../utils/results');
// Inbuilt Library Support
const ajv = require('ajv');
@@ -57,6 +60,7 @@ class ScriptRuntime {
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, oauth2CredentialVariables, collectionName);
const req = new BrunoRequest(request);
const allowScriptFilesystemAccess = get(scriptingConfig, 'filesystemAccess.allow', false);
@@ -78,9 +82,16 @@ class ScriptRuntime {
}
}
+ // extend bru with result getter methods
+ const { __brunoTestResults, test } = createBruTestResultMethods(bru, assertionResults, chai);
+
const context = {
bru,
- req
+ req,
+ test,
+ expect: chai.expect,
+ assert: chai.assert,
+ __brunoTestResults: __brunoTestResults
};
if (onConsoleLog && typeof onConsoleLog === 'function') {
@@ -114,6 +125,7 @@ class ScriptRuntime {
envVariables: cleanJson(envVariables),
runtimeVariables: cleanJson(runtimeVariables),
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
+ results: cleanJson(__brunoTestResults.getResults()),
nextRequestName: bru.nextRequest,
skipRequest: bru.skipRequest,
stopExecution: bru.stopExecution
@@ -168,6 +180,7 @@ class ScriptRuntime {
envVariables: cleanJson(envVariables),
runtimeVariables: cleanJson(runtimeVariables),
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
+ results: cleanJson(__brunoTestResults.getResults()),
nextRequestName: bru.nextRequest,
skipRequest: bru.skipRequest,
stopExecution: bru.stopExecution
@@ -192,6 +205,7 @@ class ScriptRuntime {
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, oauth2CredentialVariables, collectionName);
const req = new BrunoRequest(request);
const res = new BrunoResponse(response);
@@ -214,10 +228,17 @@ class ScriptRuntime {
}
}
+ // extend bru with result getter methods
+ const { __brunoTestResults, test } = createBruTestResultMethods(bru, assertionResults, chai);
+
const context = {
bru,
req,
- res
+ res,
+ test,
+ expect: chai.expect,
+ assert: chai.assert,
+ __brunoTestResults: __brunoTestResults
};
if (onConsoleLog && typeof onConsoleLog === 'function') {
@@ -251,6 +272,7 @@ class ScriptRuntime {
envVariables: cleanJson(envVariables),
runtimeVariables: cleanJson(runtimeVariables),
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
+ results: cleanJson(__brunoTestResults.getResults()),
nextRequestName: bru.nextRequest,
skipRequest: bru.skipRequest,
stopExecution: bru.stopExecution
@@ -305,6 +327,7 @@ class ScriptRuntime {
envVariables: cleanJson(envVariables),
runtimeVariables: cleanJson(runtimeVariables),
globalEnvironmentVariables: cleanJson(globalEnvironmentVariables),
+ results: cleanJson(__brunoTestResults.getResults()),
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 b172fc7b7..bed0589ca 100644
--- a/packages/bruno-js/src/runtime/test-runtime.js
+++ b/packages/bruno-js/src/runtime/test-runtime.js
@@ -16,6 +16,7 @@ const BrunoResponse = require('../bruno-response');
const Test = require('../test');
const TestResults = require('../test-results');
const { cleanJson } = require('../utils');
+const { createBruTestResultMethods } = require('../utils/results');
// Inbuilt Library Support
const ajv = require('ajv');
@@ -35,25 +36,6 @@ const cheerio = require('cheerio');
const tv4 = require('tv4');
const { executeQuickJsVmAsync } = require('../sandbox/quickjs');
-const getResultsSummary = (results) => {
- const summary = {
- total: results.length,
- passed: 0,
- failed: 0,
- skipped: 0,
- };
-
- results.forEach((r) => {
- const passed = r.status === "pass";
- if (passed) summary.passed += 1;
- else if (r.status === "fail") summary.failed += 1;
- else summary.skipped += 1;
- });
-
- return summary;
-}
-
-
class TestRuntime {
constructor(props) {
this.runtime = props?.runtime || 'vm2';
@@ -99,9 +81,8 @@ class TestRuntime {
}
}
- const __brunoTestResults = new TestResults();
-
- const test = Test(__brunoTestResults, chai);
+ // extend bru with result getter methods
+ const { __brunoTestResults, test } = createBruTestResultMethods(bru, assertionResults, chai);
if (!testsFile || !testsFile.length) {
return {
@@ -114,36 +95,6 @@ class TestRuntime {
};
}
- bru.getTestResults = async () => {
- let results = await __brunoTestResults.getResults();
- const summary = getResultsSummary(results);
- return {
- summary,
- results: results?.map?.(r => ({
- status: r?.status,
- description: r?.description,
- expected: r?.expected,
- actual: r?.actual,
- error: r?.error
- }))
- };
- }
- bru.getAssertionResults = async () => {
- let results = assertionResults;
- const summary = getResultsSummary(results);
- return {
- summary,
- results: results?.map?.(r => ({
- status: r?.status,
- lhsExpr: r?.lhsExpr,
- rhsExpr: r?.rhsExpr,
- operator: r?.operator,
- rhsOperand: r?.rhsOperand,
- error: r?.error
- }))
- };
- }
-
const context = {
test,
bru,
diff --git a/packages/bruno-js/src/utils.js b/packages/bruno-js/src/utils.js
index 55b454d02..289bf8dcc 100644
--- a/packages/bruno-js/src/utils.js
+++ b/packages/bruno-js/src/utils.js
@@ -144,6 +144,7 @@ const cleanJson = (data) => {
}
};
+
module.exports = {
evaluateJsExpression,
evaluateJsTemplateLiteral,
diff --git a/packages/bruno-js/src/utils/results.js b/packages/bruno-js/src/utils/results.js
new file mode 100644
index 000000000..0ed38638a
--- /dev/null
+++ b/packages/bruno-js/src/utils/results.js
@@ -0,0 +1,80 @@
+const TestResults = require('../test-results');
+const Test = require('../test');
+
+// Calculate summary statistics for test results
+const getResultsSummary = (results) => {
+ const summary = {
+ total: results.length,
+ passed: 0,
+ failed: 0,
+ skipped: 0,
+ };
+
+ results.forEach((r) => {
+ const passed = r.status === 'pass';
+ if (passed) summary.passed += 1;
+ else if (r.status === 'fail') summary.failed += 1;
+ else summary.skipped += 1;
+ });
+
+ return summary;
+};
+
+const createBruTestResultMethods = (bru, assertionResults, chai) => {
+ const __brunoTestResults = new TestResults();
+ const test = Test(__brunoTestResults, chai);
+ setupBruTestMethods(bru, __brunoTestResults, assertionResults);
+
+ return { __brunoTestResults, test };
+};
+
+const setupBruTestMethods = (bru, __brunoTestResults, assertionResults) => {
+ const getTestResults = async () => {
+ let results = await __brunoTestResults.getResults();
+ const summary = getResultsSummary(results);
+ return {
+ summary,
+ results: results.map(r => ({
+ status: r.status,
+ description: r.description,
+ expected: r.expected,
+ actual: r.actual,
+ error: r.error
+ }))
+ };
+ };
+
+ const getAssertionResults = async () => {
+ let results = assertionResults;
+ const summary = getResultsSummary(results);
+ return {
+ summary,
+ results: results.map(r => ({
+ status: r.status,
+ lhsExpr: r.lhsExpr,
+ rhsExpr: r.rhsExpr,
+ operator: r.operator,
+ rhsOperand: r.rhsOperand,
+ error: r.error
+ }))
+ };
+ };
+
+ // Set methods on bru object if provided
+ if (bru) {
+ bru.getTestResults = getTestResults;
+ bru.getAssertionResults = getAssertionResults;
+ }
+
+ // Also return the methods for direct use
+ return {
+ getTestResults,
+ getAssertionResults
+ };
+};
+
+module.exports = {
+ getResultsSummary,
+ createBruTestResultMethods,
+ setupBruTestMethods
+};
\ No newline at end of file