diff --git a/tests/unit/composables/usePlotTooltips.spec.ts b/tests/unit/composables/usePlotTooltips.spec.ts index 64695953..c94f4f12 100644 --- a/tests/unit/composables/usePlotTooltips.spec.ts +++ b/tests/unit/composables/usePlotTooltips.spec.ts @@ -6,7 +6,8 @@ import { nextTick } from "vue"; import usePlotTooltips from '@/composables/usePlotTooltips'; import { useAppStore } from '@/stores/appStore'; import { useColorStore } from '@/stores/colorStore'; -import { Axis, Dimension, type PointWithMetadata } from '@/types'; +import { useDataStore } from '@/stores/dataStore'; +import { Axis, Dimension, SummaryTableColumn, type PointWithMetadata, type SummaryTableDataRow } from '@/types'; describe('usePlotTooltips', () => { beforeEach(() => { @@ -28,14 +29,13 @@ describe('usePlotTooltips', () => { const appStore = useAppStore(); const colorStore = useColorStore(); - // Set up store state for location-based coloring appStore.filters = { [Dimension.LOCATION]: ['AFG', 'global'], [Dimension.DISEASE]: ['Cholera'], }; expect(colorStore.colorDimension).toBe(Dimension.LOCATION); + expect(appStore.dimensions.row).toBe(Dimension.DISEASE); - // Set colors so colorStore has the mapping colorStore.setColors([afgPointMetadata, globalPointMetadata]); const { tooltipCallback } = usePlotTooltips(); @@ -44,12 +44,15 @@ describe('usePlotTooltips', () => { expect(afgTooltip).toContain('Location: Afghanistan'); expect(afgTooltip).toContain('style="color: #009d9a'); // teal50 + // Row dimension (disease) is shown because it's different from color dimension (location) + expect(afgTooltip).toContain('Disease: Cholera'); expect(afgTooltip).not.toContain('Activity type'); const globalTooltip = tooltipCallback({ x: 1, y: 2, metadata: globalPointMetadata.metadata! }); expect(globalTooltip).toContain('Location: All 117 VIMC countries'); expect(globalTooltip).toContain('style="color: #6929c4'); // purple70 + expect(globalTooltip).toContain('Disease: Cholera'); expect(globalTooltip).not.toContain('Activity type'); }); @@ -60,12 +63,12 @@ describe('usePlotTooltips', () => { const appStore = useAppStore(); const colorStore = useColorStore(); - // Set up store state for disease-based coloring appStore.filters = { [Dimension.LOCATION]: ['AFG'], [Dimension.DISEASE]: ['Cholera', 'Measles'], }; expect(colorStore.colorDimension).toBe(Dimension.DISEASE); + expect(appStore.dimensions.row).toBe(Dimension.DISEASE); colorStore.setColors([choleraPointMetadata, measlesPointMetadata]); @@ -75,12 +78,16 @@ describe('usePlotTooltips', () => { expect(choleraTooltip).toContain('Disease: Cholera'); expect(choleraTooltip).toContain('style="color: #6929c4'); // purple70 + // Row dimension (disease) is NOT shown separately because it's the same as color dimension + expect(choleraTooltip.match(/Disease:/g)?.length).toBe(1); expect(choleraTooltip).not.toContain('Activity type'); const measlesTooltip = tooltipCallback({ x: 1, y: 2, metadata: measlesPointMetadata.metadata! }); expect(measlesTooltip).toContain('Disease: Measles'); expect(measlesTooltip).toContain('style="color: #009d9a'); // teal50 + // Row dimension (disease) is NOT shown separately because it's the same as color dimension + expect(measlesTooltip.match(/Disease:/g)?.length).toBe(1); expect(measlesTooltip).not.toContain('Activity type'); }); @@ -95,13 +102,13 @@ describe('usePlotTooltips', () => { await nextTick(); expect(appStore.dimensions.column).toBe(Dimension.ACTIVITY_TYPE); - // Set up store state for disease-based coloring (single location) appStore.filters = { [Dimension.LOCATION]: ['AFG'], [Dimension.DISEASE]: ['Cholera'], [Dimension.ACTIVITY_TYPE]: ['routine', 'campaign'], }; expect(colorStore.colorDimension).toBe(Dimension.DISEASE); + expect(appStore.dimensions.row).toBe(Dimension.DISEASE); colorStore.setColors([routinePointMetadata, campaignPointMetadata]); @@ -110,13 +117,90 @@ describe('usePlotTooltips', () => { const routineTooltip = tooltipCallback({ x: 1, y: 2, metadata: routinePointMetadata.metadata! }); expect(routineTooltip).toContain('Disease: Cholera'); expect(routineTooltip).toContain('Activity type: Routine'); + // Row dimension (disease) is NOT shown separately because it's the same as color dimension + expect(routineTooltip.match(/Disease:/g)?.length).toBe(1); expect(routineTooltip).toContain('style="color: #6929c4'); // purple70 const campaignTooltip = tooltipCallback({ x: 1, y: 2, metadata: campaignPointMetadata.metadata! }); expect(campaignTooltip).toContain('Disease: Cholera'); expect(campaignTooltip).toContain('Activity type: Campaign'); + // Row dimension (disease) is NOT shown separately because it's the same as color dimension + expect(campaignTooltip.match(/Disease:/g)?.length).toBe(1); expect(campaignTooltip).toContain('style="color: #6929c4'); // purple70 }); + + it('displays summary data (median, mean, and 95% confidence interval) in tooltip', () => { + const afgPointMetadata = { metadata: { [Axis.WITHIN_BAND]: 'AFG', [Axis.ROW]: 'Cholera', [Axis.COLUMN]: '' } }; + + const appStore = useAppStore(); + const colorStore = useColorStore(); + const dataStore = useDataStore(); + + appStore.filters = { + [Dimension.LOCATION]: ['AFG'], + [Dimension.DISEASE]: ['Cholera'], + }; + + colorStore.setColors([afgPointMetadata]); + + dataStore.summaryTableData = [ + { + [Dimension.LOCATION]: 'AFG', + [Dimension.DISEASE]: 'Cholera', + [SummaryTableColumn.MEDIAN]: 1234.56, + [SummaryTableColumn.MEAN]: 1456.78, + [SummaryTableColumn.CI_LOWER]: 789.12, + [SummaryTableColumn.CI_UPPER]: 2345.67, + } as SummaryTableDataRow, + ]; + + const { tooltipCallback } = usePlotTooltips(); + + const tooltip = tooltipCallback({ x: 1, y: 2, metadata: afgPointMetadata.metadata! }); + + expect(tooltip).toContain('Median: 1234.56'); + expect(tooltip).toContain('Mean: 1456.78'); + expect(tooltip).toContain('95% confidence interval:'); + expect(tooltip).toContain('789.12'); + expect(tooltip).toContain('2345.67'); + }); + + it('handles negative confidence interval values with appropriate sign', () => { + const afgPointMetadata = { metadata: { [Axis.WITHIN_BAND]: 'AFG', [Axis.ROW]: 'Cholera', [Axis.COLUMN]: '' } }; + + const appStore = useAppStore(); + const colorStore = useColorStore(); + const dataStore = useDataStore(); + + appStore.filters = { + [Dimension.LOCATION]: ['AFG'], + [Dimension.DISEASE]: ['Cholera'], + }; + + colorStore.setColors([afgPointMetadata]); + + // Set up summary table data with negative lower bound (crossing zero) + dataStore.summaryTableData = [ + { + [Dimension.LOCATION]: 'AFG', + [Dimension.DISEASE]: 'Cholera', + [SummaryTableColumn.MEDIAN]: 50.00, + [SummaryTableColumn.MEAN]: 55.00, + [SummaryTableColumn.CI_LOWER]: -100.50, + [SummaryTableColumn.CI_UPPER]: 200.75, + } as SummaryTableDataRow, + ]; + + const { tooltipCallback } = usePlotTooltips(); + + const tooltip = tooltipCallback({ x: 1, y: 2, metadata: afgPointMetadata.metadata! }); + + expect(tooltip).toContain('Median: 50.00'); + expect(tooltip).toContain('Mean: 55.00'); + // Check negative lower bound and positive upper bound with + sign when interval crosses zero + expect(tooltip).toContain('-100.50'); + expect(tooltip).toContain('+200.75'); + }); }); });