Files
calendar/packages/lib/dateTimeFormatter.test.ts
Pedro CastroandGitHub 1e67f98970 fix: add Icelandic, Lithuanian, and Norwegian locale support for Booker (#25519)
- Create unified date/time formatter with Intl-first approach and dayjs fallback
- Add support for is/lt/nb locales in Booker components
- Unify date formatting across Booker (Header, dates, weekdays)
- Update translation pipeline to include is, lt, nb
- Add tests for new locales and formatter
- Add timezone support in dayjs fallback
- Add formatter caching with circuit breaker
- Use unified formatDateTime in Header.tsx
2026-01-13 08:47:39 -03:00

342 lines
9.9 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, expect, it } from "vitest";
import "@calcom/dayjs/locales";
import { formatDateTime, formatDateTimeRange, formatWeekday, getWeekdayNames } from "./dateTimeFormatter";
describe("dateTimeFormatter", () => {
describe("formatDateTime", () => {
describe("supported locales (Intl path)", () => {
it("formats full date style in English", () => {
const date = new Date("2025-12-01T10:30:00Z");
const result = formatDateTime(date, {
locale: "en-US",
timeZone: "UTC",
dateStyle: "full",
});
// Validate actual content
expect(result).toContain("Monday");
expect(result).toContain("December");
expect(result).toContain("1");
expect(result).toContain("2025");
});
it("formats time with 12h format", () => {
const date = new Date("2025-12-01T14:30:00Z");
const result = formatDateTime(date, {
locale: "en-US",
timeZone: "UTC",
timeStyle: "short",
hour12: true,
});
expect(result).toMatch(/2:30\s*PM/);
});
it("formats month only", () => {
const date = new Date("2025-12-01T10:30:00Z");
const result = formatDateTime(date, {
locale: "en-US",
month: "long",
});
expect(result).toBe("December");
});
});
describe("unsupported locales (dayjs fallback)", () => {
it("formats Icelandic date correctly", () => {
const date = new Date("2025-12-01T10:30:00Z");
const result = formatDateTime(date, {
locale: "is",
dateStyle: "long",
});
// Validate Icelandic content
expect(result).toMatch(/desember/i); // December in Icelandic
expect(result).toContain("1");
expect(result).toContain("2025");
});
it("formats Lithuanian date correctly", () => {
const date = new Date("2025-12-01T10:30:00Z");
const result = formatDateTime(date, {
locale: "lt",
dateStyle: "long",
});
// Validate Lithuanian content
expect(result).toMatch(/gruod/i); // December in Lithuanian
expect(result).toContain("1");
expect(result).toContain("2025");
});
it("formats Norwegian Bokmål date correctly", () => {
const date = new Date("2025-12-01T10:30:00Z");
const result = formatDateTime(date, {
locale: "nb",
dateStyle: "long",
});
// Validate Norwegian content
expect(result).toMatch(/desember/i); // December in Norwegian
expect(result).toContain("1");
expect(result).toContain("2025");
});
it("formats month-only in Icelandic", () => {
const date = new Date("2025-12-01T10:30:00Z");
const result = formatDateTime(date, {
locale: "is",
month: "long",
});
expect(result).toMatch(/desember/i);
});
it("formats narrow month (first character)", () => {
const date = new Date("2025-12-01T10:30:00Z");
const result = formatDateTime(date, {
locale: "is",
month: "narrow",
});
expect(result).toHaveLength(1);
});
it("formats time with 12h format in Icelandic", () => {
const date = new Date("2025-12-01T14:30:00Z");
const result = formatDateTime(date, {
locale: "is",
timeStyle: "short",
hour12: true,
});
expect(result).toMatch(/2:30/);
expect(result).toMatch(/(PM|e\.h\.)/i); // English PM or Icelandic "eftir hádegi"
});
it("formats combined date and time", () => {
const date = new Date("2025-12-01T14:30:00Z");
const result = formatDateTime(date, {
locale: "is",
dateStyle: "medium",
timeStyle: "short",
hour12: false,
});
expect(result).toContain("1");
expect(result).toMatch(/14:30/);
});
});
});
describe("formatDateTimeRange", () => {
describe("supported locales", () => {
it("formats time range on same day", () => {
const startDate = new Date("2025-12-01T10:30:00Z");
const endDate = new Date("2025-12-01T11:30:00Z");
const result = formatDateTimeRange(startDate, endDate, {
locale: "en-US",
timeZone: "UTC",
timeStyle: "short",
});
expect(result).toMatch(/10:30/);
expect(result).toMatch(/11:30/);
});
it("formats date range across different days", () => {
const startDate = new Date("2025-12-01T10:30:00Z");
const endDate = new Date("2025-12-02T11:30:00Z");
const result = formatDateTimeRange(startDate, endDate, {
locale: "en-US",
timeZone: "UTC",
dateStyle: "short",
});
expect(result).toContain("12/1");
expect(result).toContain("12/2");
});
});
describe("unsupported locales (dayjs fallback)", () => {
it("formats time range in Icelandic", () => {
const startDate = new Date("2025-12-01T10:30:00Z");
const endDate = new Date("2025-12-01T11:30:00Z");
const result = formatDateTimeRange(startDate, endDate, {
locale: "is",
timeZone: "UTC",
timeStyle: "short",
hour12: false,
});
expect(result).toContain("10:30");
expect(result).toContain("11:30");
expect(result).toContain(""); // En dash separator
});
it("formats date range in Lithuanian", () => {
const startDate = new Date("2025-12-01T10:30:00Z");
const endDate = new Date("2025-12-02T10:30:00Z");
const result = formatDateTimeRange(startDate, endDate, {
locale: "lt",
dateStyle: "medium",
});
expect(result).toContain("");
expect(result).toContain("1");
expect(result).toContain("2");
});
});
});
describe("formatWeekday", () => {
describe("supported locales", () => {
it("formats long weekday names in English", () => {
expect(formatWeekday("en-US", 0, "long")).toBe("Sunday");
expect(formatWeekday("en-US", 1, "long")).toBe("Monday");
expect(formatWeekday("en-US", 6, "long")).toBe("Saturday");
});
it("formats short weekday names in English", () => {
expect(formatWeekday("en-US", 0, "short")).toBe("Sun");
expect(formatWeekday("en-US", 1, "short")).toBe("Mon");
});
});
describe("unsupported locales (dayjs fallback)", () => {
it("formats long weekday names in Icelandic", () => {
const monday = formatWeekday("is", 1, "long");
expect(monday).toMatch(/mánudagur/i);
});
it("formats short weekday names in Icelandic", () => {
const monday = formatWeekday("is", 1, "short");
expect(monday).toMatch(/mán/i);
});
it("formats weekdays in Lithuanian", () => {
const monday = formatWeekday("lt", 1, "long");
expect(monday).toMatch(/pirmadienis/i); // Monday in Lithuanian
});
it("formats weekdays in Norwegian Bokmål", () => {
const monday = formatWeekday("nb", 1, "long");
expect(monday).toMatch(/mandag/i); // Monday in Norwegian
});
});
});
describe("getWeekdayNames", () => {
it("returns 7 weekday names starting Sunday by default", () => {
const result = getWeekdayNames("en-US");
expect(result).toHaveLength(7);
expect(result[0]).toBe("Sunday");
expect(result[1]).toBe("Monday");
expect(result[6]).toBe("Saturday");
});
it("supports custom week start (Monday)", () => {
const result = getWeekdayNames("en-US", 1);
expect(result).toHaveLength(7);
expect(result[0]).toBe("Monday");
expect(result[6]).toBe("Sunday");
});
it("supports short format", () => {
const result = getWeekdayNames("en-US", 0, "short");
expect(result).toHaveLength(7);
expect(result[0]).toBe("Sun");
expect(result[1]).toBe("Mon");
});
it("returns Icelandic weekday names", () => {
const result = getWeekdayNames("is", 0, "long");
expect(result).toHaveLength(7);
// Validate at least one Icelandic weekday
expect(result.some((day) => day.match(/dagur$/i))).toBe(true);
});
});
describe("timezone handling", () => {
it("formats in UTC correctly for both Intl and dayjs paths", () => {
const date = new Date("2025-12-01T10:30:00Z");
// Intl path (supported locale)
const intlResult = formatDateTime(date, {
locale: "en",
timeZone: "UTC",
timeStyle: "short",
hour12: false,
});
expect(intlResult).toContain("10:30");
// Dayjs path (unsupported locale)
const dayjsResult = formatDateTime(date, {
locale: "is",
timeZone: "UTC",
timeStyle: "short",
hour12: false,
});
expect(dayjsResult).toContain("10:30");
});
it("applies Tokyo timezone offset correctly", () => {
const date = new Date("2025-12-01T10:30:00Z");
// Intl path
const intlResult = formatDateTime(date, {
locale: "en",
timeZone: "Asia/Tokyo",
timeStyle: "short",
hour12: false,
});
expect(intlResult).toContain("19:30"); // UTC+9
// Test timezone respect in dayjs fallback
const dayjsResult = formatDateTime(date, {
locale: "is",
timeZone: "Asia/Tokyo",
timeStyle: "short",
hour12: false,
});
expect(dayjsResult).toContain("19:30"); // UTC+9
});
it("handles day boundary crossing with timezone offset", () => {
const date = new Date("2025-12-01T20:30:00Z");
const result = formatDateTime(date, {
locale: "is",
timeZone: "Asia/Tokyo",
dateStyle: "short",
timeStyle: "short",
hour12: false,
});
// 20:30 UTC + 9h = 05:30 next day
expect(result).toContain("05:30");
expect(result).toContain("2"); // December 2nd
});
});
});