diff --git a/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.tsx b/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.tsx index 597222a6ef..9012578602 100644 --- a/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.tsx +++ b/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.tsx @@ -624,7 +624,7 @@ export const AutomatedAnalysisCard: DashboardCardFC key={topic} > {evaluations.map((evaluation) => { - return ; + return ; })} diff --git a/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.tsx b/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.tsx index e44592b024..75d8f9b5f3 100644 --- a/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.tsx +++ b/src/app/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.tsx @@ -43,7 +43,7 @@ export interface AutomatedAnalysisCardListProps { evaluations: CategorizedRuleEvaluations[]; } -export const AutomatedAnalysisCardList: React.FC = (props) => { +export const AutomatedAnalysisCardList: React.FC = ({ evaluations }) => { const { t } = useTranslation(); const [sortBy, setSortBy] = React.useState({}); @@ -80,7 +80,7 @@ export const AutomatedAnalysisCardList: React.FC ); const flatFiltered = React.useMemo(() => { - return props.evaluations + return evaluations .flatMap(([_, evaluations]) => { return evaluations.map((evaluation) => evaluation); }) @@ -108,7 +108,7 @@ export const AutomatedAnalysisCardList: React.FC } return 0; }); - }, [sortBy, props.evaluations]); + }, [sortBy, evaluations]); return ( diff --git a/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx b/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx index 8b2b979f2c..904abec0a8 100644 --- a/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx +++ b/src/app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.tsx @@ -25,12 +25,12 @@ import { useTranslation } from 'react-i18next'; import { transformAADescription } from './utils'; export interface ClickableAutomatedAnalysisLabelProps { - label: AnalysisResult; + result: AnalysisResult; } export const clickableAutomatedAnalysisKey = 'clickable-automated-analysis-label'; -export const ClickableAutomatedAnalysisLabel: React.FC = ({ label: result }) => { +export const ClickableAutomatedAnalysisLabel: React.FC = ({ result }) => { const { t } = useTranslation(); const [isHoveredOrFocused, setIsHoveredOrFocused] = React.useState(false); @@ -83,7 +83,7 @@ export const ClickableAutomatedAnalysisLabel: React.FC{result.name}} alertSeverityVariant={alertPopoverVariant} diff --git a/src/app/Dashboard/AutomatedAnalysis/utils.tsx b/src/app/Dashboard/AutomatedAnalysis/utils.tsx index 1892105f99..e329f92d44 100644 --- a/src/app/Dashboard/AutomatedAnalysis/utils.tsx +++ b/src/app/Dashboard/AutomatedAnalysis/utils.tsx @@ -13,13 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AnalysisResult } from '@app/Shared/Services/api.types'; -import { Stack, StackItem, Title, Label, Text } from '@patternfly/react-core'; +import { AnalysisResult, Evaluation } from '@app/Shared/Services/api.types'; +import { Stack, StackItem, Label, Title, Text } from '@patternfly/react-core'; import _ from 'lodash'; import * as React from 'react'; export const transformAADescription = (result: AnalysisResult): JSX.Element => { - const format = (s): JSX.Element => { + const format = (s: Evaluation): JSX.Element => { if (typeof s === 'string') { return {s}; } diff --git a/src/app/Recordings/ActiveRecordingsTable.tsx b/src/app/Recordings/ActiveRecordingsTable.tsx index 3ac7094e66..fc222b9809 100644 --- a/src/app/Recordings/ActiveRecordingsTable.tsx +++ b/src/app/Recordings/ActiveRecordingsTable.tsx @@ -15,8 +15,8 @@ */ import { - clickableAutomatedAnalysisKey, ClickableAutomatedAnalysisLabel, + clickableAutomatedAnalysisKey, } from '@app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel'; import { authFailMessage } from '@app/ErrorView/types'; import { DeleteOrDisableWarningType } from '@app/Modal/types'; @@ -33,8 +33,14 @@ import { RootState, StateDispatch, } from '@app/Shared/Redux/ReduxStore'; -import { ActiveRecording, NotificationCategory, RecordingState, Target } from '@app/Shared/Services/api.types'; -import { AnalysisResult, CategorizedRuleEvaluations } from '@app/Shared/Services/Report.service'; +import { + ActiveRecording, + AnalysisResult, + CategorizedRuleEvaluations, + NotificationCategory, + RecordingState, + Target, +} from '@app/Shared/Services/api.types'; import { ServiceContext } from '@app/Shared/Services/Services'; import { useDayjs } from '@app/utils/hooks/useDayjs'; import { useSort } from '@app/utils/hooks/useSort'; @@ -555,8 +561,8 @@ export const ActiveRecordingsTable: React.FC = (prop > {filteredRecordings.map((r) => ( = ({ }) => { const [dayjs, datetimeContext] = useDayjs(); const context = React.useContext(ServiceContext); + const [loadingAnalysis, setLoadingAnalysis] = React.useState(false); + const [analyses, setAnalyses] = React.useState([]); const expandedRowId = React.useMemo( () => `active-table-row-${recording.name}-${recording.startTime}-exp`, @@ -868,9 +876,6 @@ export const ActiveRecordingRow: React.FC = ({ [index, handleRowCheck], ); - const [loadingAnalysis, setLoadingAnalysis] = React.useState(false); - const [analyses, setAnalyses] = React.useState([]); - const handleLoadAnalysis = React.useCallback(() => { setLoadingAnalysis(true); context.reports @@ -1031,7 +1036,7 @@ export const ActiveRecordingRow: React.FC = ({ > {evaluations.map((evaluation) => { return ( - + ); })} diff --git a/src/app/Recordings/ArchivedRecordingsTable.tsx b/src/app/Recordings/ArchivedRecordingsTable.tsx index 9fa2be829f..f23f5d69b6 100644 --- a/src/app/Recordings/ArchivedRecordingsTable.tsx +++ b/src/app/Recordings/ArchivedRecordingsTable.tsx @@ -16,8 +16,8 @@ import { ArchiveUploadModal } from '@app/Archives/ArchiveUploadModal'; import { - clickableAutomatedAnalysisKey, ClickableAutomatedAnalysisLabel, + clickableAutomatedAnalysisKey, } from '@app/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel'; import { DeleteWarningModal } from '@app/Modal/DeleteWarningModal'; import { DeleteOrDisableWarningType } from '@app/Modal/types'; @@ -41,8 +41,9 @@ import { UPLOADS_SUBDIRECTORY, NotificationCategory, NullableTarget, + CategorizedRuleEvaluations, + AnalysisResult, } from '@app/Shared/Services/api.types'; -import { AnalysisResult, CategorizedRuleEvaluations } from '@app/Shared/Services/Report.service'; import { ServiceContext } from '@app/Shared/Services/Services'; import { useSort } from '@app/utils/hooks/useSort'; import { useSubscriptions } from '@app/utils/hooks/useSubscriptions'; @@ -768,6 +769,8 @@ export const ArchivedRecordingRow: React.FC = ({ updateFilters, }) => { const context = React.useContext(ServiceContext); + const [loadingAnalysis, setLoadingAnalysis] = React.useState(false); + const [analyses, setAnalyses] = React.useState([]); const parsedLabels = React.useMemo(() => { return parseLabels(recording.metadata.labels); @@ -790,9 +793,6 @@ export const ArchivedRecordingRow: React.FC = ({ [index, handleRowCheck], ); - const [loadingAnalysis, setLoadingAnalysis] = React.useState(false); - const [analyses, setAnalyses] = React.useState([]); - React.useEffect(() => { if (!isExpanded) { return; @@ -886,6 +886,31 @@ export const ArchivedRecordingRow: React.FC = ({ handleToggle, ]); + React.useEffect(() => { + if (!isExpanded) { + return; + } + setLoadingAnalysis(true); + context.reports + .reportJson(recording, currentSelectedTargetURL) + .pipe(first()) + .subscribe((report) => { + const map = new Map(); + report.forEach((evaluation) => { + const topicValue = map.get(evaluation.topic); + if (topicValue === undefined) { + map.set(evaluation.topic, [evaluation]); + } else { + topicValue.push(evaluation); + topicValue.sort((a, b) => b.score - a.score || a.name.localeCompare(b.name)); + } + }); + const sorted = (Array.from(map) as CategorizedRuleEvaluations[]).sort(); + setAnalyses(sorted); + setLoadingAnalysis(false); + }); + }, [context.reports, isExpanded, recording, currentSelectedTargetURL, setAnalyses, setLoadingAnalysis]); + const childRow = React.useMemo(() => { return ( @@ -911,7 +936,7 @@ export const ArchivedRecordingRow: React.FC = ({ > {evaluations.map((evaluation) => { return ( - + ); })} @@ -924,7 +949,7 @@ export const ArchivedRecordingRow: React.FC = ({ ); - }, [index, isExpanded, loadingAnalysis, analyses]); + }, [index, isExpanded, analyses, loadingAnalysis]); return ( diff --git a/src/app/utils/fakeData.ts b/src/app/utils/fakeData.ts index ccb7c4e3cd..f1d5544fd6 100644 --- a/src/app/utils/fakeData.ts +++ b/src/app/utils/fakeData.ts @@ -33,8 +33,8 @@ import { NullableTarget, EventType, CachedReportValue, - SimpleResponse, AnalysisResult, + SimpleResponse, } from '@app/Shared/Services/api.types'; import { LoginService } from '@app/Shared/Services/Login.service'; import { NotificationService, NotificationsInstance } from '@app/Shared/Services/Notifications.service'; @@ -107,9 +107,9 @@ export const fakeEvaluations: AnalysisResult[] = [ }, }, { - topic: 'classloading', name: 'Class Leak', score: 0, + topic: 'classloading', evaluation: { summary: 'leaked classes', explanation: 'classes were loaded and leaked', @@ -124,9 +124,9 @@ export const fakeEvaluations: AnalysisResult[] = [ }, }, { - topic: 'classloading', name: 'Class Loading Pressure', score: 0, + topic: 'classloading', evaluation: { summary: 'too much loading pressure', explanation: 'lots of classloading slowing things down', @@ -141,9 +141,9 @@ export const fakeEvaluations: AnalysisResult[] = [ }, }, { - topic: 'jvm_information', name: 'Discouraged Management Agent Settings', score: 50, + topic: 'jvm_information', evaluation: { summary: 'bad settings set', explanation: 'these settings can cause problems', @@ -158,9 +158,9 @@ export const fakeEvaluations: AnalysisResult[] = [ }, }, { - topic: 'exceptions', name: 'Thrown Exceptions', score: 0.2, + topic: 'exceptions', evaluation: { summary: 'many exceptions thrown which is slow', explanation: 'exception processing is slower than normal code execution', diff --git a/src/test/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.test.tsx b/src/test/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.test.tsx index 66cb70172c..ff7ac16447 100644 --- a/src/test/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.test.tsx +++ b/src/test/Dashboard/AutomatedAnalysis/AutomatedAnalysisCard.test.tsx @@ -17,10 +17,10 @@ import { AutomatedAnalysisCard } from '@app/Dashboard/AutomatedAnalysis/Automate import { RootState } from '@app/Shared/Redux/ReduxStore'; import { CachedReportValue, + AnalysisResult, ArchivedRecording, FAILED_REPORT_MESSAGE, NO_RECORDINGS_MESSAGE, - AnalysisResult, automatedAnalysisRecordingName, } from '@app/Shared/Services/api.types'; import { defaultAutomatedAnalysisRecordingConfig } from '@app/Shared/Services/service.types'; @@ -49,9 +49,9 @@ const mockEmptyCachedReport: CachedReportValue = { }; const mockRuleEvaluation1: AnalysisResult = { - topic: 'myTopic', name: 'rule1', score: 100, + topic: 'myTopic', evaluation: { summary: 'rule1 summary', explanation: 'rule1 explanation', @@ -67,9 +67,9 @@ const mockRuleEvaluation1: AnalysisResult = { }; const mockRuleEvaluation2: AnalysisResult = { - topic: 'fakeTopic', name: 'rule2', score: 0, + topic: 'fakeTopic', evaluation: { summary: 'rule2 summary', explanation: 'rule2 explanation', @@ -85,9 +85,9 @@ const mockRuleEvaluation2: AnalysisResult = { }; const mockRuleEvaluation3: AnalysisResult = { - topic: 'fakeTopic', name: 'rule3', score: 55, + topic: 'fakeTopic', evaluation: { summary: 'rule3 summary', explanation: 'rule3 explanation', @@ -103,9 +103,9 @@ const mockRuleEvaluation3: AnalysisResult = { }; const mockNaRuleEvaluation: AnalysisResult = { - topic: 'fakeTopic', name: 'N/A rule', score: -1, + topic: 'fakeTopic', evaluation: { summary: 'NArule summary', explanation: 'NArule explanation', diff --git a/src/test/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.test.tsx b/src/test/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.test.tsx index a6c290e81e..394ad1b23c 100644 --- a/src/test/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.test.tsx +++ b/src/test/Dashboard/AutomatedAnalysis/AutomatedAnalysisCardList.test.tsx @@ -23,9 +23,9 @@ import { Provider } from 'react-redux'; import renderer, { act } from 'react-test-renderer'; const mockRuleEvaluation1: AnalysisResult = { - topic: 'myTopic', name: 'rule1', score: 100, + topic: 'myTopic', evaluation: { summary: 'first thing happened', explanation: 'first reason', @@ -41,9 +41,9 @@ const mockRuleEvaluation1: AnalysisResult = { }; const mockRuleEvaluation2: AnalysisResult = { - topic: 'fakeTopic', name: 'rule2', score: 0, + topic: 'fakeTopic', evaluation: { summary: 'second thing happened', explanation: 'second reason', @@ -53,9 +53,9 @@ const mockRuleEvaluation2: AnalysisResult = { }; const mockRuleEvaluation3: AnalysisResult = { - topic: 'fakeTopic', name: 'rule3', score: 55, + topic: 'fakeTopic', evaluation: { summary: 'third thing happened', explanation: 'third reason', @@ -65,9 +65,9 @@ const mockRuleEvaluation3: AnalysisResult = { }; const mockNaRuleEvaluation: AnalysisResult = { - topic: 'fakeTopic', name: 'N/A rule', score: -1, + topic: 'fakeTopic', evaluation: { summary: 'fourth thing happened', explanation: 'fourth reason', diff --git a/src/test/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.test.tsx b/src/test/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.test.tsx index bd9dc59d99..89c268fd24 100644 --- a/src/test/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.test.tsx +++ b/src/test/Dashboard/AutomatedAnalysis/ClickableAutomatedAnalysisLabel.test.tsx @@ -21,9 +21,9 @@ import React from 'react'; import { renderDefault } from '../../Common'; const mockRuleEvaluation1: AnalysisResult = { - topic: 'myTopic', name: 'rule1', score: 100, + topic: 'myTopic', evaluation: { summary: 'rule1 summary', explanation: 'rule1 explanation', @@ -39,9 +39,9 @@ const mockRuleEvaluation1: AnalysisResult = { }; const mockRuleEvaluation2: AnalysisResult = { - topic: 'fakeTopic', name: 'rule2', score: 55, + topic: 'fakeTopic', evaluation: { summary: 'rule2 summary', explanation: 'rule2 explanation', @@ -57,9 +57,9 @@ const mockRuleEvaluation2: AnalysisResult = { }; const mockRuleEvaluation3: AnalysisResult = { - topic: 'fakeTopic', name: 'rule3', score: 0, + topic: 'fakeTopic', evaluation: { summary: 'rule3 summary', explanation: 'rule3 explanation', @@ -75,9 +75,9 @@ const mockRuleEvaluation3: AnalysisResult = { }; const mockNaRuleEvaluation: AnalysisResult = { - topic: 'fakeTopic', name: 'N/A rule', score: -1, + topic: 'fakeTopic', evaluation: { summary: 'rule4 summary', explanation: 'rule4 explanation', @@ -96,13 +96,13 @@ describe('', () => { afterEach(cleanup); it('displays label', async () => { - renderDefault(); + renderDefault(); expect(screen.getByText(mockRuleEvaluation1.name)).toBeInTheDocument(); }); it('displays popover when critical label is clicked', async () => { - const { user } = renderDefault(); + const { user } = renderDefault(); expect(screen.getByText(mockRuleEvaluation1.name)).toBeInTheDocument(); @@ -133,9 +133,11 @@ describe('', () => { expect(setting).toBeInTheDocument(); expect(keyval).toBeInTheDocument(); expect(score).toBeInTheDocument(); + const heading = screen.getByRole('heading', { name: /danger rule1/i, }); + expect(within(heading).getByText(mockRuleEvaluation1.name)).toBeInTheDocument(); await user.click(screen.getAllByText(mockRuleEvaluation1.name)[0]); @@ -150,7 +152,7 @@ describe('', () => { }); it('displays popover when warning label is clicked', async () => { - const { user } = renderDefault(); + const { user } = renderDefault(); expect(screen.getByText(mockRuleEvaluation2.name)).toBeInTheDocument(); @@ -181,9 +183,11 @@ describe('', () => { expect(setting).toBeInTheDocument(); expect(keyval).toBeInTheDocument(); expect(score).toBeInTheDocument(); + const heading = screen.getByRole('heading', { name: /warning rule2/i, }); + expect(within(heading).getByText(mockRuleEvaluation2.name)).toBeInTheDocument(); await user.click(screen.getAllByText(mockRuleEvaluation2.name)[0]); @@ -194,12 +198,11 @@ describe('', () => { expect(setting).not.toBeInTheDocument(); expect(keyval).not.toBeInTheDocument(); expect(score).not.toBeInTheDocument(); - expect(closeButton).not.toBeInTheDocument(); }); it('displays popover when ok label is clicked', async () => { - const { user } = renderDefault(); + const { user } = renderDefault(); expect(screen.getByText(mockRuleEvaluation3.name)).toBeInTheDocument(); @@ -229,9 +232,11 @@ describe('', () => { expect(setting).toBeInTheDocument(); expect(keyval).toBeInTheDocument(); expect(score).toBeInTheDocument(); + const heading = screen.getByRole('heading', { name: /success rule3/i, }); + expect(within(heading).getByText(mockRuleEvaluation3.name)).toBeInTheDocument(); await user.click(screen.getAllByText(mockRuleEvaluation3.name)[0]); @@ -242,12 +247,11 @@ describe('', () => { expect(setting).not.toBeInTheDocument(); expect(keyval).not.toBeInTheDocument(); expect(score).not.toBeInTheDocument(); - expect(closeButton).not.toBeInTheDocument(); }); it('displays popover when N/A label is clicked', async () => { - const { user } = renderDefault(); + const { user } = renderDefault(); expect(screen.getByText(mockNaRuleEvaluation.name)).toBeInTheDocument(); @@ -260,6 +264,7 @@ describe('', () => { expect(closeButton).toBeInTheDocument(); + expect(document.getElementsByClassName('pf-m-default').item(0)).toBeInTheDocument(); const summary = screen.getByText(mockNaRuleEvaluation.evaluation.summary); const explanation = screen.getByText(mockNaRuleEvaluation.evaluation.explanation); const solution = screen.getByText(mockNaRuleEvaluation.evaluation.solution); @@ -275,9 +280,11 @@ describe('', () => { expect(setting).toBeInTheDocument(); expect(keyval).toBeInTheDocument(); expect(score).toBeInTheDocument(); + const heading = screen.getByRole('heading', { name: /default /i, }); + expect(within(heading).getByText(mockNaRuleEvaluation.name)).toBeInTheDocument(); await user.click(screen.getAllByText(mockNaRuleEvaluation.name)[0]); @@ -288,7 +295,6 @@ describe('', () => { expect(setting).not.toBeInTheDocument(); expect(keyval).not.toBeInTheDocument(); expect(score).not.toBeInTheDocument(); - expect(closeButton).not.toBeInTheDocument(); }); }); diff --git a/src/test/Dashboard/AutomatedAnalysis/Filters/AutomatedAnalysisNameFilter.test.tsx b/src/test/Dashboard/AutomatedAnalysis/Filters/AutomatedAnalysisNameFilter.test.tsx index d3cc7b9748..eb4d9e0af3 100644 --- a/src/test/Dashboard/AutomatedAnalysis/Filters/AutomatedAnalysisNameFilter.test.tsx +++ b/src/test/Dashboard/AutomatedAnalysis/Filters/AutomatedAnalysisNameFilter.test.tsx @@ -21,9 +21,9 @@ import React from 'react'; import { renderDefault } from '../../../Common'; const mockRuleEvaluation1: AnalysisResult = { - topic: 'myTopic', name: 'rule1', score: 100, + topic: 'myTopic', evaluation: { summary: 'rule1 summary', explanation: 'rule1 explanation', @@ -39,9 +39,9 @@ const mockRuleEvaluation1: AnalysisResult = { }; const mockRuleEvaluation2: AnalysisResult = { - topic: 'fakeTopic', name: 'rule2', score: 0, + topic: 'fakeTopic', evaluation: { summary: 'rule2 summary', explanation: 'rule2 explanation', @@ -57,9 +57,9 @@ const mockRuleEvaluation2: AnalysisResult = { }; const mockRuleEvaluation3: AnalysisResult = { - topic: 'fakeTopic', name: 'rule3', score: 55, + topic: 'fakeTopic', evaluation: { summary: 'rule3 summary', explanation: 'rule3 explanation', @@ -75,9 +75,9 @@ const mockRuleEvaluation3: AnalysisResult = { }; const mockNaRuleEvaluation: AnalysisResult = { - topic: 'fakeTopic', name: 'N/A rule', score: -1, + topic: 'fakeTopic', evaluation: { summary: 'NArule summary', explanation: 'NArule explanation', diff --git a/src/test/Dashboard/AutomatedAnalysis/Filters/AutomatedAnalysisTopicFilter.test.tsx b/src/test/Dashboard/AutomatedAnalysis/Filters/AutomatedAnalysisTopicFilter.test.tsx index ee33341822..c2fde083b9 100644 --- a/src/test/Dashboard/AutomatedAnalysis/Filters/AutomatedAnalysisTopicFilter.test.tsx +++ b/src/test/Dashboard/AutomatedAnalysis/Filters/AutomatedAnalysisTopicFilter.test.tsx @@ -21,9 +21,9 @@ import React from 'react'; import { renderDefault } from '../../../Common'; const mockRuleEvaluation1: AnalysisResult = { - topic: 'myTopic', name: 'rule1', score: 100, + topic: 'myTopic', evaluation: { summary: 'rule1 summary', explanation: 'rule1 explanation', @@ -39,9 +39,9 @@ const mockRuleEvaluation1: AnalysisResult = { }; const mockRuleEvaluation2: AnalysisResult = { - topic: 'fakeTopic', name: 'rule2', score: 0, + topic: 'fakeTopic', evaluation: { summary: 'rule2 summary', explanation: 'rule2 explanation', @@ -57,9 +57,9 @@ const mockRuleEvaluation2: AnalysisResult = { }; const mockRuleEvaluation3: AnalysisResult = { - topic: 'fakeTopic', name: 'rule3', score: 55, + topic: 'fakeTopic', evaluation: { summary: 'rule3 summary', explanation: 'rule3 explanation', @@ -75,9 +75,9 @@ const mockRuleEvaluation3: AnalysisResult = { }; const mockNaRuleEvaluation: AnalysisResult = { - topic: 'fakeTopic', name: 'N/A rule', score: -1, + topic: 'fakeTopic', evaluation: { summary: 'NArule summary', explanation: 'NArule explanation',