import { describe, it, expect } from 'vitest'; import { EM_DASH, formatCurrency, formatDate, formatDuration, formatInteger, formatPrice, formatPricePerSqm, formatPricePerSqmShort, isFiniteNumber, } from '@/utils/format'; describe('formatCurrency', () => { it('formats values >= 1000 in compact k notation', () => { expect(formatCurrency(1500)).toBe('£1.5k'); expect(formatCurrency(2500)).toBe('£2.5k'); expect(formatCurrency(1000000)).toBe('£1000.0k'); }); it('formats sub-thousand values as rounded integers', () => { expect(formatCurrency(950)).toBe('£950'); expect(formatCurrency(123.4)).toBe('£123'); }); }); describe('formatDuration', () => { it('renders minutes for sub-hour durations', () => { expect(formatDuration(0)).toBe('0m'); expect(formatDuration(120)).toBe('2m'); expect(formatDuration(3540)).toBe('59m'); }); it('renders hours-and-minutes for multi-hour durations', () => { expect(formatDuration(3600)).toBe('1h'); expect(formatDuration(5400)).toBe('1h30m'); expect(formatDuration(86400)).toBe('24h'); }); // B9 regression cases it('returns em-dash for negative seconds (was "-2m")', () => { expect(formatDuration(-120)).toBe(EM_DASH); expect(formatDuration(-1)).toBe(EM_DASH); }); it('returns em-dash for null and undefined (was "0m")', () => { expect(formatDuration(null)).toBe(EM_DASH); expect(formatDuration(undefined)).toBe(EM_DASH); }); it('returns em-dash for NaN (was "NaNh")', () => { expect(formatDuration(NaN)).toBe(EM_DASH); }); it('returns em-dash for non-finite values (Infinity)', () => { expect(formatDuration(Infinity)).toBe(EM_DASH); expect(formatDuration(-Infinity)).toBe(EM_DASH); }); it('caps absurdly large values at >24h', () => { expect(formatDuration(86401)).toBe('>24h'); expect(formatDuration(90000)).toBe('>24h'); expect(formatDuration(31536000)).toBe('>24h'); // 1 year }); }); describe('formatDate', () => { it('formats valid ISO dates', () => { expect(formatDate('2025-01-03T00:00:00Z')).toContain('2025'); }); it('returns the input string for invalid dates', () => { expect(formatDate('not-a-date')).toBe('not-a-date'); }); }); describe('formatPricePerSqm', () => { it('returns the formatted string when both values are valid', () => { expect(formatPricePerSqm(500000, 100)).toBe('£5,000/m²'); }); it('returns null when sqm is missing', () => { expect(formatPricePerSqm(500000, null)).toBe(null); expect(formatPricePerSqm(500000, undefined)).toBe(null); expect(formatPricePerSqm(500000, 0)).toBe(null); }); }); describe('isFiniteNumber', () => { it('returns true for finite numbers', () => { expect(isFiniteNumber(0)).toBe(true); expect(isFiniteNumber(-1)).toBe(true); expect(isFiniteNumber(1.5)).toBe(true); }); it('returns false for non-finite, null, undefined, and other types', () => { expect(isFiniteNumber(null)).toBe(false); expect(isFiniteNumber(undefined)).toBe(false); expect(isFiniteNumber(NaN)).toBe(false); expect(isFiniteNumber(Infinity)).toBe(false); expect(isFiniteNumber('5')).toBe(false); }); }); describe('formatPrice', () => { it('formats finite numbers with thousands separators', () => { expect(formatPrice(0)).toBe('£0'); expect(formatPrice(2500)).toBe('£2,500'); expect(formatPrice(500000)).toBe('£500,000'); expect(formatPrice(1234.7)).toBe('£1,235'); }); // B10 regression: null/undefined must NOT render as "£0" it('returns em-dash for null/undefined/non-finite', () => { expect(formatPrice(null)).toBe(EM_DASH); expect(formatPrice(undefined)).toBe(EM_DASH); expect(formatPrice(NaN)).toBe(EM_DASH); expect(formatPrice(Infinity)).toBe(EM_DASH); }); }); describe('formatPricePerSqmShort', () => { it('formats valid positive values', () => { expect(formatPricePerSqmShort(38)).toBe('£38/m²'); expect(formatPricePerSqmShort(4500)).toBe('£4500/m²'); }); // B10: zero & negative are missing-data sentinels, not real values it('returns em-dash for null/undefined/non-finite/zero/negative', () => { expect(formatPricePerSqmShort(null)).toBe(EM_DASH); expect(formatPricePerSqmShort(undefined)).toBe(EM_DASH); expect(formatPricePerSqmShort(NaN)).toBe(EM_DASH); expect(formatPricePerSqmShort(0)).toBe(EM_DASH); expect(formatPricePerSqmShort(-5)).toBe(EM_DASH); }); }); describe('formatInteger', () => { it('rounds and stringifies finite numbers', () => { expect(formatInteger(0)).toBe('0'); expect(formatInteger(3)).toBe('3'); expect(formatInteger(65.7)).toBe('66'); }); it('returns em-dash for null/undefined/non-finite', () => { expect(formatInteger(null)).toBe(EM_DASH); expect(formatInteger(undefined)).toBe(EM_DASH); expect(formatInteger(NaN)).toBe(EM_DASH); }); });