diff --git a/src/engine/formatter.test.ts b/src/engine/formatter.test.ts new file mode 100644 index 0000000..612a120 --- /dev/null +++ b/src/engine/formatter.test.ts @@ -0,0 +1,88 @@ +import { describe, it, expect } from "vitest"; +import { formatNumber, formatValue } from "./formatter"; +import { Decimal, defaultSettings, qty, pct, DateVal, ChartValue, EngineSettings } from "./types"; + +describe("formatNumber", () => { + it("formats standard numbers", () => { + expect(formatNumber(new Decimal("12.345"), defaultSettings)).toBe("12.35"); + expect(formatNumber(new Decimal("100"), defaultSettings)).toBe("100"); + }); + + it("handles custom group separators", () => { + const s: EngineSettings = { ...defaultSettings, groupSeparator: " " }; + expect(formatNumber(new Decimal("1234567.89"), s)).toBe("1 234 567.89"); + }); + + it("handles custom decimal separators", () => { + const s: EngineSettings = { ...defaultSettings, decimalSeparator: "," }; + expect(formatNumber(new Decimal("12.345"), s)).toBe("12,35"); + }); + + it("handles scientific notation strings automatically", () => { + expect(formatNumber(new Decimal("1e25"), defaultSettings)).toBe("1e+25"); + }); + + it("applies significant digits fallback for numbers close to zero", () => { + // 0.001 rounded to 0 decimals is 0, but since >= 1e-12, it shows significant digits + const s: EngineSettings = { ...defaultSettings, precision: 0 }; + expect(formatNumber(new Decimal("0.001"), s)).toBe("0.001"); + }); + + it("treats numbers < 1e-12 as conversion noise", () => { + const s: EngineSettings = { ...defaultSettings, precision: 2 }; + expect(formatNumber(new Decimal("1e-13"), s)).toBe("0"); + }); +}); + +describe("formatValue", () => { + it("formats chart as empty string", () => { + const v: ChartValue = { kind: "chart", points: [], unitLabel: null }; + expect(formatValue(v, defaultSettings)).toBe(""); + }); + + it("formats percent values", () => { + expect(formatValue(pct(25.5), defaultSettings)).toBe("25.5%"); + }); + + it("formats dates depending on flags", () => { + // 2024-01-01 12:00:00 UTC = 1704110400000 + const d: DateVal = { kind: "date", ms: 1704110400000, hasTime: false, timeZone: "UTC" }; + expect(formatValue(d, { ...defaultSettings, dateFormat: "iso" })).toBe("2024-01-01"); + expect(formatValue(d, { ...defaultSettings, dateFormat: "dmy" })).toBe("01.01.2024"); + expect(formatValue(d, { ...defaultSettings, dateFormat: "mdy" })).toBe("01/01/2024"); + + // Test timeOnly + const dTime: DateVal = { kind: "date", ms: 1704110400000, hasTime: true, timeZone: "UTC", timeOnly: true }; + expect(formatValue(dTime, defaultSettings)).toBe("12:00"); + }); + + it("formats base conversions", () => { + expect(formatValue(qty(255, null, "hex"), defaultSettings)).toBe("0xFF"); + expect(formatValue(qty(-255, null, "hex"), defaultSettings)).toBe("-0xFF"); + expect(formatValue(qty(5, null, "binary"), defaultSettings)).toBe("0b101"); + expect(formatValue(qty(8, null, "octal"), defaultSettings)).toBe("0o10"); + }); + + it("formats representations", () => { + expect(formatValue(qty(123456, null, "scientific"), defaultSettings)).toBe("1.23e5"); + expect(formatValue(qty(1234.56, null, "plain"), defaultSettings)).toBe("1234.56"); + expect(formatValue(qty(0.75, null, "fraction"), defaultSettings)).toBe("3/4"); + expect(formatValue(qty(14, null, "roman"), defaultSettings)).toBe("XIV"); + }); + + it("formats Roman numerals fallbacks", () => { + // Beyond 3999 falls back to formatNumber + expect(formatValue(qty(4000, null, "roman"), defaultSettings)).toBe("4,000"); + }); + + it("formats units", () => { + const cm = { id: "cm", dimension: "length" as const, ratio: new Decimal(0.01), format: "cm" }; + expect(formatValue(qty(10, cm), defaultSettings)).toBe("10 cm"); + + const degC = { id: "degC", dimension: "temperature" as const, ratio: new Decimal(1), format: "°C" }; + expect(formatValue(qty(25, degC), defaultSettings)).toBe("25°C"); + + const usd = { id: "USD", dimension: "currency" as const, ratio: new Decimal(1), format: "${}" }; + expect(formatValue(qty(100, usd), defaultSettings)).toBe("$100"); + }); +});