Merged in feat/SW-1077-enter-details-edit-room (pull request #1360)
Feat/SW-1077 enter details edit room * feat(SW-1077): persist state when changing rooms * fix: issue with step state when closing accordion and transition to correct room when modifying step Approved-by: Pontus Dreij
This commit is contained in:
@@ -22,7 +22,7 @@ export const booking: SelectRateSearchParams = {
|
|||||||
rooms: [
|
rooms: [
|
||||||
{
|
{
|
||||||
adults: 2,
|
adults: 2,
|
||||||
roomTypeCode: "",
|
roomTypeCode: "SKS",
|
||||||
rateCode: "",
|
rateCode: "",
|
||||||
counterRateCode: "",
|
counterRateCode: "",
|
||||||
childrenInRoom: [{ bed: ChildBedMapEnum.IN_EXTRA_BED, age: 5 }],
|
childrenInRoom: [{ bed: ChildBedMapEnum.IN_EXTRA_BED, age: 5 }],
|
||||||
@@ -30,7 +30,7 @@ export const booking: SelectRateSearchParams = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
adults: 2,
|
adults: 2,
|
||||||
roomTypeCode: "",
|
roomTypeCode: "SKS",
|
||||||
rateCode: "",
|
rateCode: "",
|
||||||
counterRateCode: "",
|
counterRateCode: "",
|
||||||
childrenInRoom: [{ bed: ChildBedMapEnum.IN_EXTRA_BED, age: 5 }],
|
childrenInRoom: [{ bed: ChildBedMapEnum.IN_EXTRA_BED, age: 5 }],
|
||||||
@@ -128,6 +128,16 @@ export const bedType: { [x: string]: BedTypeSelection } = {
|
|||||||
},
|
},
|
||||||
extraBed: undefined,
|
extraBed: undefined,
|
||||||
},
|
},
|
||||||
|
single: {
|
||||||
|
type: BedTypeEnum.Single,
|
||||||
|
description: "Single bed",
|
||||||
|
size: {
|
||||||
|
max: 140,
|
||||||
|
min: 100,
|
||||||
|
},
|
||||||
|
value: "CSR",
|
||||||
|
extraBed: undefined,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const guestDetailsNonMember: DetailsSchema = {
|
export const guestDetailsNonMember: DetailsSchema = {
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export default async function DetailsPage({
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
const showBreakfastStep = Boolean(
|
const showBreakfastStep = Boolean(
|
||||||
breakfastPackages?.length && !roomsData[0].breakfastIncluded
|
breakfastPackages?.length && !roomsData[0]?.breakfastIncluded
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -244,11 +244,11 @@ export default async function DetailsPage({
|
|||||||
<aside className={styles.summary}>
|
<aside className={styles.summary}>
|
||||||
<MobileSummary
|
<MobileSummary
|
||||||
isMember={!!user}
|
isMember={!!user}
|
||||||
breakfastIncluded={roomsData[0].breakfastIncluded ?? false}
|
breakfastIncluded={roomsData[0]?.breakfastIncluded ?? false}
|
||||||
/>
|
/>
|
||||||
<DesktopSummary
|
<DesktopSummary
|
||||||
isMember={!!user}
|
isMember={!!user}
|
||||||
breakfastIncluded={roomsData[0].breakfastIncluded ?? false}
|
breakfastIncluded={roomsData[0]?.breakfastIncluded ?? false}
|
||||||
/>
|
/>
|
||||||
</aside>
|
</aside>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import LoadingSpinner from "@/components/LoadingSpinner"
|
|||||||
import { trackPaymentEvent } from "@/utils/tracking"
|
import { trackPaymentEvent } from "@/utils/tracking"
|
||||||
import { convertObjToSearchParams } from "@/utils/url"
|
import { convertObjToSearchParams } from "@/utils/url"
|
||||||
|
|
||||||
// import type { PersistedState } from "@/types/stores/enter-details"
|
import type { PersistedState } from "@/types/stores/enter-details"
|
||||||
|
|
||||||
export default function PaymentCallback({
|
export default function PaymentCallback({
|
||||||
returnUrl,
|
returnUrl,
|
||||||
@@ -28,7 +28,7 @@ export default function PaymentCallback({
|
|||||||
const bookingData = window.sessionStorage.getItem(detailsStorageName)
|
const bookingData = window.sessionStorage.getItem(detailsStorageName)
|
||||||
|
|
||||||
if (bookingData) {
|
if (bookingData) {
|
||||||
const detailsStorage: any = JSON.parse(bookingData) // TODO: fix type here
|
const detailsStorage: PersistedState = JSON.parse(bookingData)
|
||||||
const searchParams = convertObjToSearchParams(
|
const searchParams = convertObjToSearchParams(
|
||||||
detailsStorage.booking,
|
detailsStorage.booking,
|
||||||
searchObject
|
searchObject
|
||||||
|
|||||||
@@ -78,16 +78,14 @@ export default function SectionAccordion({
|
|||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
setIsOpen(false)
|
setIsOpen(false)
|
||||||
const isLastStep = step === StepEnum.details
|
|
||||||
const hasNextRoom = roomIndex + 1 <= roomStatuses.length
|
|
||||||
|
|
||||||
if (!isLastStep) {
|
const nextRoom = roomStatuses.find((room) => !room.isComplete)
|
||||||
const nextStep = selectNextStep(roomStatus)
|
const nextStep = nextRoom
|
||||||
if (nextStep) {
|
? Object.values(nextRoom.steps).find((step) => !step.isValid)?.step
|
||||||
setStep(nextStep, roomIndex)
|
: null
|
||||||
}
|
|
||||||
} else if (isLastStep && hasNextRoom) {
|
if (nextRoom !== undefined && nextStep !== undefined) {
|
||||||
setStep(StepEnum.selectBed, roomIndex + 1)
|
setStep(nextStep, roomStatuses.indexOf(nextRoom))
|
||||||
} else {
|
} else {
|
||||||
// Time for payment, collapse any open step
|
// Time for payment, collapse any open step
|
||||||
setStep(null)
|
setStep(null)
|
||||||
|
|||||||
@@ -36,9 +36,7 @@ export default function SelectedRoom({
|
|||||||
function changeRoom() {
|
function changeRoom() {
|
||||||
modifyRate(roomIndex)
|
modifyRate(roomIndex)
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
const newSearchParams = new URLSearchParams(searchParamsStr)
|
router.push(`${selectRate(lang)}?${searchParamsStr}`)
|
||||||
newSearchParams.set("modifyRateIndex", roomIndex.toString())
|
|
||||||
router.push(`${selectRate(lang)}?${newSearchParams.toString()}`)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { useEffect, useRef } from "react"
|
|||||||
|
|
||||||
import { createDetailsStore } from "@/stores/enter-details"
|
import { createDetailsStore } from "@/stores/enter-details"
|
||||||
import {
|
import {
|
||||||
checkIsSameRoom,
|
checkIsSameBedTypes,
|
||||||
|
checkIsSameBooking as checkIsSameBooking,
|
||||||
clearSessionStorage,
|
clearSessionStorage,
|
||||||
readFromSessionStorage,
|
readFromSessionStorage,
|
||||||
} from "@/stores/enter-details/helpers"
|
} from "@/stores/enter-details/helpers"
|
||||||
@@ -11,6 +12,7 @@ import {
|
|||||||
import { DetailsContext } from "@/contexts/Details"
|
import { DetailsContext } from "@/contexts/Details"
|
||||||
|
|
||||||
import type { DetailsStore } from "@/types/contexts/enter-details"
|
import type { DetailsStore } from "@/types/contexts/enter-details"
|
||||||
|
import { StepEnum } from "@/types/enums/step"
|
||||||
import type { DetailsProviderProps } from "@/types/providers/enter-details"
|
import type { DetailsProviderProps } from "@/types/providers/enter-details"
|
||||||
import type { InitialState } from "@/types/stores/enter-details"
|
import type { InitialState } from "@/types/stores/enter-details"
|
||||||
|
|
||||||
@@ -58,19 +60,73 @@ export default function EnterDetailsProvider({
|
|||||||
if (!storedValues) {
|
if (!storedValues) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const isSameRoom = checkIsSameRoom(storedValues.booking, booking)
|
const isSameBooking = checkIsSameBooking(storedValues.booking, booking)
|
||||||
if (!isSameRoom) {
|
if (!isSameBooking) {
|
||||||
clearSessionStorage()
|
clearSessionStorage()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = storeRef.current?.getState()
|
const updatedRooms = storedValues.rooms.map((storedRoom, idx) => {
|
||||||
storeRef.current?.setState({
|
const currentRoom = booking.rooms[idx]
|
||||||
...state,
|
const roomData = roomsData[idx]
|
||||||
rooms: storedValues.rooms,
|
|
||||||
bookingProgress: storedValues.bookingProgress,
|
if (!storedRoom.bedType) {
|
||||||
|
return storedRoom
|
||||||
|
}
|
||||||
|
|
||||||
|
const isSameBedTypes = checkIsSameBedTypes(
|
||||||
|
storedRoom.bedType.roomTypeCode,
|
||||||
|
currentRoom.roomTypeCode
|
||||||
|
)
|
||||||
|
if (isSameBedTypes) {
|
||||||
|
return storedRoom
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roomData?.bedTypes?.length === 1 && roomData.bedTypes[0]) {
|
||||||
|
return {
|
||||||
|
...storedRoom,
|
||||||
|
bedType: {
|
||||||
|
roomTypeCode: roomData.bedTypes[0].value,
|
||||||
|
description: roomData.bedTypes[0].description,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove bed type selection if bedtypes change
|
||||||
|
return {
|
||||||
|
...storedRoom,
|
||||||
|
bedType: undefined,
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}, [booking])
|
|
||||||
|
const updatedProgress = {
|
||||||
|
...storedValues.bookingProgress,
|
||||||
|
roomStatuses: storedValues.bookingProgress.roomStatuses.map(
|
||||||
|
(status, idx) => {
|
||||||
|
const hasValidBedType = Boolean(updatedRooms[idx].bedType)
|
||||||
|
if (hasValidBedType) return status
|
||||||
|
|
||||||
|
return {
|
||||||
|
...status,
|
||||||
|
steps: {
|
||||||
|
...status.steps,
|
||||||
|
[StepEnum.selectBed]: {
|
||||||
|
step: StepEnum.selectBed,
|
||||||
|
isValid: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
currentStep: StepEnum.selectBed,
|
||||||
|
isComplete: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
storeRef.current?.setState({
|
||||||
|
rooms: updatedRooms,
|
||||||
|
bookingProgress: updatedProgress,
|
||||||
|
})
|
||||||
|
}, [booking, roomsData])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DetailsContext.Provider value={storeRef.current}>
|
<DetailsContext.Provider value={storeRef.current}>
|
||||||
|
|||||||
@@ -27,19 +27,26 @@ export function extractGuestFromUser(user: NonNullable<SafeUser>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function checkIsSameRoom(
|
export function checkIsSameBedTypes(
|
||||||
|
storedBedTypes: string,
|
||||||
|
bedTypesData: string
|
||||||
|
) {
|
||||||
|
return storedBedTypes === bedTypesData
|
||||||
|
}
|
||||||
|
|
||||||
|
export function checkIsSameBooking(
|
||||||
prev: SelectRateSearchParams,
|
prev: SelectRateSearchParams,
|
||||||
next: SelectRateSearchParams
|
next: SelectRateSearchParams
|
||||||
) {
|
) {
|
||||||
const { rooms: prevRooms, ...prevBooking } = prev
|
const { rooms: prevRooms, ...prevBooking } = prev
|
||||||
|
|
||||||
const prevRoomsWithoutRateCodes = prevRooms.map(
|
const prevRoomsWithoutRateCodes = prevRooms.map(
|
||||||
({ rateCode, counterRateCode, ...room }) => room
|
({ rateCode, counterRateCode, roomTypeCode, ...room }) => room
|
||||||
)
|
)
|
||||||
const { rooms: nextRooms, ...nextBooking } = next
|
const { rooms: nextRooms, ...nextBooking } = next
|
||||||
|
|
||||||
const nextRoomsWithoutRateCodes = nextRooms.map(
|
const nextRoomsWithoutRateCodes = nextRooms.map(
|
||||||
({ rateCode, counterRateCode, ...room }) => room
|
({ rateCode, counterRateCode, roomTypeCode, ...room }) => room
|
||||||
)
|
)
|
||||||
|
|
||||||
return isEqual(
|
return isEqual(
|
||||||
@@ -313,8 +320,9 @@ export function handleStepProgression(state: DetailsState) {
|
|||||||
|
|
||||||
const roomStatus = selectRoomStatus(state)
|
const roomStatus = selectRoomStatus(state)
|
||||||
if (roomStatus.isComplete) {
|
if (roomStatus.isComplete) {
|
||||||
const nextRoomIndex = state.bookingProgress.currentRoomIndex + 1
|
const nextRoomIndex = state.bookingProgress.roomStatuses.findIndex(
|
||||||
|
(room) => !room.isComplete
|
||||||
|
)
|
||||||
roomStatus.lastCompletedStep = roomStatus.currentStep ?? undefined
|
roomStatus.lastCompletedStep = roomStatus.currentStep ?? undefined
|
||||||
roomStatus.currentStep = null
|
roomStatus.currentStep = null
|
||||||
const nextRoomStatus = selectRoomStatus(state, nextRoomIndex)
|
const nextRoomStatus = selectRoomStatus(state, nextRoomIndex)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, test } from "@jest/globals"
|
import { describe, expect, test } from "@jest/globals"
|
||||||
import { act, renderHook } from "@testing-library/react"
|
import { act, renderHook, waitFor } from "@testing-library/react"
|
||||||
import { type PropsWithChildren } from "react"
|
import { type PropsWithChildren } from "react"
|
||||||
|
|
||||||
import { Lang } from "@/constants/languages"
|
import { Lang } from "@/constants/languages"
|
||||||
@@ -18,6 +18,8 @@ import EnterDetailsProvider from "@/providers/EnterDetailsProvider"
|
|||||||
import { selectRoom, selectRoomStatus } from "./helpers"
|
import { selectRoom, selectRoomStatus } from "./helpers"
|
||||||
import { detailsStorageName, useEnterDetailsStore } from "."
|
import { detailsStorageName, useEnterDetailsStore } from "."
|
||||||
|
|
||||||
|
import type { BedTypeSelection } from "@/types/components/hotelReservation/enterDetails/bedType"
|
||||||
|
import type { SelectRateSearchParams } from "@/types/components/hotelReservation/selectRate/selectRate"
|
||||||
import { StepEnum } from "@/types/enums/step"
|
import { StepEnum } from "@/types/enums/step"
|
||||||
import type { PersistedState } from "@/types/stores/enter-details"
|
import type { PersistedState } from "@/types/stores/enter-details"
|
||||||
|
|
||||||
@@ -38,7 +40,8 @@ interface CreateWrapperParams {
|
|||||||
showBreakfastStep?: boolean
|
showBreakfastStep?: boolean
|
||||||
breakfastIncluded?: boolean
|
breakfastIncluded?: boolean
|
||||||
mustBeGuaranteed?: boolean
|
mustBeGuaranteed?: boolean
|
||||||
onlyOneBedType?: boolean
|
bookingParams?: SelectRateSearchParams
|
||||||
|
bedTypes?: BedTypeSelection[]
|
||||||
}
|
}
|
||||||
|
|
||||||
function createWrapper(params: Partial<CreateWrapperParams> = {}) {
|
function createWrapper(params: Partial<CreateWrapperParams> = {}) {
|
||||||
@@ -46,19 +49,18 @@ function createWrapper(params: Partial<CreateWrapperParams> = {}) {
|
|||||||
showBreakfastStep = true,
|
showBreakfastStep = true,
|
||||||
breakfastIncluded = false,
|
breakfastIncluded = false,
|
||||||
mustBeGuaranteed = false,
|
mustBeGuaranteed = false,
|
||||||
onlyOneBedType = false,
|
bookingParams = booking,
|
||||||
|
bedTypes = [bedType.king, bedType.queen],
|
||||||
} = params
|
} = params
|
||||||
|
|
||||||
return function Wrapper({ children }: PropsWithChildren) {
|
return function Wrapper({ children }: PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<EnterDetailsProvider
|
<EnterDetailsProvider
|
||||||
booking={booking}
|
booking={bookingParams}
|
||||||
showBreakfastStep={showBreakfastStep}
|
showBreakfastStep={showBreakfastStep}
|
||||||
roomsData={[
|
roomsData={[
|
||||||
{
|
{
|
||||||
bedTypes: onlyOneBedType
|
bedTypes,
|
||||||
? [bedType.king]
|
|
||||||
: [bedType.king, bedType.queen],
|
|
||||||
packages: null,
|
packages: null,
|
||||||
mustBeGuaranteed,
|
mustBeGuaranteed,
|
||||||
breakfastIncluded,
|
breakfastIncluded,
|
||||||
@@ -68,9 +70,7 @@ function createWrapper(params: Partial<CreateWrapperParams> = {}) {
|
|||||||
roomRate: roomRate,
|
roomRate: roomRate,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
bedTypes: onlyOneBedType
|
bedTypes,
|
||||||
? [bedType.king]
|
|
||||||
: [bedType.king, bedType.queen],
|
|
||||||
packages: null,
|
packages: null,
|
||||||
mustBeGuaranteed,
|
mustBeGuaranteed,
|
||||||
breakfastIncluded,
|
breakfastIncluded,
|
||||||
@@ -95,102 +95,104 @@ describe("Enter Details Store", () => {
|
|||||||
window.sessionStorage.clear()
|
window.sessionStorage.clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
test("initialize with correct default values", () => {
|
describe("initial state", () => {
|
||||||
const { result } = renderHook(
|
test("initialize with correct default values", () => {
|
||||||
() => useEnterDetailsStore((state) => state),
|
const { result } = renderHook(
|
||||||
{
|
() => useEnterDetailsStore((state) => state),
|
||||||
wrapper: createWrapper(),
|
{
|
||||||
}
|
wrapper: createWrapper(),
|
||||||
)
|
}
|
||||||
const state = result.current
|
)
|
||||||
|
const state = result.current
|
||||||
|
|
||||||
expect(state.booking).toEqual(booking)
|
expect(state.booking).toEqual(booking)
|
||||||
expect(state.breakfast).toEqual(undefined)
|
expect(state.breakfast).toEqual(undefined)
|
||||||
|
|
||||||
// room 1
|
// room 1
|
||||||
const room1Status = selectRoomStatus(result.current, 0)
|
const room1Status = selectRoomStatus(result.current, 0)
|
||||||
const room1 = selectRoom(result.current, 0)
|
const room1 = selectRoom(result.current, 0)
|
||||||
|
|
||||||
expect(room1Status.currentStep).toBe(StepEnum.selectBed)
|
expect(room1Status.currentStep).toBe(StepEnum.selectBed)
|
||||||
|
|
||||||
expect(room1.roomPrice.perNight.local.price).toEqual(
|
expect(room1.roomPrice.perNight.local.price).toEqual(
|
||||||
roomRate.publicRate.localPrice.pricePerNight
|
roomRate.publicRate.localPrice.pricePerNight
|
||||||
)
|
)
|
||||||
expect(room1.bedType).toEqual(undefined)
|
expect(room1.bedType).toEqual(undefined)
|
||||||
expect(Object.values(room1.guest).every((value) => value === ""))
|
expect(Object.values(room1.guest).every((value) => value === ""))
|
||||||
|
|
||||||
// room 2
|
// room 2
|
||||||
const room2Status = selectRoomStatus(result.current, 1)
|
const room2Status = selectRoomStatus(result.current, 1)
|
||||||
const room2 = selectRoom(result.current, 1)
|
const room2 = selectRoom(result.current, 1)
|
||||||
|
|
||||||
expect(room2Status.currentStep).toBe(null)
|
expect(room2Status.currentStep).toBe(null)
|
||||||
expect(room2.roomPrice.perNight.local.price).toEqual(
|
expect(room2.roomPrice.perNight.local.price).toEqual(
|
||||||
room2.roomRate.publicRate.localPrice.pricePerNight
|
room2.roomRate.publicRate.localPrice.pricePerNight
|
||||||
)
|
)
|
||||||
expect(room2.bedType).toEqual(undefined)
|
expect(room2.bedType).toEqual(undefined)
|
||||||
expect(Object.values(room2.guest).every((value) => value === ""))
|
expect(Object.values(room2.guest).every((value) => value === ""))
|
||||||
})
|
})
|
||||||
|
|
||||||
test("initialize with correct values from session storage", () => {
|
test("initialize with correct values from session storage", () => {
|
||||||
const storage: PersistedState = {
|
const storage: PersistedState = {
|
||||||
booking: booking,
|
booking: booking,
|
||||||
bookingProgress: {
|
bookingProgress: {
|
||||||
currentRoomIndex: 0,
|
currentRoomIndex: 0,
|
||||||
canProceedToPayment: true,
|
canProceedToPayment: true,
|
||||||
roomStatuses: [
|
roomStatuses: [
|
||||||
{
|
{
|
||||||
isComplete: false,
|
isComplete: false,
|
||||||
currentStep: StepEnum.selectBed,
|
currentStep: StepEnum.selectBed,
|
||||||
lastCompletedStep: undefined,
|
lastCompletedStep: undefined,
|
||||||
steps: {
|
steps: {
|
||||||
[StepEnum.selectBed]: {
|
[StepEnum.selectBed]: {
|
||||||
step: StepEnum.selectBed,
|
step: StepEnum.selectBed,
|
||||||
isValid: true,
|
isValid: true,
|
||||||
},
|
},
|
||||||
[StepEnum.breakfast]: {
|
[StepEnum.breakfast]: {
|
||||||
step: StepEnum.breakfast,
|
step: StepEnum.breakfast,
|
||||||
isValid: true,
|
isValid: true,
|
||||||
},
|
},
|
||||||
[StepEnum.details]: {
|
[StepEnum.details]: {
|
||||||
step: StepEnum.details,
|
step: StepEnum.details,
|
||||||
isValid: true,
|
isValid: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
rooms: [
|
||||||
|
{
|
||||||
|
roomFeatures: null,
|
||||||
|
roomRate: roomRate,
|
||||||
|
roomType: "Classic Double",
|
||||||
|
cancellationText: "Non-refundable",
|
||||||
|
rateDetails: [],
|
||||||
|
bedType: {
|
||||||
|
roomTypeCode: bedType.king.value,
|
||||||
|
description: bedType.king.description,
|
||||||
|
},
|
||||||
|
adults: 1,
|
||||||
|
childrenInRoom: [],
|
||||||
|
breakfast: breakfastPackage,
|
||||||
|
guest: guestDetailsNonMember,
|
||||||
|
roomPrice: roomPrice,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
|
||||||
rooms: [
|
|
||||||
{
|
|
||||||
roomFeatures: null,
|
|
||||||
roomRate: roomRate,
|
|
||||||
roomType: "Classic Double",
|
|
||||||
cancellationText: "Non-refundable",
|
|
||||||
rateDetails: [],
|
|
||||||
bedType: {
|
|
||||||
roomTypeCode: bedType.king.value,
|
|
||||||
description: bedType.king.description,
|
|
||||||
},
|
|
||||||
adults: 1,
|
|
||||||
childrenInRoom: [],
|
|
||||||
breakfast: breakfastPackage,
|
|
||||||
guest: guestDetailsNonMember,
|
|
||||||
roomPrice: roomPrice,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
|
|
||||||
window.sessionStorage.setItem(detailsStorageName, JSON.stringify(storage))
|
|
||||||
|
|
||||||
const { result } = renderHook(
|
|
||||||
() => useEnterDetailsStore((state) => state),
|
|
||||||
{
|
|
||||||
wrapper: createWrapper(),
|
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
expect(result.current.booking).toEqual(storage.booking)
|
window.sessionStorage.setItem(detailsStorageName, JSON.stringify(storage))
|
||||||
expect(result.current.rooms[0]).toEqual(storage.rooms[0])
|
|
||||||
expect(result.current.bookingProgress).toEqual(storage.bookingProgress)
|
const { result } = renderHook(
|
||||||
|
() => useEnterDetailsStore((state) => state),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result.current.booking).toEqual(storage.booking)
|
||||||
|
expect(result.current.rooms[0]).toEqual(storage.rooms[0])
|
||||||
|
expect(result.current.bookingProgress).toEqual(storage.bookingProgress)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test("add bedtype and proceed to next step", async () => {
|
test("add bedtype and proceed to next step", async () => {
|
||||||
@@ -349,86 +351,6 @@ describe("Enter Details Store", () => {
|
|||||||
expect(result.current.bookingProgress.currentRoomIndex).toEqual(1)
|
expect(result.current.bookingProgress.currentRoomIndex).toEqual(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test("total price should be set properly", async () => {
|
|
||||||
const { result } = renderHook(
|
|
||||||
() => useEnterDetailsStore((state) => state),
|
|
||||||
{
|
|
||||||
wrapper: createWrapper(),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const publicRate = roomRate.publicRate.localPrice.pricePerStay
|
|
||||||
const memberRate = roomRate.memberRate?.localPrice.pricePerStay ?? 0
|
|
||||||
|
|
||||||
const initialTotalPrice = publicRate * result.current.rooms.length
|
|
||||||
expect(result.current.totalPrice.local.price).toEqual(initialTotalPrice)
|
|
||||||
|
|
||||||
// room 1
|
|
||||||
await act(async () => {
|
|
||||||
result.current.actions.updateBedType({
|
|
||||||
roomTypeCode: bedType.king.value,
|
|
||||||
description: bedType.king.description,
|
|
||||||
})
|
|
||||||
result.current.actions.updateBreakfast(breakfastPackage)
|
|
||||||
})
|
|
||||||
|
|
||||||
let expectedTotalPrice =
|
|
||||||
initialTotalPrice + Number(breakfastPackage.localPrice.price)
|
|
||||||
expect(result.current.totalPrice.local.price).toEqual(expectedTotalPrice)
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
result.current.actions.updateDetails(guestDetailsMember)
|
|
||||||
})
|
|
||||||
|
|
||||||
expectedTotalPrice =
|
|
||||||
memberRate + publicRate + Number(breakfastPackage.localPrice.price)
|
|
||||||
expect(result.current.totalPrice.local.price).toEqual(expectedTotalPrice)
|
|
||||||
|
|
||||||
// room 2
|
|
||||||
await act(async () => {
|
|
||||||
result.current.actions.updateBedType({
|
|
||||||
roomTypeCode: bedType.king.value,
|
|
||||||
description: bedType.king.description,
|
|
||||||
})
|
|
||||||
result.current.actions.updateBreakfast(breakfastPackage)
|
|
||||||
})
|
|
||||||
|
|
||||||
expectedTotalPrice =
|
|
||||||
memberRate + publicRate + Number(breakfastPackage.localPrice.price) * 2
|
|
||||||
expect(result.current.totalPrice.local.price).toEqual(expectedTotalPrice)
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
result.current.actions.updateDetails(guestDetailsNonMember)
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(result.current.totalPrice.local.price).toEqual(expectedTotalPrice)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("room price should be set properly", async () => {
|
|
||||||
const { result } = renderHook(
|
|
||||||
() => useEnterDetailsStore((state) => state),
|
|
||||||
{
|
|
||||||
wrapper: createWrapper(),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const publicRate = roomRate.publicRate.localPrice.pricePerStay
|
|
||||||
const memberRate = roomRate.memberRate?.localPrice.pricePerStay ?? 0
|
|
||||||
|
|
||||||
let room1 = selectRoom(result.current, 0)
|
|
||||||
expect(room1.roomPrice.perStay.local.price).toEqual(publicRate)
|
|
||||||
|
|
||||||
let room2 = selectRoom(result.current, 0)
|
|
||||||
expect(room2.roomPrice.perStay.local.price).toEqual(publicRate)
|
|
||||||
|
|
||||||
await act(async () => {
|
|
||||||
result.current.actions.updateDetails(guestDetailsMember)
|
|
||||||
})
|
|
||||||
|
|
||||||
room1 = selectRoom(result.current, 0)
|
|
||||||
expect(room1.roomPrice.perStay.local.price).toEqual(memberRate)
|
|
||||||
})
|
|
||||||
|
|
||||||
test("breakfast step should be hidden when breakfast is included", async () => {
|
test("breakfast step should be hidden when breakfast is included", async () => {
|
||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => useEnterDetailsStore((state) => state),
|
() => useEnterDetailsStore((state) => state),
|
||||||
@@ -448,7 +370,7 @@ describe("Enter Details Store", () => {
|
|||||||
const { result } = renderHook(
|
const { result } = renderHook(
|
||||||
() => useEnterDetailsStore((state) => state),
|
() => useEnterDetailsStore((state) => state),
|
||||||
{
|
{
|
||||||
wrapper: createWrapper({ onlyOneBedType: true }),
|
wrapper: createWrapper({ bedTypes: [bedType.queen] }),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -460,4 +382,252 @@ describe("Enter Details Store", () => {
|
|||||||
expect(room2Status.steps[StepEnum.selectBed].isValid).toEqual(true)
|
expect(room2Status.steps[StepEnum.selectBed].isValid).toEqual(true)
|
||||||
expect(room2Status.currentStep).toEqual(null)
|
expect(room2Status.currentStep).toEqual(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("price calculation", () => {
|
||||||
|
test("total price should be set properly", async () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => useEnterDetailsStore((state) => state),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const publicRate = roomRate.publicRate.localPrice.pricePerStay
|
||||||
|
const memberRate = roomRate.memberRate?.localPrice.pricePerStay ?? 0
|
||||||
|
|
||||||
|
const initialTotalPrice = publicRate * result.current.rooms.length
|
||||||
|
expect(result.current.totalPrice.local.price).toEqual(initialTotalPrice)
|
||||||
|
|
||||||
|
// room 1
|
||||||
|
await act(async () => {
|
||||||
|
result.current.actions.updateBedType({
|
||||||
|
roomTypeCode: bedType.king.value,
|
||||||
|
description: bedType.king.description,
|
||||||
|
})
|
||||||
|
result.current.actions.updateBreakfast(breakfastPackage)
|
||||||
|
})
|
||||||
|
|
||||||
|
let expectedTotalPrice =
|
||||||
|
initialTotalPrice + Number(breakfastPackage.localPrice.price)
|
||||||
|
expect(result.current.totalPrice.local.price).toEqual(expectedTotalPrice)
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
result.current.actions.updateDetails(guestDetailsMember)
|
||||||
|
})
|
||||||
|
|
||||||
|
expectedTotalPrice =
|
||||||
|
memberRate + publicRate + Number(breakfastPackage.localPrice.price)
|
||||||
|
expect(result.current.totalPrice.local.price).toEqual(expectedTotalPrice)
|
||||||
|
|
||||||
|
// room 2
|
||||||
|
await act(async () => {
|
||||||
|
result.current.actions.updateBedType({
|
||||||
|
roomTypeCode: bedType.king.value,
|
||||||
|
description: bedType.king.description,
|
||||||
|
})
|
||||||
|
result.current.actions.updateBreakfast(breakfastPackage)
|
||||||
|
})
|
||||||
|
|
||||||
|
expectedTotalPrice =
|
||||||
|
memberRate + publicRate + Number(breakfastPackage.localPrice.price) * 2
|
||||||
|
expect(result.current.totalPrice.local.price).toEqual(expectedTotalPrice)
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
result.current.actions.updateDetails(guestDetailsNonMember)
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.current.totalPrice.local.price).toEqual(expectedTotalPrice)
|
||||||
|
})
|
||||||
|
|
||||||
|
test("room price should be set properly", async () => {
|
||||||
|
const { result } = renderHook(
|
||||||
|
() => useEnterDetailsStore((state) => state),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const publicRate = roomRate.publicRate.localPrice.pricePerStay
|
||||||
|
const memberRate = roomRate.memberRate?.localPrice.pricePerStay ?? 0
|
||||||
|
|
||||||
|
let room1 = selectRoom(result.current, 0)
|
||||||
|
expect(room1.roomPrice.perStay.local.price).toEqual(publicRate)
|
||||||
|
|
||||||
|
let room2 = selectRoom(result.current, 0)
|
||||||
|
expect(room2.roomPrice.perStay.local.price).toEqual(publicRate)
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
result.current.actions.updateDetails(guestDetailsMember)
|
||||||
|
})
|
||||||
|
|
||||||
|
room1 = selectRoom(result.current, 0)
|
||||||
|
expect(room1.roomPrice.perStay.local.price).toEqual(memberRate)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("change room", () => {
|
||||||
|
test("changing to room with new bedtypes requires selecting bed again", async () => {
|
||||||
|
const { result: firstRun } = renderHook(
|
||||||
|
() => useEnterDetailsStore((state) => state),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper({ bedTypes: [bedType.king, bedType.queen] }),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const selectedBedType = {
|
||||||
|
roomTypeCode: bedType.king.value,
|
||||||
|
description: bedType.king.description,
|
||||||
|
}
|
||||||
|
|
||||||
|
// add bedtype
|
||||||
|
await act(async () => {
|
||||||
|
firstRun.current.actions.updateBedType(selectedBedType)
|
||||||
|
})
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
firstRun.current.actions.updateBreakfast(false) // 'no breakfast' selected
|
||||||
|
})
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
firstRun.current.actions.updateDetails(guestDetailsNonMember)
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedBooking = {
|
||||||
|
...booking,
|
||||||
|
rooms: booking.rooms.map((r) => ({
|
||||||
|
...r,
|
||||||
|
roomTypeCode: "NEW",
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
|
||||||
|
// render again to change the bedtypes
|
||||||
|
const { result: secondRun } = renderHook(
|
||||||
|
() => useEnterDetailsStore((state) => state),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper({
|
||||||
|
bookingParams: updatedBooking,
|
||||||
|
bedTypes: [bedType.single, bedType.queen],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
const room = selectRoom(secondRun.current, 0)
|
||||||
|
const roomStatus = selectRoomStatus(secondRun.current, 0)
|
||||||
|
|
||||||
|
// bed type should be unset since the bed types have changed
|
||||||
|
expect(room.bedType).toEqual(undefined)
|
||||||
|
|
||||||
|
// bed step should be unselected
|
||||||
|
expect(roomStatus.currentStep).toBe(StepEnum.selectBed)
|
||||||
|
expect(roomStatus.steps[StepEnum.selectBed].isValid).toBe(false)
|
||||||
|
|
||||||
|
// other steps should still be selected
|
||||||
|
expect(room.breakfast).toBe(false)
|
||||||
|
expect(roomStatus.steps[StepEnum.breakfast]?.isValid).toBe(true)
|
||||||
|
expect(room.guest).toEqual(guestDetailsNonMember)
|
||||||
|
expect(roomStatus.steps[StepEnum.details].isValid).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("changing to room with single bedtype option should skip step", async () => {
|
||||||
|
const { result: firstRun } = renderHook(
|
||||||
|
() => useEnterDetailsStore((state) => state),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper({ bedTypes: [bedType.king, bedType.queen] }),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const selectedBedType = {
|
||||||
|
roomTypeCode: bedType.king.value,
|
||||||
|
description: bedType.king.description,
|
||||||
|
}
|
||||||
|
|
||||||
|
// add bedtype
|
||||||
|
await act(async () => {
|
||||||
|
firstRun.current.actions.updateBedType(selectedBedType)
|
||||||
|
})
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
firstRun.current.actions.updateBreakfast(breakfastPackage)
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedBooking = {
|
||||||
|
...booking,
|
||||||
|
rooms: booking.rooms.map((r) => ({
|
||||||
|
...r,
|
||||||
|
roomTypeCode: "NEW",
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
|
||||||
|
// render again to change the bedtypes
|
||||||
|
const { result: secondRun } = renderHook(
|
||||||
|
() => useEnterDetailsStore((state) => state),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper({
|
||||||
|
bookingParams: updatedBooking,
|
||||||
|
bedTypes: [bedType.queen],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
const room = selectRoom(secondRun.current, 0)
|
||||||
|
const roomStatus = selectRoomStatus(secondRun.current, 0)
|
||||||
|
|
||||||
|
expect(room.bedType).toEqual({
|
||||||
|
roomTypeCode: bedType.queen.value,
|
||||||
|
description: bedType.queen.description,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(roomStatus.steps[StepEnum.selectBed].isValid).toBe(true)
|
||||||
|
expect(roomStatus.steps[StepEnum.breakfast]?.isValid).toBe(true)
|
||||||
|
|
||||||
|
expect(roomStatus.steps[StepEnum.details].isValid).toBe(false)
|
||||||
|
expect(roomStatus.currentStep).toBe(StepEnum.details)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("if booking has changed, stored values should be discarded", async () => {
|
||||||
|
const { result: firstRun } = renderHook(
|
||||||
|
() => useEnterDetailsStore((state) => state),
|
||||||
|
{
|
||||||
|
wrapper: createWrapper({ bedTypes: [bedType.king, bedType.queen] }),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const selectedBedType = {
|
||||||
|
roomTypeCode: bedType.king.value,
|
||||||
|
description: bedType.king.description,
|
||||||
|
}
|
||||||
|
|
||||||
|
// add bedtype
|
||||||
|
await act(async () => {
|
||||||
|
firstRun.current.actions.updateBedType(selectedBedType)
|
||||||
|
})
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
firstRun.current.actions.updateBreakfast(breakfastPackage)
|
||||||
|
})
|
||||||
|
|
||||||
|
const updatedBooking = {
|
||||||
|
...booking,
|
||||||
|
hotelId: "0001",
|
||||||
|
fromDate: "2030-01-01",
|
||||||
|
toDate: "2030-01-02",
|
||||||
|
}
|
||||||
|
|
||||||
|
renderHook(() => useEnterDetailsStore((state) => state), {
|
||||||
|
wrapper: createWrapper({
|
||||||
|
bookingParams: updatedBooking,
|
||||||
|
bedTypes: [bedType.queen],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
const storageItem = window.sessionStorage.getItem(detailsStorageName)
|
||||||
|
expect(storageItem).toBe(null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user