Add packages/common for shared utilities

Create new @reviq/common package with environment-agnostic utilities:
- Date formatting: formatDate, formatDateTime, formatLongDate,
  formatRelativeDate, formatRelativeTime
- User utilities: getUserInitials, formatRole

Consolidate date formatting from publisher-dashboard into shared package.
All utilities include comprehensive test coverage with bun:test.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
igm
2026-01-11 12:34:10 +08:00
parent 7358129802
commit b1d07626f3
25 changed files with 639 additions and 300 deletions

View File

@@ -0,0 +1,84 @@
import { describe, expect, test } from "bun:test";
import { formatRole, getUserInitials } from "./user.js";
describe("getUserInitials", () => {
test("returns '??' for null", () => {
expect(getUserInitials(null)).toBe("??");
});
test("returns '??' for undefined", () => {
expect(getUserInitials(undefined)).toBe("??");
});
test("returns initials from display name with two words", () => {
expect(
getUserInitials({ displayName: "John Doe", email: "john@example.com" }),
).toBe("JD");
});
test("returns initials from display name with multiple words", () => {
expect(
getUserInitials({
displayName: "John Michael Doe",
email: "john@example.com",
}),
).toBe("JD");
});
test("returns first two characters for single word display name", () => {
expect(
getUserInitials({ displayName: "John", email: "john@example.com" }),
).toBe("JO");
});
test("returns uppercase initials", () => {
expect(
getUserInitials({
displayName: "john doe",
email: "john@example.com",
}),
).toBe("JD");
});
test("falls back to email when no display name", () => {
expect(getUserInitials({ email: "john@example.com" })).toBe("JO");
});
test("handles null display name", () => {
expect(
getUserInitials({ displayName: null, email: "alice@example.com" }),
).toBe("AL");
});
test("handles empty display name", () => {
expect(
getUserInitials({ displayName: "", email: "bob@example.com" }),
).toBe("BO");
});
});
describe("formatRole", () => {
test("capitalizes 'admin'", () => {
expect(formatRole("admin")).toBe("Admin");
});
test("capitalizes 'member'", () => {
expect(formatRole("member")).toBe("Member");
});
test("capitalizes 'owner'", () => {
expect(formatRole("owner")).toBe("Owner");
});
test("handles already capitalized roles", () => {
expect(formatRole("Admin")).toBe("Admin");
});
test("handles single character", () => {
expect(formatRole("a")).toBe("A");
});
test("handles empty string", () => {
expect(formatRole("")).toBe("");
});
});