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
2 changes: 2 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6227,6 +6227,8 @@ const CONST = {
TRAIN: 'train',
},

RESERVATION_ADDRESS_TEST_ID: 'ReservationAddress',

CANCELLATION_POLICY: {
UNKNOWN: 'UNKNOWN',
NON_REFUNDABLE: 'NON_REFUNDABLE',
Expand Down
18 changes: 13 additions & 5 deletions src/components/ReportActionItem/TripDetailsView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import Navigation from '@libs/Navigation/Navigation';
import StringUtils from '@libs/StringUtils';
import variables from '@styles/variables';
import * as Expensicons from '@src/components/Icon/Expensicons';
import CONST from '@src/CONST';
import type {ReservationData} from '@src/libs/TripReservationUtils';
import {getReservationsFromTripTransactions, getTripReservationIcon} from '@src/libs/TripReservationUtils';
import {getReservationsFromTripTransactions, getTripReservationCode, getTripReservationIcon} from '@src/libs/TripReservationUtils';
import ROUTES from '@src/ROUTES';
import type {Reservation, ReservationTimeDetails} from '@src/types/onyx/Transaction';
import type Transaction from '@src/types/onyx/Transaction';
Expand Down Expand Up @@ -61,14 +62,14 @@ function ReservationView({reservation, transactionID, tripRoomReportID, reservat
const formattedDate = getFormattedDate();

const bottomDescription = useMemo(() => {
const code = `${reservation.confirmations && reservation.confirmations?.length > 0 ? `${reservation.confirmations.at(0)?.value} • ` : ''}`;
const code = getTripReservationCode(reservation);
if (reservation.type === CONST.RESERVATION_TYPE.FLIGHT) {
const longName = reservation.company?.longName ? `${reservation.company?.longName} • ` : '';
const shortName = reservation?.company?.shortName ? `${reservation?.company?.shortName} ` : '';
return `${code}${longName}${shortName}${reservation.route?.number}`;
}
if (reservation.type === CONST.RESERVATION_TYPE.HOTEL) {
return `${code}${reservation.start.address}`;
return `${code}${StringUtils.removeDoubleQuotes(reservation.start.address)}`;
}
if (reservation.type === CONST.RESERVATION_TYPE.CAR) {
const vendor = reservation.vendor ? `${reservation.vendor} • ` : '';
Expand All @@ -77,7 +78,7 @@ function ReservationView({reservation, transactionID, tripRoomReportID, reservat
if (reservation.type === CONST.RESERVATION_TYPE.TRAIN) {
return reservation.route?.name;
}
return reservation.start.address ?? reservation.start.location;
return StringUtils.removeDoubleQuotes(reservation.start.address) ?? reservation.start.location;
}, [reservation]);

const titleComponent = () => {
Expand Down Expand Up @@ -107,7 +108,14 @@ function ReservationView({reservation, transactionID, tripRoomReportID, reservat
>
{reservation.type === CONST.RESERVATION_TYPE.CAR ? reservation.carInfo?.name : Str.recapitalize(reservation.start.longName ?? '')}
</Text>
{!!bottomDescription && <Text style={[styles.textSmall, styles.colorMuted, styles.lh14]}>{bottomDescription}</Text>}
{!!bottomDescription && (
<Text
style={[styles.textSmall, styles.colorMuted, styles.lh14]}
testID={CONST.RESERVATION_ADDRESS_TEST_ID}
>
{bottomDescription}
</Text>
)}
</View>
);
};
Expand Down
9 changes: 8 additions & 1 deletion src/libs/StringUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,4 +99,11 @@ function getFirstLine(text = '') {
return lines.at(0);
}

export default {sanitizeString, isEmptyString, removeInvisibleCharacters, normalizeAccents, normalizeCRLF, lineBreaksToSpaces, getFirstLine};
/**
* Remove double quotes from the string
*/
function removeDoubleQuotes(text = '') {
return text.replace(/"/g, '');
}

export default {sanitizeString, isEmptyString, removeInvisibleCharacters, normalizeAccents, normalizeCRLF, lineBreaksToSpaces, getFirstLine, removeDoubleQuotes};
9 changes: 8 additions & 1 deletion src/libs/TripReservationUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,12 @@ function getTripEReceiptIcon(transaction?: Transaction): IconAsset | undefined {
}
}

export {getTripReservationIcon, getReservationsFromTripTransactions, getTripEReceiptIcon};
/**
* Extracts the confirmation code from a reservation
*/
function getTripReservationCode(reservation: Reservation): string {
return `${reservation.confirmations && reservation.confirmations?.length > 0 ? `${reservation.confirmations.at(0)?.value} • ` : ''}`;
}

export {getTripReservationIcon, getReservationsFromTripTransactions, getTripEReceiptIcon, getTripReservationCode};
export type {ReservationData};
4 changes: 3 additions & 1 deletion src/pages/Travel/HotelTripDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import Text from '@components/Text';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import StringUtils from '@libs/StringUtils';
import CONST from '@src/CONST';
import type {PersonalDetails} from '@src/types/onyx';
import type {Reservation} from '@src/types/onyx/Transaction';
Expand Down Expand Up @@ -41,9 +42,10 @@ function HotelTripDetails({reservation, personalDetails}: HotelTripDetailsProps)
<Text style={[styles.textHeadlineH1, styles.mh5, styles.mv3]}>{Str.recapitalize(reservation.start.longName ?? '')}</Text>
<MenuItemWithTopDescription
description={translate('common.address')}
title={reservation.start.address}
title={StringUtils.removeDoubleQuotes(reservation.start.address)}
interactive={false}
numberOfLinesTitle={2}
pressableTestID={CONST.RESERVATION_ADDRESS_TEST_ID}
/>
<MenuItemWithTopDescription
description={translate('travel.hotelDetails.checkIn')}
Expand Down
67 changes: 67 additions & 0 deletions tests/ui/ReservationAddressTest.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {render, screen} from '@testing-library/react-native';
import {ReservationView} from '@components/ReportActionItem/TripDetailsView';
import StringUtils from '@libs/StringUtils';
import {getTripReservationCode} from '@libs/TripReservationUtils';
import HotelTripDetails from '@pages/Travel/HotelTripDetails';
import CONST from '@src/CONST';

const mockReservationData = {
reservation: {
cancellationDeadline: '2025-04-02T19:00:00-05:00',
cancellationPolicy: CONST.CANCELLATION_POLICY.PARTIALLY_REFUNDABLE,
confirmations: [
{
name: 'Itinerary Number',
value: '0',
},
],
end: {
address: '"500 E 105TH ST"\n64131, KANSAS CITY, MO, US',
date: '2025-04-10T12:00:00',
longName: 'Courtyard By Marriott Kansas City South',
},
paymentType: 'PAY_AT_HOTEL',
roomClass: 'Small Room',
start: {
address: '"500 E 105TH ST"\n64131, KANSAS CITY, MO, US',
date: '2025-04-03T15:00:00',
longName: 'Courtyard By Marriott Kansas City South',
},
travelerPersonalInfo: {
email: 'blimpich+travel@expensifail.com',
name: 'Ben Limpich',
},
type: CONST.RESERVATION_TYPE.HOTEL,
duration: 1000,
},
transactionID: '2101152939974863962',
reportID: '8884312680252586',
reservationIndex: 0,
};

describe('ReservationAddressTest', () => {
it('ReservationView should render the reservation address without double quotes', () => {
render(
<ReservationView
reservation={mockReservationData.reservation}
transactionID={mockReservationData.transactionID}
tripRoomReportID={mockReservationData.reportID}
reservationIndex={mockReservationData.reservationIndex}
/>,
);

const expectedAddress = `${getTripReservationCode(mockReservationData.reservation)}${StringUtils.removeDoubleQuotes(mockReservationData.reservation.start.address)}`;
expect(screen.getByTestId(CONST.RESERVATION_ADDRESS_TEST_ID)).toHaveTextContent(expectedAddress);
});

it('HotelTripDetails should render the reservation address without double quotes', () => {
render(
<HotelTripDetails
reservation={mockReservationData.reservation}
personalDetails={undefined}
/>,
);

expect(screen.getByTestId(CONST.RESERVATION_ADDRESS_TEST_ID)).toHaveTextContent(StringUtils.removeDoubleQuotes(mockReservationData.reservation.start.address));
});
});