import { describe, expect, it } from "vitest" import { Lang } from "@scandic-hotels/common/constants/language" import { dt } from "@scandic-hotels/common/dt" import { getDaysUntilText } from "./getDaysUntilText" import type { IntlShape, MessageDescriptor } from "react-intl" const mockIntl = { formatMessage: ( descriptor: MessageDescriptor, values?: Record ) => { const messages: Record = { "nextStay.today": "Today", "nextStay.tomorrow": "Tomorrow", "nextStay.inXDays": `In {days} days`, "nextStay.inXMonths": `In {months} month{months, plural, =1 {} other {s}}`, } let message: string = messages[descriptor.id as string] || (typeof descriptor.defaultMessage === "string" ? descriptor.defaultMessage : "") || "" if (values) { Object.entries(values).forEach(([key, value]) => { message = message.replace(`{${key}}`, String(value)) }) if (values.months === 1) { message = message.replace("{months, plural, =1 {} other {s}}", "") } else { message = message.replace("{months, plural, =1 {} other {s}}", "s") } } return message }, } as IntlShape describe("getDaysUntilText", () => { const lang = Lang.en describe("past dates", () => { it("should return formatted date for past check-in dates", () => { const yesterday = dt().subtract(1, "day").format("YYYY-MM-DD") const result = getDaysUntilText(yesterday, lang, mockIntl) expect(result).toContain(dt(yesterday).format("D MMM YYYY")) }) it("should handle dates from several days ago", () => { const pastDate = dt().subtract(10, "days").format("YYYY-MM-DD") const result = getDaysUntilText(pastDate, lang, mockIntl) expect(result).toContain(dt(pastDate).format("D MMM YYYY")) }) it("should handle dates from months ago", () => { const pastDate = dt().subtract(2, "months").format("YYYY-MM-DD") const result = getDaysUntilText(pastDate, lang, mockIntl) expect(result).toContain(dt(pastDate).format("D MMM YYYY")) }) }) describe("same day check-in", () => { it("should return 'Today' for today's check-in", () => { const today = dt().format("YYYY-MM-DD") const result = getDaysUntilText(today, lang, mockIntl) expect(result).toBe("Today") }) it("should return 'Today' regardless of time of day", () => { // Testing with different times but same date const todayMorning = dt().hour(8).format("YYYY-MM-DD") const todayEvening = dt().hour(20).format("YYYY-MM-DD") expect(getDaysUntilText(todayMorning, lang, mockIntl)).toBe("Today") expect(getDaysUntilText(todayEvening, lang, mockIntl)).toBe("Today") }) }) describe("tomorrow check-in", () => { it("should return 'Tomorrow' for next day check-in", () => { const tomorrow = dt().add(1, "day").format("YYYY-MM-DD") const result = getDaysUntilText(tomorrow, lang, mockIntl) expect(result).toBe("Tomorrow") }) }) describe("days until check-in (2-30 days)", () => { it("should return 'In X days' for 2 days", () => { const futureDate = dt().add(2, "days").format("YYYY-MM-DD") const result = getDaysUntilText(futureDate, lang, mockIntl) expect(result).toBe("In 2 days") }) it("should return 'In X days' for 15 days", () => { const futureDate = dt().add(15, "days").format("YYYY-MM-DD") const result = getDaysUntilText(futureDate, lang, mockIntl) expect(result).toBe("In 15 days") }) it("should return 'In X days' for exactly 30 days (boundary)", () => { const futureDate = dt().add(30, "days").format("YYYY-MM-DD") const result = getDaysUntilText(futureDate, lang, mockIntl) expect(result).toBe("In 30 days") }) it("should handle the full range from 2 to 30 days", () => { for (let days = 2; days <= 30; days++) { const futureDate = dt().add(days, "days").format("YYYY-MM-DD") const result = getDaysUntilText(futureDate, lang, mockIntl) expect(result).toBe(`In ${days} days`) } }) }) describe("months until check-in (beyond 30 days)", () => { it("should return 'In 1 month' for 31 days", () => { const futureDate = dt().add(31, "days").format("YYYY-MM-DD") const result = getDaysUntilText(futureDate, lang, mockIntl) expect(result).toBe("In 1 month") }) it("should return 'In 2 months' for dates 2 months away", () => { const futureDate = dt().add(2, "months").format("YYYY-MM-DD") const result = getDaysUntilText(futureDate, lang, mockIntl) expect(result).toBe("In 2 months") }) it("should use proper month calculation for 3 months", () => { const futureDate = dt().add(3, "months").format("YYYY-MM-DD") const result = getDaysUntilText(futureDate, lang, mockIntl) expect(result).toBe("In 3 months") }) it("should handle dates far in the future (6 months)", () => { const futureDate = dt().add(6, "months").format("YYYY-MM-DD") const result = getDaysUntilText(futureDate, lang, mockIntl) expect(result).toBe("In 6 months") }) it("should handle dates far in the future (1 year)", () => { const futureDate = dt().add(1, "year").format("YYYY-MM-DD") const result = getDaysUntilText(futureDate, lang, mockIntl) expect(result).toBe("In 12 months") }) }) describe("edge cases", () => { it("should handle dates with different time components consistently", () => { // Dates with times should be normalized to start of day const dateWithTime1 = dt() .add(5, "days") .hour(3) .minute(30) .format("YYYY-MM-DD HH:mm") const dateWithTime2 = dt() .add(5, "days") .hour(22) .minute(45) .format("YYYY-MM-DD HH:mm") const result1 = getDaysUntilText(dateWithTime1, lang, mockIntl) const result2 = getDaysUntilText(dateWithTime2, lang, mockIntl) expect(result1).toBe("In 5 days") expect(result2).toBe("In 5 days") }) it("should respect locale parameter", () => { const futureDate = dt().add(5, "days").format("YYYY-MM-DD") // Test with different locales const resultEN = getDaysUntilText(futureDate, Lang.en, mockIntl) const resultSV = getDaysUntilText(futureDate, Lang.sv, mockIntl) // Both should work without errors expect(resultEN).toBe("In 5 days") expect(resultSV).toBe("In 5 days") }) it("should handle ISO date strings with timezone", () => { const isoDate = dt().add(7, "days").toISOString() const result = getDaysUntilText(isoDate, lang, mockIntl) expect(result).toBe("In 7 days") }) }) describe("boundary transitions", () => { it("should transition correctly from days to months at 31 days", () => { const date30 = dt().add(30, "days").format("YYYY-MM-DD") const date31 = dt().add(31, "days").format("YYYY-MM-DD") expect(getDaysUntilText(date30, lang, mockIntl)).toBe("In 30 days") expect(getDaysUntilText(date31, lang, mockIntl)).toBe("In 1 month") }) it("should transition correctly from tomorrow to 2 days", () => { const date1 = dt().add(1, "day").format("YYYY-MM-DD") const date2 = dt().add(2, "days").format("YYYY-MM-DD") expect(getDaysUntilText(date1, lang, mockIntl)).toBe("Tomorrow") expect(getDaysUntilText(date2, lang, mockIntl)).toBe("In 2 days") }) it("should transition correctly from today to tomorrow", () => { const date0 = dt().format("YYYY-MM-DD") const date1 = dt().add(1, "day").format("YYYY-MM-DD") expect(getDaysUntilText(date0, lang, mockIntl)).toBe("Today") expect(getDaysUntilText(date1, lang, mockIntl)).toBe("Tomorrow") }) }) })