Packages
noora
0.64.0
0.84.1
0.84.0
0.83.2
0.83.1
0.83.0
0.82.7
0.82.6
0.82.5
0.82.4
0.82.3
0.82.2
0.82.1
0.82.0
0.81.4
0.81.3
0.81.2
0.81.1
0.81.0
0.80.1
0.80.0
0.79.1
0.79.0
0.78.1
0.78.0
0.77.4
0.77.3
0.77.2
0.77.1
0.77.0
0.76.0
0.75.0
0.74.0
0.73.0
0.72.0
0.71.0
0.70.0
0.69.1
0.69.0
0.68.0
0.67.0
0.66.0
0.65.0
0.64.2
0.64.1
0.64.0
0.63.2
0.63.1
0.63.0
0.62.0
0.61.0
0.60.0
0.59.0
0.58.0
0.57.0
0.56.1
0.56.0
0.55.0
0.54.0
0.53.1
0.53.0
0.52.1
0.52.0
0.51.0
0.50.1
0.50.0
0.49.0
0.48.0
0.47.0
0.46.0
0.45.0
0.44.3
0.44.2
0.44.1
0.44.0
0.43.0
0.42.0
0.41.0
0.40.6
0.40.5
0.40.4
0.40.3
0.40.2
0.40.1
0.40.0
0.39.1
0.39.0
0.38.0
0.37.0
0.36.0
0.35.0
0.34.0
0.33.0
0.32.1
0.32.0
0.31.0
0.30.0
0.29.2
0.29.1
0.29.0
0.28.5
0.28.4
0.28.3
0.28.2
0.28.1
0.28.0
0.27.0
0.26.1
0.26.0
0.25.0
0.24.0
0.23.1
0.23.0
0.22.1
0.22.0
0.21.0
0.20.0
0.19.0
0.18.0
0.17.0
0.16.0
0.15.0
0.14.0
0.13.0
0.12.1
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.9.0
0.8.0
0.7.0
0.6.1
0.6.0
0.5.1
0.5.0
0.4.0
0.3.2
0.3.1
0.3.0
0.2.2
0.2.0
0.1.0
0.1.0-rc.2
0.1.0-rc.1
0.1.0-alpha.6
0.1.0-alpha.5
0.1.0-alpha.4
0.1.0-alpha.3
0.1.0-alpha.2
0.1.0-alpha.1
A component library for Phoenix LiveView applications
Current section
Files
Jump to
Current section
Files
js/DatePicker/dateUtils.test.js
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import {
adjustMonth,
compareMonths,
compareDates,
toISODateString,
calculateRangeFromDuration,
formatDateParts,
parseDateFromParts,
parseISODate,
initCalendarMonthsFromRange,
calculateWeeksForMonth,
getVisibleRangeForMonth,
} from "./dateUtils.js";
describe("adjustMonth", () => {
it("increments month within same year", () => {
expect(adjustMonth(2024, 5, 1)).toEqual({ year: 2024, month: 6 });
});
it("decrements month within same year", () => {
expect(adjustMonth(2024, 5, -1)).toEqual({ year: 2024, month: 4 });
});
it("wraps from December to January of next year", () => {
expect(adjustMonth(2024, 12, 1)).toEqual({ year: 2025, month: 1 });
});
it("wraps from January to December of previous year", () => {
expect(adjustMonth(2024, 1, -1)).toEqual({ year: 2023, month: 12 });
});
it("handles multi-month jumps forward", () => {
expect(adjustMonth(2024, 10, 5)).toEqual({ year: 2025, month: 3 });
});
it("handles multi-month jumps backward", () => {
expect(adjustMonth(2024, 3, -5)).toEqual({ year: 2023, month: 10 });
});
it("handles delta of zero", () => {
expect(adjustMonth(2024, 6, 0)).toEqual({ year: 2024, month: 6 });
});
it("handles jumping more than a year", () => {
expect(adjustMonth(2024, 6, 15)).toEqual({ year: 2025, month: 9 });
});
});
describe("compareMonths", () => {
it("returns 0 for equal months", () => {
expect(
compareMonths({ year: 2024, month: 6 }, { year: 2024, month: 6 }),
).toBe(0);
});
it("returns negative when a is before b (same year)", () => {
expect(
compareMonths({ year: 2024, month: 3 }, { year: 2024, month: 6 }),
).toBeLessThan(0);
});
it("returns positive when a is after b (same year)", () => {
expect(
compareMonths({ year: 2024, month: 9 }, { year: 2024, month: 6 }),
).toBeGreaterThan(0);
});
it("returns negative when a year is before b year", () => {
expect(
compareMonths({ year: 2023, month: 12 }, { year: 2024, month: 1 }),
).toBeLessThan(0);
});
it("returns positive when a year is after b year", () => {
expect(
compareMonths({ year: 2025, month: 1 }, { year: 2024, month: 12 }),
).toBeGreaterThan(0);
});
});
describe("compareDates", () => {
it("returns 0 for equal dates", () => {
expect(
compareDates(
{ year: 2024, month: 6, day: 15 },
{ year: 2024, month: 6, day: 15 },
),
).toBe(0);
});
it("returns negative when a is before b (different year)", () => {
expect(
compareDates(
{ year: 2023, month: 12, day: 31 },
{ year: 2024, month: 1, day: 1 },
),
).toBeLessThan(0);
});
it("returns positive when a is after b (different year)", () => {
expect(
compareDates(
{ year: 2024, month: 1, day: 1 },
{ year: 2023, month: 12, day: 31 },
),
).toBeGreaterThan(0);
});
it("returns negative when a is before b (same year, different month)", () => {
expect(
compareDates(
{ year: 2024, month: 3, day: 15 },
{ year: 2024, month: 6, day: 10 },
),
).toBeLessThan(0);
});
it("returns positive when a is after b (same year, different month)", () => {
expect(
compareDates(
{ year: 2024, month: 6, day: 10 },
{ year: 2024, month: 3, day: 15 },
),
).toBeGreaterThan(0);
});
it("returns negative when a is before b (same month, different day)", () => {
expect(
compareDates(
{ year: 2024, month: 6, day: 10 },
{ year: 2024, month: 6, day: 20 },
),
).toBeLessThan(0);
});
it("returns positive when a is after b (same month, different day)", () => {
expect(
compareDates(
{ year: 2024, month: 6, day: 20 },
{ year: 2024, month: 6, day: 10 },
),
).toBeGreaterThan(0);
});
});
describe("toISODateString", () => {
it("formats a date as YYYY-MM-DD", () => {
const date = new Date(2024, 5, 15); // June 15, 2024
expect(toISODateString(date)).toBe("2024-06-15");
});
it("pads single-digit months", () => {
const date = new Date(2024, 0, 15); // January 15, 2024
expect(toISODateString(date)).toBe("2024-01-15");
});
it("pads single-digit days", () => {
const date = new Date(2024, 5, 5); // June 5, 2024
expect(toISODateString(date)).toBe("2024-06-05");
});
it("handles end of year dates", () => {
const date = new Date(2024, 11, 31); // December 31, 2024
expect(toISODateString(date)).toBe("2024-12-31");
});
it("handles start of year dates", () => {
const date = new Date(2024, 0, 1); // January 1, 2024
expect(toISODateString(date)).toBe("2024-01-01");
});
});
describe("calculateRangeFromDuration", () => {
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date(2024, 5, 15, 12, 0, 0)); // June 15, 2024, noon
});
afterEach(() => {
vi.useRealTimers();
});
it("calculates range for hours", () => {
const { start, end } = calculateRangeFromDuration({
amount: 24,
unit: "hour",
});
expect(end.getTime()).toBe(new Date(2024, 5, 15, 12, 0, 0).getTime());
expect(start.getTime()).toBe(new Date(2024, 5, 14, 12, 0, 0).getTime());
});
it("calculates range for days", () => {
const { start, end } = calculateRangeFromDuration({
amount: 7,
unit: "day",
});
expect(end.getTime()).toBe(new Date(2024, 5, 15, 12, 0, 0).getTime());
expect(start.getTime()).toBe(new Date(2024, 5, 8, 12, 0, 0).getTime());
});
it("calculates range for weeks", () => {
const { start, end } = calculateRangeFromDuration({
amount: 2,
unit: "week",
});
expect(end.getTime()).toBe(new Date(2024, 5, 15, 12, 0, 0).getTime());
expect(start.getTime()).toBe(new Date(2024, 5, 1, 12, 0, 0).getTime());
});
it("calculates range for months", () => {
const { start, end } = calculateRangeFromDuration({
amount: 3,
unit: "month",
});
expect(end.getTime()).toBe(new Date(2024, 5, 15, 12, 0, 0).getTime());
expect(start.getMonth()).toBe(2); // March
});
it("calculates range for years", () => {
const { start, end } = calculateRangeFromDuration({
amount: 1,
unit: "year",
});
expect(end.getTime()).toBe(new Date(2024, 5, 15, 12, 0, 0).getTime());
expect(start.getFullYear()).toBe(2023);
});
it("defaults to days for unknown unit", () => {
const { start, end } = calculateRangeFromDuration({
amount: 5,
unit: "unknown",
});
expect(end.getTime()).toBe(new Date(2024, 5, 15, 12, 0, 0).getTime());
expect(start.getTime()).toBe(new Date(2024, 5, 10, 12, 0, 0).getTime());
});
});
describe("formatDateParts", () => {
it("returns empty strings for null date", () => {
expect(formatDateParts(null)).toEqual({ day: "", month: "", year: "" });
});
it("formats date into padded parts", () => {
const date = new Date(2024, 5, 15); // June 15, 2024
expect(formatDateParts(date)).toEqual({
day: "15",
month: "06",
year: "2024",
});
});
it("pads single-digit days", () => {
const date = new Date(2024, 5, 5); // June 5, 2024
expect(formatDateParts(date)).toEqual({
day: "05",
month: "06",
year: "2024",
});
});
it("pads single-digit months", () => {
const date = new Date(2024, 0, 15); // January 15, 2024
expect(formatDateParts(date)).toEqual({
day: "15",
month: "01",
year: "2024",
});
});
it("handles date strings", () => {
expect(formatDateParts("2024-06-15")).toEqual({
day: "15",
month: "06",
year: "2024",
});
});
});
describe("parseDateFromParts", () => {
it("parses valid date parts", () => {
const date = parseDateFromParts("15", "06", "2024");
expect(date).toBeInstanceOf(Date);
expect(date.getDate()).toBe(15);
expect(date.getMonth()).toBe(5); // June (0-indexed)
expect(date.getFullYear()).toBe(2024);
});
it("returns null for non-numeric day", () => {
expect(parseDateFromParts("abc", "06", "2024")).toBeNull();
});
it("returns null for non-numeric month", () => {
expect(parseDateFromParts("15", "abc", "2024")).toBeNull();
});
it("returns null for non-numeric year", () => {
expect(parseDateFromParts("15", "06", "abc")).toBeNull();
});
it("returns null for day out of range (0)", () => {
expect(parseDateFromParts("0", "06", "2024")).toBeNull();
});
it("returns null for day out of range (32)", () => {
expect(parseDateFromParts("32", "06", "2024")).toBeNull();
});
it("returns null for month out of range (0)", () => {
expect(parseDateFromParts("15", "0", "2024")).toBeNull();
});
it("returns null for month out of range (13)", () => {
expect(parseDateFromParts("15", "13", "2024")).toBeNull();
});
it("returns null for year below 1000", () => {
expect(parseDateFromParts("15", "06", "999")).toBeNull();
});
it("returns null for invalid date (Feb 30)", () => {
expect(parseDateFromParts("30", "02", "2024")).toBeNull();
});
it("returns null for invalid date (Feb 29 in non-leap year)", () => {
expect(parseDateFromParts("29", "02", "2023")).toBeNull();
});
it("accepts Feb 29 in leap year", () => {
const date = parseDateFromParts("29", "02", "2024");
expect(date).toBeInstanceOf(Date);
expect(date.getDate()).toBe(29);
expect(date.getMonth()).toBe(1); // February
});
it("handles padded inputs", () => {
const date = parseDateFromParts("05", "06", "2024");
expect(date.getDate()).toBe(5);
});
});
describe("parseISODate", () => {
it("parses ISO date string", () => {
expect(parseISODate("2024-06-15")).toEqual({
year: 2024,
month: 6,
day: 15,
});
});
it("parses ISO datetime string (strips time)", () => {
expect(parseISODate("2024-06-15T10:30:00")).toEqual({
year: 2024,
month: 6,
day: 15,
});
});
it("returns null for null input", () => {
expect(parseISODate(null)).toBeNull();
});
it("returns null for empty string", () => {
expect(parseISODate("")).toBeNull();
});
it("returns null for invalid format", () => {
expect(parseISODate("invalid")).toBeNull();
});
it("returns null for partial date", () => {
expect(parseISODate("2024-06")).toBeNull();
});
it("returns null for non-padded date format", () => {
expect(parseISODate("2024-1-5")).toBeNull();
});
});
describe("initCalendarMonthsFromRange", () => {
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date(2024, 5, 15)); // June 15, 2024
});
afterEach(() => {
vi.useRealTimers();
});
it("returns adjacent months when no dates provided", () => {
const { startCalendarMonth, endCalendarMonth } =
initCalendarMonthsFromRange(null, null);
expect(startCalendarMonth).toEqual({ year: 2024, month: 5 }); // May
expect(endCalendarMonth).toEqual({ year: 2024, month: 6 }); // June
});
it("returns start and end months when dates are in different months", () => {
const { startCalendarMonth, endCalendarMonth } =
initCalendarMonthsFromRange(
{ year: 2024, month: 3, day: 10 },
{ year: 2024, month: 8, day: 20 },
);
expect(startCalendarMonth).toEqual({ year: 2024, month: 3 });
expect(endCalendarMonth).toEqual({ year: 2024, month: 8 });
});
it("adjusts when dates are in the same month", () => {
const { startCalendarMonth, endCalendarMonth } =
initCalendarMonthsFromRange(
{ year: 2024, month: 6, day: 10 },
{ year: 2024, month: 6, day: 20 },
);
expect(startCalendarMonth).toEqual({ year: 2024, month: 5 }); // Previous month
expect(endCalendarMonth).toEqual({ year: 2024, month: 6 });
});
it("handles same month in January (wraps to previous year)", () => {
const { startCalendarMonth, endCalendarMonth } =
initCalendarMonthsFromRange(
{ year: 2024, month: 1, day: 10 },
{ year: 2024, month: 1, day: 20 },
);
expect(startCalendarMonth).toEqual({ year: 2023, month: 12 }); // December of previous year
expect(endCalendarMonth).toEqual({ year: 2024, month: 1 });
});
it("handles date range spanning years", () => {
const { startCalendarMonth, endCalendarMonth } =
initCalendarMonthsFromRange(
{ year: 2023, month: 11, day: 15 },
{ year: 2024, month: 2, day: 10 },
);
expect(startCalendarMonth).toEqual({ year: 2023, month: 11 });
expect(endCalendarMonth).toEqual({ year: 2024, month: 2 });
});
});
describe("calculateWeeksForMonth", () => {
it("returns 6 weeks by default", () => {
const weeks = calculateWeeksForMonth(2024, 6, 0);
expect(weeks).toHaveLength(6);
});
it("returns requested number of weeks", () => {
const weeks = calculateWeeksForMonth(2024, 6, 0, 4);
expect(weeks).toHaveLength(4);
});
it("returns 7 days per week", () => {
const weeks = calculateWeeksForMonth(2024, 6, 0);
weeks.forEach((week) => {
expect(week).toHaveLength(7);
});
});
it("has null for days outside the month", () => {
const weeks = calculateWeeksForMonth(2024, 6, 0); // June 2024 starts on Saturday
// First week should have nulls for Sun-Fri
expect(weeks[0][0]).toBeNull(); // Sunday
expect(weeks[0][5]).toBeNull(); // Friday
expect(weeks[0][6]).not.toBeNull(); // Saturday (June 1)
});
it("creates DateValue objects for days in month", () => {
const weeks = calculateWeeksForMonth(2024, 6, 0);
// Find a day that's definitely in the month
const juneFirst = weeks[0][6]; // June 1, 2024 is Saturday
expect(juneFirst).not.toBeNull();
expect(juneFirst.year).toBe(2024);
expect(juneFirst.month).toBe(6);
expect(juneFirst.day).toBe(1);
});
it("respects startOfWeek parameter (Monday start)", () => {
// June 2024: 1st is Saturday
// With Monday start (1), Saturday is index 5
const weeks = calculateWeeksForMonth(2024, 6, 1);
// First week with Monday start should have June 1 at index 5 (Saturday position)
expect(weeks[0][5]).not.toBeNull();
expect(weeks[0][5].day).toBe(1);
});
it("contains all days of the month", () => {
const weeks = calculateWeeksForMonth(2024, 6, 0); // June has 30 days
const allDays = weeks.flat().filter((d) => d !== null);
expect(allDays).toHaveLength(30);
const dayNumbers = allDays.map((d) => d.day).sort((a, b) => a - b);
expect(dayNumbers[0]).toBe(1);
expect(dayNumbers[dayNumbers.length - 1]).toBe(30);
});
});
describe("getVisibleRangeForMonth", () => {
it("returns start and end DateValue objects for the month", () => {
const { start, end } = getVisibleRangeForMonth(2024, 6);
expect(start.year).toBe(2024);
expect(start.month).toBe(6);
expect(start.day).toBe(1);
expect(end.year).toBe(2024);
expect(end.month).toBe(6);
expect(end.day).toBe(30); // June has 30 days
});
it("calculates correct last day for months with 31 days", () => {
const { end } = getVisibleRangeForMonth(2024, 7); // July
expect(end.day).toBe(31);
});
it("calculates correct last day for February in leap year", () => {
const { end } = getVisibleRangeForMonth(2024, 2);
expect(end.day).toBe(29);
});
it("calculates correct last day for February in non-leap year", () => {
const { end } = getVisibleRangeForMonth(2023, 2);
expect(end.day).toBe(28);
});
});