feat(LOY-422): Upcoming Stays Redesign * feat(LOY-422): Upcoming Stays Redesign * feat(LOY-422): Carousel next/previous arrows * chore(LOY-422): add new material icon * refactor(LOY-422): restructure new and old upcoming stays * fix(LOY-422): handle less than 1 case * chore(LOY-422): remove uneeded id * chore(LOY-422): remove intl label for date edge case Approved-by: Matilda Landström
234 lines
7.7 KiB
TypeScript
234 lines
7.7 KiB
TypeScript
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<string, string | number | boolean | Date>
|
|
) => {
|
|
const messages: Record<string, string> = {
|
|
"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")
|
|
})
|
|
})
|
|
})
|