From b274d946c3d08286c8bc85409c4472180f25e299 Mon Sep 17 00:00:00 2001 From: abdulqoyumadegoke Date: Thu, 30 Jul 2026 06:57:39 +0100 Subject: [PATCH] test(format): Add unit tests and formatXLM utility for XLM amount formatting --- lib/__tests__/formatXLM.test.ts | 39 +++++++++++++++++++++++++++++++++ lib/format.ts | 18 +++++++++++++++ lib/portfolio.ts | 7 +++--- 3 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 lib/__tests__/formatXLM.test.ts create mode 100644 lib/format.ts diff --git a/lib/__tests__/formatXLM.test.ts b/lib/__tests__/formatXLM.test.ts new file mode 100644 index 0000000..6d6858d --- /dev/null +++ b/lib/__tests__/formatXLM.test.ts @@ -0,0 +1,39 @@ +import { describe, it, expect } from "vitest"; +import { formatXLM } from "../format"; + +describe("formatXLM amount formatter", () => { + it("formats 10000 XLM with thousand separators and 2 decimal places", () => { + expect(formatXLM(10000)).toBe("10,000.00 XLM"); + }); + + it("formats 0 XLM as '0.00 XLM'", () => { + expect(formatXLM(0)).toBe("0.00 XLM"); + }); + + it("formats fractional amounts (0.5 XLM) to 2 decimal places ('0.50 XLM')", () => { + expect(formatXLM(0.5)).toBe("0.50 XLM"); + }); + + it("formats large numbers (1000000 XLM) with thousand separators ('1,000,000.00 XLM')", () => { + expect(formatXLM(1000000)).toBe("1,000,000.00 XLM"); + }); + + it("formats negative values with a leading minus sign ('-500.00 XLM')", () => { + expect(formatXLM(-500)).toBe("-500.00 XLM"); + }); + + it("handles string inputs cleanly", () => { + expect(formatXLM("10000")).toBe("10,000.00 XLM"); + expect(formatXLM("0.5")).toBe("0.50 XLM"); + expect(formatXLM("-500")).toBe("-500.00 XLM"); + }); + + it("avoids floating-point representation rounding errors", () => { + expect(formatXLM(0.1 + 0.2)).toBe("0.30 XLM"); + expect(formatXLM(1234.5678)).toBe("1,234.57 XLM"); + }); + + it("handles invalid or NaN inputs gracefully with fallback", () => { + expect(formatXLM("invalid")).toBe("0.00 XLM"); + }); +}); diff --git a/lib/format.ts b/lib/format.ts new file mode 100644 index 0000000..e793a32 --- /dev/null +++ b/lib/format.ts @@ -0,0 +1,18 @@ +/** + * Formats a numeric or string XLM amount with thousand separators, + * 2 decimal places, and trailing " XLM" suffix. + * Handles negative values and prevents floating-point representation errors. + */ +export function formatXLM(amount: number | string | bigint): string { + const num = typeof amount === "string" ? Number(amount) : Number(amount); + if (isNaN(num)) { + return "0.00 XLM"; + } + + const formatted = num.toLocaleString("en-US", { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }); + + return `${formatted} XLM`; +} diff --git a/lib/portfolio.ts b/lib/portfolio.ts index f446269..be5d47a 100644 --- a/lib/portfolio.ts +++ b/lib/portfolio.ts @@ -1,3 +1,5 @@ +import { formatXLM } from "./format"; + export interface InvestmentPosition { invoice_id: string; invoice_title: string; @@ -21,9 +23,6 @@ export function calculateActiveTotal(positions: InvestmentPosition[]): Portfolio return { activeTotal, - formattedTotal: `${activeTotal.toLocaleString(undefined, { - minimumFractionDigits: 2, - maximumFractionDigits: 2, - })} XLM`, + formattedTotal: formatXLM(activeTotal), }; } \ No newline at end of file