diff --git a/__mocks__/@react-native-reanimated/index.ts b/__mocks__/@react-native-reanimated/index.ts deleted file mode 100644 index 28efba1dde69..000000000000 --- a/__mocks__/@react-native-reanimated/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -// __mocks__/react-native-reanimated/index.js -const actualAnimated = jest.requireActual('react-native-reanimated/mock'); - -const mock = { - ...actualAnimated, - createAnimatedPropAdapter: jest.fn(), - useReducedMotion: jest.fn(), -}; - -export default mock; diff --git a/src/pages/InviteReportParticipantsPage.tsx b/src/pages/InviteReportParticipantsPage.tsx index 2377189c7efa..78cb5dfcd991 100644 --- a/src/pages/InviteReportParticipantsPage.tsx +++ b/src/pages/InviteReportParticipantsPage.tsx @@ -11,7 +11,6 @@ import InviteMemberListItem from '@components/SelectionList/InviteMemberListItem import type {Section} from '@components/SelectionList/types'; import withNavigationTransitionEnd from '@components/withNavigationTransitionEnd'; import type {WithNavigationTransitionEndProps} from '@components/withNavigationTransitionEnd'; -import useDebouncedState from '@hooks/useDebouncedState'; import useLocalize from '@hooks/useLocalize'; import useThemeStyles from '@hooks/useThemeStyles'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; @@ -45,7 +44,7 @@ function InviteReportParticipantsPage({betas, personalDetails, report, didScreen const styles = useThemeStyles(); const {translate} = useLocalize(); - const [searchTerm, debouncedSearchTerm, setSearchTerm] = useDebouncedState(''); + const [searchTerm, setSearchTerm] = useState(''); const [selectedOptions, setSelectedOptions] = useState([]); const [invitePersonalDetails, setInvitePersonalDetails] = useState([]); const [recentReports, setRecentReports] = useState([]); @@ -58,7 +57,7 @@ function InviteReportParticipantsPage({betas, personalDetails, report, didScreen ); useEffect(() => { - const inviteOptions = OptionsListUtils.getMemberInviteOptions(options.personalDetails, betas ?? [], debouncedSearchTerm, excludedUsers, false, options.reports, true); + const inviteOptions = OptionsListUtils.getMemberInviteOptions(options.personalDetails, betas ?? [], searchTerm, excludedUsers, false, options.reports, true); // Update selectedOptions with the latest personalDetails information const detailsMap: Record = {}; @@ -78,7 +77,7 @@ function InviteReportParticipantsPage({betas, personalDetails, report, didScreen setRecentReports(inviteOptions.recentReports); setSelectedOptions(newSelectedOptions); // eslint-disable-next-line react-hooks/exhaustive-deps -- we don't want to recalculate when selectedOptions change - }, [personalDetails, betas, debouncedSearchTerm, excludedUsers, options]); + }, [personalDetails, betas, searchTerm, excludedUsers, options]); const sections = useMemo(() => { const sectionsArr: Sections = []; @@ -89,11 +88,11 @@ function InviteReportParticipantsPage({betas, personalDetails, report, didScreen // Filter all options that is a part of the search term or in the personal details let filterSelectedOptions = selectedOptions; - if (debouncedSearchTerm !== '') { + if (searchTerm !== '') { filterSelectedOptions = selectedOptions.filter((option) => { const accountID = option?.accountID; const isOptionInPersonalDetails = invitePersonalDetails.some((personalDetail) => accountID && personalDetail?.accountID === accountID); - const searchValue = OptionsListUtils.getSearchValueForPhoneOrEmail(debouncedSearchTerm); + const searchValue = OptionsListUtils.getSearchValueForPhoneOrEmail(searchTerm); const isPartOfSearchTerm = !!option.text?.toLowerCase().includes(searchValue) || !!option.login?.toLowerCase().includes(searchValue); return isPartOfSearchTerm || isOptionInPersonalDetails; }); @@ -131,7 +130,7 @@ function InviteReportParticipantsPage({betas, personalDetails, report, didScreen } return sectionsArr; - }, [invitePersonalDetails, debouncedSearchTerm, selectedOptions, translate, userToInvite, areOptionsInitialized, recentReports]); + }, [invitePersonalDetails, searchTerm, selectedOptions, translate, userToInvite, areOptionsInitialized, recentReports]); const toggleOption = useCallback( (option: OptionsListUtils.MemberForList) => { @@ -172,7 +171,7 @@ function InviteReportParticipantsPage({betas, personalDetails, report, didScreen }, [selectedOptions, backRoute, reportID, validate]); const headerMessage = useMemo(() => { - const searchValue = debouncedSearchTerm.trim().toLowerCase(); + const searchValue = searchTerm.trim().toLowerCase(); const expensifyEmails = CONST.EXPENSIFY_EMAILS as string[]; if (!userToInvite && expensifyEmails.includes(searchValue)) { return translate('messages.errorMessageInvalidEmail'); @@ -188,7 +187,7 @@ function InviteReportParticipantsPage({betas, personalDetails, report, didScreen return translate('messages.userIsAlreadyMember', {login: searchValue, name: reportName ?? ''}); } return OptionsListUtils.getHeaderMessage(invitePersonalDetails.length !== 0, !!userToInvite, searchValue); - }, [debouncedSearchTerm, userToInvite, excludedUsers, invitePersonalDetails, translate, reportName]); + }, [searchTerm, userToInvite, excludedUsers, invitePersonalDetails, translate, reportName]); const footerContent = useMemo( () => ( diff --git a/tests/ui/GroupChatNameTests.tsx b/tests/ui/GroupChatNameTests.tsx deleted file mode 100644 index e0ac71120768..000000000000 --- a/tests/ui/GroupChatNameTests.tsx +++ /dev/null @@ -1,393 +0,0 @@ -/* eslint-disable testing-library/no-node-access */ -import type * as NativeNavigation from '@react-navigation/native'; -import {act, render, screen, waitFor} from '@testing-library/react-native'; -import React from 'react'; -import Onyx from 'react-native-onyx'; -import * as Localize from '@libs/Localize'; -import * as AppActions from '@userActions/App'; -import * as User from '@userActions/User'; -import App from '@src/App'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type {Participant} from '@src/types/onyx/Report'; -import PusherHelper from '../utils/PusherHelper'; -import * as TestHelper from '../utils/TestHelper'; -import {navigateToSidebarOption} from '../utils/TestHelper'; -import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; -import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; - -// We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App -jest.setTimeout(50000); - -jest.mock('../../src/components/ConfirmedRoute.tsx'); - -// Needed for: https://stackoverflow.com/questions/76903168/mocking-libraries-in-jest -jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ - /* eslint-disable-next-line @typescript-eslint/naming-convention */ - __esModule: true, - default: { - ignoreLogs: jest.fn(), - ignoreAllLogs: jest.fn(), - }, -})); - -/** - * We need to keep track of the transitionEnd callback so we can trigger it in our tests - */ -let transitionEndCB: () => void; - -type ListenerMock = { - triggerTransitionEnd: () => void; - addListener: jest.Mock; -}; - -/** - * This is a helper function to create a mock for the addListener function of the react-navigation library. - * The reason we need this is because we need to trigger the transitionEnd event in our tests to simulate - * the transitionEnd event that is triggered when the screen transition animation is completed. - * - * This can't be moved to a utils file because Jest wants any external function to stay in the scope. - * Details: https://github.com/jestjs/jest/issues/2567 - */ -const createAddListenerMock = (): ListenerMock => { - const transitionEndListeners: Array<() => void> = []; - const triggerTransitionEnd = () => { - transitionEndListeners.forEach((transitionEndListener) => transitionEndListener()); - }; - - const addListener: jest.Mock = jest.fn().mockImplementation((listener, callback: () => void) => { - if (listener === 'transitionEnd') { - transitionEndListeners.push(callback); - } - return () => { - transitionEndListeners.filter((cb) => cb !== callback); - }; - }); - - return {triggerTransitionEnd, addListener}; -}; - -jest.mock('@react-navigation/native', () => { - const actualNav = jest.requireActual('@react-navigation/native'); - const {triggerTransitionEnd, addListener} = createAddListenerMock(); - transitionEndCB = triggerTransitionEnd; - - const useNavigation = () => - ({ - navigate: jest.fn(), - ...actualNav.useNavigation, - getState: () => ({ - routes: [], - }), - addListener, - } as typeof NativeNavigation.useNavigation); - - return { - ...actualNav, - useNavigation, - getState: () => ({ - routes: [], - }), - } as typeof NativeNavigation; -}); - -beforeAll(() => { - TestHelper.beforeAllSetupUITests(); -}); - -const REPORT_ID = '1'; -const USER_A_ACCOUNT_ID = 1; -const USER_A_EMAIL = 'user_a@test.com'; -const USER_B_ACCOUNT_ID = 2; -const USER_B_EMAIL = 'user_b@test.com'; -const USER_C_ACCOUNT_ID = 3; -const USER_C_EMAIL = 'user_c@test.com'; -const USER_D_ACCOUNT_ID = 4; -const USER_D_EMAIL = 'user_d@test.com'; -const USER_E_ACCOUNT_ID = 5; -const USER_E_EMAIL = 'user_e@test.com'; -const USER_F_ACCOUNT_ID = 6; -const USER_F_EMAIL = 'user_f@test.com'; -const USER_G_ACCOUNT_ID = 7; -const USER_G_EMAIL = 'user_g@test.com'; -const USER_H_ACCOUNT_ID = 8; -const USER_H_EMAIL = 'user_h@test.com'; - -/** - * Sets up a test with a logged in user. Returns the test instance. - */ -function signInAndGetApp(reportName = '', participantAccountIDs?: number[]): Promise { - // Render the App and sign in as a test user. - render(); - - const participants: Record = {}; - participantAccountIDs?.forEach((id) => { - participants[id] = { - hidden: false, - role: id === 1 ? CONST.REPORT.ROLE.ADMIN : CONST.REPORT.ROLE.MEMBER, - } as Participant; - }); - - return waitForBatchedUpdatesWithAct() - .then(async () => { - await waitForBatchedUpdatesWithAct(); - const hintText = Localize.translateLocal('loginForm.loginForm'); - const loginForm = screen.queryAllByLabelText(hintText); - expect(loginForm).toHaveLength(1); - - await act(async () => { - await TestHelper.signInWithTestUser(USER_A_ACCOUNT_ID, USER_A_EMAIL, undefined, undefined, 'A'); - }); - return waitForBatchedUpdatesWithAct(); - }) - .then(() => { - User.subscribeToUserEvents(); - return waitForBatchedUpdates(); - }) - .then(async () => { - // Simulate setting an unread report and personal details - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${REPORT_ID}`, { - reportID: REPORT_ID, - reportName, - lastMessageText: 'Test', - participants, - lastActorAccountID: USER_B_ACCOUNT_ID, - type: CONST.REPORT.TYPE.CHAT, - chatType: CONST.REPORT.CHAT_TYPE.GROUP, - }); - - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, { - [USER_A_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_A_EMAIL, USER_A_ACCOUNT_ID, 'A'), - [USER_B_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_B_EMAIL, USER_B_ACCOUNT_ID, 'B'), - [USER_C_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_C_EMAIL, USER_C_ACCOUNT_ID, 'C'), - [USER_D_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_D_EMAIL, USER_D_ACCOUNT_ID, 'D'), - [USER_E_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_E_EMAIL, USER_E_ACCOUNT_ID, 'E'), - [USER_F_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_F_EMAIL, USER_F_ACCOUNT_ID, 'F'), - [USER_G_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_G_EMAIL, USER_G_ACCOUNT_ID, 'G'), - [USER_H_ACCOUNT_ID]: TestHelper.buildPersonalDetails(USER_H_EMAIL, USER_H_ACCOUNT_ID, 'H'), - }); - - // We manually setting the sidebar as loaded since the onLayout event does not fire in tests - AppActions.setSidebarLoaded(); - return waitForBatchedUpdatesWithAct(); - }); -} - -/** - * Tests for checking the group chat names at places like LHN, chat header, details page etc. - * Note that limit of 5 names is only for the header. - */ -describe('Tests for group chat name', () => { - beforeEach(() => { - jest.clearAllMocks(); - Onyx.clear(); - - // Unsubscribe to pusher channels - PusherHelper.teardown(); - }); - - const participantAccountIDs4 = [USER_A_ACCOUNT_ID, USER_B_ACCOUNT_ID, USER_C_ACCOUNT_ID, USER_D_ACCOUNT_ID]; - const participantAccountIDs8 = [...participantAccountIDs4, USER_E_ACCOUNT_ID, USER_F_ACCOUNT_ID, USER_G_ACCOUNT_ID, USER_H_ACCOUNT_ID]; - - it('Should show correctly in LHN', () => - signInAndGetApp('A, B, C, D', participantAccountIDs4).then(() => { - // Verify the sidebar links are rendered - const sidebarLinksHintText = Localize.translateLocal('sidebarScreen.listOfChats'); - const sidebarLinks = screen.queryAllByLabelText(sidebarLinksHintText); - expect(sidebarLinks).toHaveLength(1); - - // Verify there is only one option in the sidebar - const optionRowsHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(optionRowsHintText); - expect(optionRows).toHaveLength(1); - - const displayNameHintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); - const displayNameText = screen.queryByLabelText(displayNameHintText); - - return waitFor(() => expect(displayNameText?.props?.children?.[0]).toBe('A, B, C, D')); - })); - - it('Should show correctly in LHN when report name is not present', () => - signInAndGetApp('', participantAccountIDs4).then(() => { - // Verify the sidebar links are rendered - const sidebarLinksHintText = Localize.translateLocal('sidebarScreen.listOfChats'); - const sidebarLinks = screen.queryAllByLabelText(sidebarLinksHintText); - expect(sidebarLinks).toHaveLength(1); - - // Verify there is only one option in the sidebar - const optionRowsHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(optionRowsHintText); - expect(optionRows).toHaveLength(1); - - const displayNameHintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); - const displayNameText = screen.queryByLabelText(displayNameHintText); - - return waitFor(() => expect(displayNameText?.props?.children?.[0]).toBe('A, B, C, D')); - })); - - it('Should show all 8 names in LHN when 8 participants are present', () => - signInAndGetApp('', participantAccountIDs8).then(() => { - // Verify the sidebar links are rendered - const sidebarLinksHintText = Localize.translateLocal('sidebarScreen.listOfChats'); - const sidebarLinks = screen.queryAllByLabelText(sidebarLinksHintText); - expect(sidebarLinks).toHaveLength(1); - - // Verify there is only one option in the sidebar - const optionRowsHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(optionRowsHintText); - expect(optionRows).toHaveLength(1); - - const displayNameHintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); - const displayNameText = screen.queryByLabelText(displayNameHintText); - - return waitFor(() => expect(displayNameText?.props?.children?.[0]).toBe('A, B, C, D, E, F, G, H')); - })); - - it('Check if group name shows fine for report header', () => - signInAndGetApp('', participantAccountIDs4) - .then(() => { - // Verify the sidebar links are rendered - const sidebarLinksHintText = Localize.translateLocal('sidebarScreen.listOfChats'); - const sidebarLinks = screen.queryAllByLabelText(sidebarLinksHintText); - expect(sidebarLinks).toHaveLength(1); - - // Verify there is only one option in the sidebar - const optionRowsHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(optionRowsHintText); - expect(optionRows).toHaveLength(1); - - const displayNameHintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); - const displayNameText = screen.queryByLabelText(displayNameHintText); - - expect(displayNameText?.props?.children?.[0]).toBe('A, B, C, D'); - - return navigateToSidebarOption(0); - }) - .then(waitForBatchedUpdates) - .then(async () => { - await act(() => transitionEndCB?.()); - const name = 'A, B, C, D'; - const displayNameTexts = screen.queryAllByLabelText(name); - return waitFor(() => expect(displayNameTexts).toHaveLength(1)); - })); - - it('Should show only 5 names when there are 8 participants in the report header', () => - signInAndGetApp('', participantAccountIDs8) - .then(() => { - // Verify the sidebar links are rendered - const sidebarLinksHintText = Localize.translateLocal('sidebarScreen.listOfChats'); - const sidebarLinks = screen.queryAllByLabelText(sidebarLinksHintText); - expect(sidebarLinks).toHaveLength(1); - - // Verify there is only one option in the sidebar - const optionRowsHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(optionRowsHintText); - expect(optionRows).toHaveLength(1); - - const displayNameHintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); - const displayNameText = screen.queryByLabelText(displayNameHintText); - - expect(displayNameText?.props?.children?.[0]).toBe('A, B, C, D, E, F, G, H'); - - return navigateToSidebarOption(0); - }) - .then(waitForBatchedUpdates) - .then(async () => { - await act(() => transitionEndCB?.()); - const name = 'A, B, C, D, E'; - const displayNameTexts = screen.queryAllByLabelText(name); - return waitFor(() => expect(displayNameTexts).toHaveLength(1)); - })); - - it('Should show exact name in header when report name is available with 4 participants', () => - signInAndGetApp('Test chat', participantAccountIDs4) - .then(() => { - // Verify the sidebar links are rendered - const sidebarLinksHintText = Localize.translateLocal('sidebarScreen.listOfChats'); - const sidebarLinks = screen.queryAllByLabelText(sidebarLinksHintText); - expect(sidebarLinks).toHaveLength(1); - - // Verify there is only one option in the sidebar - const optionRowsHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(optionRowsHintText); - expect(optionRows).toHaveLength(1); - - const displayNameHintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); - const displayNameText = screen.queryByLabelText(displayNameHintText); - - expect(displayNameText?.props?.children?.[0]).toBe('Test chat'); - - return navigateToSidebarOption(0); - }) - .then(waitForBatchedUpdates) - .then(async () => { - await act(() => transitionEndCB?.()); - const name = 'Test chat'; - const displayNameTexts = screen.queryAllByLabelText(name); - return waitFor(() => expect(displayNameTexts).toHaveLength(1)); - })); - - it('Should show exact name in header when report name is available with 8 participants', () => - signInAndGetApp("Let's talk", participantAccountIDs8) - .then(() => { - // Verify the sidebar links are rendered - const sidebarLinksHintText = Localize.translateLocal('sidebarScreen.listOfChats'); - const sidebarLinks = screen.queryAllByLabelText(sidebarLinksHintText); - expect(sidebarLinks).toHaveLength(1); - - // Verify there is only one option in the sidebar - const optionRowsHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(optionRowsHintText); - expect(optionRows).toHaveLength(1); - - const displayNameHintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); - const displayNameText = screen.queryByLabelText(displayNameHintText); - - expect(displayNameText?.props?.children?.[0]).toBe("Let's talk"); - - return navigateToSidebarOption(0); - }) - .then(waitForBatchedUpdates) - .then(async () => { - await act(() => transitionEndCB?.()); - const name = "Let's talk"; - const displayNameTexts = screen.queryAllByLabelText(name); - return waitFor(() => expect(displayNameTexts).toHaveLength(1)); - })); - - it('Should show last message preview in LHN', () => - signInAndGetApp('A, B, C, D', participantAccountIDs4).then(() => { - // Verify the sidebar links are rendered - const sidebarLinksHintText = Localize.translateLocal('sidebarScreen.listOfChats'); - const sidebarLinks = screen.queryAllByLabelText(sidebarLinksHintText); - expect(sidebarLinks).toHaveLength(1); - - // Verify there is only one option in the sidebar - const optionRowsHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(optionRowsHintText); - expect(optionRows).toHaveLength(1); - - const lastChatHintText = Localize.translateLocal('accessibilityHints.lastChatMessagePreview'); - const lastChatText = screen.queryByLabelText(lastChatHintText); - - return waitFor(() => expect(lastChatText?.props?.children).toBe('B: Test')); - })); - - it('Should sort the names before displaying', () => - signInAndGetApp('', [USER_E_ACCOUNT_ID, ...participantAccountIDs4]).then(() => { - // Verify the sidebar links are rendered - const sidebarLinksHintText = Localize.translateLocal('sidebarScreen.listOfChats'); - const sidebarLinks = screen.queryAllByLabelText(sidebarLinksHintText); - expect(sidebarLinks).toHaveLength(1); - - // Verify there is only one option in the sidebar - const optionRowsHintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(optionRowsHintText); - expect(optionRows).toHaveLength(1); - - const displayNameHintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames'); - const displayNameText = screen.queryByLabelText(displayNameHintText); - - return waitFor(() => expect(displayNameText?.props?.children?.[0]).toBe('A, B, C, D, E')); - })); -}); diff --git a/tests/ui/UnreadIndicatorsTest.tsx b/tests/ui/UnreadIndicatorsTest.tsx index 5cf63dba5654..7b820281a5a6 100644 --- a/tests/ui/UnreadIndicatorsTest.tsx +++ b/tests/ui/UnreadIndicatorsTest.tsx @@ -4,26 +4,30 @@ import {act, fireEvent, render, screen, waitFor} from '@testing-library/react-na import {addSeconds, format, subMinutes, subSeconds} from 'date-fns'; import {utcToZonedTime} from 'date-fns-tz'; import React from 'react'; -import {AppState, DeviceEventEmitter} from 'react-native'; +import {AppState, DeviceEventEmitter, Linking} from 'react-native'; import type {ViewStyle} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; +import type Animated from 'react-native-reanimated'; import * as CollectionUtils from '@libs/CollectionUtils'; import DateUtils from '@libs/DateUtils'; import * as Localize from '@libs/Localize'; import LocalNotification from '@libs/Notification/LocalNotification'; import * as NumberUtils from '@libs/NumberUtils'; +import * as Pusher from '@libs/Pusher/pusher'; +import PusherConnectionManager from '@libs/PusherConnectionManager'; import FontUtils from '@styles/utils/FontUtils'; import * as AppActions from '@userActions/App'; import * as Report from '@userActions/Report'; import * as User from '@userActions/User'; import App from '@src/App'; +import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import appSetup from '@src/setup'; import type {ReportAction, ReportActions} from '@src/types/onyx'; import PusherHelper from '../utils/PusherHelper'; import * as TestHelper from '../utils/TestHelper'; -import {navigateToSidebarOption} from '../utils/TestHelper'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct'; @@ -43,6 +47,12 @@ jest.mock('react-native/Libraries/LogBox/LogBox', () => ({ }, })); +jest.mock('react-native-reanimated', () => ({ + ...jest.requireActual('react-native-reanimated/mock'), + createAnimatedPropAdapter: jest.fn, + useReducedMotion: jest.fn, +})); + /** * We need to keep track of the transitionEnd callback so we can trigger it in our tests */ @@ -105,7 +115,23 @@ jest.mock('@react-navigation/native', () => { }); beforeAll(() => { - TestHelper.beforeAllSetupUITests(true); + // In this test, we are generically mocking the responses of all API requests by mocking fetch() and having it + // return 200. In other tests, we might mock HttpUtils.xhr() with a more specific mock data response (which means + // fetch() never gets called so it does not need mocking) or we might have fetch throw an error to test error handling + // behavior. But here we just want to treat all API requests as a generic "success" and in the cases where we need to + // simulate data arriving we will just set it into Onyx directly with Onyx.merge() or Onyx.set() etc. + global.fetch = TestHelper.getGlobalFetchMock(); + + Linking.setInitialURL('https://new.expensify.com/'); + appSetup(); + + // Connect to Pusher + PusherConnectionManager.init(); + Pusher.init({ + appKey: CONFIG.PUSHER.APP_KEY, + cluster: CONFIG.PUSHER.CLUSTER, + authEndpoint: `${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api/AuthenticatePusher?`, + }); }); function scrollUpToRevealNewMessagesBadge() { @@ -147,6 +173,13 @@ function navigateToSidebar(): Promise { return waitForBatchedUpdates(); } +async function navigateToSidebarOption(index: number): Promise { + const hintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); + const optionRows = screen.queryAllByAccessibilityHint(hintText); + fireEvent(optionRows[index], 'press'); + await waitForBatchedUpdatesWithAct(); +} + function areYouOnChatListScreen(): boolean { const hintText = Localize.translateLocal('sidebarScreen.listOfChats'); const sidebarLinks = screen.queryAllByLabelText(hintText); diff --git a/tests/unit/ReportUtilsTest.ts b/tests/unit/ReportUtilsTest.ts index 7efa9a93b850..326e020464e0 100644 --- a/tests/unit/ReportUtilsTest.ts +++ b/tests/unit/ReportUtilsTest.ts @@ -9,7 +9,6 @@ import type {ModifiedExpenseAction} from '@src/types/onyx/ReportAction'; import {toCollectionDataSet} from '@src/types/utils/CollectionDataSet'; import * as NumberUtils from '../../src/libs/NumberUtils'; import * as LHNTestUtils from '../utils/LHNTestUtils'; -import {fakePersonalDetails} from '../utils/LHNTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; // Be sure to include the mocked permissions library or else the beta tests won't work @@ -822,97 +821,4 @@ describe('ReportUtils', () => { expect(ReportUtils.getAllAncestorReportActions(reports[4])).toEqual(resultAncestors); }); }); - - describe('getGroupChatName tests', () => { - afterEach(() => Onyx.clear()); - - describe('When participantAccountIDs is passed to getGroupChatName', () => { - it('Should show all participants name if count <= 5 and shouldApplyLimit is false', async () => { - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, fakePersonalDetails); - expect(ReportUtils.getGroupChatName([1, 2, 3, 4])).toEqual('Four, One, Three, Two'); - }); - - it('Should show all participants name if count <= 5 and shouldApplyLimit is true', async () => { - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, fakePersonalDetails); - expect(ReportUtils.getGroupChatName([1, 2, 3, 4], true)).toEqual('Four, One, Three, Two'); - }); - - it('Should show 5 participants name if count > 5 and shouldApplyLimit is true', async () => { - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, fakePersonalDetails); - expect(ReportUtils.getGroupChatName([1, 2, 3, 4, 5, 6, 7, 8], true)).toEqual('Five, Four, One, Three, Two'); - }); - - it('Should show all participants name if count > 5 and shouldApplyLimit is false', async () => { - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, fakePersonalDetails); - expect(ReportUtils.getGroupChatName([1, 2, 3, 4, 5, 6, 7, 8], false)).toEqual('Eight, Five, Four, One, Seven, Six, Three, Two'); - }); - - it('Should use correct display name for participants', async () => { - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, participantsPersonalDetails); - expect(ReportUtils.getGroupChatName([1, 2, 3, 4], true)).toEqual('(833) 240-3627, floki@vikings.net, Lagertha, Ragnar'); - }); - }); - - describe('When participantAccountIDs is not passed to getGroupChatName and report ID is passed', () => { - it('Should show report name if count <= 5 and shouldApplyLimit is false', async () => { - const report = { - ...LHNTestUtils.getFakeReport([1, 2, 3, 4], 0, false, [1]), - chatType: CONST.REPORT.CHAT_TYPE.GROUP, - reportID: `1`, - reportName: "Let's talk", - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1`, report); - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, fakePersonalDetails); - expect(ReportUtils.getGroupChatName(undefined, false, '1')).toEqual("Let's talk"); - }); - - it('Should show report name if count <= 5 and shouldApplyLimit is true', async () => { - const report = { - ...LHNTestUtils.getFakeReport([1, 2, 3, 4], 0, false, [1]), - chatType: CONST.REPORT.CHAT_TYPE.GROUP, - reportID: `1`, - reportName: "Let's talk", - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1`, report); - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, fakePersonalDetails); - expect(ReportUtils.getGroupChatName(undefined, true, '1')).toEqual("Let's talk"); - }); - - it('Should show report name if count > 5 and shouldApplyLimit is true', async () => { - const report = { - ...LHNTestUtils.getFakeReport([1, 2, 3, 4, 5, 6, 7, 8], 0, false, [1, 2]), - chatType: CONST.REPORT.CHAT_TYPE.GROUP, - reportID: `1`, - reportName: "Let's talk", - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1`, report); - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, fakePersonalDetails); - expect(ReportUtils.getGroupChatName(undefined, true, '1')).toEqual("Let's talk"); - }); - - it('Should show report name if count > 5 and shouldApplyLimit is false', async () => { - const report = { - ...LHNTestUtils.getFakeReport([1, 2, 3, 4, 5, 6, 7, 8], 0, false, [1, 2]), - chatType: CONST.REPORT.CHAT_TYPE.GROUP, - reportID: `1`, - reportName: "Let's talk", - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1`, report); - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, fakePersonalDetails); - expect(ReportUtils.getGroupChatName(undefined, false, '1')).toEqual("Let's talk"); - }); - - it('Should show participant names if report name is not available', async () => { - const report = { - ...LHNTestUtils.getFakeReport([1, 2, 3, 4, 5, 6, 7, 8], 0, false, [1, 2]), - chatType: CONST.REPORT.CHAT_TYPE.GROUP, - reportID: `1`, - reportName: '', - }; - await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}1`, report); - await Onyx.merge(ONYXKEYS.PERSONAL_DETAILS_LIST, fakePersonalDetails); - expect(ReportUtils.getGroupChatName(undefined, false, '1')).toEqual('Eight, Five, Four, One, Seven, Six, Three, Two'); - }); - }); - }); }); diff --git a/tests/utils/LHNTestUtils.tsx b/tests/utils/LHNTestUtils.tsx index eb678212f8bf..89c31d92843e 100644 --- a/tests/utils/LHNTestUtils.tsx +++ b/tests/utils/LHNTestUtils.tsx @@ -112,13 +112,6 @@ const fakePersonalDetails: PersonalDetailsList = { avatar: 'none', firstName: 'Nine', }, - 10: { - accountID: 10, - login: 'email10@test.com', - displayName: 'Email Ten', - avatar: 'none', - firstName: 'Ten', - }, }; let lastFakeReportID = 0; @@ -127,25 +120,16 @@ let lastFakeReportActionID = 0; /** * @param millisecondsInThePast the number of milliseconds in the past for the last message timestamp (to order reports by most recent messages) */ -function getFakeReport(participantAccountIDs = [1, 2], millisecondsInThePast = 0, isUnread = false, adminIDs: number[] = []): Report { +function getFakeReport(participantAccountIDs = [1, 2], millisecondsInThePast = 0, isUnread = false): Report { const lastVisibleActionCreated = DateUtils.getDBTime(Date.now() - millisecondsInThePast); - const participants = ReportUtils.buildParticipantsFromAccountIDs(participantAccountIDs); - - adminIDs.forEach((id) => { - participants[id] = { - hidden: false, - role: CONST.REPORT.ROLE.ADMIN, - }; - }); - return { type: CONST.REPORT.TYPE.CHAT, reportID: `${++lastFakeReportID}`, reportName: 'Report', lastVisibleActionCreated, lastReadTime: isUnread ? DateUtils.subtractMillisecondsFromDateTime(lastVisibleActionCreated, 1) : lastVisibleActionCreated, - participants, + participants: ReportUtils.buildParticipantsFromAccountIDs(participantAccountIDs), }; } diff --git a/tests/utils/TestHelper.ts b/tests/utils/TestHelper.ts index be18206d7d36..9ca0969abc6a 100644 --- a/tests/utils/TestHelper.ts +++ b/tests/utils/TestHelper.ts @@ -1,20 +1,12 @@ -import {fireEvent, screen} from '@testing-library/react-native'; import {Str} from 'expensify-common'; -import {Linking} from 'react-native'; import Onyx from 'react-native-onyx'; -import * as Localize from '@libs/Localize'; -import * as Pusher from '@libs/Pusher/pusher'; -import PusherConnectionManager from '@libs/PusherConnectionManager'; -import CONFIG from '@src/CONFIG'; import CONST from '@src/CONST'; import * as Session from '@src/libs/actions/Session'; import HttpUtils from '@src/libs/HttpUtils'; import * as NumberUtils from '@src/libs/NumberUtils'; import ONYXKEYS from '@src/ONYXKEYS'; -import appSetup from '@src/setup'; import type {Response as OnyxResponse, PersonalDetails, Report} from '@src/types/onyx'; import waitForBatchedUpdates from './waitForBatchedUpdates'; -import waitForBatchedUpdatesWithAct from './waitForBatchedUpdatesWithAct'; type MockFetch = ReturnType & { pause?: () => void; @@ -255,44 +247,5 @@ const createAddListenerMock = () => { return {triggerTransitionEnd, addListener}; }; -async function navigateToSidebarOption(index: number): Promise { - const hintText = Localize.translateLocal('accessibilityHints.navigatesToChat'); - const optionRows = screen.queryAllByAccessibilityHint(hintText); - fireEvent(optionRows[index], 'press'); - await waitForBatchedUpdatesWithAct(); -} - -function beforeAllSetupUITests(shouldConnectToPusher = false) { - // In this test, we are generically mocking the responses of all API requests by mocking fetch() and having it - // return 200. In other tests, we might mock HttpUtils.xhr() with a more specific mock data response (which means - // fetch() never gets called so it does not need mocking) or we might have fetch throw an error to test error handling - // behavior. But here we just want to treat all API requests as a generic "success" and in the cases where we need to - // simulate data arriving we will just set it into Onyx directly with Onyx.merge() or Onyx.set() etc. - global.fetch = getGlobalFetchMock(); - - Linking.setInitialURL('https://new.expensify.com/'); - appSetup(); - - if (shouldConnectToPusher) { - PusherConnectionManager.init(); - Pusher.init({ - appKey: CONFIG.PUSHER.APP_KEY, - cluster: CONFIG.PUSHER.CLUSTER, - authEndpoint: `${CONFIG.EXPENSIFY.DEFAULT_API_ROOT}api/AuthenticatePusher?`, - }); - } -} - export type {MockFetch, FormData}; -export { - assertFormDataMatchesObject, - buildPersonalDetails, - buildTestReportComment, - createAddListenerMock, - getGlobalFetchMock, - setPersonalDetails, - signInWithTestUser, - signOutTestUser, - navigateToSidebarOption, - beforeAllSetupUITests, -}; +export {assertFormDataMatchesObject, buildPersonalDetails, buildTestReportComment, createAddListenerMock, getGlobalFetchMock, setPersonalDetails, signInWithTestUser, signOutTestUser};