diff --git a/src/__tests__/CompareResults/RevisionRowExpandable.test.tsx b/src/__tests__/CompareResults/RevisionRowExpandable.test.tsx index bf3667388..53e061568 100644 --- a/src/__tests__/CompareResults/RevisionRowExpandable.test.tsx +++ b/src/__tests__/CompareResults/RevisionRowExpandable.test.tsx @@ -21,6 +21,46 @@ function renderWithRoute(component: ReactElement) { }); } +describe('RevisionRowExpandable for student-t testVersion', () => { + it('should display median difference and percentage when both median values are present', async () => { + const { testCompareData } = getTestData(); + // testCompareData[0]: base_median_value=704.84, new_median_value=712.44, unit='ms' + // expected difference: 7.6, percentage: 1.08 + renderWithRoute( + , + ); + + const header = await screen.findByText(/Difference of medians/); + const medianBox = header.closest('div'); + expect(medianBox).toHaveTextContent('1.08%'); + expect(medianBox).toHaveTextContent('7.6 ms'); + }); + + it('should not display median difference when median values are absent', async () => { + const { testCompareData } = getTestData(); + const noMedians = { + ...testCompareData[0], + base_median_value: 0, + new_median_value: 0, + }; + + renderWithRoute( + , + ); + + await screen.findByText(/Difference of means/); + expect(screen.queryByText(/Difference of medians/)).not.toBeInTheDocument(); + }); +}); + describe('RevisionRowExpandable for mann-whitney-u testVersion', () => { it('should display ModeInterpretation for mann-whitney-u', async () => { const { mockMannWhitneyResultData } = getTestData(); diff --git a/src/__tests__/CompareResults/__snapshots__/ResultsView.test.tsx.snap b/src/__tests__/CompareResults/__snapshots__/ResultsView.test.tsx.snap index b095553c6..2d0538e89 100644 --- a/src/__tests__/CompareResults/__snapshots__/ResultsView.test.tsx.snap +++ b/src/__tests__/CompareResults/__snapshots__/ResultsView.test.tsx.snap @@ -67,8 +67,7 @@ exports[`Results View Should display Base, New and Common graphs with replicates Comparison result - : - + : ( lower @@ -460,8 +459,7 @@ exports[`Results View Should display Base, New and Common graphs with tooltips 1 Comparison result - : - + : ( lower diff --git a/src/common/testVersions/index.ts b/src/common/testVersions/index.ts index 8c61c882e..54ac054c2 100644 --- a/src/common/testVersions/index.ts +++ b/src/common/testVersions/index.ts @@ -12,6 +12,10 @@ export interface TestVersionStrategy { newAvg: number | null; }; renderColumns(result: CombinedResultsItemType): ReactNode; + renderExpandedLeft(result: CombinedResultsItemType): ReactNode; + getComparisonResult(result: CombinedResultsItemType): string; + renderExpandedRight(result: CombinedResultsItemType): ReactNode; + renderExpandedBottom(result: CombinedResultsItemType): ReactNode; } // Registry mapping each TestVersion to its concrete strategy. diff --git a/src/common/testVersions/mannWhitney.tsx b/src/common/testVersions/mannWhitney.tsx index 034915734..1ee055949 100644 --- a/src/common/testVersions/mannWhitney.tsx +++ b/src/common/testVersions/mannWhitney.tsx @@ -2,6 +2,10 @@ import ThumbDownIcon from '@mui/icons-material/ThumbDown'; import ThumbUpIcon from '@mui/icons-material/ThumbUp'; import Box from '@mui/material/Box'; +import { MannWhitneyCompareMetrics } from '../../components/CompareResults/MannWhitneyCompareMetrics'; +import { ModeInterpretation } from '../../components/CompareResults/ModeInterpretation'; +import PValCliffsDeltaComp from '../../components/CompareResults/PValCliffsDeltaComp'; +import { StatisticsWarnings } from '../../components/CompareResults/StatisticsWarnings'; import { CombinedResultsItemType, MannWhitneyResultsItem, @@ -202,6 +206,53 @@ export const mannWhitneyStrategy = { }; }, + renderExpandedLeft() { + return null; + }, + + getComparisonResult(result: CombinedResultsItemType) { + return capitalize( + (result as MannWhitneyResultsItem).direction_of_change ?? '', + ); + }, + + renderExpandedRight(result: CombinedResultsItemType) { + const mwResult = result as MannWhitneyResultsItem; + const { cles, cles_direction } = mwResult.cles ?? { + cles: '', + cles_direction: '', + }; + const { cliffs_delta, cliffs_interpretation } = mwResult; + const pValue = mwResult.mann_whitney_test?.pvalue; + const p_value_cles = mwResult.mann_whitney_test?.interpretation + ? capitalize(mwResult.mann_whitney_test.interpretation) + : ''; + + return ( + <> + + + + ); + }, + + renderExpandedBottom(result: CombinedResultsItemType) { + const mwResult = result as MannWhitneyResultsItem; + return ( +
+ + +
+ ); + }, + renderColumns(result: CombinedResultsItemType) { const { cliffs_delta, direction_of_change, mann_whitney_test, cles } = result as MannWhitneyResultsItem; diff --git a/src/common/testVersions/studentT.tsx b/src/common/testVersions/studentT.tsx index cc6276301..29e8749d3 100644 --- a/src/common/testVersions/studentT.tsx +++ b/src/common/testVersions/studentT.tsx @@ -5,8 +5,11 @@ import ThumbDownIcon from '@mui/icons-material/ThumbDown'; import ThumbUpIcon from '@mui/icons-material/ThumbUp'; import Box from '@mui/material/Box'; +import Distribution from '../../components/CompareResults/Distribution'; +import { Strings } from '../../resources/Strings'; import { CombinedResultsItemType, CompareResultsItem } from '../../types/state'; import { TableConfig } from '../../types/types'; +import { formatNumber } from '../../utils/format'; import { getPlatformShortName } from '../../utils/platform'; import { determineStatus, @@ -154,6 +157,82 @@ export const studentTStrategy = { }; }, + renderExpandedLeft(result: CombinedResultsItemType) { + return ; + }, + + getComparisonResult(result: CombinedResultsItemType) { + return (result as CompareResultsItem).new_is_better ? 'better' : 'worse'; + }, + + renderExpandedRight(result: CombinedResultsItemType) { + const { + delta_percentage: deltaPercent, + delta_value: delta, + confidence_text: confidenceText, + confidence: confidenceValue, + base_median_value: baseMedian, + new_median_value: newMedian, + base_measurement_unit: baseUnit, + new_measurement_unit: newUnit, + } = result as CompareResultsItem; + + const deltaUnit = baseUnit || newUnit || ''; + const formatTwoDigits = new Intl.NumberFormat('en-US', { + maximumFractionDigits: 2, + }); + const medianDifference = + baseMedian && newMedian + ? formatTwoDigits.format(newMedian - baseMedian) + : ''; + const medianPercentage = + baseMedian && newMedian + ? formatTwoDigits.format(((newMedian - baseMedian) / baseMedian) * 100) + : ''; + + const { confidenceNote } = Strings.components.expandableRow; + + return ( + <> + + Difference of means: {deltaPercent}% ({formatNumber(delta)} + {deltaUnit ? ' ' + deltaUnit : null}) + + {newMedian && baseMedian ? ( + + Difference of medians: {medianPercentage}% ( + {medianDifference} + {deltaUnit ? ' ' + deltaUnit : null}) + + ) : null} + {confidenceText ? ( +
+ + Confidence: {confidenceText} + {confidenceValue ? ' ' + `(${confidenceValue})` : null} + + + **Note: {confidenceNote}{' '} + +
+ ) : ( + + Confidence: Not available{' '} + + )} + + ); + }, + + renderExpandedBottom() { + return null; + }, + renderColumns(result: CombinedResultsItemType) { const { is_improvement: improvement, diff --git a/src/components/CompareResults/MannWhitneyCompareMetrics.tsx b/src/components/CompareResults/MannWhitneyCompareMetrics.tsx index c89f4bc34..3bed1c25d 100644 --- a/src/components/CompareResults/MannWhitneyCompareMetrics.tsx +++ b/src/components/CompareResults/MannWhitneyCompareMetrics.tsx @@ -1,22 +1,16 @@ import { Box } from '@mui/material'; -import { MANN_WHITNEY_U } from '../../common/constants'; import { MannWhitneyResultsItem } from '../../types/state'; -import { TestVersion } from '../../types/types'; import { getModeInterpretation } from '../../utils/helpers'; const METRIC_HEADERS = ['Metric', 'Base', 'New', 'Interpretation']; -interface MannWhitneyCompareMetricsProps { - result: MannWhitneyResultsItem; - testVersion: TestVersion; -} - export const MannWhitneyCompareMetrics = ({ result, - testVersion, -}: MannWhitneyCompareMetricsProps) => { - if (!result || testVersion !== MANN_WHITNEY_U) { +}: { + result: MannWhitneyResultsItem; +}) => { + if (!result) { return null; } diff --git a/src/components/CompareResults/ModeInterpretation.tsx b/src/components/CompareResults/ModeInterpretation.tsx index dcb10f47e..59f08bf62 100644 --- a/src/components/CompareResults/ModeInterpretation.tsx +++ b/src/components/CompareResults/ModeInterpretation.tsx @@ -1,17 +1,13 @@ import { Box } from '@mui/material'; -import { MANN_WHITNEY_U } from '../../common/constants'; import { MannWhitneyResultsItem } from '../../types/state'; -import { TestVersion } from '../../types/types'; export const ModeInterpretation = ({ result, - testVersion, }: { result: MannWhitneyResultsItem; - testVersion: TestVersion; }) => { - if (!result || !result.silverman_kde || testVersion !== MANN_WHITNEY_U) { + if (!result || !result.silverman_kde) { return null; } diff --git a/src/components/CompareResults/RevisionRowExpandable.tsx b/src/components/CompareResults/RevisionRowExpandable.tsx index d31e4b27c..0a8697abd 100644 --- a/src/components/CompareResults/RevisionRowExpandable.tsx +++ b/src/components/CompareResults/RevisionRowExpandable.tsx @@ -4,31 +4,13 @@ import Grid from '@mui/material/Grid'; import Stack from '@mui/material/Stack'; import CommonGraph from './CommonGraph'; -import Distribution from './Distribution'; -import { ModeInterpretation } from './ModeInterpretation'; -import { MANN_WHITNEY_U, STUDENT_T } from '../../common/constants'; +import { getStrategy } from '../../common/testVersions'; import { Strings } from '../../resources/Strings'; import { Spacing } from '../../styles'; -import type { - CombinedResultsItemType, - CompareResultsItem, - MannWhitneyResultsItem, -} from '../../types/state'; +import type { CombinedResultsItemType } from '../../types/state'; import { TestVersion } from '../../types/types'; -import { formatNumber } from './../../utils/format'; -import { MannWhitneyCompareMetrics } from './MannWhitneyCompareMetrics'; -import PValCliffsDeltaComp from './PValCliffsDeltaComp'; -import { StatisticsWarnings } from './StatisticsWarnings'; -import { capitalize } from '../../utils/helpers'; -const strings = Strings.components.expandableRow; -const { singleRun, confidenceNote } = strings; - -const numberFormatterTwoDigits = new Intl.NumberFormat('en-US', { - maximumFractionDigits: 2, -}); -const formatNumberTwoDigits = (value: number) => - numberFormatterTwoDigits.format(value); +const { singleRun } = Strings.components.expandableRow; function RevisionRowExpandable(props: RevisionRowExpandableProps) { const { result, id, testVersion } = props; @@ -39,31 +21,15 @@ function RevisionRowExpandable(props: RevisionRowExpandableProps) { base_runs_replicates: baseRunsReplicates, new_runs_replicates: newRunsReplicates, platform, - delta_percentage: deltaPercent, - delta_value: delta, - confidence_text: confidenceText, - confidence: confidenceValue, - base_median_value: baseMedian, - new_median_value: newMedian, - base_measurement_unit: baseUnit, - new_measurement_unit: newUnit, - base_app: baseApplication, - new_app: newApplication, more_runs_are_needed: moreRunsAreNeeded, lower_is_better: lowerIsBetter, - new_is_better: newIsBetter, + base_app: baseApplication, + new_app: newApplication, + base_measurement_unit: baseUnit, + new_measurement_unit: newUnit, } = result; - const unit = baseUnit || newUnit; - const deltaUnit = unit ? `${unit}` : ''; - let medianDifference = ''; - let medianPercentage = ''; - if (baseMedian && newMedian) { - medianDifference = formatNumberTwoDigits(newMedian - baseMedian); - medianPercentage = formatNumberTwoDigits( - ((newMedian - baseMedian) / baseMedian) * 100, - ); - } + const strategy = getStrategy(testVersion); const baseValues = baseRunsReplicates && baseRunsReplicates.length @@ -73,31 +39,6 @@ function RevisionRowExpandable(props: RevisionRowExpandableProps) { const newValues = newRunsReplicates && newRunsReplicates.length ? newRunsReplicates : newRuns; - const renderPValCliffsDeltaComp = (result: MannWhitneyResultsItem) => { - if (testVersion === MANN_WHITNEY_U && result) { - const { cles, cles_direction } = result?.cles ?? { - cles: '', - cles_direction: '', - }; - const { cliffs_delta, cliffs_interpretation } = result; - const pValue = result?.mann_whitney_test?.pvalue; - const p_value_cles = result?.mann_whitney_test?.interpretation - ? capitalize(result.mann_whitney_test.interpretation) - : ''; - return ( - - ); - } - return; - }; - return ( - {/******* student t test rendering **************/} - {testVersion === STUDENT_T && ( - - )} + {strategy.renderExpandedLeft(result)} @@ -149,80 +87,14 @@ function RevisionRowExpandable(props: RevisionRowExpandableProps) { )} - Comparison result:{' '} - {testVersion === MANN_WHITNEY_U - ? capitalize( - (result as MannWhitneyResultsItem).direction_of_change ?? - '', - ) - : newIsBetter - ? 'better' - : 'worse'}{' '} + Comparison result: {strategy.getComparisonResult(result)}{' '} ({lowerIsBetter ? 'lower' : 'higher'} is better) - {/******* student t test rendering **************/} - {testVersion === STUDENT_T && ( - <> - - Difference of means: {deltaPercent}% ( - {formatNumber(delta)} - {deltaUnit ? ' ' + deltaUnit : null}) - - {newMedian && baseMedian ? ( - - Difference of medians: {medianPercentage}% ( - {medianDifference} - {deltaUnit ? ' ' + deltaUnit : null}) - - ) : null} - {confidenceText ? ( -
- - Confidence: {confidenceText} - {confidenceValue ? ' ' + `(${confidenceValue})` : null} - - - **Note: {confidenceNote}{' '} - -
- ) : ( - - Confidence: Not available{' '} - - )} - - )} - {/******* mann-whitney rendering **************/} - {renderPValCliffsDeltaComp(result as MannWhitneyResultsItem)} - + {strategy.renderExpandedRight(result)}
- - {/******* mann-whitney rendering **************/} -
- - -
-
+ {strategy.renderExpandedBottom(result)}
); diff --git a/src/components/CompareResults/StatisticsWarnings.tsx b/src/components/CompareResults/StatisticsWarnings.tsx index bc43aad86..513de1b6b 100644 --- a/src/components/CompareResults/StatisticsWarnings.tsx +++ b/src/components/CompareResults/StatisticsWarnings.tsx @@ -1,19 +1,14 @@ import { Warning } from '@mui/icons-material'; import { Box } from '@mui/system'; -import { MANN_WHITNEY_U } from '../../common/constants'; import { Colors } from '../../styles'; import { MannWhitneyResultsItem } from '../../types/state'; -import { TestVersion } from '../../types/types'; export const StatisticsWarnings = ({ result, - testVersion, }: { result: MannWhitneyResultsItem; - testVersion: TestVersion; }) => { - if (testVersion !== MANN_WHITNEY_U) return null; const componentStyles = { backgroundColor: 'transparent', marginTop: 2,