mirror of
https://github.com/usebruno/bruno.git
synced 2026-06-16 04:11:29 +00:00
@@ -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};
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
@@ -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 }) => (
|
||||
<span className={`inline-flex items-center ${status === 'pass' ? 'test-success' : 'test-failure'}`}>
|
||||
{status === 'pass' ? (
|
||||
<IconCircleCheck size={14} className="mr-1" aria-label="Test passed" />
|
||||
) : (
|
||||
<IconCircleX size={14} className="mr-1" aria-label="Test failed" />
|
||||
)}
|
||||
</span>
|
||||
);
|
||||
|
||||
const ErrorMessage = ({ error }) => error && (
|
||||
<>
|
||||
<br />
|
||||
<span className="error-message pl-8" role="alert">
|
||||
{error}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
|
||||
const ResultItem = ({ result, type }) => (
|
||||
<div className="test-result-item">
|
||||
<ResultIcon status={result.status} />
|
||||
<span className={result.status === 'pass' ? 'test-success' : 'test-failure'}>
|
||||
{type === 'assertion'
|
||||
? `${result.lhsExpr}: ${result.rhsExpr}`
|
||||
: result.description
|
||||
}
|
||||
</span>
|
||||
<ErrorMessage error={result.error} />
|
||||
</div>
|
||||
);
|
||||
|
||||
const TestSection = ({
|
||||
title,
|
||||
results,
|
||||
isExpanded,
|
||||
onToggle,
|
||||
type = 'test'
|
||||
}) => {
|
||||
const passedResults = results.filter((result) => result.status === 'pass');
|
||||
const failedResults = results.filter((result) => result.status === 'fail');
|
||||
|
||||
if (results.length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className='mb-4'>
|
||||
<div
|
||||
className="font-medium test-summary flex items-center cursor-pointer hover:bg-opacity-10 hover:bg-gray-500 rounded py-2"
|
||||
onClick={onToggle}
|
||||
>
|
||||
<span className="dropdown-icon mr-2 flex items-center">
|
||||
{isExpanded ?
|
||||
<IconChevronDown size={18} stroke={1.5} /> :
|
||||
<IconChevronRight size={18} stroke={1.5} />
|
||||
}
|
||||
</span>
|
||||
<span className="flex-grow">
|
||||
{title} ({results.length}), Passed: {passedResults.length}, Failed: {failedResults.length}
|
||||
</span>
|
||||
</div>
|
||||
{isExpanded && (
|
||||
<ul className="ml-5">
|
||||
{results.map((result) => (
|
||||
<li key={result.uid} className="py-1">
|
||||
<ResultItem result={result} type={type} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TestResults = ({ results, assertionResults, preRequestTestResults, postResponseTestResults }) => {
|
||||
results = results || [];
|
||||
assertionResults = assertionResults || [];
|
||||
if (!results.length && !assertionResults.length) {
|
||||
preRequestTestResults = preRequestTestResults || [];
|
||||
postResponseTestResults = postResponseTestResults || [];
|
||||
|
||||
const [expandedSections, setExpandedSections] = useState({
|
||||
preRequest: true,
|
||||
tests: true,
|
||||
postResponse: true,
|
||||
assertions: true
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setExpandedSections({
|
||||
preRequest: preRequestTestResults.length > 0,
|
||||
tests: results.length > 0,
|
||||
postResponse: postResponseTestResults.length > 0,
|
||||
assertions: assertionResults.length > 0
|
||||
});
|
||||
}, [results.length, assertionResults.length, preRequestTestResults.length, postResponseTestResults.length]);
|
||||
|
||||
const toggleSection = (section) => {
|
||||
setExpandedSections({
|
||||
...expandedSections,
|
||||
[section]: !expandedSections[section]
|
||||
});
|
||||
};
|
||||
|
||||
if (!results.length && !assertionResults.length && !preRequestTestResults.length && !postResponseTestResults.length) {
|
||||
return <div className="px-3">No tests found</div>;
|
||||
}
|
||||
|
||||
const passedTests = results.filter((result) => result.status === 'pass');
|
||||
const failedTests = results.filter((result) => result.status === 'fail');
|
||||
|
||||
const passedAssertions = assertionResults.filter((result) => result.status === 'pass');
|
||||
const failedAssertions = assertionResults.filter((result) => result.status === 'fail');
|
||||
|
||||
return (
|
||||
<StyledWrapper className="flex flex-col">
|
||||
<div className="pb-2 font-medium test-summary">
|
||||
Tests ({results.length}/{results.length}), Passed: {passedTests.length}, Failed: {failedTests.length}
|
||||
</div>
|
||||
<ul className="">
|
||||
{results.map((result) => (
|
||||
<li key={result.uid} className="py-1">
|
||||
{result.status === 'pass' ? (
|
||||
<span className="test-success">✔ {result.description}</span>
|
||||
) : (
|
||||
<>
|
||||
<span className="test-failure">✘ {result.description}</span>
|
||||
<br />
|
||||
<span className="error-message pl-8">{result.error}</span>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<StyledWrapper className="flex flex-col px-3">
|
||||
<TestSection
|
||||
title="Pre-Request Tests"
|
||||
results={preRequestTestResults}
|
||||
isExpanded={expandedSections.preRequest}
|
||||
onToggle={() => toggleSection('preRequest')}
|
||||
type="test"
|
||||
/>
|
||||
|
||||
<div className="py-2 font-medium test-summary">
|
||||
Assertions ({assertionResults.length}/{assertionResults.length}), Passed: {passedAssertions.length}, Failed:{' '}
|
||||
{failedAssertions.length}
|
||||
</div>
|
||||
<ul className="">
|
||||
{assertionResults.map((result) => (
|
||||
<li key={result.uid} className="py-1">
|
||||
{result.status === 'pass' ? (
|
||||
<span className="test-success">
|
||||
✔ {result.lhsExpr}: {result.rhsExpr}
|
||||
</span>
|
||||
) : (
|
||||
<>
|
||||
<span className="test-failure">
|
||||
✘ {result.lhsExpr}: {result.rhsExpr}
|
||||
</span>
|
||||
<br />
|
||||
<span className="error-message pl-8">{result.error}</span>
|
||||
</>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<TestSection
|
||||
title="Post-Response Tests"
|
||||
results={postResponseTestResults}
|
||||
isExpanded={expandedSections.postResponse}
|
||||
onToggle={() => toggleSection('postResponse')}
|
||||
type="test"
|
||||
/>
|
||||
|
||||
<TestSection
|
||||
title="Tests"
|
||||
results={results}
|
||||
isExpanded={expandedSections.tests}
|
||||
onToggle={() => toggleSection('tests')}
|
||||
type="test"
|
||||
/>
|
||||
|
||||
<TestSection
|
||||
title="Assertions"
|
||||
results={assertionResults}
|
||||
isExpanded={expandedSections.assertions}
|
||||
onToggle={() => toggleSection('assertions')}
|
||||
type="assertion"
|
||||
/>
|
||||
</StyledWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
<div className="flex items-center">
|
||||
|
||||
@@ -73,7 +73,12 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
return <Timeline collection={collection} item={item} width={rightPaneWidth} />;
|
||||
}
|
||||
case 'tests': {
|
||||
return <TestResults results={item.testResults} assertionResults={item.assertionResults} />;
|
||||
return <TestResults
|
||||
results={item.testResults}
|
||||
assertionResults={item.assertionResults}
|
||||
preRequestTestResults={item.preRequestTestResults}
|
||||
postResponseTestResults={item.postResponseTestResults}
|
||||
/>;
|
||||
}
|
||||
|
||||
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
|
||||
</div>
|
||||
<div className={getTabClassname('tests')} role="tab" onClick={() => selectTab('tests')}>
|
||||
<TestResultsLabel results={item.testResults} assertionResults={item.assertionResults} />
|
||||
<TestResultsLabel
|
||||
results={item.testResults}
|
||||
assertionResults={item.assertionResults}
|
||||
preRequestTestResults={item.preRequestTestResults}
|
||||
postResponseTestResults={item.postResponseTestResults}
|
||||
/>
|
||||
</div>
|
||||
{!isLoading ? (
|
||||
<div className="flex flex-grow justify-end items-center">
|
||||
{hasScriptError && !showScriptErrorCard && (
|
||||
<ScriptErrorIcon
|
||||
itemUid={item.uid}
|
||||
onClick={() => setShowScriptErrorCard(true)}
|
||||
<ScriptErrorIcon
|
||||
itemUid={item.uid}
|
||||
onClick={() => setShowScriptErrorCard(true)}
|
||||
/>
|
||||
)}
|
||||
{focusedTab?.responsePaneTab === "timeline" ? (
|
||||
@@ -168,9 +178,9 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
>
|
||||
{isLoading ? <Overlay item={item} collection={collection} /> : null}
|
||||
{hasScriptError && showScriptErrorCard && (
|
||||
<ScriptError
|
||||
item={item}
|
||||
onClose={() => setShowScriptErrorCard(false)}
|
||||
<ScriptError
|
||||
item={item}
|
||||
onClose={() => setShowScriptErrorCard(false)}
|
||||
/>
|
||||
)}
|
||||
{!item?.response ? (
|
||||
|
||||
@@ -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 <RunnerTimeline request={requestSent} response={responseReceived} />;
|
||||
}
|
||||
case 'tests': {
|
||||
return <TestResults results={testResults} assertionResults={assertionResults} />;
|
||||
return <TestResults
|
||||
results={testResults}
|
||||
assertionResults={assertionResults}
|
||||
preRequestTestResults={preRequestTestResults}
|
||||
postResponseTestResults={postResponseTestResults}
|
||||
/>;
|
||||
}
|
||||
|
||||
default: {
|
||||
@@ -86,7 +91,12 @@ const ResponsePane = ({ rightPaneWidth, item, collection }) => {
|
||||
Timeline
|
||||
</div>
|
||||
<div className={getTabClassname('tests')} role="tab" onClick={() => selectTab('tests')}>
|
||||
<TestResultsLabel results={testResults} assertionResults={assertionResults} />
|
||||
<TestResultsLabel
|
||||
results={testResults}
|
||||
assertionResults={assertionResults}
|
||||
preRequestTestResults={preRequestTestResults}
|
||||
postResponseTestResults={postResponseTestResults}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-grow justify-end items-center">
|
||||
<StatusCode status={status} />
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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: []
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 : '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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 = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -144,6 +144,7 @@ const cleanJson = (data) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
evaluateJsExpression,
|
||||
evaluateJsTemplateLiteral,
|
||||
|
||||
80
packages/bruno-js/src/utils/results.js
Normal file
80
packages/bruno-js/src/utils/results.js
Normal file
@@ -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
|
||||
};
|
||||
Reference in New Issue
Block a user