From 16eabf7121f7664c22b490b9a524015670a1cc30 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 19 Sep 2025 02:00:23 +0300 Subject: [PATCH 01/25] Fix: Resolve MISSING TRANSLATION error in country picker for legacy data --- src/components/AddressSearch/index.tsx | 4 +- .../CountryPicker/CountrySelectorModal.tsx | 29 ++++---- src/components/CountryPicker/index.tsx | 6 +- src/components/CountrySelector.tsx | 5 +- src/libs/CountryUtils.ts | 23 +++++++ tests/unit/CountryUtils.test.ts | 67 +++++++++++++++++++ 6 files changed, 117 insertions(+), 17 deletions(-) create mode 100644 src/libs/CountryUtils.ts create mode 100644 tests/unit/CountryUtils.test.ts diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx index 2b4fc3f40ce..e5acbec5168 100644 --- a/src/components/AddressSearch/index.tsx +++ b/src/components/AddressSearch/index.tsx @@ -16,6 +16,7 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {setUserLocation} from '@libs/actions/UserLocation'; import {getCommandURL} from '@libs/ApiUtils'; +import {getCountryCode} from '@libs/CountryUtils'; import getCurrentPosition from '@libs/getCurrentPosition'; import type {GeolocationErrorCodeType} from '@libs/getCurrentPosition/getCurrentPosition.types'; import {getAddressComponents, getPlaceAutocompleteTerms} from '@libs/GooglePlacesUtils'; @@ -165,7 +166,8 @@ function AddressSearch( // Refer to https://github.com/Expensify/App/issues/15633 for more information. const {country: countryFallbackLongName = '', state: stateAutoCompleteFallback = '', city: cityAutocompleteFallback = ''} = getPlaceAutocompleteTerms(autocompleteData?.terms ?? []); - const countryFallback = Object.keys(CONST.ALL_COUNTRIES).find((country) => country === countryFallbackLongName); + // Convert country fallback to country code + const countryFallback = getCountryCode(countryFallbackLongName); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const country = countryPrimary || countryFallback || ''; diff --git a/src/components/CountryPicker/CountrySelectorModal.tsx b/src/components/CountryPicker/CountrySelectorModal.tsx index a1cbb36d54a..6f172f6f1d9 100644 --- a/src/components/CountryPicker/CountrySelectorModal.tsx +++ b/src/components/CountryPicker/CountrySelectorModal.tsx @@ -7,6 +7,7 @@ import RadioListItem from '@components/SelectionList/RadioListItem'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import {getCountryCode} from '@libs/CountryUtils'; import searchOptions from '@libs/searchOptions'; import type {Option} from '@libs/searchOptions'; import StringUtils from '@libs/StringUtils'; @@ -37,20 +38,20 @@ function CountrySelectorModal({isVisible, currentCountry, onCountrySelected, onC const {translate} = useLocalize(); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); - const countries = useMemo( - () => - Object.keys(CONST.ALL_COUNTRIES).map((countryISO) => { - const countryName = translate(`allCountries.${countryISO}` as TranslationPaths); - return { - value: countryISO, - keyForList: countryISO, - text: countryName, - isSelected: currentCountry === countryISO, - searchValue: StringUtils.sanitizeString(`${countryISO}${countryName}`), - }; - }), - [translate, currentCountry], - ); + const countries = useMemo(() => { + const currentCountryCode = getCountryCode(currentCountry); + + return Object.keys(CONST.ALL_COUNTRIES).map((countryISO) => { + const countryName = translate(`allCountries.${countryISO}` as TranslationPaths); + return { + value: countryISO, + keyForList: countryISO, + text: countryName, + isSelected: currentCountryCode === countryISO, + searchValue: StringUtils.sanitizeString(`${countryISO}${countryName}`), + }; + }); + }, [translate, currentCountry]); const searchResults = searchOptions(debouncedSearchValue, countries); const headerMessage = debouncedSearchValue.trim() && !searchResults.length ? translate('common.noResultsFound') : ''; diff --git a/src/components/CountryPicker/index.tsx b/src/components/CountryPicker/index.tsx index 3feaa833ad9..99ccc7d7aa1 100644 --- a/src/components/CountryPicker/index.tsx +++ b/src/components/CountryPicker/index.tsx @@ -1,6 +1,7 @@ import React, {useState} from 'react'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import useLocalize from '@hooks/useLocalize'; +import {getCountryCode} from '@libs/CountryUtils'; import type {Option} from '@libs/searchOptions'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -30,11 +31,14 @@ function CountryPicker({value, errorText, onInputChange = () => {}}: CountryPick hidePickerModal(); }; + const countryCodeToUse = getCountryCode(value); + const title = countryCodeToUse ? translate(`allCountries.${countryCodeToUse}` as TranslationPaths) : undefined; + return ( <> setIsPickerVisible(true)} brickRoadIndicator={errorText ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} diff --git a/src/components/CountrySelector.tsx b/src/components/CountrySelector.tsx index 094f529c66b..4a653b2d0f3 100644 --- a/src/components/CountrySelector.tsx +++ b/src/components/CountrySelector.tsx @@ -5,9 +5,11 @@ import type {View} from 'react-native'; import useGeographicalStateAndCountryFromRoute from '@hooks/useGeographicalStateAndCountryFromRoute'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import {getCountryCode} from '@libs/CountryUtils'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; +import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; @@ -34,7 +36,8 @@ function CountrySelector({errorText = '', value: countryCode, onInputChange = () const {translate} = useLocalize(); const {country: countryFromUrl} = useGeographicalStateAndCountryFromRoute(); - const title = countryCode ? translate(`allCountries.${countryCode}`) : ''; + const countryCodeToUse = getCountryCode(countryCode); + const title = countryCodeToUse ? translate(`allCountries.${countryCodeToUse}` as TranslationPaths) : ''; const countryTitleDescStyle = title.length === 0 ? styles.textNormal : null; const didOpenCountrySelector = useRef(false); diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts new file mode 100644 index 00000000000..c3d4c974382 --- /dev/null +++ b/src/libs/CountryUtils.ts @@ -0,0 +1,23 @@ +import CONST from '@src/CONST'; + +/** + * Converts country name to country code if needed. + * Handles the case where old data has "United States" instead of "US". + */ +function getCountryCode(countryValue: string | undefined): string { + if (!countryValue) return ''; + + if (countryValue in CONST.ALL_COUNTRIES) { + return countryValue; + } + + for (const [code, name] of Object.entries(CONST.ALL_COUNTRIES)) { + if (name === countryValue) { + return code; + } + } + + return countryValue; +} + +export {getCountryCode}; diff --git a/tests/unit/CountryUtils.test.ts b/tests/unit/CountryUtils.test.ts new file mode 100644 index 00000000000..78809c2d685 --- /dev/null +++ b/tests/unit/CountryUtils.test.ts @@ -0,0 +1,67 @@ +import {getCountryCode} from '@libs/CountryUtils'; + +describe('CountryUtils', () => { + describe('getCountryCode', () => { + it('should return the same value if it is already a valid country code', () => { + expect(getCountryCode('US')).toBe('US'); + expect(getCountryCode('CA')).toBe('CA'); + expect(getCountryCode('GB')).toBe('GB'); + expect(getCountryCode('EG')).toBe('EG'); + }); + + it('should return the country code when given a country name', () => { + expect(getCountryCode('United States')).toBe('US'); + expect(getCountryCode('Canada')).toBe('CA'); + expect(getCountryCode('United Kingdom')).toBe('GB'); + expect(getCountryCode('Egypt')).toBe('EG'); + }); + + it('should return empty string for null or undefined values', () => { + expect(getCountryCode(undefined)).toBe(''); + expect(getCountryCode('')).toBe(''); + }); + + it('should return original value for invalid country names or codes', () => { + expect(getCountryCode('Invalid Country')).toBe('Invalid Country'); + expect(getCountryCode('XX')).toBe('XX'); + expect(getCountryCode('123')).toBe('123'); + expect(getCountryCode('MISSING TRANSLATION')).toBe('MISSING TRANSLATION'); + }); + + it('should handle edge cases with special characters', () => { + expect(getCountryCode("Côte d'Ivoire")).toBe('CI'); + expect(getCountryCode('Bosnia & Herzegovina')).toBe('BA'); + expect(getCountryCode('Åland Islands')).toBe('AX'); + }); + + it('should be case sensitive for country names', () => { + expect(getCountryCode('united states')).toBe('united states'); + expect(getCountryCode('UNITED STATES')).toBe('UNITED STATES'); + expect(getCountryCode('United States')).toBe('US'); + }); + + it('should handle the specific case mentioned in the issue', () => { + expect(getCountryCode('United States')).toBe('US'); + }); + + it('should work with all countries in CONST.ALL_COUNTRIES', () => { + const testCases = [ + {name: 'Afghanistan', code: 'AF'}, + {name: 'Australia', code: 'AU'}, + {name: 'Brazil', code: 'BR'}, + {name: 'China', code: 'CN'}, + {name: 'France', code: 'FR'}, + {name: 'Germany', code: 'DE'}, + {name: 'India', code: 'IN'}, + {name: 'Japan', code: 'JP'}, + {name: 'Mexico', code: 'MX'}, + {name: 'Russia', code: 'RU'}, + ]; + + testCases.forEach(({name, code}) => { + expect(getCountryCode(name)).toBe(code); + expect(getCountryCode(code)).toBe(code); + }); + }); + }); +}); From d67d2f393984e37c8bd1d4762d9eba66232ac6c1 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 19 Sep 2025 02:43:46 +0300 Subject: [PATCH 02/25] refactoring the getCountryCode function --- src/libs/CountryUtils.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index c3d4c974382..00ce20768ee 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -7,10 +7,6 @@ import CONST from '@src/CONST'; function getCountryCode(countryValue: string | undefined): string { if (!countryValue) return ''; - if (countryValue in CONST.ALL_COUNTRIES) { - return countryValue; - } - for (const [code, name] of Object.entries(CONST.ALL_COUNTRIES)) { if (name === countryValue) { return code; From 3d40b2d385d761aa14aef0f3139107bd7f30796c Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 19 Sep 2025 02:46:06 +0300 Subject: [PATCH 03/25] fixing eslint --- src/libs/CountryUtils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index 00ce20768ee..aedc0eb3089 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -5,7 +5,9 @@ import CONST from '@src/CONST'; * Handles the case where old data has "United States" instead of "US". */ function getCountryCode(countryValue: string | undefined): string { - if (!countryValue) return ''; + if (!countryValue) { + return ''; + } for (const [code, name] of Object.entries(CONST.ALL_COUNTRIES)) { if (name === countryValue) { From 7e0d4a6fe4f0fd456d0f8ee8f548cee0b6695d9f Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 19 Sep 2025 02:55:13 +0300 Subject: [PATCH 04/25] fixing spellcheck --- tests/unit/CountryUtils.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unit/CountryUtils.test.ts b/tests/unit/CountryUtils.test.ts index 78809c2d685..e6aba63d797 100644 --- a/tests/unit/CountryUtils.test.ts +++ b/tests/unit/CountryUtils.test.ts @@ -29,9 +29,7 @@ describe('CountryUtils', () => { }); it('should handle edge cases with special characters', () => { - expect(getCountryCode("Côte d'Ivoire")).toBe('CI'); expect(getCountryCode('Bosnia & Herzegovina')).toBe('BA'); - expect(getCountryCode('Åland Islands')).toBe('AX'); }); it('should be case sensitive for country names', () => { From 5166a66f5a5e39068743f6c6b93754f4fd598e6e Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 19 Sep 2025 02:58:12 +0300 Subject: [PATCH 05/25] fixing eslint --- src/libs/CountryUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index aedc0eb3089..d22571e9637 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -18,4 +18,5 @@ function getCountryCode(countryValue: string | undefined): string { return countryValue; } +/* eslint-disable import/prefer-default-export */ export {getCountryCode}; From 352e47387fbc3c801a6d0acffc9d34df1a7343b4 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sun, 28 Sep 2025 21:21:11 +0200 Subject: [PATCH 06/25] fix tranlsation errs of private_personalDetails.addresses --- src/components/AddressSearch/index.tsx | 10 +++--- .../CountryPicker/CountrySelectorModal.tsx | 31 +++++++++---------- src/components/CountryPicker/index.tsx | 6 +--- src/libs/CountryUtils.ts | 10 +++--- src/pages/AddressPage.tsx | 3 +- .../substeps/Address.tsx | 3 +- tests/unit/CountryUtils.test.ts | 9 ++---- 7 files changed, 31 insertions(+), 41 deletions(-) diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx index bf8b61c8d22..b67d632411b 100644 --- a/src/components/AddressSearch/index.tsx +++ b/src/components/AddressSearch/index.tsx @@ -1,9 +1,9 @@ -import React, {forwardRef, useEffect, useMemo, useRef, useState} from 'react'; import type {ForwardedRef} from 'react'; -import {Keyboard, LogBox, View} from 'react-native'; +import React, {forwardRef, useEffect, useMemo, useRef, useState} from 'react'; import type {LayoutChangeEvent} from 'react-native'; -import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete'; +import {Keyboard, LogBox, View} from 'react-native'; import type {GooglePlaceData, GooglePlaceDetail} from 'react-native-google-places-autocomplete'; +import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete'; import ActivityIndicator from '@components/ActivityIndicator'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import LocationErrorMessage from '@components/LocationErrorMessage'; @@ -17,7 +17,6 @@ import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; import {setUserLocation} from '@libs/actions/UserLocation'; import {getCommandURL} from '@libs/ApiUtils'; -import {getCountryCode} from '@libs/CountryUtils'; import getCurrentPosition from '@libs/getCurrentPosition'; import type {GeolocationErrorCodeType} from '@libs/getCurrentPosition/getCurrentPosition.types'; import {getAddressComponents, getPlaceAutocompleteTerms} from '@libs/GooglePlacesUtils'; @@ -167,8 +166,7 @@ function AddressSearch( // Refer to https://github.com/Expensify/App/issues/15633 for more information. const {country: countryFallbackLongName = '', state: stateAutoCompleteFallback = '', city: cityAutocompleteFallback = ''} = getPlaceAutocompleteTerms(autocompleteData?.terms ?? []); - // Convert country fallback to country code - const countryFallback = getCountryCode(countryFallbackLongName); + const countryFallback = Object.keys(CONST.ALL_COUNTRIES).find((country) => country === countryFallbackLongName); // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing const country = countryPrimary || countryFallback || ''; diff --git a/src/components/CountryPicker/CountrySelectorModal.tsx b/src/components/CountryPicker/CountrySelectorModal.tsx index ebb048320e0..f394ba19bb5 100644 --- a/src/components/CountryPicker/CountrySelectorModal.tsx +++ b/src/components/CountryPicker/CountrySelectorModal.tsx @@ -7,9 +7,8 @@ import RadioListItem from '@components/SelectionListWithSections/RadioListItem'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCountryCode} from '@libs/CountryUtils'; -import searchOptions from '@libs/searchOptions'; import type {Option} from '@libs/searchOptions'; +import searchOptions from '@libs/searchOptions'; import StringUtils from '@libs/StringUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -38,20 +37,20 @@ function CountrySelectorModal({isVisible, currentCountry, onCountrySelected, onC const {translate} = useLocalize(); const [searchValue, debouncedSearchValue, setSearchValue] = useDebouncedState(''); - const countries = useMemo(() => { - const currentCountryCode = getCountryCode(currentCountry); - - return Object.keys(CONST.ALL_COUNTRIES).map((countryISO) => { - const countryName = translate(`allCountries.${countryISO}` as TranslationPaths); - return { - value: countryISO, - keyForList: countryISO, - text: countryName, - isSelected: currentCountryCode === countryISO, - searchValue: StringUtils.sanitizeString(`${countryISO}${countryName}`), - }; - }); - }, [translate, currentCountry]); + const countries = useMemo( + () => + Object.keys(CONST.ALL_COUNTRIES).map((countryISO) => { + const countryName = translate(`allCountries.${countryISO}` as TranslationPaths); + return { + value: countryISO, + keyForList: countryISO, + text: countryName, + isSelected: currentCountry === countryISO, + searchValue: StringUtils.sanitizeString(`${countryISO}${countryName}`), + }; + }), + [translate, currentCountry], + ); const searchResults = searchOptions(debouncedSearchValue, countries); const headerMessage = debouncedSearchValue.trim() && !searchResults.length ? translate('common.noResultsFound') : ''; diff --git a/src/components/CountryPicker/index.tsx b/src/components/CountryPicker/index.tsx index 99ccc7d7aa1..3feaa833ad9 100644 --- a/src/components/CountryPicker/index.tsx +++ b/src/components/CountryPicker/index.tsx @@ -1,7 +1,6 @@ import React, {useState} from 'react'; import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription'; import useLocalize from '@hooks/useLocalize'; -import {getCountryCode} from '@libs/CountryUtils'; import type {Option} from '@libs/searchOptions'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; @@ -31,14 +30,11 @@ function CountryPicker({value, errorText, onInputChange = () => {}}: CountryPick hidePickerModal(); }; - const countryCodeToUse = getCountryCode(value); - const title = countryCodeToUse ? translate(`allCountries.${countryCodeToUse}` as TranslationPaths) : undefined; - return ( <> setIsPickerVisible(true)} brickRoadIndicator={errorText ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined} diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index d22571e9637..77219d5bb06 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -1,21 +1,21 @@ -import CONST from '@src/CONST'; +import CONST, {Country} from '@src/CONST'; /** * Converts country name to country code if needed. * Handles the case where old data has "United States" instead of "US". */ -function getCountryCode(countryValue: string | undefined): string { +function getCountryCode(countryValue: string | undefined): Country | undefined | '' { if (!countryValue) { - return ''; + return countryValue as Country | undefined | ''; } for (const [code, name] of Object.entries(CONST.ALL_COUNTRIES)) { if (name === countryValue) { - return code; + return code as Country; } } - return countryValue; + return countryValue as Country; } /* eslint-disable import/prefer-default-export */ diff --git a/src/pages/AddressPage.tsx b/src/pages/AddressPage.tsx index 6e9d10747a9..6ce31e06e1f 100644 --- a/src/pages/AddressPage.tsx +++ b/src/pages/AddressPage.tsx @@ -7,6 +7,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; +import {getCountryCode} from '@libs/CountryUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {BackToParams} from '@libs/Navigation/types'; import type {FormOnyxValues} from '@src/components/Form/types'; @@ -102,7 +103,7 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true, backTo onSubmit={updateAddress} submitButtonText={translate('common.save')} city={city} - country={currentCountry} + country={getCountryCode(currentCountry)} onAddressChanged={handleAddressChange} state={state} street1={street1} diff --git a/src/pages/MissingPersonalDetails/substeps/Address.tsx b/src/pages/MissingPersonalDetails/substeps/Address.tsx index 7317dad5648..c11c1e5f104 100644 --- a/src/pages/MissingPersonalDetails/substeps/Address.tsx +++ b/src/pages/MissingPersonalDetails/substeps/Address.tsx @@ -12,6 +12,7 @@ import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; import usePersonalDetailsFormSubmit from '@hooks/usePersonalDetailsFormSubmit'; import useThemeStyles from '@hooks/useThemeStyles'; +import {getCountryCode} from '@libs/CountryUtils'; import {isRequiredFulfilled} from '@libs/ValidationUtils'; import type {CountryZipRegex, CustomSubStepProps} from '@pages/MissingPersonalDetails/types'; import CONST from '@src/CONST'; @@ -100,7 +101,7 @@ function AddressStep({isEditing, onNext, personalDetailsValues}: CustomSubStepPr return; } if (addressPartKey === INPUT_IDS.COUNTRY) { - setCurrentCountry(addressPart as Country | ''); + setCurrentCountry(getCountryCode(addressPart) as Country | ''); setState(''); setCity(''); setZipcode(''); diff --git a/tests/unit/CountryUtils.test.ts b/tests/unit/CountryUtils.test.ts index e6aba63d797..f682f0d8d3a 100644 --- a/tests/unit/CountryUtils.test.ts +++ b/tests/unit/CountryUtils.test.ts @@ -16,11 +16,6 @@ describe('CountryUtils', () => { expect(getCountryCode('Egypt')).toBe('EG'); }); - it('should return empty string for null or undefined values', () => { - expect(getCountryCode(undefined)).toBe(''); - expect(getCountryCode('')).toBe(''); - }); - it('should return original value for invalid country names or codes', () => { expect(getCountryCode('Invalid Country')).toBe('Invalid Country'); expect(getCountryCode('XX')).toBe('XX'); @@ -38,11 +33,11 @@ describe('CountryUtils', () => { expect(getCountryCode('United States')).toBe('US'); }); - it('should handle the specific case mentioned in the issue', () => { + it('should convert common country names to codes', () => { expect(getCountryCode('United States')).toBe('US'); }); - it('should work with all countries in CONST.ALL_COUNTRIES', () => { + it('should handle multiple country formats correctly', () => { const testCases = [ {name: 'Afghanistan', code: 'AF'}, {name: 'Australia', code: 'AU'}, From 3654b182d5de33fd90da2863c0659848f4c29956 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sun, 28 Sep 2025 21:24:07 +0200 Subject: [PATCH 07/25] fix eslint --- src/libs/CountryUtils.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index 77219d5bb06..17f0cc8b84b 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -1,4 +1,5 @@ -import CONST, {Country} from '@src/CONST'; +import CONST from '@src/CONST'; +import type {Country} from '@src/CONST'; /** * Converts country name to country code if needed. From 610325578324dc6f92b6296f0bd8e6e50f6e5406 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sun, 28 Sep 2025 21:28:40 +0200 Subject: [PATCH 08/25] fixing eslint --- src/pages/MissingPersonalDetails/substeps/Address.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/MissingPersonalDetails/substeps/Address.tsx b/src/pages/MissingPersonalDetails/substeps/Address.tsx index c11c1e5f104..99b8d3317e7 100644 --- a/src/pages/MissingPersonalDetails/substeps/Address.tsx +++ b/src/pages/MissingPersonalDetails/substeps/Address.tsx @@ -101,7 +101,7 @@ function AddressStep({isEditing, onNext, personalDetailsValues}: CustomSubStepPr return; } if (addressPartKey === INPUT_IDS.COUNTRY) { - setCurrentCountry(getCountryCode(addressPart) as Country | ''); + setCurrentCountry(getCountryCode(addressPart)!); setState(''); setCity(''); setZipcode(''); From 298d8c53fb3899c97cfda894760726649608b579 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sun, 28 Sep 2025 21:33:47 +0200 Subject: [PATCH 09/25] fixing eslint --- src/components/AddressSearch/index.tsx | 6 +++--- src/libs/CountryUtils.ts | 4 ++-- src/pages/MissingPersonalDetails/substeps/Address.tsx | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/AddressSearch/index.tsx b/src/components/AddressSearch/index.tsx index b67d632411b..4e0fe3d646f 100644 --- a/src/components/AddressSearch/index.tsx +++ b/src/components/AddressSearch/index.tsx @@ -1,9 +1,9 @@ -import type {ForwardedRef} from 'react'; import React, {forwardRef, useEffect, useMemo, useRef, useState} from 'react'; -import type {LayoutChangeEvent} from 'react-native'; +import type {ForwardedRef} from 'react'; import {Keyboard, LogBox, View} from 'react-native'; -import type {GooglePlaceData, GooglePlaceDetail} from 'react-native-google-places-autocomplete'; +import type {LayoutChangeEvent} from 'react-native'; import {GooglePlacesAutocomplete} from 'react-native-google-places-autocomplete'; +import type {GooglePlaceData, GooglePlaceDetail} from 'react-native-google-places-autocomplete'; import ActivityIndicator from '@components/ActivityIndicator'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import LocationErrorMessage from '@components/LocationErrorMessage'; diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index 17f0cc8b84b..08478752de0 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -5,9 +5,9 @@ import type {Country} from '@src/CONST'; * Converts country name to country code if needed. * Handles the case where old data has "United States" instead of "US". */ -function getCountryCode(countryValue: string | undefined): Country | undefined | '' { +function getCountryCode(countryValue: string | undefined): Country | '' { if (!countryValue) { - return countryValue as Country | undefined | ''; + return ''; } for (const [code, name] of Object.entries(CONST.ALL_COUNTRIES)) { diff --git a/src/pages/MissingPersonalDetails/substeps/Address.tsx b/src/pages/MissingPersonalDetails/substeps/Address.tsx index 99b8d3317e7..c11c1e5f104 100644 --- a/src/pages/MissingPersonalDetails/substeps/Address.tsx +++ b/src/pages/MissingPersonalDetails/substeps/Address.tsx @@ -101,7 +101,7 @@ function AddressStep({isEditing, onNext, personalDetailsValues}: CustomSubStepPr return; } if (addressPartKey === INPUT_IDS.COUNTRY) { - setCurrentCountry(getCountryCode(addressPart)!); + setCurrentCountry(getCountryCode(addressPart) as Country | ''); setState(''); setCity(''); setZipcode(''); From fddd420dda86be026ad68a6a104f7aab2f271049 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sun, 28 Sep 2025 21:36:05 +0200 Subject: [PATCH 10/25] eslint fix --- src/pages/MissingPersonalDetails/substeps/Address.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/MissingPersonalDetails/substeps/Address.tsx b/src/pages/MissingPersonalDetails/substeps/Address.tsx index c11c1e5f104..124cd6833aa 100644 --- a/src/pages/MissingPersonalDetails/substeps/Address.tsx +++ b/src/pages/MissingPersonalDetails/substeps/Address.tsx @@ -101,7 +101,7 @@ function AddressStep({isEditing, onNext, personalDetailsValues}: CustomSubStepPr return; } if (addressPartKey === INPUT_IDS.COUNTRY) { - setCurrentCountry(getCountryCode(addressPart) as Country | ''); + setCurrentCountry(getCountryCode(addressPart)); setState(''); setCity(''); setZipcode(''); From 1fdd0dd70881746c65385c141a4f92ca22e5feb2 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Tue, 30 Sep 2025 01:22:13 +0200 Subject: [PATCH 11/25] making getcountrycodes at the default values of usestates --- src/components/CountryPicker/CountrySelectorModal.tsx | 2 +- src/components/CountrySelector.tsx | 5 +---- src/pages/AddressPage.tsx | 6 +++--- src/pages/MissingPersonalDetails/substeps/Address.tsx | 4 ++-- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/components/CountryPicker/CountrySelectorModal.tsx b/src/components/CountryPicker/CountrySelectorModal.tsx index f394ba19bb5..a886ccdd5d7 100644 --- a/src/components/CountryPicker/CountrySelectorModal.tsx +++ b/src/components/CountryPicker/CountrySelectorModal.tsx @@ -7,8 +7,8 @@ import RadioListItem from '@components/SelectionListWithSections/RadioListItem'; import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import type {Option} from '@libs/searchOptions'; import searchOptions from '@libs/searchOptions'; +import type {Option} from '@libs/searchOptions'; import StringUtils from '@libs/StringUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; diff --git a/src/components/CountrySelector.tsx b/src/components/CountrySelector.tsx index 4a653b2d0f3..094f529c66b 100644 --- a/src/components/CountrySelector.tsx +++ b/src/components/CountrySelector.tsx @@ -5,11 +5,9 @@ import type {View} from 'react-native'; import useGeographicalStateAndCountryFromRoute from '@hooks/useGeographicalStateAndCountryFromRoute'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCountryCode} from '@libs/CountryUtils'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; -import type {TranslationPaths} from '@src/languages/types'; import ROUTES from '@src/ROUTES'; import MenuItemWithTopDescription from './MenuItemWithTopDescription'; @@ -36,8 +34,7 @@ function CountrySelector({errorText = '', value: countryCode, onInputChange = () const {translate} = useLocalize(); const {country: countryFromUrl} = useGeographicalStateAndCountryFromRoute(); - const countryCodeToUse = getCountryCode(countryCode); - const title = countryCodeToUse ? translate(`allCountries.${countryCodeToUse}` as TranslationPaths) : ''; + const title = countryCode ? translate(`allCountries.${countryCode}`) : ''; const countryTitleDescStyle = title.length === 0 ? styles.textNormal : null; const didOpenCountrySelector = useRef(false); diff --git a/src/pages/AddressPage.tsx b/src/pages/AddressPage.tsx index 6ce31e06e1f..bcff7a6c2f9 100644 --- a/src/pages/AddressPage.tsx +++ b/src/pages/AddressPage.tsx @@ -37,7 +37,7 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true, backTo // Check if country is valid const {street} = address ?? {}; const [street1, street2] = street ? street.split('\n') : [undefined, undefined]; - const [currentCountry, setCurrentCountry] = useState(address?.country ?? defaultCountry); + const [currentCountry, setCurrentCountry] = useState(getCountryCode(address?.country ?? defaultCountry)); const [state, setState] = useState(address?.state); const [city, setCity] = useState(address?.city); const [zipcode, setZipcode] = useState(address?.zip); @@ -47,7 +47,7 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true, backTo return; } setState(address.state); - setCurrentCountry(address.country); + setCurrentCountry(getCountryCode(address.country)); setCity(address.city); setZipcode(address.zip); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps @@ -103,7 +103,7 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true, backTo onSubmit={updateAddress} submitButtonText={translate('common.save')} city={city} - country={getCountryCode(currentCountry)} + country={currentCountry} onAddressChanged={handleAddressChange} state={state} street1={street1} diff --git a/src/pages/MissingPersonalDetails/substeps/Address.tsx b/src/pages/MissingPersonalDetails/substeps/Address.tsx index 124cd6833aa..6bc8a33e41f 100644 --- a/src/pages/MissingPersonalDetails/substeps/Address.tsx +++ b/src/pages/MissingPersonalDetails/substeps/Address.tsx @@ -27,7 +27,7 @@ function AddressStep({isEditing, onNext, personalDetailsValues}: CustomSubStepPr const {translate} = useLocalize(); const styles = useThemeStyles(); - const [currentCountry, setCurrentCountry] = useState(personalDetailsValues[INPUT_IDS.COUNTRY]); + const [currentCountry, setCurrentCountry] = useState(getCountryCode(personalDetailsValues[INPUT_IDS.COUNTRY])); const [state, setState] = useState(personalDetailsValues[INPUT_IDS.STATE]); const [city, setCity] = useState(personalDetailsValues[INPUT_IDS.CITY]); const [zipcode, setZipcode] = useState(personalDetailsValues[INPUT_IDS.ZIP_POST_CODE]); @@ -101,7 +101,7 @@ function AddressStep({isEditing, onNext, personalDetailsValues}: CustomSubStepPr return; } if (addressPartKey === INPUT_IDS.COUNTRY) { - setCurrentCountry(getCountryCode(addressPart)); + setCurrentCountry(addressPart as Country | ''); setState(''); setCity(''); setZipcode(''); From 9c798d8c06ddd0d49ad9ae059b49e5773c98f50a Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Tue, 30 Sep 2025 01:27:38 +0200 Subject: [PATCH 12/25] fixing eslint --- src/pages/AddressPage.tsx | 2 +- src/pages/MissingPersonalDetails/substeps/Address.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/AddressPage.tsx b/src/pages/AddressPage.tsx index bcff7a6c2f9..f7e112ab124 100644 --- a/src/pages/AddressPage.tsx +++ b/src/pages/AddressPage.tsx @@ -37,7 +37,7 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true, backTo // Check if country is valid const {street} = address ?? {}; const [street1, street2] = street ? street.split('\n') : [undefined, undefined]; - const [currentCountry, setCurrentCountry] = useState(getCountryCode(address?.country ?? defaultCountry)); + const [currentCountry, setCurrentCountry] = useState(() => getCountryCode(address?.country ?? defaultCountry)); const [state, setState] = useState(address?.state); const [city, setCity] = useState(address?.city); const [zipcode, setZipcode] = useState(address?.zip); diff --git a/src/pages/MissingPersonalDetails/substeps/Address.tsx b/src/pages/MissingPersonalDetails/substeps/Address.tsx index 6bc8a33e41f..bd7f654940b 100644 --- a/src/pages/MissingPersonalDetails/substeps/Address.tsx +++ b/src/pages/MissingPersonalDetails/substeps/Address.tsx @@ -27,7 +27,7 @@ function AddressStep({isEditing, onNext, personalDetailsValues}: CustomSubStepPr const {translate} = useLocalize(); const styles = useThemeStyles(); - const [currentCountry, setCurrentCountry] = useState(getCountryCode(personalDetailsValues[INPUT_IDS.COUNTRY])); + const [currentCountry, setCurrentCountry] = useState(() => getCountryCode(personalDetailsValues[INPUT_IDS.COUNTRY])); const [state, setState] = useState(personalDetailsValues[INPUT_IDS.STATE]); const [city, setCity] = useState(personalDetailsValues[INPUT_IDS.CITY]); const [zipcode, setZipcode] = useState(personalDetailsValues[INPUT_IDS.ZIP_POST_CODE]); From 7be77e59fdc182af4283f91a4944deaefea63277 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 3 Oct 2025 03:07:42 +0200 Subject: [PATCH 13/25] remove getCountryCode from Address and Addresspage and add it to the personal address page --- src/pages/AddressPage.tsx | 5 ++--- .../MissingPersonalDetails/substeps/Address.tsx | 2 +- .../Profile/PersonalDetails/PersonalAddressPage.tsx | 13 ++++++++++++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/pages/AddressPage.tsx b/src/pages/AddressPage.tsx index f7e112ab124..6e9d10747a9 100644 --- a/src/pages/AddressPage.tsx +++ b/src/pages/AddressPage.tsx @@ -7,7 +7,6 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCountryCode} from '@libs/CountryUtils'; import Navigation from '@libs/Navigation/Navigation'; import type {BackToParams} from '@libs/Navigation/types'; import type {FormOnyxValues} from '@src/components/Form/types'; @@ -37,7 +36,7 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true, backTo // Check if country is valid const {street} = address ?? {}; const [street1, street2] = street ? street.split('\n') : [undefined, undefined]; - const [currentCountry, setCurrentCountry] = useState(() => getCountryCode(address?.country ?? defaultCountry)); + const [currentCountry, setCurrentCountry] = useState(address?.country ?? defaultCountry); const [state, setState] = useState(address?.state); const [city, setCity] = useState(address?.city); const [zipcode, setZipcode] = useState(address?.zip); @@ -47,7 +46,7 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true, backTo return; } setState(address.state); - setCurrentCountry(getCountryCode(address.country)); + setCurrentCountry(address.country); setCity(address.city); setZipcode(address.zip); // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps diff --git a/src/pages/MissingPersonalDetails/substeps/Address.tsx b/src/pages/MissingPersonalDetails/substeps/Address.tsx index bd7f654940b..966c6b8d406 100644 --- a/src/pages/MissingPersonalDetails/substeps/Address.tsx +++ b/src/pages/MissingPersonalDetails/substeps/Address.tsx @@ -27,7 +27,7 @@ function AddressStep({isEditing, onNext, personalDetailsValues}: CustomSubStepPr const {translate} = useLocalize(); const styles = useThemeStyles(); - const [currentCountry, setCurrentCountry] = useState(() => getCountryCode(personalDetailsValues[INPUT_IDS.COUNTRY])); + const [currentCountry, setCurrentCountry] = useState(personalDetailsValues[INPUT_IDS.COUNTRY]); const [state, setState] = useState(personalDetailsValues[INPUT_IDS.STATE]); const [city, setCity] = useState(personalDetailsValues[INPUT_IDS.CITY]); const [zipcode, setZipcode] = useState(personalDetailsValues[INPUT_IDS.ZIP_POST_CODE]); diff --git a/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx b/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx index f436add45d6..4f16e3a4d72 100644 --- a/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx @@ -2,6 +2,7 @@ import React, {useMemo} from 'react'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import {getCountryCode} from '@libs/CountryUtils'; import {getCurrentAddress} from '@libs/PersonalDetailsUtils'; import AddressPage from '@pages/AddressPage'; import type {FormOnyxValues} from '@src/components/Form/types'; @@ -33,7 +34,17 @@ function PersonalAddressPage() { const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); const [defaultCountry, defaultCountryStatus] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: true}); const isLoading = isLoadingOnyxValue(defaultCountryStatus); - const address = useMemo(() => getCurrentAddress(privatePersonalDetails), [privatePersonalDetails]); + const address = useMemo(() => { + const currentAddress = getCurrentAddress(privatePersonalDetails); + if (currentAddress?.country) { + const normalizedCountry = getCountryCode(currentAddress.country) || currentAddress.country; + return { + ...currentAddress, + country: normalizedCountry, + }; + } + return currentAddress; + }, [privatePersonalDetails]); if (isLoading) { return ; } From 1196f11b0cc67fe26a42b36370f244e4175fcda1 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 3 Oct 2025 03:08:26 +0200 Subject: [PATCH 14/25] minor edit --- src/pages/MissingPersonalDetails/substeps/Address.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pages/MissingPersonalDetails/substeps/Address.tsx b/src/pages/MissingPersonalDetails/substeps/Address.tsx index 966c6b8d406..7317dad5648 100644 --- a/src/pages/MissingPersonalDetails/substeps/Address.tsx +++ b/src/pages/MissingPersonalDetails/substeps/Address.tsx @@ -12,7 +12,6 @@ import TextInput from '@components/TextInput'; import useLocalize from '@hooks/useLocalize'; import usePersonalDetailsFormSubmit from '@hooks/usePersonalDetailsFormSubmit'; import useThemeStyles from '@hooks/useThemeStyles'; -import {getCountryCode} from '@libs/CountryUtils'; import {isRequiredFulfilled} from '@libs/ValidationUtils'; import type {CountryZipRegex, CustomSubStepProps} from '@pages/MissingPersonalDetails/types'; import CONST from '@src/CONST'; From 48b53e56dc8c2b49d3117ef25b105298d1f14c39 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 3 Oct 2025 03:10:00 +0200 Subject: [PATCH 15/25] adding getCountryCode to MissingPersonalDetailsContent --- .../MissingPersonalDetailsContent.tsx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx b/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx index 161e357f45a..2e0dc9c017e 100644 --- a/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx +++ b/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx @@ -12,6 +12,7 @@ import useSubStep from '@hooks/useSubStep'; import useThemeStyles from '@hooks/useThemeStyles'; import {clearDraftValues} from '@libs/actions/FormActions'; import {updatePersonalDetailsAndShipExpensifyCards} from '@libs/actions/PersonalDetails'; +import {getCountryCode} from '@libs/CountryUtils'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -41,7 +42,18 @@ function MissingPersonalDetailsContent({privatePersonalDetails, draftValues}: Mi const ref: ForwardedRef = useRef(null); - const values = useMemo(() => getSubstepValues(privatePersonalDetails, draftValues), [privatePersonalDetails, draftValues]); + const values = useMemo(() => { + const substepValues = getSubstepValues(privatePersonalDetails, draftValues); + + if (substepValues.country) { + const normalizedCountry = getCountryCode(substepValues.country) || substepValues.country; + return { + ...substepValues, + country: normalizedCountry, + }; + } + return substepValues; + }, [privatePersonalDetails, draftValues]); const startFrom = useMemo(() => getInitialSubstep(values), [values]); From 41e1bacdce0fd31e1a78ae87077f4b92272a4450 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 3 Oct 2025 11:37:56 +0200 Subject: [PATCH 16/25] using a common hook --- src/hooks/useNormalizedCountry.ts | 22 +++++++++++++++++++ src/libs/CountryUtils.ts | 8 ++----- .../MissingPersonalDetailsContent.tsx | 16 +++----------- .../PersonalDetails/PersonalAddressPage.tsx | 15 +++---------- 4 files changed, 30 insertions(+), 31 deletions(-) create mode 100644 src/hooks/useNormalizedCountry.ts diff --git a/src/hooks/useNormalizedCountry.ts b/src/hooks/useNormalizedCountry.ts new file mode 100644 index 00000000000..b3460e56426 --- /dev/null +++ b/src/hooks/useNormalizedCountry.ts @@ -0,0 +1,22 @@ +import {useMemo} from 'react'; +import {getCountryCode} from '@libs/CountryUtils'; + +/** + * Hook to normalize country data by converting country names to country codes. + * Handles the case where old data has "United States" instead of "US". + */ +function useNormalizedCountry(data: T | undefined): T | undefined { + return useMemo(() => { + if (!data?.country) { + return data; + } + + const normalizedCountry = getCountryCode(data.country); + return { + ...data, + country: normalizedCountry, + }; + }, [data]); +} + +export default useNormalizedCountry; diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index 08478752de0..2178382520f 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -5,18 +5,14 @@ import type {Country} from '@src/CONST'; * Converts country name to country code if needed. * Handles the case where old data has "United States" instead of "US". */ -function getCountryCode(countryValue: string | undefined): Country | '' { - if (!countryValue) { - return ''; - } - +function getCountryCode(countryValue: string | undefined): Country | undefined { for (const [code, name] of Object.entries(CONST.ALL_COUNTRIES)) { if (name === countryValue) { return code as Country; } } - return countryValue as Country; + return countryValue as Country | undefined; } /* eslint-disable import/prefer-default-export */ diff --git a/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx b/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx index 2e0dc9c017e..e06bb774288 100644 --- a/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx +++ b/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx @@ -7,12 +7,12 @@ import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader'; import type {InteractiveStepSubHeaderHandle} from '@components/InteractiveStepSubHeader'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; +import useNormalizedCountry from '@hooks/useNormalizedCountry'; import useOnyx from '@hooks/useOnyx'; import useSubStep from '@hooks/useSubStep'; import useThemeStyles from '@hooks/useThemeStyles'; import {clearDraftValues} from '@libs/actions/FormActions'; import {updatePersonalDetailsAndShipExpensifyCards} from '@libs/actions/PersonalDetails'; -import {getCountryCode} from '@libs/CountryUtils'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -42,18 +42,8 @@ function MissingPersonalDetailsContent({privatePersonalDetails, draftValues}: Mi const ref: ForwardedRef = useRef(null); - const values = useMemo(() => { - const substepValues = getSubstepValues(privatePersonalDetails, draftValues); - - if (substepValues.country) { - const normalizedCountry = getCountryCode(substepValues.country) || substepValues.country; - return { - ...substepValues, - country: normalizedCountry, - }; - } - return substepValues; - }, [privatePersonalDetails, draftValues]); + const rawValues = useMemo(() => getSubstepValues(privatePersonalDetails, draftValues), [privatePersonalDetails, draftValues]); + const values = useNormalizedCountry(rawValues)!; const startFrom = useMemo(() => getInitialSubstep(values), [values]); diff --git a/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx b/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx index 4f16e3a4d72..5e23224ad92 100644 --- a/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx @@ -1,8 +1,8 @@ import React, {useMemo} from 'react'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import useLocalize from '@hooks/useLocalize'; +import useNormalizedCountry from '@hooks/useNormalizedCountry'; import useOnyx from '@hooks/useOnyx'; -import {getCountryCode} from '@libs/CountryUtils'; import {getCurrentAddress} from '@libs/PersonalDetailsUtils'; import AddressPage from '@pages/AddressPage'; import type {FormOnyxValues} from '@src/components/Form/types'; @@ -34,17 +34,8 @@ function PersonalAddressPage() { const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); const [defaultCountry, defaultCountryStatus] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: true}); const isLoading = isLoadingOnyxValue(defaultCountryStatus); - const address = useMemo(() => { - const currentAddress = getCurrentAddress(privatePersonalDetails); - if (currentAddress?.country) { - const normalizedCountry = getCountryCode(currentAddress.country) || currentAddress.country; - return { - ...currentAddress, - country: normalizedCountry, - }; - } - return currentAddress; - }, [privatePersonalDetails]); + const rawAddress = useMemo(() => getCurrentAddress(privatePersonalDetails), [privatePersonalDetails]); + const address = useNormalizedCountry(rawAddress); if (isLoading) { return ; } From a223492d432c599e20c9f1d429f770fffb632ae4 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 3 Oct 2025 11:50:56 +0200 Subject: [PATCH 17/25] fixing eslint --- .../MissingPersonalDetails/MissingPersonalDetailsContent.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx b/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx index e06bb774288..96f3d056cea 100644 --- a/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx +++ b/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx @@ -43,7 +43,7 @@ function MissingPersonalDetailsContent({privatePersonalDetails, draftValues}: Mi const ref: ForwardedRef = useRef(null); const rawValues = useMemo(() => getSubstepValues(privatePersonalDetails, draftValues), [privatePersonalDetails, draftValues]); - const values = useNormalizedCountry(rawValues)!; + const values = useNormalizedCountry(rawValues) as PersonalDetailsForm; const startFrom = useMemo(() => getInitialSubstep(values), [values]); From df2d38eddbcf99da57fa1f55ab76aa22ec40170f Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Fri, 3 Oct 2025 12:10:32 +0200 Subject: [PATCH 18/25] fixing eslint --- src/hooks/useNormalizedCountry.ts | 2 ++ .../MissingPersonalDetails/MissingPersonalDetailsContent.tsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/hooks/useNormalizedCountry.ts b/src/hooks/useNormalizedCountry.ts index b3460e56426..0c25fe74d47 100644 --- a/src/hooks/useNormalizedCountry.ts +++ b/src/hooks/useNormalizedCountry.ts @@ -5,6 +5,8 @@ import {getCountryCode} from '@libs/CountryUtils'; * Hook to normalize country data by converting country names to country codes. * Handles the case where old data has "United States" instead of "US". */ +function useNormalizedCountry(data: T): T; +function useNormalizedCountry(data: T | undefined): T | undefined; function useNormalizedCountry(data: T | undefined): T | undefined { return useMemo(() => { if (!data?.country) { diff --git a/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx b/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx index 96f3d056cea..7f86c14329a 100644 --- a/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx +++ b/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx @@ -43,7 +43,7 @@ function MissingPersonalDetailsContent({privatePersonalDetails, draftValues}: Mi const ref: ForwardedRef = useRef(null); const rawValues = useMemo(() => getSubstepValues(privatePersonalDetails, draftValues), [privatePersonalDetails, draftValues]); - const values = useNormalizedCountry(rawValues) as PersonalDetailsForm; + const values = useNormalizedCountry(rawValues); const startFrom = useMemo(() => getInitialSubstep(values), [values]); From a6f2b605ae28a783ddaacdd66e18d201e362672e Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sun, 5 Oct 2025 07:23:37 +0200 Subject: [PATCH 19/25] remvoing hook and using util funciton instead --- src/hooks/useNormalizedCountry.ts | 24 ------------------- src/libs/CountryUtils.ts | 17 ++++++++++--- .../MissingPersonalDetailsContent.tsx | 5 ++-- .../PersonalDetails/PersonalAddressPage.tsx | 5 ++-- 4 files changed, 18 insertions(+), 33 deletions(-) delete mode 100644 src/hooks/useNormalizedCountry.ts diff --git a/src/hooks/useNormalizedCountry.ts b/src/hooks/useNormalizedCountry.ts deleted file mode 100644 index 0c25fe74d47..00000000000 --- a/src/hooks/useNormalizedCountry.ts +++ /dev/null @@ -1,24 +0,0 @@ -import {useMemo} from 'react'; -import {getCountryCode} from '@libs/CountryUtils'; - -/** - * Hook to normalize country data by converting country names to country codes. - * Handles the case where old data has "United States" instead of "US". - */ -function useNormalizedCountry(data: T): T; -function useNormalizedCountry(data: T | undefined): T | undefined; -function useNormalizedCountry(data: T | undefined): T | undefined { - return useMemo(() => { - if (!data?.country) { - return data; - } - - const normalizedCountry = getCountryCode(data.country); - return { - ...data, - country: normalizedCountry, - }; - }, [data]); -} - -export default useNormalizedCountry; diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index 2178382520f..ce320f3ade1 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -2,9 +2,21 @@ import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; /** - * Converts country name to country code if needed. + * Normalizes the address containing a country field by converting country names to country codes. * Handles the case where old data has "United States" instead of "US". */ +function normalizeCountryCode(data: T | undefined): T | undefined { + if (!data?.country) { + return data; + } + + const normalizedCountry = getCountryCode(data.country); + return { + ...data, + country: normalizedCountry, + }; +} + function getCountryCode(countryValue: string | undefined): Country | undefined { for (const [code, name] of Object.entries(CONST.ALL_COUNTRIES)) { if (name === countryValue) { @@ -15,5 +27,4 @@ function getCountryCode(countryValue: string | undefined): Country | undefined { return countryValue as Country | undefined; } -/* eslint-disable import/prefer-default-export */ -export {getCountryCode}; +export {normalizeCountryCode}; diff --git a/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx b/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx index 7f86c14329a..dec00be5e97 100644 --- a/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx +++ b/src/pages/MissingPersonalDetails/MissingPersonalDetailsContent.tsx @@ -7,12 +7,12 @@ import InteractiveStepSubHeader from '@components/InteractiveStepSubHeader'; import type {InteractiveStepSubHeaderHandle} from '@components/InteractiveStepSubHeader'; import ScreenWrapper from '@components/ScreenWrapper'; import useLocalize from '@hooks/useLocalize'; -import useNormalizedCountry from '@hooks/useNormalizedCountry'; import useOnyx from '@hooks/useOnyx'; import useSubStep from '@hooks/useSubStep'; import useThemeStyles from '@hooks/useThemeStyles'; import {clearDraftValues} from '@libs/actions/FormActions'; import {updatePersonalDetailsAndShipExpensifyCards} from '@libs/actions/PersonalDetails'; +import {normalizeCountryCode} from '@libs/CountryUtils'; import Navigation from '@libs/Navigation/Navigation'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -42,8 +42,7 @@ function MissingPersonalDetailsContent({privatePersonalDetails, draftValues}: Mi const ref: ForwardedRef = useRef(null); - const rawValues = useMemo(() => getSubstepValues(privatePersonalDetails, draftValues), [privatePersonalDetails, draftValues]); - const values = useNormalizedCountry(rawValues); + const values = useMemo(() => normalizeCountryCode(getSubstepValues(privatePersonalDetails, draftValues)) as PersonalDetailsForm, [privatePersonalDetails, draftValues]); const startFrom = useMemo(() => getInitialSubstep(values), [values]); diff --git a/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx b/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx index 5e23224ad92..8d819cba418 100644 --- a/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx @@ -1,8 +1,8 @@ import React, {useMemo} from 'react'; import FullScreenLoadingIndicator from '@components/FullscreenLoadingIndicator'; import useLocalize from '@hooks/useLocalize'; -import useNormalizedCountry from '@hooks/useNormalizedCountry'; import useOnyx from '@hooks/useOnyx'; +import {normalizeCountryCode} from '@libs/CountryUtils'; import {getCurrentAddress} from '@libs/PersonalDetailsUtils'; import AddressPage from '@pages/AddressPage'; import type {FormOnyxValues} from '@src/components/Form/types'; @@ -34,8 +34,7 @@ function PersonalAddressPage() { const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); const [defaultCountry, defaultCountryStatus] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: true}); const isLoading = isLoadingOnyxValue(defaultCountryStatus); - const rawAddress = useMemo(() => getCurrentAddress(privatePersonalDetails), [privatePersonalDetails]); - const address = useNormalizedCountry(rawAddress); + const address = useMemo(() => normalizeCountryCode(getCurrentAddress(privatePersonalDetails)), [privatePersonalDetails]); if (isLoading) { return ; } From 400645a0d847532d1f2a28761d33bc86503232b7 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sun, 5 Oct 2025 07:24:16 +0200 Subject: [PATCH 20/25] fixing eslint --- src/libs/CountryUtils.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index ce320f3ade1..bd8a9f30da6 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -27,4 +27,5 @@ function getCountryCode(countryValue: string | undefined): Country | undefined { return countryValue as Country | undefined; } +/* eslint-disable import/prefer-default-export */ export {normalizeCountryCode}; From 97804d3223ad969aa2258d546a207ce7ae413d8d Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sun, 5 Oct 2025 07:34:58 +0200 Subject: [PATCH 21/25] fixing eslint types --- src/libs/CountryUtils.ts | 9 ++++++--- .../Profile/PersonalDetails/PersonalAddressPage.tsx | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index bd8a9f30da6..478fccb92e9 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -1,11 +1,15 @@ import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; +import {PersonalDetailsForm} from '@src/types/form'; +import {Address} from '@src/types/onyx/PrivatePersonalDetails'; + +type AddressType = PersonalDetailsForm | Address | undefined; /** * Normalizes the address containing a country field by converting country names to country codes. * Handles the case where old data has "United States" instead of "US". */ -function normalizeCountryCode(data: T | undefined): T | undefined { +function normalizeCountryCode(data: AddressType): AddressType { if (!data?.country) { return data; } @@ -13,7 +17,7 @@ function normalizeCountryCode(data: T | undefined) const normalizedCountry = getCountryCode(data.country); return { ...data, - country: normalizedCountry, + country: normalizedCountry ?? '', }; } @@ -26,6 +30,5 @@ function getCountryCode(countryValue: string | undefined): Country | undefined { return countryValue as Country | undefined; } - /* eslint-disable import/prefer-default-export */ export {normalizeCountryCode}; diff --git a/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx b/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx index 8d819cba418..6f1c3180424 100644 --- a/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx +++ b/src/pages/settings/Profile/PersonalDetails/PersonalAddressPage.tsx @@ -34,7 +34,7 @@ function PersonalAddressPage() { const [isLoadingApp] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); const [defaultCountry, defaultCountryStatus] = useOnyx(ONYXKEYS.COUNTRY, {canBeMissing: true}); const isLoading = isLoadingOnyxValue(defaultCountryStatus); - const address = useMemo(() => normalizeCountryCode(getCurrentAddress(privatePersonalDetails)), [privatePersonalDetails]); + const address = useMemo(() => normalizeCountryCode(getCurrentAddress(privatePersonalDetails)) as Address, [privatePersonalDetails]); if (isLoading) { return ; } From 7f3c67b7b8d329eda47267183fbecddc6eb891fb Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sun, 5 Oct 2025 09:52:21 +0200 Subject: [PATCH 22/25] fixing eslint --- src/libs/CountryUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index 478fccb92e9..ab6acd13847 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -1,7 +1,7 @@ import CONST from '@src/CONST'; import type {Country} from '@src/CONST'; -import {PersonalDetailsForm} from '@src/types/form'; -import {Address} from '@src/types/onyx/PrivatePersonalDetails'; +import type {PersonalDetailsForm} from '@src/types/form'; +import type {Address} from '@src/types/onyx/PrivatePersonalDetails'; type AddressType = PersonalDetailsForm | Address | undefined; @@ -30,5 +30,5 @@ function getCountryCode(countryValue: string | undefined): Country | undefined { return countryValue as Country | undefined; } -/* eslint-disable import/prefer-default-export */ -export {normalizeCountryCode}; + +export {normalizeCountryCode, getCountryCode}; From 329141cf7e52bcfc463a1a3f2c724305c7a1a4dc Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Sun, 5 Oct 2025 09:56:11 +0200 Subject: [PATCH 23/25] fixing types --- src/libs/CountryUtils.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/libs/CountryUtils.ts b/src/libs/CountryUtils.ts index ab6acd13847..cc4d6a2c327 100644 --- a/src/libs/CountryUtils.ts +++ b/src/libs/CountryUtils.ts @@ -15,9 +15,14 @@ function normalizeCountryCode(data: AddressType): AddressType { } const normalizedCountry = getCountryCode(data.country); + + if (!normalizedCountry) { + return data; + } + return { ...data, - country: normalizedCountry ?? '', + country: normalizedCountry, }; } From b3356222587347efd0e840161155a0f274c9b43d Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab <59809993+abzokhattab@users.noreply.github.com> Date: Mon, 6 Oct 2025 17:18:11 +0200 Subject: [PATCH 24/25] adding tests for normalizeCountryCode --- tests/unit/CountryUtils.test.ts | 95 ++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/tests/unit/CountryUtils.test.ts b/tests/unit/CountryUtils.test.ts index f682f0d8d3a..efd6cc9df3b 100644 --- a/tests/unit/CountryUtils.test.ts +++ b/tests/unit/CountryUtils.test.ts @@ -1,4 +1,5 @@ -import {getCountryCode} from '@libs/CountryUtils'; +import {getCountryCode, normalizeCountryCode} from '@libs/CountryUtils'; +import type {Address} from '@src/types/onyx/PrivatePersonalDetails'; describe('CountryUtils', () => { describe('getCountryCode', () => { @@ -57,4 +58,96 @@ describe('CountryUtils', () => { }); }); }); + + describe('normalizeCountryCode', () => { + it('should return undefined when data is undefined', () => { + expect(normalizeCountryCode(undefined)).toBeUndefined(); + }); + + it('should return data unchanged when country field is missing', () => { + const data = {street: '123 Main St', city: 'New York', state: 'NY'}; + expect(normalizeCountryCode(data)).toEqual(data); + }); + + it('should return data unchanged when country is undefined', () => { + const data = {street: '123 Main St', city: 'New York', state: 'NY', country: undefined}; + expect(normalizeCountryCode(data)).toEqual(data); + }); + + it('should convert country name to country code', () => { + const data = {street: '123 Main St', city: 'New York', state: 'NY', country: 'United States'} as unknown as Address; + const result = normalizeCountryCode(data); + expect(result).toEqual({street: '123 Main St', city: 'New York', state: 'NY', country: 'US'}); + }); + + it('should preserve country code if already a valid code', () => { + const data: Address = {street: '456 Oak Ave', city: 'Toronto', state: 'ON', country: 'CA'}; + const result = normalizeCountryCode(data); + expect(result).toEqual({street: '456 Oak Ave', city: 'Toronto', state: 'ON', country: 'CA'}); + }); + + it('should handle multiple country name conversions', () => { + const testCases = [ + {input: 'United States', expected: 'US'}, + {input: 'Canada', expected: 'CA'}, + {input: 'United Kingdom', expected: 'GB'}, + {input: 'Germany', expected: 'DE'}, + {input: 'France', expected: 'FR'}, + {input: 'Japan', expected: 'JP'}, + {input: 'Australia', expected: 'AU'}, + ]; + + testCases.forEach(({input, expected}) => { + const data = {street: '789 Test St', city: 'Test City', country: input} as unknown as Address; + const result = normalizeCountryCode(data); + expect(result?.country).toBe(expected); + }); + }); + + it('should preserve invalid country values', () => { + const data = {street: '789 Test St', city: 'Test City', country: 'Invalid Country'} as unknown as Address; + const result = normalizeCountryCode(data); + expect(result).toEqual({street: '789 Test St', city: 'Test City', country: 'Invalid Country'}); + }); + + it('should handle special characters in country names', () => { + const data = {street: 'Zmaja od Bosne', city: 'Sarajevo', country: 'Bosnia & Herzegovina'} as unknown as Address; + const result = normalizeCountryCode(data); + expect(result).toEqual({street: 'Zmaja od Bosne', city: 'Sarajevo', country: 'BA'}); + }); + + it('should be case sensitive when normalizing country names', () => { + const dataLowerCase = {street: '789 Test St', city: 'Test City', country: 'united states'} as unknown as Address; + const dataUpperCase = {street: '789 Test St', city: 'Test City', country: 'UNITED STATES'} as unknown as Address; + const dataProperCase = {street: '789 Test St', city: 'Test City', country: 'United States'} as unknown as Address; + + expect(normalizeCountryCode(dataLowerCase)).toEqual({street: '789 Test St', city: 'Test City', country: 'united states'}); + expect(normalizeCountryCode(dataUpperCase)).toEqual({street: '789 Test St', city: 'Test City', country: 'UNITED STATES'}); + expect(normalizeCountryCode(dataProperCase)).toEqual({street: '789 Test St', city: 'Test City', country: 'US'}); + }); + + it('should preserve all other fields in the data object', () => { + const data = { + street: '123 Main St', + city: 'Los Angeles', + state: 'CA', + zipCode: '90001', + country: 'United States', + } as unknown as Address; + const result = normalizeCountryCode(data); + expect(result).toEqual({ + street: '123 Main St', + city: 'Los Angeles', + state: 'CA', + zipCode: '90001', + country: 'US', + }); + }); + + it('should handle MISSING TRANSLATION value', () => { + const data = {street: '789 Test St', city: 'Test City', country: 'MISSING TRANSLATION'} as unknown as Address; + const result = normalizeCountryCode(data); + expect(result).toEqual({street: '789 Test St', city: 'Test City', country: 'MISSING TRANSLATION'}); + }); + }); }); From 89724b488f17448e24e2b86778146c6ad37b6644 Mon Sep 17 00:00:00 2001 From: Abdelrahman Khattab Date: Mon, 6 Oct 2025 18:06:06 +0200 Subject: [PATCH 25/25] fixing cspell --- tests/unit/CountryUtils.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/CountryUtils.test.ts b/tests/unit/CountryUtils.test.ts index efd6cc9df3b..2d01ca76d1f 100644 --- a/tests/unit/CountryUtils.test.ts +++ b/tests/unit/CountryUtils.test.ts @@ -111,9 +111,9 @@ describe('CountryUtils', () => { }); it('should handle special characters in country names', () => { - const data = {street: 'Zmaja od Bosne', city: 'Sarajevo', country: 'Bosnia & Herzegovina'} as unknown as Address; + const data = {street: '123 Main St', city: 'Sarajevo', country: 'Bosnia & Herzegovina'} as unknown as Address; const result = normalizeCountryCode(data); - expect(result).toEqual({street: 'Zmaja od Bosne', city: 'Sarajevo', country: 'BA'}); + expect(result).toEqual({street: '123 Main St', city: 'Sarajevo', country: 'BA'}); }); it('should be case sensitive when normalizing country names', () => {