Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/pages/home/UpcomingTravelSection/useTripRoomReports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import useOnyx from '@hooks/useOnyx';
import {isTripRoom} from '@libs/ReportUtils';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Report} from '@src/types/onyx';

function useTripRoomReports(): Report[] {
const [reports] = useOnyx(ONYXKEYS.COLLECTION.REPORT);
Comment thread
TMisiukiewicz marked this conversation as resolved.

return Object.values(reports ?? {}).filter((report): report is Report => !!report && isTripRoom(report));
Comment thread
TMisiukiewicz marked this conversation as resolved.
}

export default useTripRoomReports;
Original file line number Diff line number Diff line change
@@ -1,43 +1,30 @@
import {accountIDSelector} from '@selectors/Session';
import {useMemo} from 'react';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import useOnyx from '@hooks/useOnyx';
import {isTripRoom} from '@libs/ReportUtils';
import type {ReservationData} from '@libs/TripReservationUtils';
import {getReservationsFromTripReport} from '@libs/TripReservationUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Report} from '@src/types/onyx';
import mapOnyxCollectionItems from '@src/utils/mapOnyxCollectionItems';
import useTripRoomReports from './useTripRoomReports';

type UpcomingReservation = ReservationData & {
reportID: string;
};

const tripRoomSelector = (report: OnyxEntry<Report>): Report | undefined => {
if (!report || !isTripRoom(report)) {
return;
}
return report;
};

const allTripRoomsSelector = (reports: OnyxCollection<Report>) => mapOnyxCollectionItems(reports, tripRoomSelector);

function useUpcomingTravelReservations(): UpcomingReservation[] {
const [tripRoomReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {selector: allTripRoomsSelector});
const tripRoomReports = useTripRoomReports();
const [accountID] = useOnyx(ONYXKEYS.SESSION, {selector: accountIDSelector});

return useMemo(() => {
const now = new Date();
const windowEnd = new Date(now);
windowEnd.setDate(windowEnd.getDate() + CONST.UPCOMING_TRAVEL_WINDOW_DAYS);

const reports = Object.values(tripRoomReports ?? {});
const upcoming: UpcomingReservation[] = [];

for (const report of reports) {
for (const report of tripRoomReports) {
// Only include reservations where the current user is the traveler
if (!report || report.ownerAccountID !== accountID) {
if (report.ownerAccountID !== accountID) {
continue;
}
const reservations = getReservationsFromTripReport(report);
Expand Down
103 changes: 103 additions & 0 deletions tests/unit/hooks/useUpcomingTravelReservations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -656,4 +656,107 @@ describe('useUpcomingTravelReservations', () => {
expect(result.current).toEqual([]);
});
});

it('should skip reservations with invalid start date and keep valid ones', async () => {
const invalidFlight = makeAirPnr('PNR_INVALID', 'not-a-date', 'not-a-date');
const validFlight = makeAirPnr('PNR_VALID', daysFromNow(2), daysFromNow(2, 15));
const tripRoom = makeTripRoomReport('1000', [invalidFlight, validFlight]);

await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1000`, tripRoom);
await waitForBatchedUpdates();

const {result} = renderHook(() => useUpcomingTravelReservations());

await waitFor(() => {
expect(result.current).toHaveLength(1);
});
expect(result.current.at(0)?.reservation.reservationID).toBe('PNR_VALID');
});

it('should return empty array when all reservations have invalid start dates', async () => {
const invalidFlight = makeAirPnr('PNR_INVALID_ALL', '', '');
const tripRoom = makeTripRoomReport('1001', [invalidFlight]);

await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1001`, tripRoom);
await waitForBatchedUpdates();

const {result} = renderHook(() => useUpcomingTravelReservations());

await waitFor(() => {
expect(result.current).toEqual([]);
});
});

it('should include reservation at the exact 7-day boundary', async () => {
const boundaryFlight = makeAirPnr('PNR_BOUNDARY', daysFromNow(CONST.UPCOMING_TRAVEL_WINDOW_DAYS, 0), daysFromNow(CONST.UPCOMING_TRAVEL_WINDOW_DAYS, 3));
const tripRoom = makeTripRoomReport('1002', [boundaryFlight]);

await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1002`, tripRoom);
await waitForBatchedUpdates();

const {result} = renderHook(() => useUpcomingTravelReservations());

await waitFor(() => {
expect(result.current).toHaveLength(1);
});
expect(result.current.at(0)?.reservation.reservationID).toBe('PNR_BOUNDARY');
});

it('should return empty array for trip room without tripData', async () => {
const tripRoom = {
reportID: '1003',
ownerAccountID: TEST_ACCOUNT_ID,
type: CONST.REPORT.TYPE.CHAT,
chatType: CONST.REPORT.CHAT_TYPE.TRIP_ROOM,
reportName: 'Trip 1003',
policyID: 'policy1',
} as Report;

await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1003`, tripRoom);
await waitForBatchedUpdates();

const {result} = renderHook(() => useUpcomingTravelReservations());

await waitFor(() => {
expect(result.current).toEqual([]);
});
});

it('should return empty array for trip room with empty pnrs array', async () => {
const tripRoom = makeTripRoomReport('1004', []);

await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1004`, tripRoom);
await waitForBatchedUpdates();

const {result} = renderHook(() => useUpcomingTravelReservations());

await waitFor(() => {
expect(result.current).toEqual([]);
});
});

it('should ignore non-trip-room reports', async () => {
const flight = makeAirPnr('PNR_NON_TRIP', daysFromNow(2), daysFromNow(2, 15));
const nonTripReport = {
reportID: '1005',
ownerAccountID: TEST_ACCOUNT_ID,
type: CONST.REPORT.TYPE.CHAT,
chatType: CONST.REPORT.CHAT_TYPE.POLICY_ROOM,
reportName: 'Policy Room',
policyID: 'policy1',
tripData: {
tripID: 'trip-1005',
payload: {pnrs: [flight]},
},
} as Report;

await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1005`, nonTripReport);
await waitForBatchedUpdates();

const {result} = renderHook(() => useUpcomingTravelReservations());

await waitFor(() => {
expect(result.current).toEqual([]);
});
});
});
Loading