when-am-i / files / src / utils / yearScale.test.ts
yearScale.test.ts
bedc0609 2026-06-23 Pavel Safronov | 1 | import { describe, it, expect } from 'vitest'; |
| 2 | import { buildYearScale } from './yearScale'; | |
| 3 | ||
| 4 | describe('buildYearScale', () => { | |
| 5 | it('places the endpoints at 0 and 1', () => { | |
| 6 | const s = buildYearScale(-243, 2026, [1954]); | |
| 7 | expect(s.toFraction(-243)).toBe(0); | |
| 8 | expect(s.toFraction(2026)).toBe(1); | |
| 9 | }); | |
| 10 | ||
| 11 | it('clamps years outside the range (including ±Infinity for open-ended eras)', () => { | |
| 12 | const s = buildYearScale(-243, 2026, [1954]); | |
| 13 | expect(s.toFraction(-Infinity)).toBe(0); | |
| 14 | expect(s.toFraction(-5000)).toBe(0); | |
| 15 | expect(s.toFraction(Infinity)).toBe(1); | |
| 16 | expect(s.toFraction(9999)).toBe(1); | |
| 17 | }); | |
| 18 | ||
| 19 | it('anchors on the region key years (a single boundary lands dead-centre)', () => { | |
| 20 | const s = buildYearScale(-243, 2026, [1954]); | |
| 21 | expect(s.anchors).toEqual([-243, 1954, 2026]); | |
| 22 | expect(s.toFraction(1954)).toBeCloseTo(0.5, 6); | |
| 23 | }); | |
| 24 | ||
| 25 | it('sorts and de-dupes key years and drops any outside the range', () => { | |
| 26 | const s = buildYearScale(-243, 2026, [1910, 1896, 1896, 9999, -500, 1907, 1897]); | |
| 27 | expect(s.anchors).toEqual([-243, 1896, 1897, 1907, 1910, 2026]); | |
| 28 | }); | |
| 29 | ||
| 30 | it('spreads clustered key dates evenly so each is readable (Korea 1896–1910)', () => { | |
| 31 | const s = buildYearScale(-243, 2026, [1896, 1897, 1907, 1910]); | |
| 32 | const fracs = s.anchors.map((y) => s.toFraction(y)); | |
| 33 | const step = 1 / (s.anchors.length - 1); | |
| 34 | for (let i = 1; i < fracs.length; i++) { | |
| 35 | // Equal-width segments → even spacing, even though 1896→1897 is one | |
| 36 | // year and 1910→2026 is 116 (proportional spacing would collide them). | |
| 37 | expect(fracs[i] - fracs[i - 1]).toBeCloseTo(step, 6); | |
| 38 | } | |
| 39 | }); | |
| 40 | ||
| 41 | it('falls back to round epoch anchors when the region has no key years', () => { | |
| 42 | const s = buildYearScale(-243, 2026, []); | |
| 43 | expect(s.anchors).toEqual([-243, 1500, 1800, 1900, 2026]); | |
| 44 | }); | |
| 45 | ||
| 46 | it('is monotonic increasing in year', () => { | |
| 47 | const s = buildYearScale(-243, 2026, [1896, 1897, 1907, 1910]); | |
| 48 | let prev = -Infinity; | |
| 49 | for (let y = -243; y <= 2026; y += 7) { | |
| 50 | const f = s.toFraction(y); | |
| 51 | expect(f).toBeGreaterThanOrEqual(prev); | |
| 52 | prev = f; | |
| 53 | } | |
| 54 | }); | |
| 55 | ||
| 56 | it('round-trips year → fraction → year', () => { | |
| 57 | const s = buildYearScale(-243, 2026, [1792, 1806, 1871]); | |
| 58 | for (const y of [-243, 500, 1792, 1806, 1850, 1871, 1945, 2026]) { | |
| 59 | expect(s.toYear(s.toFraction(y))).toBeCloseTo(y, 6); | |
| 60 | } | |
| 61 | }); | |
| 62 | ||
| 63 | it('degrades to a single linear segment when there are no usable anchors', () => { | |
| 64 | const s = buildYearScale(1980, 2026, []); // no epoch anchors in range either | |
| 65 | expect(s.anchors).toEqual([1980, 2026]); | |
| 66 | expect(s.toFraction(2003)).toBeCloseTo((2003 - 1980) / (2026 - 1980), 6); | |
| 67 | }); | |
| 68 | }); |