import { describe, expect, it } from 'vitest' import { cmFromDisplay, cmFromFtIn, cmFromMeters, cmFromSpacing, displayFromCm, formatCm, formatDimensions, formatSpacing, spacingFromCm, } from './units' describe('cm conversions', () => { // Every cm column is REAL, so entry is exact — no whole-cm quantization. A 1 ft // grid is 30.48 cm, not 30, or the error accumulates across the garden (#47). it('feet+inches → exact cm', () => { expect(cmFromFtIn(1)).toBe(30.48) expect(cmFromFtIn(4)).toBe(121.92) expect(cmFromFtIn(8)).toBe(243.84) expect(cmFromFtIn(4, 6)).toBe(137.16) }) it('meters → exact cm', () => { expect(cmFromMeters(10)).toBe(1000) expect(cmFromMeters(1.22)).toBe(122) expect(cmFromMeters(0.305)).toBe(30.5) }) it('rounds away float dust, not precision', () => { // 12 × 2.54 is 30.479999999999997 in IEEE-754; 1 ft must read as 30.48. expect(cmFromFtIn(0, 12)).toBe(30.48) expect(cmFromSpacing(12, 'imperial')).toBe(30.48) expect(cmFromSpacing(1, 'imperial')).toBe(2.54) }) it('round-trips display → cm → display at display precision', () => { const imperial = [0.5, 1, 2.5, 4, 8, 12.5, 24] for (const ft of imperial) { expect(displayFromCm(cmFromDisplay(ft, 'imperial'), 'imperial')).toBe(ft) } const metric = [0.15, 1, 1.22, 3.05, 10, 24.5] for (const m of metric) { expect(displayFromCm(cmFromDisplay(m, 'metric'), 'metric')).toBe(m) } }) }) describe('displayFromCm', () => { it('metric keeps cm precision', () => { expect(displayFromCm(1000, 'metric')).toBe(10) expect(displayFromCm(122, 'metric')).toBe(1.22) }) it('imperial rounds to 0.1 ft so integers stay clean', () => { expect(displayFromCm(122, 'imperial')).toBe(4) // not 4.0026 expect(displayFromCm(244, 'imperial')).toBe(8) // not 8.01 }) }) describe('formatCm', () => { it('metric shows meters', () => { expect(formatCm(122, 'metric')).toBe('1.22 m') expect(formatCm(1000, 'metric')).toBe('10 m') }) it('imperial shows feet and inches, carrying 12″ up', () => { expect(formatCm(122, 'imperial')).toBe('4′ 0″') expect(formatCm(244, 'imperial')).toBe('8′ 0″') expect(formatCm(137, 'imperial')).toBe('4′ 6″') }) it('formats width × height', () => { expect(formatDimensions(122, 244, 'imperial')).toBe('4′ 0″ × 8′ 0″') }) }) describe('plant spacing (small scale)', () => { it('formats cm in metric, whole inches in imperial', () => { expect(formatSpacing(15, 'metric')).toBe('15 cm') expect(formatSpacing(15, 'imperial')).toBe('6″') // 15/2.54 = 5.9 → 6 expect(formatSpacing(30, 'imperial')).toBe('12″') }) it('parses an entered value back to exact cm', () => { expect(cmFromSpacing(15, 'metric')).toBe(15) expect(cmFromSpacing(6, 'imperial')).toBe(15.24) }) it('prefills an input from cm', () => { expect(spacingFromCm(15, 'metric')).toBe(15) expect(spacingFromCm(30, 'imperial')).toBe(11.8) // 30/2.54 = 11.81 → 11.8 expect(spacingFromCm(30.48, 'imperial')).toBe(12) // an exactly-12in grid reads back as 12 }) })