Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/arrays.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { describe, expect, it } from "vitest";
import { chunk, dedupe, groupBy } from "./arrays";

describe("chunk", () => {
it("splits arrays into fixed-size groups", () => {
expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]);
});

it("returns an empty array for invalid chunk sizes", () => {
expect(chunk([1, 2, 3], 0)).toEqual([]);
expect(chunk([1, 2, 3], -1)).toEqual([]);
});
});

describe("dedupe", () => {
it("keeps first occurrences in insertion order", () => {
expect(dedupe(["a", "b", "a", "c", "b"])).toEqual(["a", "b", "c"]);
});
});

describe("groupBy", () => {
it("groups items by the selected key", () => {
const rows = [
{ kind: "fruit", name: "apple" },
{ kind: "veg", name: "carrot" },
{ kind: "fruit", name: "pear" },
];

expect(groupBy(rows, (row) => row.kind)).toEqual({
fruit: [rows[0], rows[2]],
veg: [rows[1]],
});
});
});
39 changes: 39 additions & 0 deletions src/dates.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { describe, expect, it } from "vitest";
import { addDays, daysBetween, isWeekend, startOfDayUTC } from "./dates";

describe("daysBetween", () => {
it("returns rounded absolute day difference", () => {
const a = new Date("2026-05-27T00:00:00Z");
const b = new Date("2026-05-29T13:00:00Z");

expect(daysBetween(a, b)).toBe(3);
expect(daysBetween(b, a)).toBe(3);
});
});

describe("isWeekend", () => {
it("detects Saturday and Sunday", () => {
expect(isWeekend(new Date("2026-05-30T12:00:00Z"))).toBe(true);
expect(isWeekend(new Date("2026-05-31T12:00:00Z"))).toBe(true);
});

it("returns false for weekdays", () => {
expect(isWeekend(new Date("2026-05-27T12:00:00Z"))).toBe(false);
});
});

describe("addDays", () => {
it("returns a new date offset by the requested day count", () => {
const original = new Date("2026-05-27T08:30:00Z");
const result = addDays(original, 5);

expect(result.toISOString()).toBe("2026-06-01T08:30:00.000Z");
expect(original.toISOString()).toBe("2026-05-27T08:30:00.000Z");
});
});

describe("startOfDayUTC", () => {
it("normalizes to midnight UTC", () => {
expect(startOfDayUTC(new Date("2026-05-27T23:59:30Z")).toISOString()).toBe("2026-05-27T00:00:00.000Z");
});
});
41 changes: 41 additions & 0 deletions src/money.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { describe, expect, it } from "vitest";
import { formatCents, netPayoutCents, platformFeeCents, splitEvenly } from "./money";

describe("formatCents", () => {
it("formats USD and EUR with currency symbols", () => {
expect(formatCents(1234)).toBe("$12.34");
expect(formatCents(1234, "EUR")).toBe("€12.34");
});

it("prefixes unknown currency codes", () => {
expect(formatCents(500, "GBP")).toBe("GBP 5.00");
});
});

describe("platformFeeCents", () => {
it("rounds basis point fees", () => {
expect(platformFeeCents(999, 250)).toBe(25);
});

it("returns zero for non-positive gross or bps", () => {
expect(platformFeeCents(0, 250)).toBe(0);
expect(platformFeeCents(1000, 0)).toBe(0);
});
});

describe("netPayoutCents", () => {
it("subtracts platform fee from the gross amount", () => {
expect(netPayoutCents(1000, 2000)).toBe(800);
});
});

describe("splitEvenly", () => {
it("splits remainder cents across the earliest recipients", () => {
expect(splitEvenly(10, 3)).toEqual([4, 3, 3]);
});

it("returns an empty list for non-positive recipient counts", () => {
expect(splitEvenly(10, 0)).toEqual([]);
expect(splitEvenly(10, -2)).toEqual([]);
});
});
38 changes: 37 additions & 1 deletion src/strings.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,43 @@
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 repeated dashes", () => {
expect(slugify(" Hello, Tiny---Utils! ")).toBe("hello-tiny-utils");
});
});

describe("truncate", () => {
it("returns an empty string for non-positive max length", () => {
expect(truncate("hello", 0)).toBe("");
expect(truncate("hello", -3)).toBe("");
});

it("keeps short input unchanged", () => {
expect(truncate("short", 10)).toBe("short");
});

it("uses the suffix within the max length", () => {
expect(truncate("abcdef", 5, "...")).toBe("ab...");
});
});

describe("titleCase", () => {
it("capitalizes each whitespace-separated word", () => {
expect(titleCase("hello WORLD tiny")).toBe("Hello World Tiny");
});

it("preserves empty segments when input has leading whitespace", () => {
expect(titleCase(" mixed")).toBe(" Mixed");
});
});

describe("escapeHtml", () => {
it("escapes common HTML-sensitive characters", () => {
expect(escapeHtml(`Tom & "Sue" <tag>'`)).toBe("Tom &amp; &quot;Sue&quot; &lt;tag&gt;&#39;");
});
});