Skip to content

feat: support chai in scripts #4552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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};
}
Expand All @@ -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};
}
`;

Expand Down
238 changes: 191 additions & 47 deletions packages/bruno-app/src/components/ResponsePane/TestResults/index.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,207 @@
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 TestResults = ({ results, assertionResults, preRequestTestResults, postResponseTestResults }) => {
results = results || [];
assertionResults = assertionResults || [];
if (!results.length && !assertionResults.length) {
return <div className="px-3">No tests found</div>;
}

preRequestTestResults = preRequestTestResults || [];
postResponseTestResults = postResponseTestResults || [];

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');

const passedPreRequestTests = preRequestTestResults.filter((result) => result.status === 'pass');
const failedPreRequestTests = preRequestTestResults.filter((result) => result.status === 'fail');

const passedPostResponseTests = postResponseTestResults.filter((result) => result.status === 'pass');
const failedPostResponseTests = postResponseTestResults.filter((result) => result.status === 'fail');

const [expandedSections, setExpandedSections] = useState({
preRequest: true,
tests: true,
postResponse: true,
assertions: true
});

// Update expanded sections when test results change
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>;
}

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">&#x2714;&nbsp; {result.description}</span>
) : (
<>
<span className="test-failure">&#x2718;&nbsp; {result.description}</span>
<br />
<span className="error-message pl-8">{result.error}</span>
</>
)}
</li>
))}
</ul>
{preRequestTestResults.length > 0 && (
<div className="mb-4 test-section">
<div
className="font-medium test-summary flex items-center cursor-pointer hover:bg-opacity-10 hover:bg-gray-500 rounded py-2"
onClick={() => toggleSection('preRequest')}
>
<span className="dropdown-icon mr-2 flex items-center">
{expandedSections.preRequest ?
<IconChevronDown size={18} stroke={1.5} /> :
<IconChevronRight size={18} stroke={1.5} />
}
</span>
<span className="flex-grow">
Pre-Request Tests ({preRequestTestResults.length}), Passed: {passedPreRequestTests.length}, Failed: {failedPreRequestTests.length}
</span>
</div>
{expandedSections.preRequest && (
<ul className="ml-5">
{preRequestTestResults.map((result) => (
<li key={result.uid} className="py-1">
{result.status === 'pass' ? (
<span className="test-success">&#x2714;&nbsp; {result.description}</span>
) : (
<>
<span className="test-failure">&#x2718;&nbsp; {result.description}</span>
<br />
<span className="error-message pl-8">{result.error}</span>
</>
)}
</li>
))}
</ul>
)}
</div>
)}

{postResponseTestResults.length > 0 && (
<div className="mb-4 test-section">
<div
className="font-medium test-summary flex items-center cursor-pointer hover:bg-opacity-10 hover:bg-gray-500 rounded py-2"
onClick={() => toggleSection('postResponse')}
>
<span className="dropdown-icon mr-2 flex items-center">
{expandedSections.postResponse ?
<IconChevronDown size={18} stroke={1.5} /> :
<IconChevronRight size={18} stroke={1.5} />
}
</span>
<span className="flex-grow">
Post-Response Tests ({postResponseTestResults.length}), Passed: {passedPostResponseTests.length}, Failed: {failedPostResponseTests.length}
</span>
</div>
{expandedSections.postResponse && (
<ul className="ml-5">
{postResponseTestResults.map((result) => (
<li key={result.uid} className="py-1">
{result.status === 'pass' ? (
<span className="test-success">&#x2714;&nbsp; {result.description}</span>
) : (
<>
<span className="test-failure">&#x2718;&nbsp; {result.description}</span>
<br />
<span className="error-message pl-8">{result.error}</span>
</>
)}
</li>
))}
</ul>
)}
</div>
)}

{results.length > 0 && (
<div className="mb-4 test-section">
<div
className="font-medium test-summary flex items-center cursor-pointer hover:bg-opacity-10 hover:bg-gray-500 rounded py-2"
onClick={() => toggleSection('tests')}
>
<span className="dropdown-icon mr-2 flex items-center">
{expandedSections.tests ?
<IconChevronDown size={18} stroke={1.5} /> :
<IconChevronRight size={18} stroke={1.5} />
}
</span>
<span className="flex-grow">
Tests ({results.length}), Passed: {passedTests.length}, Failed: {failedTests.length}
</span>
</div>
{expandedSections.tests && (
<ul className="ml-5">
{results.map((result) => (
<li key={result.uid} className="py-1">
{result.status === 'pass' ? (
<span className="test-success">&#x2714;&nbsp; {result.description}</span>
) : (
<>
<span className="test-failure">&#x2718;&nbsp; {result.description}</span>
<br />
<span className="error-message pl-8">{result.error}</span>
</>
)}
</li>
))}
</ul>
)}
</div>
)}

<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">
&#x2714;&nbsp; {result.lhsExpr}: {result.rhsExpr}
</span>
) : (
<>
<span className="test-failure">
&#x2718;&nbsp; {result.lhsExpr}: {result.rhsExpr}
</span>
<br />
<span className="error-message pl-8">{result.error}</span>
</>
)}
</li>
))}
</ul>
{assertionResults.length > 0 && (
<div className="test-section">
<div
className="font-medium test-summary flex items-center cursor-pointer hover:bg-opacity-10 hover:bg-gray-500 rounded py-2"
onClick={() => toggleSection('assertions')}
>
<span className="dropdown-icon mr-2 flex items-center">
{expandedSections.assertions ?
<IconChevronDown size={18} stroke={1.5} /> :
<IconChevronRight size={18} stroke={1.5} />
}
</span>
<span className="flex-grow">
Assertions ({assertionResults.length}), Passed: {passedAssertions.length}, Failed: {failedAssertions.length}
</span>
</div>
{expandedSections.assertions && (
<ul className="ml-5">
{assertionResults.map((result) => (
<li key={result.uid} className="py-1">
{result.status === 'pass' ? (
<span className="test-success">
&#x2714;&nbsp; {result.lhsExpr}: {result.rhsExpr}
</span>
) : (
<>
<span className="test-failure">
&#x2718;&nbsp; {result.lhsExpr}: {result.rhsExpr}
</span>
<br />
<span className="error-message pl-8">{result.error}</span>
</>
)}
</li>
))}
</ul>
)}
</div>
)}
</StyledWrapper>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
}

Expand All @@ -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">
Expand Down
14 changes: 12 additions & 2 deletions packages/bruno-app/src/components/ResponsePane/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,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: {
Expand Down Expand Up @@ -138,7 +143,12 @@ 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">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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: {
Expand Down Expand Up @@ -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} />
Expand Down
Loading