spb/cancerindex
Public
TypeScript 97.2%
SQL 1.5%
CSS 0.6%
JavaScript 0.5%
1import { describe, it, expect } from 'vitest';2import { parseCancerList, parseExplorerParams, serializeExplorerParams, apiQueryFor, yearRangeLabel, sexLabel, ageLabel, MAX_CANCERS } from '@/lib/explorer-params';34const D = { metric: 'mortality_count', geography: 'united-states', cancers: ['a', 'b', 'c', 'd', 'e'], from: 1999, to: 2024 };56describe('parseCancerList', () => {7 it('accepts comma-separated, repeated and mixed forms', () => {8 expect(parseCancerList('lung,breast')).toEqual(['lung', 'breast']);9 expect(parseCancerList(['lung', 'breast'])).toEqual(['lung', 'breast']);10 expect(parseCancerList(['lung,breast', 'colon'])).toEqual(['lung', 'breast', 'colon']);11 });12 it('normalizes case, drops duplicates, invalid tokens and blanks', () => {13 expect(parseCancerList(' Lung , lung, ,CI-CAN-00000042, ../etc, ok_slug ')).toEqual(['lung', 'CI-CAN-00000042']);14 });15 it('caps at MAX_CANCERS keeping order', () => {16 const many = Array.from({ length: 10 }, (_, i) => `c${i}`).join(',');17 expect(parseCancerList(many)).toEqual(['c0', 'c1', 'c2', 'c3', 'c4', 'c5']);18 expect(parseCancerList(many).length).toBe(MAX_CANCERS);19 });20 it('returns [] for undefined', () => {21 expect(parseCancerList(undefined)).toEqual([]);22 });23});2425describe('parseExplorerParams', () => {26 it('fills every gap from the computed defaults', () => {27 const s = parseExplorerParams({}, D);28 expect(s).toEqual({ metric: 'mortality_count', cancers: ['a', 'b', 'c', 'd', 'e'], geography: 'united-states', sex: 'all', age: 'all', from: 1999, to: 2024, view: 'lines', normalize: 'none', page: 1 });29 });30 it('reads valid values and rejects malformed ones', () => {31 const s = parseExplorerParams({ metric: 'AS_Mortality_Rate', cancers: 'lung', geography: 'usa', sex: 'Female', age: '65+', from: '2010', to: '2005', view: 'multiples', page: '3' }, D);32 expect(s.metric).toBe('as_mortality_rate');33 expect(s.cancers).toEqual(['lung']);34 expect(s.geography).toBe('USA'); // 3-letter → ISO3 upper-case35 expect(s.sex).toBe('female');36 expect(s.age).toBe('65+');37 expect([s.from, s.to]).toEqual([2005, 2010]); // swapped when inverted38 expect(s.view).toBe('multiples');39 expect(s.page).toBe(3);40 const bad = parseExplorerParams({ metric: 'DROP TABLE', geography: 'a b', sex: '1;2', age: '<script>', from: 'x', to: '99999', view: 'pie', page: '-2' }, D);41 expect(bad.metric).toBe('mortality_count');42 expect(bad.geography).toBe('united-states');43 expect(bad.sex).toBe('all');44 expect(bad.age).toBe('all');45 expect(bad.from).toBe(1999);46 expect(bad.to).toBe(2024);47 expect(bad.view).toBe('lines');48 expect(bad.page).toBe(1);49 });50 it('accepts "any" for sex (series become cancer × sex)', () => {51 expect(parseExplorerParams({ sex: 'any' }, D).sex).toBe('any');52 });53});5455describe('serializeExplorerParams / apiQueryFor', () => {56 const s = parseExplorerParams({ cancers: 'lung,breast', view: 'multiples', page: '2' }, D);57 it('writes every dimension explicitly, comma-joins cancers, omits defaults for view/page', () => {58 expect(serializeExplorerParams(s, { page: 1, view: 'lines' })).toBe('?metric=mortality_count&cancers=lung%2Cbreast&geography=united-states&sex=all&age=all&from=1999&to=2024');59 expect(serializeExplorerParams(s)).toContain('view=multiples');60 expect(serializeExplorerParams(s)).toContain('page=2');61 });62 it('round-trips through the parser', () => {63 const qs = new URLSearchParams(serializeExplorerParams(s).slice(1));64 const sp: Record<string, string> = {};65 for (const [k, v] of qs) sp[k] = v;66 expect(parseExplorerParams(sp, { metric: 'x', geography: 'y', cancers: [] })).toEqual(s);67 });68 it('builds the public API query with repeated cancer= params and no sex when "any"', () => {69 expect(apiQueryFor(s)).toBe('?metric=mortality_count&cancer=lung&cancer=breast&geography=united-states&sex=all&age=all&from=1999&to=2024&limit=200');70 expect(apiQueryFor({ ...s, sex: 'any' })).not.toContain('sex=');71 });72});7374describe('labels', () => {75 it('year range, sex and age labels', () => {76 expect(yearRangeLabel(1999, 2024)).toBe('1999–2024');77 expect(yearRangeLabel(2024, 2024)).toBe('2024');78 expect(yearRangeLabel(null, null)).toBe('all years');79 expect(yearRangeLabel(2000, null)).toBe('from 2000');80 expect(sexLabel('all')).toBe('both sexes');81 expect(sexLabel('any')).toBe('by sex');82 expect(ageLabel('all')).toBe('all ages');83 expect(ageLabel('65+')).toBe('ages 65+');84 });85});86