From 59dc8c54bb20240ca5a491f609dd55f11ab069b8 Mon Sep 17 00:00:00 2001 From: strongkeep-debug Date: Wed, 27 May 2026 00:16:30 -0700 Subject: [PATCH] test: lift utility coverage --- package.json | 2 +- src/arrays.test.ts | 40 ++++++++++++++++++++++++++++++++++++++++ src/dates.test.ts | 41 +++++++++++++++++++++++++++++++++++++++++ src/money.test.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ src/strings.test.ts | 35 ++++++++++++++++++++++++++++++++++- 5 files changed, 159 insertions(+), 2 deletions(-) create mode 100644 src/arrays.test.ts create mode 100644 src/dates.test.ts create mode 100644 src/money.test.ts diff --git a/package.json b/package.json index 39cc814..9924cb5 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "module", "scripts": { "test": "vitest run", - "test:coverage": "vitest run --coverage --coverage.reporter=text-summary --coverage.include='src/**/*.ts' --coverage.exclude='src/**/*.test.ts'" + "test:coverage": "vitest run --coverage --coverage.reporter=text-summary --coverage.include=src/**/*.ts --coverage.exclude=src/**/*.test.ts" }, "devDependencies": { "@vitest/coverage-v8": "^2.0.0", diff --git a/src/arrays.test.ts b/src/arrays.test.ts new file mode 100644 index 0000000..933f9af --- /dev/null +++ b/src/arrays.test.ts @@ -0,0 +1,40 @@ +import { describe, expect, it } from "vitest"; +import { chunk, dedupe, groupBy } from "./arrays"; + +describe("chunk", () => { + it("splits arrays into fixed-size groups and keeps a shorter tail", () => { + expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]); + }); + + it("returns an empty list for non-positive chunk sizes", () => { + expect(chunk([1, 2, 3], 0)).toEqual([]); + expect(chunk([1, 2, 3], -1)).toEqual([]); + }); +}); + +describe("dedupe", () => { + it("preserves first-seen order while removing duplicates", () => { + expect(dedupe(["a", "b", "a", "c", "b"])).toEqual(["a", "b", "c"]); + }); +}); + +describe("groupBy", () => { + it("groups items by string or numeric keys", () => { + const grouped = groupBy( + [ + { kind: "fruit", name: "apple" }, + { kind: "veg", name: "carrot" }, + { kind: "fruit", name: "pear" }, + ], + (item) => item.kind, + ); + + expect(grouped).toEqual({ + fruit: [ + { kind: "fruit", name: "apple" }, + { kind: "fruit", name: "pear" }, + ], + veg: [{ kind: "veg", name: "carrot" }], + }); + }); +}); diff --git a/src/dates.test.ts b/src/dates.test.ts new file mode 100644 index 0000000..977479c --- /dev/null +++ b/src/dates.test.ts @@ -0,0 +1,41 @@ +import { describe, expect, it } from "vitest"; +import { addDays, daysBetween, isWeekend, startOfDayUTC } from "./dates"; + +describe("daysBetween", () => { + it("returns an absolute rounded day count", () => { + const morning = new Date("2026-05-01T06:00:00Z"); + const nextEvening = new Date("2026-05-02T20:00:00Z"); + + expect(daysBetween(morning, nextEvening)).toBe(2); + expect(daysBetween(nextEvening, morning)).toBe(2); + }); +}); + +describe("isWeekend", () => { + it("identifies Saturdays and Sundays as weekends", () => { + expect(isWeekend(new Date("2026-05-23T12:00:00Z"))).toBe(true); + expect(isWeekend(new Date("2026-05-24T12:00:00Z"))).toBe(true); + }); + + it("treats weekdays as non-weekends", () => { + expect(isWeekend(new Date("2026-05-25T12:00:00Z"))).toBe(false); + }); +}); + +describe("addDays", () => { + it("returns a new date shifted by the requested number of days", () => { + const input = new Date("2026-05-01T10:30:00Z"); + const shifted = addDays(input, 5); + + expect(shifted.toISOString()).toBe("2026-05-06T10:30:00.000Z"); + expect(input.toISOString()).toBe("2026-05-01T10:30:00.000Z"); + }); +}); + +describe("startOfDayUTC", () => { + it("normalizes a date to midnight in UTC", () => { + expect(startOfDayUTC(new Date("2026-05-01T23:59:59Z")).toISOString()).toBe( + "2026-05-01T00:00:00.000Z", + ); + }); +}); diff --git a/src/money.test.ts b/src/money.test.ts new file mode 100644 index 0000000..d63754c --- /dev/null +++ b/src/money.test.ts @@ -0,0 +1,43 @@ +import { describe, expect, it } from "vitest"; +import { formatCents, netPayoutCents, platformFeeCents, splitEvenly } from "./money"; + +describe("formatCents", () => { + it("formats USD with a dollar prefix", () => { + expect(formatCents(12345)).toBe("$123.45"); + }); + + it("formats EUR with the library's euro prefix", () => { + expect(formatCents(12345, "EUR")).toBe("€123.45"); + }); + + it("falls back to an ISO-like currency prefix for other currencies", () => { + expect(formatCents(12345, "GBP")).toBe("GBP 123.45"); + }); +}); + +describe("platformFeeCents", () => { + it("rounds basis-point fees to whole cents", () => { + expect(platformFeeCents(999, 250)).toBe(25); + }); + + it("returns zero for non-positive gross amounts or fee rates", () => { + expect(platformFeeCents(0, 250)).toBe(0); + expect(platformFeeCents(1000, 0)).toBe(0); + }); +}); + +describe("netPayoutCents", () => { + it("subtracts the platform fee from the gross amount", () => { + expect(netPayoutCents(1000, 250)).toBe(975); + }); +}); + +describe("splitEvenly", () => { + it("splits cents evenly and distributes the remainder from the front", () => { + expect(splitEvenly(10, 3)).toEqual([4, 3, 3]); + }); + + it("returns no shares for non-positive recipient counts", () => { + expect(splitEvenly(10, 0)).toEqual([]); + }); +}); diff --git a/src/strings.test.ts b/src/strings.test.ts index daeb1cc..c2dc36b 100644 --- a/src/strings.test.ts +++ b/src/strings.test.ts @@ -1,7 +1,40 @@ import { describe, it, expect } from "vitest"; -import { slugify } from "./strings"; +import { escapeHtml, slugify, titleCase, truncate } from "./strings"; describe("slugify", () => { it("lowercases and dashifies basic input", () => { expect(slugify("Hello World")).toBe("hello-world"); }); + + it("removes punctuation and collapses duplicate dashes", () => { + expect(slugify(" Hello, --- Tiny Utils!! ")).toBe("hello-tiny-utils"); + }); +}); + +describe("truncate", () => { + it("keeps short strings unchanged", () => { + expect(truncate("short", 10)).toBe("short"); + }); + + it("truncates longer strings with the default suffix", () => { + expect(truncate("abcdef", 5)).toBe("abcd…"); + }); + + it("supports empty results and custom suffixes", () => { + expect(truncate("abcdef", 0)).toBe(""); + expect(truncate("abcdef", 4, "..")).toBe("ab.."); + }); +}); + +describe("titleCase", () => { + it("capitalizes words and lowercases the remainder", () => { + expect(titleCase("hELLO tiny UTILS")).toBe("Hello Tiny Utils"); + }); +}); + +describe("escapeHtml", () => { + it("escapes characters that are significant in HTML", () => { + expect(escapeHtml(`Tom & "Jerry" 's>`)).toBe( + "Tom & "Jerry" <cat>'s>", + ); + }); });