Files
aurum/frontend/src/lib/format.test.ts
ZProger f5c6591310 test: юнит-тесты на форматирование и разбивки крипто-портфеля + версия 1.1.6
Vitest для чистой логики: lib/format.ts (заодно поймал баг в
trimTrailingZeros на "-0.000") и расчёт разбивки по риску/монете/сети
на вкладке Crypto — вынес их из компонентов в отдельные тестируемые
функции. Тесты подключены в CI. Версия приложения обновлена до 1.1.6.
2026-09-09 00:52:27 +03:00

156 lines
5.5 KiB
TypeScript

import { beforeEach, describe, expect, it } from "vitest";
import { setLanguage } from "@/lib/i18n";
import {
formatCryptoAmount,
formatCurrency,
formatSignedCurrency,
formatTransactionDate,
getIntlLocale,
getMonthLabels,
maskAmount,
pluralizeRu,
trimTrailingZeros,
} from "@/lib/format";
describe("getIntlLocale", () => {
it("maps ru to ru-RU and en to en-US", () => {
expect(getIntlLocale("ru")).toBe("ru-RU");
expect(getIntlLocale("en")).toBe("en-US");
});
});
describe("formatCurrency", () => {
beforeEach(() => setLanguage("en"));
it("keeps unambiguous currency symbols compact, not spelled out", () => {
// The exact regression this suite exists to catch: currencyDisplay
// "code" would turn this into "USD 614" — see lib/format.ts's
// createCurrencyFormatter docstring and the PR #18 review that flagged it.
expect(formatCurrency(614, "USD")).toBe("$614");
});
it("drops the region prefix on ambiguous symbols (CNY, HKD) instead of spelling out the code", () => {
const cny = formatCurrency(614, "CNY");
const hkd = formatCurrency(614, "HKD");
expect(cny).not.toContain("CN¥");
expect(cny).not.toContain("CNY");
expect(hkd).not.toContain("HK$");
expect(hkd).not.toContain("HKD");
});
it("rounds to whole units", () => {
expect(formatCurrency(614.5, "USD")).toBe("$615");
expect(formatCurrency(614.4, "USD")).toBe("$614");
});
it("accepts a numeric string the same as a number", () => {
expect(formatCurrency("614", "USD")).toBe(formatCurrency(614, "USD"));
});
});
describe("formatCryptoAmount", () => {
beforeEach(() => setLanguage("en"));
it("uses 2 decimals once the value reaches 1", () => {
expect(formatCryptoAmount(1234.5678, "USD")).toBe("$1,234.57");
});
it("uses 2 decimals for exactly zero", () => {
expect(formatCryptoAmount(0, "USD")).toBe("$0.00");
});
it("scales precision for sub-cent values so they don't round away to $0.00", () => {
// 8 leading zeros after the decimal point + 4 digits of real precision.
expect(formatCryptoAmount(0.000000006894, "USD")).toBe("$0.000000006894");
});
});
describe("formatSignedCurrency", () => {
beforeEach(() => setLanguage("en"));
it("prefixes a plus sign on positive amounts", () => {
expect(formatSignedCurrency(100, "USD")).toBe("+$100");
});
it("leaves the formatter's own minus sign on negative amounts, no double sign", () => {
expect(formatSignedCurrency(-100, "USD")).toBe("-$100");
});
it("adds no sign for zero", () => {
expect(formatSignedCurrency(0, "USD")).toBe("$0");
});
});
describe("maskAmount", () => {
it("replaces the formatted value when hidden", () => {
expect(maskAmount("$1,234", true)).toBe("••••");
});
it("passes the formatted value through when not hidden", () => {
expect(maskAmount("$1,234", false)).toBe("$1,234");
});
});
describe("trimTrailingZeros", () => {
it("strips a wei-level all-zero fractional part down to the integer", () => {
expect(trimTrailingZeros("3.000000000000000000")).toBe("3");
});
it("trims only the trailing zeros, keeping real precision", () => {
expect(trimTrailingZeros("3.140000")).toBe("3.14");
});
it("leaves a value with no decimal point untouched", () => {
expect(trimTrailingZeros("42")).toBe("42");
});
it("collapses an all-zero fractional part to a bare 0, not empty or a dangling minus", () => {
expect(trimTrailingZeros("0.000")).toBe("0");
expect(trimTrailingZeros("-0.000")).toBe("0");
});
});
describe("getMonthLabels", () => {
it("returns 12 labels for each language", () => {
expect(getMonthLabels("en")).toHaveLength(12);
expect(getMonthLabels("ru")).toHaveLength(12);
});
it("starts January in both languages", () => {
expect(getMonthLabels("en")[0]).toBe("Jan");
expect(getMonthLabels("ru")[0]).toBe("Янв");
});
});
describe("formatTransactionDate", () => {
it("omits the year by default", () => {
expect(formatTransactionDate("2026-08-29", false)).toBe(getIntlLocale("en") === "en-US" ? "Aug 29" : "Aug 29");
});
it("includes the year when asked", () => {
expect(formatTransactionDate("2026-08-29", true)).toContain("2026");
});
});
describe("pluralizeRu", () => {
it("picks the singular form for 1 (and 21, 31, ... but not 11)", () => {
expect(pluralizeRu(1, "актив", "актива", "активов")).toBe("актив");
expect(pluralizeRu(21, "актив", "актива", "активов")).toBe("актив");
expect(pluralizeRu(11, "актив", "актива", "активов")).not.toBe("актив");
});
it("picks the few form for 2-4 (and 22-24, but not 12-14)", () => {
expect(pluralizeRu(2, "актив", "актива", "активов")).toBe("актива");
expect(pluralizeRu(4, "актив", "актива", "активов")).toBe("актива");
expect(pluralizeRu(22, "актив", "актива", "активов")).toBe("актива");
expect(pluralizeRu(12, "актив", "актива", "активов")).toBe("активов");
});
it("picks the many form for 0, 5-20, and 11-14", () => {
expect(pluralizeRu(0, "актив", "актива", "активов")).toBe("активов");
expect(pluralizeRu(5, "актив", "актива", "активов")).toBe("активов");
expect(pluralizeRu(11, "актив", "актива", "активов")).toBe("активов");
expect(pluralizeRu(14, "актив", "актива", "активов")).toBe("активов");
});
});