From 24b67f9c231f0ee96546baa12478b75fd7e753db Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Fri, 20 Jun 2025 05:25:04 +0500 Subject: [PATCH 01/18] fix: auto-format expiration date input in AddPaymentCard form --- .../AddPaymentCard/PaymentCardForm.tsx | 47 ++++++++++++++++++- src/languages/en.ts | 2 +- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 07341b6803d41..f880489bd62e6 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -140,6 +140,49 @@ function PaymentCardForm({ const label = CARD_LABELS[isDebitCard ? CARD_TYPES.DEBIT_CARD : CARD_TYPES.PAYMENT_CARD]; const cardNumberRef = useRef(null); + const [expirationDate, setExpirationDate] = useState(data?.expirationDate); + + const previousvalueRef = useRef(''); + + const onChangeExpirationDate = useCallback((newValue: string) => { + if (typeof newValue !== 'string') { + return; + } + + let value = newValue.replace(CONST.REGEX.NON_NUMERIC, ''); + + let didAutoCorrectFirstDigit = false; + + if (value.length === 1) { + const firstDigit = value.charAt(0); + if (parseInt(firstDigit, 10) > 1) { + value = `0${firstDigit}`; + didAutoCorrectFirstDigit = true; + } + } + + if (value.length >= 2) { + const month = parseInt(value.slice(0, 2), 10); + + if (month > 12) { + value = `01${value.charAt(1)}`; + } + } + + let formattedValue = value; + + const prevValue = previousvalueRef.current.replace(CONST.REGEX.NON_NUMERIC, ''); + + if ((value.length === 2 && prevValue.length === 1) || didAutoCorrectFirstDigit) { + formattedValue = `${value}/`; + } else if (value.length > 2) { + formattedValue = `${value.slice(0, 2)}/${value.slice(2, 4)}`; + } + + previousvalueRef.current = newValue; + + setExpirationDate(formattedValue); + }, []); const [cardNumber, setCardNumber] = useState(''); @@ -251,6 +294,8 @@ function PaymentCardForm({ diff --git a/src/languages/en.ts b/src/languages/en.ts index 3e457c7648420..8b148ec3a8394 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -1738,7 +1738,7 @@ const translations = { nameOnCard: 'Name on card', paymentCardNumber: 'Card number', expiration: 'Expiration date', - expirationDate: 'MMYY', + expirationDate: 'MM/YY', cvv: 'CVV', billingAddress: 'Billing address', growlMessageOnSave: 'Your payment card was successfully added', From 9b792879d530136306ba29be60a4a63bdabde102 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Fri, 20 Jun 2025 06:09:13 +0500 Subject: [PATCH 02/18] use prevValue.length less than 2 to add slash --- src/components/AddPaymentCard/PaymentCardForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index f880489bd62e6..5fb60a82868f6 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -173,7 +173,7 @@ function PaymentCardForm({ const prevValue = previousvalueRef.current.replace(CONST.REGEX.NON_NUMERIC, ''); - if ((value.length === 2 && prevValue.length === 1) || didAutoCorrectFirstDigit) { + if ((value.length === 2 && prevValue.length < 2) || didAutoCorrectFirstDigit) { formattedValue = `${value}/`; } else if (value.length > 2) { formattedValue = `${value.slice(0, 2)}/${value.slice(2, 4)}`; From 85a24c960e57bec3d76e0a5bce55c7b034e8f358 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Sun, 22 Jun 2025 02:52:30 +0500 Subject: [PATCH 03/18] remove didAutoCorrectFirstDigit check --- src/components/AddPaymentCard/PaymentCardForm.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 5fb60a82868f6..0b33969022b0c 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -151,13 +151,11 @@ function PaymentCardForm({ let value = newValue.replace(CONST.REGEX.NON_NUMERIC, ''); - let didAutoCorrectFirstDigit = false; if (value.length === 1) { const firstDigit = value.charAt(0); if (parseInt(firstDigit, 10) > 1) { value = `0${firstDigit}`; - didAutoCorrectFirstDigit = true; } } @@ -173,7 +171,7 @@ function PaymentCardForm({ const prevValue = previousvalueRef.current.replace(CONST.REGEX.NON_NUMERIC, ''); - if ((value.length === 2 && prevValue.length < 2) || didAutoCorrectFirstDigit) { + if ((value.length === 2 && prevValue.length < 2)) { formattedValue = `${value}/`; } else if (value.length > 2) { formattedValue = `${value.slice(0, 2)}/${value.slice(2, 4)}`; From d749d92afffee73519cfcf30542356306f0bc5f2 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Tue, 24 Jun 2025 22:15:17 +0500 Subject: [PATCH 04/18] use first char 1 from value for month above 12 bellow 19 --- src/components/AddPaymentCard/PaymentCardForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 0b33969022b0c..303c1589c9675 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -163,7 +163,7 @@ function PaymentCardForm({ const month = parseInt(value.slice(0, 2), 10); if (month > 12) { - value = `01${value.charAt(1)}`; + value = `0${value.charAt(0)}${value.charAt(1)}`; } } From c00b03e5338ba17a626b895a0baa0322bf67c791 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Fri, 27 Jun 2025 16:14:09 +0500 Subject: [PATCH 05/18] fix code format with prettier --- src/components/AddPaymentCard/PaymentCardForm.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 303c1589c9675..e0611b0bd9086 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -151,7 +151,6 @@ function PaymentCardForm({ let value = newValue.replace(CONST.REGEX.NON_NUMERIC, ''); - if (value.length === 1) { const firstDigit = value.charAt(0); if (parseInt(firstDigit, 10) > 1) { @@ -167,17 +166,17 @@ function PaymentCardForm({ } } - let formattedValue = value; + const prevValue = previousvalueRef.current?.replace(CONST.REGEX.NON_NUMERIC, '') ?? ''; - const prevValue = previousvalueRef.current.replace(CONST.REGEX.NON_NUMERIC, ''); + let formattedValue = value; - if ((value.length === 2 && prevValue.length < 2)) { + if (value.length === 2 && prevValue.length < 2) { formattedValue = `${value}/`; } else if (value.length > 2) { formattedValue = `${value.slice(0, 2)}/${value.slice(2, 4)}`; } - previousvalueRef.current = newValue; + previousvalueRef.current = formattedValue; setExpirationDate(formattedValue); }, []); From 30b2d76824560fa72428a54dce100b53d63a9323 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Fri, 27 Jun 2025 19:47:58 +0500 Subject: [PATCH 06/18] Update translation in all locales --- src/languages/de.ts | 2 +- src/languages/es.ts | 2 +- src/languages/fr.ts | 2 +- src/languages/it.ts | 2 +- src/languages/ja.ts | 2 +- src/languages/nl.ts | 2 +- src/languages/pl.ts | 2 +- src/languages/pt-BR.ts | 2 +- src/languages/zh-hans.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/languages/de.ts b/src/languages/de.ts index 2eb5a5949b4de..00f2cd20a2ac4 100644 --- a/src/languages/de.ts +++ b/src/languages/de.ts @@ -1782,7 +1782,7 @@ const translations = { nameOnCard: 'Name auf der Karte', paymentCardNumber: 'Kartennummer', expiration: 'Ablaufdatum', - expirationDate: 'MMYY', + expirationDate: 'MM/YY', cvv: 'CVV', billingAddress: 'Rechnungsadresse', growlMessageOnSave: 'Ihre Zahlungskarte wurde erfolgreich hinzugefügt', diff --git a/src/languages/es.ts b/src/languages/es.ts index e2449b925c881..84d5861b5d1ac 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -1757,7 +1757,7 @@ const translations = { nameOnCard: 'Nombre en la tarjeta', paymentCardNumber: 'Número de la tarjeta', expiration: 'Fecha de vencimiento', - expirationDate: 'MMAA', + expirationDate: 'MM/AA', cvv: 'CVV', billingAddress: 'Dirección de envio', growlMessageOnSave: 'Tu tarjeta de pago se añadió correctamente', diff --git a/src/languages/fr.ts b/src/languages/fr.ts index 39f1298757f4a..10b98d6fd4a21 100644 --- a/src/languages/fr.ts +++ b/src/languages/fr.ts @@ -1782,7 +1782,7 @@ const translations = { nameOnCard: 'Nom sur la carte', paymentCardNumber: 'Numéro de carte', expiration: "Date d'expiration", - expirationDate: 'MMYY', + expirationDate: 'MM/YY', cvv: 'CVV', billingAddress: 'Adresse de facturation', growlMessageOnSave: 'Votre carte de paiement a été ajoutée avec succès', diff --git a/src/languages/it.ts b/src/languages/it.ts index c978531328562..4a369d71c370a 100644 --- a/src/languages/it.ts +++ b/src/languages/it.ts @@ -1774,7 +1774,7 @@ const translations = { nameOnCard: 'Nome sulla carta', paymentCardNumber: 'Numero di carta', expiration: 'Data di scadenza', - expirationDate: 'MMYY', + expirationDate: 'MM/YY', cvv: 'CVV', billingAddress: 'Indirizzo di fatturazione', growlMessageOnSave: 'La tua carta di pagamento è stata aggiunta con successo', diff --git a/src/languages/ja.ts b/src/languages/ja.ts index 4e6328b81a7ee..241c90baf25e6 100644 --- a/src/languages/ja.ts +++ b/src/languages/ja.ts @@ -1771,7 +1771,7 @@ const translations = { nameOnCard: 'カード名義人', paymentCardNumber: 'カード番号', expiration: '有効期限', - expirationDate: 'MMYY', + expirationDate: 'MM/YY', cvv: 'CVV', billingAddress: '請求先住所', growlMessageOnSave: 'お支払いカードが正常に追加されました', diff --git a/src/languages/nl.ts b/src/languages/nl.ts index 09453c52ad530..5f0d1efe4e53f 100644 --- a/src/languages/nl.ts +++ b/src/languages/nl.ts @@ -1775,7 +1775,7 @@ const translations = { nameOnCard: 'Naam op kaart', paymentCardNumber: 'Kaartnummer', expiration: 'Vervaldatum', - expirationDate: 'MMYY', + expirationDate: 'MM/YY', cvv: 'CVV', billingAddress: 'Factuuradres', growlMessageOnSave: 'Uw betaalkaart is succesvol toegevoegd', diff --git a/src/languages/pl.ts b/src/languages/pl.ts index c1e2ec0bdf53a..8d91abdc00fd0 100644 --- a/src/languages/pl.ts +++ b/src/languages/pl.ts @@ -1770,7 +1770,7 @@ const translations = { nameOnCard: 'Imię na karcie', paymentCardNumber: 'Numer karty', expiration: 'Data wygaśnięcia', - expirationDate: 'MMYY', + expirationDate: 'MM/YY', cvv: 'CVV', billingAddress: 'Adres rozliczeniowy', growlMessageOnSave: 'Twoja karta płatnicza została pomyślnie dodana', diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts index a6794f99d7600..34c5a41b500ed 100644 --- a/src/languages/pt-BR.ts +++ b/src/languages/pt-BR.ts @@ -1773,7 +1773,7 @@ const translations = { nameOnCard: 'Nome no cartão', paymentCardNumber: 'Número do cartão', expiration: 'Data de validade', - expirationDate: 'MMYY', + expirationDate: 'MM/YY', cvv: 'CVV', billingAddress: 'Endereço de cobrança', growlMessageOnSave: 'Seu cartão de pagamento foi adicionado com sucesso', diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts index 293c3744fcd60..63c7c66a361a5 100644 --- a/src/languages/zh-hans.ts +++ b/src/languages/zh-hans.ts @@ -1755,7 +1755,7 @@ const translations = { nameOnCard: '卡上的姓名', paymentCardNumber: '卡号', expiration: '到期日期', - expirationDate: 'MMYY', + expirationDate: 'MM/YY', cvv: 'CVV', billingAddress: '账单地址', growlMessageOnSave: '您的支付卡已成功添加', From c74ecb1ce4ca4086b6512d4fd70fc95fa9ce6e06 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Fri, 27 Jun 2025 19:53:00 +0500 Subject: [PATCH 07/18] fix: lint error --- src/components/AddPaymentCard/PaymentCardForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index e0611b0bd9086..55e0a8cbd72c8 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -133,7 +133,7 @@ function PaymentCardForm({ currencySelectorRoute, }: PaymentCardFormProps) { const styles = useThemeStyles(); - const [data, metadata] = useOnyx(ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM); + const [data, metadata] = useOnyx(ONYXKEYS.FORMS.ADD_PAYMENT_CARD_FORM, {canBeMissing: true}); const {translate} = useLocalize(); const route = useRoute(); From 3c6d33f8648095a256e58ef4bc606f037acb1fc1 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Sun, 29 Jun 2025 00:16:49 +0500 Subject: [PATCH 08/18] fix: prevent entering 00 as month by rejecting second 0 --- src/components/AddPaymentCard/PaymentCardForm.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 55e0a8cbd72c8..4d9e4d0585b3b 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -161,6 +161,10 @@ function PaymentCardForm({ if (value.length >= 2) { const month = parseInt(value.slice(0, 2), 10); + if (value.length === 2 && value === '00') { + return; + } + if (month > 12) { value = `0${value.charAt(0)}${value.charAt(1)}`; } From 5a3158c7e002a91c65c7d0c53bf66f0e7d4e5361 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Sun, 29 Jun 2025 00:31:14 +0500 Subject: [PATCH 09/18] fix: handle edge case where user pastes 001 or similar value, replace it with 0 --- src/components/AddPaymentCard/PaymentCardForm.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 4d9e4d0585b3b..9a930cf3109c8 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -161,8 +161,8 @@ function PaymentCardForm({ if (value.length >= 2) { const month = parseInt(value.slice(0, 2), 10); - if (value.length === 2 && value === '00') { - return; + if (value.charAt(0) === '0' && value.charAt(1) === '0') { + value = '0'; } if (month > 12) { From 11e69d2346d0b5ab45b5eae1832b411efbb073d6 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Sun, 29 Jun 2025 00:39:30 +0500 Subject: [PATCH 10/18] fix: changed files ESLint --- src/components/AddPaymentCard/PaymentCardForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 9a930cf3109c8..8dbe5b44e801a 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -161,7 +161,7 @@ function PaymentCardForm({ if (value.length >= 2) { const month = parseInt(value.slice(0, 2), 10); - if (value.charAt(0) === '0' && value.charAt(1) === '0') { + if (value.startsWith('00')) { value = '0'; } From 8c250f2f2ec33f94e2c082fe2265f71de82bb05e Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Sun, 29 Jun 2025 02:56:24 +0500 Subject: [PATCH 11/18] update expiration date validation --- src/components/AddPaymentCard/PaymentCardForm.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 8dbe5b44e801a..08d318822bd9f 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -198,7 +198,10 @@ function PaymentCardForm({ errors.cardNumber = translate(label.error.cardNumber); } - if (values.expirationDate && !isValidExpirationDate(values.expirationDate)) { + // When user pastes 5 digit value without slash, trim it to the first 4 digits before validation. + const normalizedExpirationDate = values.expirationDate?.length === 5 && !values.expirationDate.includes('/') ? values.expirationDate.slice(0, 4) : values.expirationDate; + + if (normalizedExpirationDate && !isValidExpirationDate(normalizedExpirationDate)) { errors.expirationDate = translate(label.error.expirationDate); } From 02fac7ec2122832230ef473d5805bf60d7db4530 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Tue, 1 Jul 2025 03:19:42 +0500 Subject: [PATCH 12/18] Fix expiration value formatting for 3-digit pasted input --- src/components/AddPaymentCard/PaymentCardForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 08d318822bd9f..89b35c9d5c071 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -166,7 +166,7 @@ function PaymentCardForm({ } if (month > 12) { - value = `0${value.charAt(0)}${value.charAt(1)}`; + value = `0${value.charAt(0)}${value.charAt(1)}${value.charAt(2)}`; } } From c43d3637b3995722abf455f706e7a905669f7cf4 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Tue, 1 Jul 2025 03:29:49 +0500 Subject: [PATCH 13/18] Add testID to Expiration date InputWrapper --- src/components/AddPaymentCard/PaymentCardForm.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 89b35c9d5c071..6361f8d1c1acb 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -303,6 +303,7 @@ function PaymentCardForm({ InputComponent={TextInput} inputID={INPUT_IDS.EXPIRATION_DATE} label={translate(label.defaults.expiration)} + testID={label.defaults.expiration} aria-label={translate(label.defaults.expiration)} role={CONST.ROLE.PRESENTATION} placeholder={translate(label.defaults.expirationDate)} From a79f68d181a70ae4e44c885316fb3a9d5a83d4ea Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Tue, 1 Jul 2025 05:02:06 +0500 Subject: [PATCH 14/18] add unit test for expiration date formatting --- tests/unit/PaymentCardFormTest.tsx | 122 +++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 tests/unit/PaymentCardFormTest.tsx diff --git a/tests/unit/PaymentCardFormTest.tsx b/tests/unit/PaymentCardFormTest.tsx new file mode 100644 index 0000000000000..0f3dad2ada303 --- /dev/null +++ b/tests/unit/PaymentCardFormTest.tsx @@ -0,0 +1,122 @@ +import {PortalProvider} from '@gorhom/portal'; +import {NavigationContainer} from '@react-navigation/native'; +import {createStackNavigator} from '@react-navigation/stack'; +import {fireEvent, render, screen} from '@testing-library/react-native'; +import React from 'react'; +import Onyx from 'react-native-onyx'; +import ComposeProviders from '@components/ComposeProviders'; +import {LocaleContextProvider} from '@components/LocaleContextProvider'; +import OnyxProvider from '@components/OnyxProvider'; +import {CurrentReportIDContextProvider} from '@hooks/useCurrentReportID'; +import AddPaymentCard from '@pages/settings/Subscription/PaymentCard'; +import ONYXKEYS from '@src/ONYXKEYS'; +import SCREENS from '@src/SCREENS'; + +jest.mock('@react-native-community/geolocation', () => ({ + setRNConfiguration: jest.fn(), +})); + +jest.mock('@libs/ReportUtils', () => ({ + UnreadIndicatorUpdaterHelper: jest.fn(), + getReportIDFromLink: jest.fn(() => ''), + parseReportRouteParams: jest.fn(() => ({reportID: ''})), +})); + +jest.mock('@pages/settings/Subscription/PaymentCard', () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return jest.requireActual('@pages/settings/Subscription/PaymentCard/index.tsx'); +}); + +beforeAll(() => { + Onyx.init({keys: ONYXKEYS}); +}); + +afterAll(() => { + jest.clearAllMocks(); +}); + +describe('Subscription/AddPaymentCard', () => { + const Stack = createStackNavigator(); + + const renderAddPaymentCardPage = (initialRouteName: typeof SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD) => { + return render( + + + + + + + + + , + ); + }; + + describe('AddPaymentCardPage Expiration Date Formatting', () => { + const runFormatTest = async (input: string, formattedAs: string) => { + renderAddPaymentCardPage(SCREENS.SETTINGS.SUBSCRIPTION.ADD_PAYMENT_CARD); + const expirationDateField = await screen.findByTestId('addPaymentCardPage.expiration'); + fireEvent.changeText(expirationDateField, input); + expect(expirationDateField.props.value).toBe(formattedAs); + }; + + it('formats "0" as "0"', async () => { + await runFormatTest('0', '0'); + }); + + it('formats "2" as "02/"', async () => { + await runFormatTest('2', '02/'); + }); + + it('formats "11" as "11/"', async () => { + await runFormatTest('11', '11/'); + }); + + it('formats "13" as "01/3"', async () => { + await runFormatTest('13', '01/3'); + }); + + it('formats "20" as "02/0"', async () => { + await runFormatTest('20', '02/0'); + }); + + it('formats "45" as "04/5"', async () => { + await runFormatTest('45', '04/5'); + }); + + it('formats "98" as "09/8"', async () => { + await runFormatTest('98', '09/8'); + }); + + it('formats "123" as "12/3"', async () => { + await runFormatTest('123', '12/3'); + }); + + it('formats "567" as "05/67"', async () => { + await runFormatTest('567', '05/67'); + }); + + it('formats "00" as "0"', async () => { + await runFormatTest('00', '0'); + }); + + it('formats "11111" as "11/11"', async () => { + await runFormatTest('11111', '11/11'); + }); + + it('formats "99/99" as "09/99"', async () => { + await runFormatTest('99/99', '09/99'); + }); + + it('formats "0825" as "08/25"', async () => { + await runFormatTest('0825', '08/25'); + }); + + it('formats "12255" as "12/25"', async () => { + await runFormatTest('12255', '12/25'); + }); + }); +}); From d2ec4e5db1bcb42a753a298a4429a014c702eff8 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Mon, 14 Jul 2025 12:30:03 +0500 Subject: [PATCH 15/18] Fix typo in variable name previousValueRef Co-authored-by: Monil Bhavsar --- src/components/AddPaymentCard/PaymentCardForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 6361f8d1c1acb..8538a1e54c626 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -142,7 +142,7 @@ function PaymentCardForm({ const cardNumberRef = useRef(null); const [expirationDate, setExpirationDate] = useState(data?.expirationDate); - const previousvalueRef = useRef(''); + const previousValueRef = useRef(''); const onChangeExpirationDate = useCallback((newValue: string) => { if (typeof newValue !== 'string') { From 8c8f4bbd89c613076314af1e47aa0010f7198ff9 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Mon, 14 Jul 2025 12:45:15 +0500 Subject: [PATCH 16/18] fix code format and typo in variable name --- src/components/AddPaymentCard/PaymentCardForm.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index b115b9eca0704..00cc9a85f06ea 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -160,18 +160,15 @@ function PaymentCardForm({ if (value.length >= 2) { const month = parseInt(value.slice(0, 2), 10); - if (value.startsWith('00')) { value = '0'; } - if (month > 12) { value = `0${value.charAt(0)}${value.charAt(1)}${value.charAt(2)}`; } } - const prevValue = previousvalueRef.current?.replace(CONST.REGEX.NON_NUMERIC, '') ?? ''; - + const prevValue = previousValueRef.current?.replace(CONST.REGEX.NON_NUMERIC, '') ?? ''; let formattedValue = value; if (value.length === 2 && prevValue.length < 2) { @@ -180,8 +177,7 @@ function PaymentCardForm({ formattedValue = `${value.slice(0, 2)}/${value.slice(2, 4)}`; } - previousvalueRef.current = formattedValue; - + previousValueRef.current = formattedValue; setExpirationDate(formattedValue); }, []); From fa33e0ef393e28c928a84a81dbed3d52501f1397 Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Mon, 14 Jul 2025 13:05:50 +0500 Subject: [PATCH 17/18] add comment to explain expiration date formatting --- src/components/AddPaymentCard/PaymentCardForm.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 00cc9a85f06ea..1b844c1410bd8 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -144,6 +144,8 @@ function PaymentCardForm({ const previousValueRef = useRef(''); +// Formats user input into a valid expiration date (MM/YY) and automatically adds slash after the month. +// Ensures the month is always between 01 and 12 by correcting invalid value to match the proper format. const onChangeExpirationDate = useCallback((newValue: string) => { if (typeof newValue !== 'string') { return; From 01591fa59b5570674109236922b71190f3facdcd Mon Sep 17 00:00:00 2001 From: Samran Ahmed Date: Mon, 14 Jul 2025 13:07:25 +0500 Subject: [PATCH 18/18] fix prettier --- src/components/AddPaymentCard/PaymentCardForm.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/AddPaymentCard/PaymentCardForm.tsx b/src/components/AddPaymentCard/PaymentCardForm.tsx index 1b844c1410bd8..83a4bbef6a9e7 100644 --- a/src/components/AddPaymentCard/PaymentCardForm.tsx +++ b/src/components/AddPaymentCard/PaymentCardForm.tsx @@ -144,8 +144,8 @@ function PaymentCardForm({ const previousValueRef = useRef(''); -// Formats user input into a valid expiration date (MM/YY) and automatically adds slash after the month. -// Ensures the month is always between 01 and 12 by correcting invalid value to match the proper format. + // Formats user input into a valid expiration date (MM/YY) and automatically adds slash after the month. + // Ensures the month is always between 01 and 12 by correcting invalid value to match the proper format. const onChangeExpirationDate = useCallback((newValue: string) => { if (typeof newValue !== 'string') { return;