From 71a253acf1255942d71ee1d0c3712722cc06560b Mon Sep 17 00:00:00 2001 From: Morgan English Date: Thu, 16 Jan 2025 11:31:41 +1300 Subject: [PATCH 1/7] Moved collection results to runner title bar so they are move visible. Added breakdown of test results within collection. Added filtering based on passing/failing requests and tests by click on results text. --- .../src/components/RunnerResults/index.jsx | 65 ++++++++++++++++--- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/packages/bruno-app/src/components/RunnerResults/index.jsx b/packages/bruno-app/src/components/RunnerResults/index.jsx index f7c1e4d9c6..e35bf78a25 100644 --- a/packages/bruno-app/src/components/RunnerResults/index.jsx +++ b/packages/bruno-app/src/components/RunnerResults/index.jsx @@ -24,6 +24,23 @@ export default function RunnerResults({ collection }) { const dispatch = useDispatch(); const [selectedItem, setSelectedItem] = useState(null); const [delay, setDelay] = useState(null); + const [activeFilter, setActiveFilter] = useState('all'); + + + const getActiveFilterPredicate = () => { + switch (activeFilter) { + case 'passing_requests': + return (item) => item.status !== 'error' && item.testStatus === 'pass' && item.assertionStatus === 'pass'; + case 'failing_requests': + return (item) => (item.status !== 'error' && item.testStatus === 'fail') || item.assertionStatus === 'fail'; + case 'passing_tests': + return (item) => item.testResults?.some((result) => result.status === 'pass'); + case 'failing_tests': + return (item) => item.testResults?.some((result) => result.status === 'fail' || result.status === 'error'); + default: + return () => true + } + } // ref for the runner output body const runnerBodyRef = useRef(); @@ -99,12 +116,42 @@ export default function RunnerResults({ collection }) { }; const totalRequestsInCollection = getTotalRequestCountInCollection(collectionCopy); - const passedRequests = items.filter((item) => { - return item.status !== 'error' && item.testStatus === 'pass' && item.assertionStatus === 'pass'; - }); - const failedRequests = items.filter((item) => { - return (item.status !== 'error' && item.testStatus === 'fail') || item.assertionStatus === 'fail'; - }); + const displayCollectionResults = () => { + let passedRequests = 0; + let failedRequests = 0; + let totalTestsInCollection = 0; + let passedTests = 0; + let failedTests = 0; + items.forEach(item => { + const isPassedRequest = item.status !== 'error' && item.testStatus === 'pass' && item.assertionStatus === 'pass'; + const isFailedRequest = (item.status !== 'error' && item.testStatus === 'fail') || item.assertionStatus === 'fail'; + + if (isPassedRequest) passedRequests++; + if (isFailedRequest) failedRequests++; + + const testResults = Array.isArray(item?.testResults) ? item.testResults : []; + totalTestsInCollection += testResults.length; + testResults.forEach(result => { + if (result.status === 'pass') passedTests++; + if (result.status === 'fail' || result.status === 'error') failedTests++; + }); + }); + + return ( +
+
+ setActiveFilter('all')} className={`cursor-pointer ${activeFilter === 'all' ? 'underline font-semibold' : ''} hover:font-semibold`}>Total Requests: {items.length}, + setActiveFilter('passing_requests')} className={`cursor-pointer ${activeFilter === 'passing_requests' ? 'underline font-semibold' : ''} hover:font-semibold`}>Passed: {passedRequests}, + setActiveFilter('failing_requests') } className={`cursor-pointer ${activeFilter === 'failing_requests' ? 'underline font-semibold' : ''} hover:font-semibold`}>Failed: {failedRequests} +
+
+ setActiveFilter('all')} className={`cursor-pointer ${activeFilter === 'all' ? 'underline font-semibold' : ''} hover:font-semibold`}>Total Tests: {totalTestsInCollection}, + setActiveFilter('passing_tests')} className={`cursor-pointer ${activeFilter === 'passing_tests' ? 'underline font-semibold' : ''} hover:font-semibold`}>Passed: {passedTests}, + setActiveFilter('failing_tests')} className={`cursor-pointer ${activeFilter === 'failing_tests' ? 'underline font-semibold' : ''} hover:font-semibold`}>Failed: {failedTests} +
+
+ ) + } if (!items || !items.length) { return ( @@ -149,6 +196,7 @@ export default function RunnerResults({ collection }) { Runner + {displayCollectionResults()} {runnerInfo.status !== 'ended' && runnerInfo.cancelTokenUid && ( + + + + - {displayCollectionResults()} - {runnerInfo.status !== 'ended' && runnerInfo.cancelTokenUid && ( - - )} + + {/* Action Buttons or Cancel Execution */} + {runnerInfo.status !== 'ended' && runnerInfo.cancelTokenUid ? ( +
+ +
+ ) : runnerInfo.status === 'ended' ? ( +
+ + +
+ ) : null}
0 ? 'w-1/2' : 'w-full'}`} ref={runnerBodyRef} > -
- Total Requests: {items.length}, Passed: {passedRequests.length}, Failed: {failedRequests.length}, Skipped:{' '} - {skippedRequests.length} -
{tagsEnabled && areTagsAdded && (
Tags: @@ -425,67 +516,61 @@ export default function RunnerResults({ collection }) { {item.status == 'error' ?
{item.error}
: null}
    - {item.preRequestTestResults - ? item.preRequestTestResults.map((result) => ( -
  • - {result.status === 'pass' ? ( - - + {filterTestResults(item.preRequestTestResults).map((result) => ( +
  • + {result.status === 'pass' ? ( + + + {result.description} + + ) : ( + <> + + {result.description} - ) : ( - <> - - - {result.description} - - {result.error} - - )} -
  • - )) - : null} - {item.postResponseTestResults - ? item.postResponseTestResults.map((result) => ( -
  • - {result.status === 'pass' ? ( - - + {result.error} + + )} +
  • + ))} + {filterTestResults(item.postResponseTestResults).map((result) => ( +
  • + {result.status === 'pass' ? ( + + + {result.description} + + ) : ( + <> + + {result.description} - ) : ( - <> - - - {result.description} - - {result.error} - - )} -
  • - )) - : null} - {item.testResults - ? item.testResults.map((result) => ( -
  • - {result.status === 'pass' ? ( - - + {result.error} + + )} +
  • + ))} + {filterTestResults(item.testResults).map((result) => ( +
  • + {result.status === 'pass' ? ( + + + {result.description} + + ) : ( + <> + + {result.description} - ) : ( - <> - - - {result.description} - - {result.error} - - )} -
  • - )) - : null} - {item.assertionResults?.map((result) => ( + {result.error} + + )} + + ))} + {filterTestResults(item.assertionResults).map((result) => (
  • {result.status === 'pass' ? ( @@ -510,41 +595,51 @@ export default function RunnerResults({ collection }) { })}
- {runnerInfo.status === 'ended' ? ( -
- - - -
- ) : null}
{selectedItem ? (
-
- {selectedItem.displayName} - - {allTestsPassed(selectedItem) ? - - : null} - {anyTestFailed(selectedItem) ? - - : null} - {selectedItem.status === 'skipped' ? - - : null} - +
+
+ {selectedItem.displayName} + + {allTestsPassed(selectedItem) ? + + : null} + {anyTestFailed(selectedItem) ? + + : null} + {selectedItem.status === 'skipped' ? + + : null} + +
+
- ) : null} + ) : ( +
+
+
+
+ +
+

+ Click on the status code to view the response +

+
+
+
+ )}
); From 827631b61aafcd173cf68979a15e6be56ad3cbba Mon Sep 17 00:00:00 2001 From: Chirag Chandrashekhar Date: Thu, 6 Nov 2025 21:28:40 +0530 Subject: [PATCH 3/7] refactored the RunnerResults component to be more clear and readable --- .../src/components/RunnerResults/index.jsx | 614 ++++++------------ 1 file changed, 216 insertions(+), 398 deletions(-) diff --git a/packages/bruno-app/src/components/RunnerResults/index.jsx b/packages/bruno-app/src/components/RunnerResults/index.jsx index f4f02d687a..1c1410a4d3 100644 --- a/packages/bruno-app/src/components/RunnerResults/index.jsx +++ b/packages/bruno-app/src/components/RunnerResults/index.jsx @@ -3,17 +3,15 @@ import path from 'utils/common/path'; import { useDispatch } from 'react-redux'; import { get, cloneDeep } from 'lodash'; import { runCollectionFolder, cancelRunnerExecution, mountCollection, updateRunnerConfiguration } from 'providers/ReduxStore/slices/collections/actions'; -import { resetCollectionRunner } from 'providers/ReduxStore/slices/collections'; -import { findItemInCollection, getTotalRequestCountInCollection } from 'utils/collections'; -import { IconRefresh, IconCircleCheck, IconCircleX, IconCircleOff, IconCheck, IconX, IconRun, IconLoader2, IconDownload, IconExternalLink } from '@tabler/icons'; +import { resetCollectionRunner, updateRunnerTagsDetails } from 'providers/ReduxStore/slices/collections'; +import { findItemInCollection, getTotalRequestCountInCollection, areItemsLoading, getRequestItemsForCollectionRun } from 'utils/collections'; +import { IconRefresh, IconCircleCheck, IconCircleX, IconCircleOff, IconCheck, IconX, IconRun, IconExternalLink } from '@tabler/icons'; import ResponsePane from './ResponsePane'; import StyledWrapper from './StyledWrapper'; -import { areItemsLoading } from 'utils/collections'; import RunnerTags from './RunnerTags/index'; import RunConfigurationPanel from './RunConfigurationPanel'; -import { getRequestItemsForCollectionRun } from 'utils/collections/index'; -import { updateRunnerTagsDetails } from 'providers/ReduxStore/slices/collections/index'; +// === Utility functions === const getDisplayName = (fullPath, pathname, name = '') => { let relativePath = path.relative(fullPath, pathname); const { dir = '' } = path.parse(relativePath); @@ -26,21 +24,64 @@ const getTestStatus = (results) => { return failed.length ? 'fail' : 'pass'; }; -const allTestsPassed = (item) => { - return item.status !== 'error' && - item.testStatus === 'pass' && - item.assertionStatus === 'pass' && - item.preRequestTestStatus === 'pass' && - item.postResponseTestStatus === 'pass'; +const allTestsPassed = (item) => + item.status !== 'error' && + item.testStatus === 'pass' && + item.assertionStatus === 'pass' && + item.preRequestTestStatus === 'pass' && + item.postResponseTestStatus === 'pass'; + +const anyTestFailed = (item) => + item.status === 'error' || + item.testStatus === 'fail' || + item.assertionStatus === 'fail' || + item.preRequestTestStatus === 'fail' || + item.postResponseTestStatus === 'fail'; + +// === Centralized filters definition === +const FILTERS = { + all: { + label: 'All', + predicate: () => true, + resultFilter: (results) => results, + }, + passed: { + label: 'Passed', + predicate: (item) => allTestsPassed(item), + resultFilter: (results) => results?.filter(r => r.status === 'pass'), + }, + failed: { + label: 'Failed', + predicate: (item) => anyTestFailed(item), + resultFilter: (results) => results?.filter(r => ['fail', 'error'].includes(r.status)), + }, + skipped: { + label: 'Skipped', + predicate: (item) => item.status === 'skipped', + resultFilter: (results) => results, + }, }; -const anyTestFailed = (item) => { - return item.status === 'error' || - item.testStatus === 'fail' || - item.assertionStatus === 'fail' || - item.preRequestTestStatus === 'fail' || - item.postResponseTestStatus === 'fail'; -}; +// === Reusable filter button === +const FilterButton = ({ label, count, active, onClick }) => ( + +); export default function RunnerResults({ collection }) { const dispatch = useDispatch(); @@ -49,88 +90,27 @@ export default function RunnerResults({ collection }) { const [activeFilter, setActiveFilter] = useState('all'); const [selectedRequestItems, setSelectedRequestItems] = useState([]); const [configureMode, setConfigureMode] = useState(false); - - const getActiveFilterPredicate = () => { - switch (activeFilter) { - case 'passed': - return (item) => allTestsPassed(item); - case 'failed': - return (item) => anyTestFailed(item); - case 'skipped': - return (item) => item.status === 'skipped'; - case 'all': - default: - return () => true - } - } - - const filterTestResults = (results) => { - if (!results || !Array.isArray(results)) return []; - if (activeFilter === 'all') return results; - if (activeFilter === 'passed') return results.filter(r => r.status === 'pass'); - if (activeFilter === 'failed') return results.filter(r => r.status === 'fail' || r.status === 'error'); - if (activeFilter === 'skipped') return results; - return results; - } - - // ref for the runner output body const runnerBodyRef = useRef(); - const autoScrollRunnerBody = () => { - if (runnerBodyRef?.current) { - // mimics the native terminal scroll style - runnerBodyRef.current.scrollTo(0, 100000); - } - }; - - useEffect(() => { - if (!collection.runnerResult) { - setSelectedItem(null); - } - autoScrollRunnerBody(); - }, [collection, setSelectedItem]); - - useEffect(() => { - const runnerInfo = get(collection, 'runnerResult.info', {}); - if (runnerInfo.status === 'running') { - setConfigureMode(false); - } - }, [collection.runnerResult]); - - useEffect(() => { - const savedConfiguration = get(collection, 'runnerConfiguration', null); - if (savedConfiguration) { - if (savedConfiguration.selectedRequestItems && configureMode) { - setSelectedRequestItems(savedConfiguration.selectedRequestItems); - } - if (savedConfiguration.delay !== undefined && delay === null) { - setDelay(savedConfiguration.delay); - } - } - }, [collection.runnerConfiguration, configureMode, delay]); - const collectionCopy = cloneDeep(collection); const runnerInfo = get(collection, 'runnerResult.info', {}); - - // tags for the collection run const tags = get(collection, 'runnerTags', { include: [], exclude: [] }); - - // have tags been enabled for the collection run const tagsEnabled = get(collection, 'runnerTagsEnabled', false); - - // have tags been added for the collection run const areTagsAdded = tags.include.length > 0 || tags.exclude.length > 0; - const requestItemsForCollectionRun = getRequestItemsForCollectionRun({ recursive: true, tags, items: collection.items }); + // === Derived data === + const requestItemsForCollectionRun = getRequestItemsForCollectionRun({ + recursive: true, + tags, + items: collection.items + }); const totalRequestItemsCountForCollectionRun = requestItemsForCollectionRun.length; const shouldDisableCollectionRun = totalRequestItemsCountForCollectionRun <= 0; const items = cloneDeep(get(collection, 'runnerResult.items', [])) .map((item) => { const info = findItemInCollection(collectionCopy, item.uid); - if (!info) { - return null; - } + if (!info) return null; const newItem = { ...item, name: info.name, @@ -140,7 +120,7 @@ export default function RunnerResults({ collection }) { displayName: getDisplayName(collection.pathname, info.pathname, info.name), tags: [...(info.request?.tags || [])].sort(), }; - if (newItem.status !== 'error' && newItem.status !== 'skipped' && newItem.status !== 'running') { + if (!['error', 'skipped', 'running'].includes(newItem.status)) { newItem.testStatus = getTestStatus(newItem.testResults); newItem.assertionStatus = getTestStatus(newItem.assertionResults); newItem.preRequestTestStatus = getTestStatus(newItem.preRequestTestResults); @@ -150,14 +130,52 @@ export default function RunnerResults({ collection }) { }) .filter(Boolean); - const ensureCollectionIsMounted = () => { - if(collection.mountStatus === 'mounted'){ - return; + const activeFilterConfig = FILTERS[activeFilter]; + const filteredItems = items.filter(activeFilterConfig.predicate); + + const filterTestResults = (results) => { + if (!results || !Array.isArray(results)) return []; + return activeFilterConfig.resultFilter(results); + }; + + // === Effects === + const autoScrollRunnerBody = () => { + if (runnerBodyRef?.current) runnerBodyRef.current.scrollTo(0, 100000); + }; + + useEffect(() => { + if (!collection.runnerResult) setSelectedItem(null); + autoScrollRunnerBody(); + }, [collection]); + + useEffect(() => { + const runnerInfo = get(collection, 'runnerResult.info', {}); + if (runnerInfo.status === 'running') setConfigureMode(false); + }, [collection.runnerResult]); + + useEffect(() => { + const savedConfig = get(collection, 'runnerConfiguration', null); + if (savedConfig) { + if (savedConfig.selectedRequestItems && configureMode) { + setSelectedRequestItems(savedConfig.selectedRequestItems); + } + if (savedConfig.delay !== undefined && delay === null) { + setDelay(savedConfig.delay); + } } + }, [collection.runnerConfiguration, configureMode, delay]); + + useEffect(() => { + if (tagsEnabled) setConfigureMode(false); + }, [tagsEnabled]); + + // === Helper methods === + const ensureCollectionIsMounted = () => { + if (collection.mountStatus === 'mounted') return; dispatch(mountCollection({ collectionUid: collection.uid, collectionPathname: collection.pathname, - brunoConfig: collection.brunoConfig + brunoConfig: collection.brunoConfig, })); }; @@ -173,28 +191,21 @@ export default function RunnerResults({ collection }) { const runAgain = () => { ensureCollectionIsMounted(); - // Get the saved configuration to determine what to run - const savedConfiguration = get(collection, 'runnerConfiguration', null); - const savedSelectedItems = savedConfiguration?.selectedRequestItems || []; - const savedDelay = savedConfiguration?.delay !== undefined ? savedConfiguration.delay : delay; - dispatch( - runCollectionFolder( - collection.uid, - runnerInfo.folderUid, - true, - Number(savedDelay), - tagsEnabled && tags, - savedSelectedItems - ) - ); + const savedConfig = get(collection, 'runnerConfiguration', null); + const savedItems = savedConfig?.selectedRequestItems || []; + const savedDelay = savedConfig?.delay !== undefined ? savedConfig.delay : delay; + dispatch(runCollectionFolder( + collection.uid, + runnerInfo.folderUid, + true, + Number(savedDelay), + tagsEnabled && tags, + savedItems + )); }; const resetRunner = () => { - dispatch( - resetCollectionRunner({ - collectionUid: collection.uid - }) - ); + dispatch(resetCollectionRunner({ collectionUid: collection.uid })); setSelectedRequestItems([]); setConfigureMode(false); setDelay(null); @@ -209,30 +220,18 @@ export default function RunnerResults({ collection }) { setConfigureMode(!configureMode); }; - useEffect(() => { - if(tagsEnabled) { - setConfigureMode(false); - } - }, [tagsEnabled]); - + // === Filter counts === const totalRequestsInCollection = getTotalRequestCountInCollection(collectionCopy); - - const passedRequests = items.filter(allTestsPassed); - const failedRequests = items.filter(anyTestFailed); - - const skippedRequests = items.filter((item) => { - return item.status === 'skipped'; - }); - let isCollectionLoading = areItemsLoading(collection); - - // Calculate filter counts for unified filter const filterCounts = { all: items.length, - passed: passedRequests.length, - failed: failedRequests.length, - skipped: skippedRequests.length + passed: items.filter(allTestsPassed).length, + failed: items.filter(anyTestFailed).length, + skipped: items.filter(i => i.status === 'skipped').length, }; + let isCollectionLoading = areItemsLoading(collection); + + // === Early render: no items === if (!items || !items.length) { return ( @@ -244,31 +243,22 @@ export default function RunnerResults({ collection }) {
You have {totalRequestsInCollection} requests in this collection. - {isCollectionLoading && ( - - (Loading...) - - )} + {isCollectionLoading && (Loading...)}
- {isCollectionLoading ?
Requests in this collection are still loading.
: null} + {isCollectionLoading &&
Requests in this collection are still loading.
}
setDelay(e.target.value)} />
- {/* Tags for the collection run */} - {/* Configure requests option */}
- +
@@ -292,13 +284,10 @@ export default function RunnerResults({ collection }) { > {configureMode && selectedRequestItems.length > 0 ? `Run ${selectedRequestItems.length} Selected Request${selectedRequestItems.length > 1 ? 's' : ''}` - : "Run Collection" - } + : 'Run Collection'} - + @@ -316,114 +305,40 @@ export default function RunnerResults({ collection }) { ); } + // === Main render === return ( - {/* Filter Bar and Action Buttons */} + {/* Filter Bar and Actions */}
- {/* Filter Bar */}
- {/* Filter by text container - left border radius only */}
- + Filter by:
- {/* Filter buttons container - right border radius only */}
- - - - + {Object.entries(FILTERS).map(([key, { label }]) => ( + setActiveFilter(key)} + /> + ))}
- {/* Action Buttons or Cancel Execution */} {runnerInfo.status !== 'ended' && runnerInfo.cancelTokenUid ? (
- +
) : runnerInfo.status === 'ended' ? (
-
0 ? 'w-1/2' : 'w-full'}`} - ref={runnerBodyRef} - > + {/* Results List */} +
{tagsEnabled && areTagsAdded && (
Tags:
-
- {tags.include.join(', ')} -
-
- {tags.exclude.join(', ')} -
+
{tags.include.join(', ')}
+
{tags.exclude.join(', ')}
)} - {runnerInfo?.statusText ? -
- {runnerInfo?.statusText} -
- : null} + {runnerInfo?.statusText &&
{runnerInfo.statusText}
} - {/* Items list */}
- {items.filter(getActiveFilterPredicate()).map((item) => { - return ( -
-
-
- - {allTestsPassed(item) ? - - : null} - {item.status === 'skipped' ? - - : null} - {anyTestFailed(item) ? - - : null} - - - {item.displayName} - - {item.status !== 'error' && item.status !== 'skipped' && item.status !== 'completed' ? ( - - ) : item.responseReceived?.status ? ( - setSelectedItem(item)}> - {item.responseReceived?.status} - -  - {item.responseReceived?.statusText} - - ) : ( - setSelectedItem(item)}> - (request failed) - - )} -
- {tagsEnabled && areTagsAdded && item?.tags?.length > 0 && ( -
- Tags: {item.tags.filter(t => tags.include.includes(t)).join(', ')} -
- )} - {item.status == 'error' ?
{item.error}
: null} - -
    - {filterTestResults(item.preRequestTestResults).map((result) => ( -
  • - {result.status === 'pass' ? ( - - - {result.description} - - ) : ( - <> - - - {result.description} - - {result.error} - - )} -
  • - ))} - {filterTestResults(item.postResponseTestResults).map((result) => ( -
  • - {result.status === 'pass' ? ( - - - {result.description} - - ) : ( - <> - - - {result.description} - - {result.error} - - )} -
  • - ))} - {filterTestResults(item.testResults).map((result) => ( -
  • - {result.status === 'pass' ? ( - - - {result.description} - - ) : ( - <> - - - {result.description} - - {result.error} - - )} -
  • - ))} - {filterTestResults(item.assertionResults).map((result) => ( -
  • - {result.status === 'pass' ? ( - - - {result.lhsExpr}: {result.rhsExpr} - - ) : ( - <> - - - {result.lhsExpr}: {result.rhsExpr} - - {result.error} - - )} -
  • - ))} -
-
+ {filteredItems.map((item) => ( +
+
+ {allTestsPassed(item) && } + {item.status === 'skipped' && } + {anyTestFailed(item) && } + + {item.displayName} + + {item.status !== 'error' && item.status !== 'skipped' && item.status !== 'completed' ? ( + + ) : item.responseReceived?.status ? ( + setSelectedItem(item)}> + {item.responseReceived?.status} - {item.responseReceived?.statusText} + + ) : ( + setSelectedItem(item)}>(request failed) + )}
- ); - })} + {tagsEnabled && areTagsAdded && item?.tags?.length > 0 && ( +
+ Tags: {item.tags.filter(t => tags.include.includes(t)).join(', ')} +
+ )} + {item.status === 'error' &&
{item.error}
} + +
    + {[item.preRequestTestResults, item.postResponseTestResults, item.testResults, item.assertionResults].map((results, idx) => + filterTestResults(results).map((result) => ( +
  • + {result.status === 'pass' ? ( + + + {result.description || `${result.lhsExpr}: ${result.rhsExpr}`} + + ) : ( + <> + + + {result.description || `${result.lhsExpr}: ${result.rhsExpr}`} + + {result.error} + + )} +
  • + )) + )} +
+
+ ))}
-
+ + {/* Response Pane */} {selectedItem ? (
{selectedItem.displayName} - - {allTestsPassed(selectedItem) ? - - : null} - {anyTestFailed(selectedItem) ? - - : null} - {selectedItem.status === 'skipped' ? - - : null} - + {allTestsPassed(selectedItem) && } + {anyTestFailed(selectedItem) && } + {selectedItem.status === 'skipped' && }
) : (
-
-
-
- -
-

- Click on the status code to view the response -

+
+
+
+

+ Click on the status code to view the response +

)} From 15e3ea6cb3a03ef9f6470dc925e3ce9dff2338f6 Mon Sep 17 00:00:00 2001 From: Chirag Chandrashekhar Date: Mon, 10 Nov 2025 08:47:22 +0530 Subject: [PATCH 4/7] refactor: revert formatting changes while preserving new UI and filtering logic - Restore original function formatting with return statements and braces - Restore removed input attributes (autoCorrect, autoCapitalize, spellCheck) - Revert ternary operator changes to match original code style - Restore original variable names (savedConfiguration) and comments - Restore original test results rendering structure - Preserve new filter bar UI, filtering logic, and response view improvements --- .../src/components/RunnerResults/index.jsx | 361 ++++++++++++------ 1 file changed, 241 insertions(+), 120 deletions(-) diff --git a/packages/bruno-app/src/components/RunnerResults/index.jsx b/packages/bruno-app/src/components/RunnerResults/index.jsx index 1c1410a4d3..442bbbc30b 100644 --- a/packages/bruno-app/src/components/RunnerResults/index.jsx +++ b/packages/bruno-app/src/components/RunnerResults/index.jsx @@ -11,7 +11,6 @@ import StyledWrapper from './StyledWrapper'; import RunnerTags from './RunnerTags/index'; import RunConfigurationPanel from './RunConfigurationPanel'; -// === Utility functions === const getDisplayName = (fullPath, pathname, name = '') => { let relativePath = path.relative(fullPath, pathname); const { dir = '' } = path.parse(relativePath); @@ -24,19 +23,21 @@ const getTestStatus = (results) => { return failed.length ? 'fail' : 'pass'; }; -const allTestsPassed = (item) => - item.status !== 'error' && - item.testStatus === 'pass' && - item.assertionStatus === 'pass' && - item.preRequestTestStatus === 'pass' && - item.postResponseTestStatus === 'pass'; +const allTestsPassed = (item) => { + return item.status !== 'error' && + item.testStatus === 'pass' && + item.assertionStatus === 'pass' && + item.preRequestTestStatus === 'pass' && + item.postResponseTestStatus === 'pass'; +}; -const anyTestFailed = (item) => - item.status === 'error' || - item.testStatus === 'fail' || - item.assertionStatus === 'fail' || - item.preRequestTestStatus === 'fail' || - item.postResponseTestStatus === 'fail'; +const anyTestFailed = (item) => { + return item.status === 'error' || + item.testStatus === 'fail' || + item.assertionStatus === 'fail' || + item.preRequestTestStatus === 'fail' || + item.postResponseTestStatus === 'fail'; +}; // === Centralized filters definition === const FILTERS = { @@ -90,27 +91,31 @@ export default function RunnerResults({ collection }) { const [activeFilter, setActiveFilter] = useState('all'); const [selectedRequestItems, setSelectedRequestItems] = useState([]); const [configureMode, setConfigureMode] = useState(false); + // ref for the runner output body const runnerBodyRef = useRef(); const collectionCopy = cloneDeep(collection); const runnerInfo = get(collection, 'runnerResult.info', {}); + + // tags for the collection run const tags = get(collection, 'runnerTags', { include: [], exclude: [] }); + + // have tags been enabled for the collection run const tagsEnabled = get(collection, 'runnerTagsEnabled', false); + + // have tags been added for the collection run const areTagsAdded = tags.include.length > 0 || tags.exclude.length > 0; - // === Derived data === - const requestItemsForCollectionRun = getRequestItemsForCollectionRun({ - recursive: true, - tags, - items: collection.items - }); + const requestItemsForCollectionRun = getRequestItemsForCollectionRun({ recursive: true, tags, items: collection.items }); const totalRequestItemsCountForCollectionRun = requestItemsForCollectionRun.length; const shouldDisableCollectionRun = totalRequestItemsCountForCollectionRun <= 0; const items = cloneDeep(get(collection, 'runnerResult.items', [])) .map((item) => { const info = findItemInCollection(collectionCopy, item.uid); - if (!info) return null; + if (!info) { + return null; + } const newItem = { ...item, name: info.name, @@ -120,7 +125,7 @@ export default function RunnerResults({ collection }) { displayName: getDisplayName(collection.pathname, info.pathname, info.name), tags: [...(info.request?.tags || [])].sort(), }; - if (!['error', 'skipped', 'running'].includes(newItem.status)) { + if (newItem.status !== 'error' && newItem.status !== 'skipped' && newItem.status !== 'running') { newItem.testStatus = getTestStatus(newItem.testResults); newItem.assertionStatus = getTestStatus(newItem.assertionResults); newItem.preRequestTestStatus = getTestStatus(newItem.preRequestTestResults); @@ -138,44 +143,47 @@ export default function RunnerResults({ collection }) { return activeFilterConfig.resultFilter(results); }; - // === Effects === const autoScrollRunnerBody = () => { - if (runnerBodyRef?.current) runnerBodyRef.current.scrollTo(0, 100000); + if (runnerBodyRef?.current) { + // mimics the native terminal scroll style + runnerBodyRef.current.scrollTo(0, 100000); + } }; useEffect(() => { - if (!collection.runnerResult) setSelectedItem(null); + if (!collection.runnerResult) { + setSelectedItem(null); + } autoScrollRunnerBody(); - }, [collection]); + }, [collection, setSelectedItem]); useEffect(() => { const runnerInfo = get(collection, 'runnerResult.info', {}); - if (runnerInfo.status === 'running') setConfigureMode(false); + if (runnerInfo.status === 'running') { + setConfigureMode(false); + } }, [collection.runnerResult]); useEffect(() => { - const savedConfig = get(collection, 'runnerConfiguration', null); - if (savedConfig) { - if (savedConfig.selectedRequestItems && configureMode) { - setSelectedRequestItems(savedConfig.selectedRequestItems); + const savedConfiguration = get(collection, 'runnerConfiguration', null); + if (savedConfiguration) { + if (savedConfiguration.selectedRequestItems && configureMode) { + setSelectedRequestItems(savedConfiguration.selectedRequestItems); } - if (savedConfig.delay !== undefined && delay === null) { - setDelay(savedConfig.delay); + if (savedConfiguration.delay !== undefined && delay === null) { + setDelay(savedConfiguration.delay); } } }, [collection.runnerConfiguration, configureMode, delay]); - useEffect(() => { - if (tagsEnabled) setConfigureMode(false); - }, [tagsEnabled]); - - // === Helper methods === const ensureCollectionIsMounted = () => { - if (collection.mountStatus === 'mounted') return; + if(collection.mountStatus === 'mounted'){ + return; + } dispatch(mountCollection({ collectionUid: collection.uid, collectionPathname: collection.pathname, - brunoConfig: collection.brunoConfig, + brunoConfig: collection.brunoConfig })); }; @@ -191,21 +199,28 @@ export default function RunnerResults({ collection }) { const runAgain = () => { ensureCollectionIsMounted(); - const savedConfig = get(collection, 'runnerConfiguration', null); - const savedItems = savedConfig?.selectedRequestItems || []; - const savedDelay = savedConfig?.delay !== undefined ? savedConfig.delay : delay; - dispatch(runCollectionFolder( - collection.uid, - runnerInfo.folderUid, - true, - Number(savedDelay), - tagsEnabled && tags, - savedItems - )); + // Get the saved configuration to determine what to run + const savedConfiguration = get(collection, 'runnerConfiguration', null); + const savedSelectedItems = savedConfiguration?.selectedRequestItems || []; + const savedDelay = savedConfiguration?.delay !== undefined ? savedConfiguration.delay : delay; + dispatch( + runCollectionFolder( + collection.uid, + runnerInfo.folderUid, + true, + Number(savedDelay), + tagsEnabled && tags, + savedSelectedItems + ) + ); }; const resetRunner = () => { - dispatch(resetCollectionRunner({ collectionUid: collection.uid })); + dispatch( + resetCollectionRunner({ + collectionUid: collection.uid + }) + ); setSelectedRequestItems([]); setConfigureMode(false); setDelay(null); @@ -220,7 +235,12 @@ export default function RunnerResults({ collection }) { setConfigureMode(!configureMode); }; - // === Filter counts === + useEffect(() => { + if(tagsEnabled) { + setConfigureMode(false); + } + }, [tagsEnabled]); + const totalRequestsInCollection = getTotalRequestCountInCollection(collectionCopy); const filterCounts = { all: items.length, @@ -230,8 +250,6 @@ export default function RunnerResults({ collection }) { }; let isCollectionLoading = areItemsLoading(collection); - - // === Early render: no items === if (!items || !items.length) { return ( @@ -243,22 +261,31 @@ export default function RunnerResults({ collection }) {
You have {totalRequestsInCollection} requests in this collection. - {isCollectionLoading && (Loading...)} + {isCollectionLoading && ( + + (Loading...) + + )}
- {isCollectionLoading &&
Requests in this collection are still loading.
} + {isCollectionLoading ?
Requests in this collection are still loading.
: null}
setDelay(e.target.value)} />
+ {/* Tags for the collection run */} + {/* Configure requests option */}
- +
@@ -284,10 +309,13 @@ export default function RunnerResults({ collection }) { > {configureMode && selectedRequestItems.length > 0 ? `Run ${selectedRequestItems.length} Selected Request${selectedRequestItems.length > 1 ? 's' : ''}` - : 'Run Collection'} + : "Run Collection" + } - +
@@ -305,7 +333,6 @@ export default function RunnerResults({ collection }) { ); } - // === Main render === return ( {/* Filter Bar and Actions */} @@ -356,83 +383,177 @@ export default function RunnerResults({ collection }) {
- {/* Results List */} -
+
{tagsEnabled && areTagsAdded && (
Tags:
-
{tags.include.join(', ')}
-
{tags.exclude.join(', ')}
+
+ {tags.include.join(', ')} +
+
+ {tags.exclude.join(', ')} +
)} - {runnerInfo?.statusText &&
{runnerInfo.statusText}
} + {runnerInfo?.statusText ? +
+ {runnerInfo?.statusText} +
+ : null} + {/* Items list */}
- {filteredItems.map((item) => ( -
-
- {allTestsPassed(item) && } - {item.status === 'skipped' && } - {anyTestFailed(item) && } - - {item.displayName} - - {item.status !== 'error' && item.status !== 'skipped' && item.status !== 'completed' ? ( - - ) : item.responseReceived?.status ? ( - setSelectedItem(item)}> - {item.responseReceived?.status} - {item.responseReceived?.statusText} - - ) : ( - setSelectedItem(item)}>(request failed) - )} -
- {tagsEnabled && areTagsAdded && item?.tags?.length > 0 && ( -
- Tags: {item.tags.filter(t => tags.include.includes(t)).join(', ')} -
- )} - {item.status === 'error' &&
{item.error}
} - -
    - {[item.preRequestTestResults, item.postResponseTestResults, item.testResults, item.assertionResults].map((results, idx) => - filterTestResults(results).map((result) => ( -
  • - {result.status === 'pass' ? ( - - - {result.description || `${result.lhsExpr}: ${result.rhsExpr}`} - - ) : ( - <> - - - {result.description || `${result.lhsExpr}: ${result.rhsExpr}`} + {filteredItems.map((item) => { + return ( +
    +
    +
    + + {allTestsPassed(item) ? + + : null} + {item.status === 'skipped' ? + + : null} + {anyTestFailed(item) ? + + : null} + + + {item.displayName} + + {item.status !== 'error' && item.status !== 'skipped' && item.status !== 'completed' ? ( + + ) : item.responseReceived?.status ? ( + setSelectedItem(item)}> + {item.responseReceived?.status} + -  + {item.responseReceived?.statusText} + + ) : ( + setSelectedItem(item)}> + (request failed) + + )} +
    + {tagsEnabled && areTagsAdded && item?.tags?.length > 0 && ( +
    + Tags: {item.tags.filter(t => tags.include.includes(t)).join(', ')} +
    + )} + {item.status == 'error' ?
    {item.error}
    : null} + +
      + {item.preRequestTestResults + ? filterTestResults(item.preRequestTestResults).map((result) => ( +
    • + {result.status === 'pass' ? ( + + + {result.description} + + ) : ( + <> + + + {result.description} + + {result.error} + + )} +
    • + )) + : null} + {item.postResponseTestResults + ? filterTestResults(item.postResponseTestResults).map((result) => ( +
    • + {result.status === 'pass' ? ( + + + {result.description} + + ) : ( + <> + + + {result.description} + + {result.error} + + )} +
    • + )) + : null} + {item.testResults + ? filterTestResults(item.testResults).map((result) => ( +
    • + {result.status === 'pass' ? ( + + + {result.description} + + ) : ( + <> + + + {result.description} + + {result.error} + + )} +
    • + )) + : null} + {filterTestResults(item.assertionResults).map((result) => ( +
    • + {result.status === 'pass' ? ( + + + {result.lhsExpr}: {result.rhsExpr} - {result.error} - - )} -
    • - )) - )} -
    -
    - ))} + ) : ( + <> + + + {result.lhsExpr}: {result.rhsExpr} + + {result.error} + + )} +
  • + ))} +
+
+
+ ); + })}
- {/* Response Pane */} {selectedItem ? (
{selectedItem.displayName} - {allTestsPassed(selectedItem) && } - {anyTestFailed(selectedItem) && } - {selectedItem.status === 'skipped' && } + + {allTestsPassed(selectedItem) ? + + : null} + {anyTestFailed(selectedItem) ? + + : null} + {selectedItem.status === 'skipped' ? + + : null} +