From 63033804aa01e0323ad0ed3b71b1f2af8007edfb Mon Sep 17 00:00:00 2001 From: Linh Date: Tue, 11 Mar 2025 09:16:29 +0700 Subject: [PATCH 1/4] fix: rbr appears in profile after remove second contact --- .../settings/Profile/Contacts/ContactMethodDetailsPage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx index 2a578f9515c4..7ab0fd56e97d 100644 --- a/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx +++ b/src/pages/settings/Profile/Contacts/ContactMethodDetailsPage.tsx @@ -92,6 +92,7 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) { const loginData = useMemo(() => loginList?.[contactMethod], [loginList, contactMethod]); const isDefaultContactMethod = useMemo(() => session?.email === loginData?.partnerUserID, [session?.email, loginData?.partnerUserID]); const validateLoginError = getEarliestErrorField(loginData, 'validateLogin'); + const prevPendingDeletedLogin = usePrevious(loginData?.pendingFields?.deletedLogin); /** * Attempt to set this contact method as user's "Default contact method" @@ -167,10 +168,12 @@ function ContactMethodDetailsPage({route}: ContactMethodDetailsPageProps) { }, [loginData?.validatedDate, loginData?.errorFields?.addedLogin]); useEffect(() => { - if (loginData?.validatedDate) { + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing + if (loginData?.validatedDate || prevPendingDeletedLogin) { return; } resetContactMethodValidateCodeSentState(contactMethod); + // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps -- The prevPendingDeletedLogin is a ref, so no need to add it to dependencies. }, [contactMethod, loginData?.validatedDate]); const getThreeDotsMenuItems = useCallback(() => { From 1d32bf20bec7b24702a4da1ca7ff8a067878e64f Mon Sep 17 00:00:00 2001 From: Linh Date: Tue, 11 Mar 2025 11:04:25 +0700 Subject: [PATCH 2/4] fix: write unit test for ContactMethodDetailsPage --- tests/unit/ContactMethodDetailsPageTest.tsx | 83 +++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 tests/unit/ContactMethodDetailsPageTest.tsx diff --git a/tests/unit/ContactMethodDetailsPageTest.tsx b/tests/unit/ContactMethodDetailsPageTest.tsx new file mode 100644 index 000000000000..8c9b0cb7ebe3 --- /dev/null +++ b/tests/unit/ContactMethodDetailsPageTest.tsx @@ -0,0 +1,83 @@ +import {render} from '@testing-library/react-native'; +import Onyx from 'react-native-onyx'; +// eslint-disable-next-line no-restricted-syntax +import * as UserActions from '@libs/actions/User'; +import ContactMethodDetailsPage from '@pages/settings/Profile/Contacts/ContactMethodDetailsPage'; +import ONYXKEYS from '@src/ONYXKEYS'; +import type {MockFetch} from '../utils/TestHelper'; +import {getGlobalFetchMock} from '../utils/TestHelper'; +import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; + +jest.mock('@libs/Navigation/Navigation', () => ({ + goBack: jest.fn(), +})); + +jest.mock('@libs/actions/User', () => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const originalModule = jest.requireActual('@libs/actions/User'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return { + ...originalModule, + resetContactMethodValidateCodeSentState: jest.fn(), + }; +}); + +const fakeEmail = 'fake@gmail.com'; +const mockRoute = { + params: { + backTo: '', + contactMethod: fakeEmail, + }, +}; +const mockLoginList = { + [fakeEmail]: { + partnerName: 'expensify.com', + partnerUserID: fakeEmail, + validatedDate: 'fake-validatedDate', + }, +}; + +describe('ContactMethodDetailsPage', () => { + let mockFetch: MockFetch; + beforeAll(() => { + Onyx.init({ + keys: ONYXKEYS, + }); + }); + beforeEach(() => { + global.fetch = getGlobalFetchMock(); + mockFetch = fetch as MockFetch; + return Onyx.clear().then(waitForBatchedUpdates); + }); + + function ContactMethodDetailsPageRenderer() { + return ( + + ); + } + + it('should not call resetContactMethodValidateCodeSentState when contact method is validated', async () => { + // Given a login list with a validated contact method + Onyx.merge(ONYXKEYS.LOGIN_LIST, mockLoginList); + await waitForBatchedUpdates(); + + // Given the page is rendered + render(); + + // When a deleteContactMethod called + UserActions.deleteContactMethod(fakeEmail, mockLoginList); + await waitForBatchedUpdates(); + + // When the deletion is successful + mockFetch?.succeed(); + await waitForBatchedUpdates(); + mockFetch?.resume(); + await waitForBatchedUpdates(); + + // Then resetContactMethodValidateCodeSentState should not be called + expect(UserActions.resetContactMethodValidateCodeSentState).not.toHaveBeenCalled(); + }); +}); From db1d309f6e9e3561abe626fc9471f2c77a310a62 Mon Sep 17 00:00:00 2001 From: Linh Date: Tue, 11 Mar 2025 13:44:14 +0700 Subject: [PATCH 3/4] fix: update unit test --- tests/unit/ContactMethodDetailsPageTest.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/ContactMethodDetailsPageTest.tsx b/tests/unit/ContactMethodDetailsPageTest.tsx index 8c9b0cb7ebe3..3e9fe7f44e74 100644 --- a/tests/unit/ContactMethodDetailsPageTest.tsx +++ b/tests/unit/ContactMethodDetailsPageTest.tsx @@ -59,7 +59,7 @@ describe('ContactMethodDetailsPage', () => { ); } - it('should not call resetContactMethodValidateCodeSentState when contact method is validated', async () => { + it('should not call resetContactMethodValidateCodeSentState when we got a delete pending field', async () => { // Given a login list with a validated contact method Onyx.merge(ONYXKEYS.LOGIN_LIST, mockLoginList); await waitForBatchedUpdates(); From 35fca5332362d05a73b4bb05adb773e1b6e4737e Mon Sep 17 00:00:00 2001 From: Linh Date: Sat, 15 Mar 2025 21:50:13 +0700 Subject: [PATCH 4/4] fix: change request --- tests/unit/ContactMethodDetailsPageTest.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/unit/ContactMethodDetailsPageTest.tsx b/tests/unit/ContactMethodDetailsPageTest.tsx index 3e9fe7f44e74..b71cb7e5e74b 100644 --- a/tests/unit/ContactMethodDetailsPageTest.tsx +++ b/tests/unit/ContactMethodDetailsPageTest.tsx @@ -7,6 +7,7 @@ import ONYXKEYS from '@src/ONYXKEYS'; import type {MockFetch} from '../utils/TestHelper'; import {getGlobalFetchMock} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; +import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; jest.mock('@libs/Navigation/Navigation', () => ({ goBack: jest.fn(), @@ -69,7 +70,7 @@ describe('ContactMethodDetailsPage', () => { // When a deleteContactMethod called UserActions.deleteContactMethod(fakeEmail, mockLoginList); - await waitForBatchedUpdates(); + await waitForBatchedUpdatesWithAct(); // When the deletion is successful mockFetch?.succeed();