mirror of
https://github.com/Zproger/Aurum.git
synced 2026-10-13 02:56:39 +07:00
Vitest для чистой логики: lib/format.ts (заодно поймал баг в trimTrailingZeros на "-0.000") и расчёт разбивки по риску/монете/сети на вкладке Crypto — вынес их из компонентов в отдельные тестируемые функции. Тесты подключены в CI. Версия приложения обновлена до 1.1.6.
156 lines
5.5 KiB
TypeScript
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("активов");
|
|
});
|
|
});
|