diff --git a/client/components/CandidateTests/CandidateTestPlanRun/CandidateTestPlanRun.css b/client/components/CandidateTests/CandidateTestPlanRun/CandidateTestPlanRun.css index af31f7b01..2af3c9c0d 100644 --- a/client/components/CandidateTests/CandidateTestPlanRun/CandidateTestPlanRun.css +++ b/client/components/CandidateTests/CandidateTestPlanRun/CandidateTestPlanRun.css @@ -157,3 +157,12 @@ position: relative; top: -5px; } + +.incomplete-test-plan-information-text { + margin-top: 5%; +} + +.status-bar { + display: flex; + align-items: center; +} diff --git a/client/components/CandidateTests/CandidateTestPlanRun/index.jsx b/client/components/CandidateTests/CandidateTestPlanRun/index.jsx index b6c3357d5..726fd33d8 100644 --- a/client/components/CandidateTests/CandidateTestPlanRun/index.jsx +++ b/client/components/CandidateTests/CandidateTestPlanRun/index.jsx @@ -29,6 +29,7 @@ import ThankYouModal from '../CandidateModals/ThankYouModal'; import getMetrics from '../../Reports/getMetrics'; import FeedbackListItem from '../FeedbackListItem'; import DisclosureComponent from '../../common/DisclosureComponent'; +import StatusBar from '../../TestRun/StatusBar'; // https://codesandbox.io/s/react-hookresize-observer-example-ft88x function useSize(target) { @@ -165,6 +166,21 @@ const CandidateTestPlanRun = () => { setThankYouModalShowing(true); }; + const handleIncompleteTestPlans = () => { + const runnableTests = testPlanReport.runnableTests; + const finalizedTests = testPlanReport.finalizedTestResults; + const finalizedTestIds = finalizedTests.map(x => x.test.id); + + const hasIncompleteTests = + runnableTests.length != finalizedTests.length; + + const viewedCompleteTests = viewedTests.filter(viewedTest => + finalizedTestIds.includes(viewedTest) + ); + + return { hasIncompleteTests, viewedCompleteTests }; + }; + useEffect(() => { if (data) { if ( @@ -345,6 +361,9 @@ const CandidateTestPlanRun = () => { } }; + const { hasIncompleteTests, viewedCompleteTests } = + handleIncompleteTestPlans(); + const heading = (
@@ -369,6 +388,10 @@ const CandidateTestPlanRun = () => { )} + {hasIncompleteTests && + ( + + )}
); @@ -492,21 +515,34 @@ const CandidateTestPlanRun = () => { testPlanReport.finalizedTestResults[ currentTestIndex ]; - const { testsPassedCount } = getMetrics({ testResult }); - return ( - <> -

- Test Result:{' '} - {testsPassedCount ? 'PASS' : 'FAIL'} -

- - - ); + + if (testResult) { + const { testsPassedCount } = getMetrics({ + testResult + }); + return ( + <> +

+ Test Result:{' '} + {testsPassedCount ? 'PASS' : 'FAIL'} +

+ + + ); + } else { + return ( + <> +

+ Test Result: INCOMPLETE +

+ + ); + } }) ]} stacked @@ -542,7 +578,7 @@ const CandidateTestPlanRun = () => { currentTestIndex={currentTestIndex} toggleShowClick={toggleTestNavigator} handleTestClick={handleTestClick} - viewedTests={viewedTests} + viewedTests={viewedCompleteTests} /> { true ); }} - disabled={!isLastTest} + disabled={ + !isLastTest || + hasIncompleteTests + } > Finish diff --git a/client/components/TestQueueRow/TestQueueRow.css b/client/components/TestQueueRow/TestQueueRow.css index 4035df1db..9d86b243e 100644 --- a/client/components/TestQueueRow/TestQueueRow.css +++ b/client/components/TestQueueRow/TestQueueRow.css @@ -107,6 +107,14 @@ button.more-actions:active { width: initial; } +.next-report-status-information-text { + font-size: small; + text-align: center; + display: block; + margin: 5%; + color:gray; +} + /* tr.test-queue-run-row td:first-child { padding: 0.75rem; } */ diff --git a/client/components/TestQueueRow/index.jsx b/client/components/TestQueueRow/index.jsx index 94367c80e..4216c5f28 100644 --- a/client/components/TestQueueRow/index.jsx +++ b/client/components/TestQueueRow/index.jsx @@ -427,12 +427,20 @@ const TestQueueRow = ({ }; const evaluateNewReportStatus = () => { - const { status, conflictsLength } = testPlanReport; + const { status, conflictsLength, runnableTestsLength, draftTestPlanRuns } = + testPlanReport; + const conflictsCount = conflictsLength; + const draftTestPlansTestResultsLengths = draftTestPlanRuns.map(x => x.testResultsLength); + + const hasIncompleteTests = draftTestPlansTestResultsLengths.filter(x => x != runnableTestsLength).length > 0 // If there are no conflicts OR the test has been marked as "final", // and admin can mark a test run as "draft" let newStatus; + let newStatusCanContinue; + let newStatusInformationText; + if ( (status !== 'IN_REVIEW' && conflictsCount === 0 && @@ -450,11 +458,24 @@ const TestQueueRow = ({ ) { newStatus = 'CANDIDATE'; } - return newStatus; + + if (newStatus === 'CANDIDATE' && hasIncompleteTests) { + newStatusCanContinue = false; + newStatusInformationText = + 'Incomplete test plans cannot transition to the candidate report status.'; + } else { + newStatusCanContinue = true; + } + + return { newStatus, newStatusCanContinue, newStatusInformationText }; }; const { status, results } = evaluateStatusAndResults(); - const nextReportStatus = evaluateNewReportStatus(); + const { + newStatus: nextReportStatus, + newStatusCanContinue: nextReportStatusCanContinue, + newStatusInformationText: nextReportStatusInformationText + } = evaluateNewReportStatus(); const getRowId = tester => [ @@ -533,25 +554,37 @@ const TestQueueRow = ({
{status}
{isSignedIn && isTester && (
- {isAdmin && !isLoading && nextReportStatus && ( - <> - - + {isAdmin && + !isLoading && + nextReportStatus && + nextReportStatusCanContinue && ( + <> + + + )} + + {nextReportStatusInformationText && ( + + {nextReportStatusInformationText} + )} {results}
diff --git a/client/components/TestRun/StatusBar/index.jsx b/client/components/TestRun/StatusBar/index.jsx index 011c7aa2e..2c5f21d32 100644 --- a/client/components/TestRun/StatusBar/index.jsx +++ b/client/components/TestRun/StatusBar/index.jsx @@ -6,6 +6,8 @@ import nextId from 'react-id-generator'; const StatusBar = ({ hasConflicts = false, + hasIncompleteTestRuns = false, + isCandidateTestRun = false, handleReviewConflictsButtonClick = () => {} }) => { const [statuses, setStatuses] = useState([]); @@ -34,6 +36,17 @@ const StatusBar = ({ }); } + if(hasIncompleteTestRuns && isCandidateTestRun) { + const variant = 'warning'; + const icon = 'alert'; + const message = 'This Candidate Test Plan has incomplete test results and cannot be finished. Please move this Test Plan back to Draft and complete recording all test results.'; + statuses.push({ + icon, + message, + variant + }); + } + setStatuses(statuses); }, []); @@ -59,6 +72,8 @@ const StatusBar = ({ StatusBar.propTypes = { issues: PropTypes.array, hasConflicts: PropTypes.bool, + hasIncompleteTestRuns: PropTypes.bool, + isCandidateTestRun: PropTypes.bool, handleReviewConflictsButtonClick: PropTypes.func };