diff --git a/tests/perf-test/README.md b/contributingGuides/REASSURE_PERFORMANCE_TEST.md similarity index 90% rename from tests/perf-test/README.md rename to contributingGuides/REASSURE_PERFORMANCE_TEST.md index 2b66f7c147f3..0de450b78875 100644 --- a/tests/perf-test/README.md +++ b/contributingGuides/REASSURE_PERFORMANCE_TEST.md @@ -7,8 +7,11 @@ We use Reassure for monitoring performance regression. It helps us check if our - Reassure builds on the existing React Testing Library setup and adds a performance measurement functionality. It's intended to be used on local machine and on a remote server as part of your continuous integration setup. - To make sure the results are reliable and consistent, Reassure runs tests twice – once for the current branch and once for the base branch. -## Performance Testing Strategy (`measurePerformance`) +## Performance Testing Strategy (`measureRenders`) +- Before adding new tests, check if the proposed scenario or component is already covered in existing tests. Duplicate tests can slow down the CI suite, making it harder to spot meaningful regressions. +- Test only scenarios that cover new or unique interactions. Avoid testing repetitive user actions that could be captured within a single, comprehensive scenario. +- Where applicable, use utility functions and helper methods to consolidate common actions (e.g., data mocking, scenario setup) across tests. This reduces redundancy and allows tests to be more focused and reusable. - The primary focus is on testing business cases rather than small, reusable parts that typically don't introduce regressions, although some tests in that area are still necessary. - To achieve this goal, it's recommended to stay relatively high up in the React tree, targeting whole screens to recreate real-life scenarios that users may encounter. - For example, consider scenarios where an additional `useMemo` call could impact performance negatively. @@ -84,7 +87,7 @@ test('Count increments on press', async () => { await screen.findByText('Count: 2'); }; - await measurePerformance( + await measureRenders( , { scenario, runs: 20 } ); diff --git a/package.json b/package.json index 9fa11a49e9f1..a433160a2abd 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "ios-build": "bundle exec fastlane ios build_unsigned", "android-build": "bundle exec fastlane android build_local", "test": "TZ=utc NODE_OPTIONS=--experimental-vm-modules jest", + "perf-test": "NODE_OPTIONS=--experimental-vm-modules npx reassure", "typecheck": "NODE_OPTIONS=--max_old_space_size=8192 tsc", "lint": "NODE_OPTIONS=--max_old_space_size=8192 eslint . --max-warnings=0 --cache --cache-location=node_modules/.cache/eslint", "lint-changed": "NODE_OPTIONS=--max_old_space_size=8192 eslint --max-warnings=0 --config ./.eslintrc.changed.js $(git diff --diff-filter=AM --name-only origin/main HEAD -- \"*.ts\" \"*.tsx\")", diff --git a/src/components/PopoverMenu.tsx b/src/components/PopoverMenu.tsx index 9aab414971de..275b8e659ef7 100644 --- a/src/components/PopoverMenu.tsx +++ b/src/components/PopoverMenu.tsx @@ -1,7 +1,7 @@ /* eslint-disable react/jsx-props-no-spreading */ import lodashIsEqual from 'lodash/isEqual'; -import type {RefObject} from 'react'; -import React, {Fragment, useLayoutEffect, useState} from 'react'; +import type {ReactNode, RefObject} from 'react'; +import React, {useLayoutEffect, useState} from 'react'; import {StyleSheet, View} from 'react-native'; import type {StyleProp, TextStyle, ViewStyle} from 'react-native'; import type {ModalProps} from 'react-native-modal'; @@ -129,6 +129,14 @@ type PopoverMenuProps = Partial & { shouldUpdateFocusedIndex?: boolean; }; +const renderWithConditionalWrapper = (shouldUseScrollView: boolean, contentContainerStyle: StyleProp, children: ReactNode): React.JSX.Element => { + if (shouldUseScrollView) { + return {children}; + } + // eslint-disable-next-line react/jsx-no-useless-fragment + return <>{children}; +}; + function PopoverMenu({ menuItems, onItemSelected, @@ -171,7 +179,6 @@ function PopoverMenu({ const {windowHeight} = useWindowDimensions(); const [focusedIndex, setFocusedIndex] = useArrowKeyFocusManager({initialFocusedIndex: currentMenuItemsFocusedIndex, maxIndex: currentMenuItems.length - 1, isActive: isVisible}); - const WrapComponent = shouldUseScrollView ? ScrollView : Fragment; const selectItem = (index: number) => { const selectedItem = currentMenuItems.at(index); @@ -235,6 +242,39 @@ function PopoverMenu({ ); }; + const renderedMenuItems = currentMenuItems.map((item, menuIndex) => { + const {text, onSelected, subMenuItems, shouldCallAfterModalHide, ...menuItemProps} = item; + return ( + + selectItem(menuIndex)} + focused={focusedIndex === menuIndex} + shouldShowSelectedItemCheck={shouldShowSelectedItemCheck} + shouldCheckActionAllowedOnPress={false} + onFocus={() => { + if (!shouldUpdateFocusedIndex) { + return; + } + setFocusedIndex(menuIndex); + }} + style={{backgroundColor: item.isSelected ? theme.activeComponentBG : undefined}} + wrapperStyle={StyleUtils.getItemBackgroundColorStyle(!!item.isSelected, focusedIndex === menuIndex, theme.activeComponentBG, theme.hoverComponentBG)} + shouldRemoveHoverBackground={item.isSelected} + titleStyle={StyleSheet.flatten([styles.flex1, item.titleStyle])} + // Spread other props dynamically + {...menuItemProps} + /> + + ); + }); + const renderHeaderText = () => { if (!headerText || enteredSubMenuIndexes.length !== 0) { return; @@ -298,40 +338,7 @@ function PopoverMenu({ {renderHeaderText()} {enteredSubMenuIndexes.length > 0 && renderBackButtonItem()} - {/** eslint-disable-next-line react/jsx-props-no-spreading */} - - {currentMenuItems.map((item, menuIndex) => { - const {text, onSelected, subMenuItems, shouldCallAfterModalHide, ...menuItemProps} = item; - return ( - - selectItem(menuIndex)} - focused={focusedIndex === menuIndex} - shouldShowSelectedItemCheck={shouldShowSelectedItemCheck} - shouldCheckActionAllowedOnPress={false} - onFocus={() => { - if (!shouldUpdateFocusedIndex) { - return; - } - setFocusedIndex(menuIndex); - }} - wrapperStyle={StyleUtils.getItemBackgroundColorStyle(!!item.isSelected, focusedIndex === menuIndex, theme.activeComponentBG, theme.hoverComponentBG)} - shouldRemoveHoverBackground={item.isSelected} - titleStyle={StyleSheet.flatten([styles.flex1, item.titleStyle])} - // eslint-disable-next-line react/jsx-props-no-spreading - {...menuItemProps} - /> - - ); - })} - + {renderWithConditionalWrapper(shouldUseScrollView, scrollContainerStyle, renderedMenuItems)} diff --git a/tests/perf-test/ReportActionCompose.perf-test.tsx b/tests/perf-test/ReportActionCompose.perf-test.tsx index d1d1dd45abc2..845727c75c97 100644 --- a/tests/perf-test/ReportActionCompose.perf-test.tsx +++ b/tests/perf-test/ReportActionCompose.perf-test.tsx @@ -113,52 +113,13 @@ test('[ReportActionCompose] should render Composer with text input interactions' await measureRenders(, {scenario}); }); -test('[ReportActionCompose] should scroll to hide suggestions', async () => { +test('[ReportActionCompose] should press create button', async () => { const scenario = async () => { - // Query for the composer - const composer = await screen.findByTestId('composer'); - - // scroll to hide suggestions - fireEvent.scroll(composer); - }; - - await waitForBatchedUpdates(); - await measureRenders(, {scenario}); -}); - -test('[ReportActionCompose] should press to block suggestions', async () => { - const scenario = async () => { - // Query for the composer - const composer = await screen.findByTestId('composer'); - - // press to block suggestions - fireEvent.press(composer); - }; - - await waitForBatchedUpdates(); - await measureRenders(, {scenario}); -}); - -test('[ReportActionCompose] should press add attachemnt button', async () => { - const scenario = async () => { - // Query for the attachment button + // Query for the create button const hintAttachmentButtonText = Localize.translateLocal('common.create'); - const attachmentButton = await screen.findByLabelText(hintAttachmentButtonText); - - fireEvent.press(attachmentButton, mockEvent); - }; - - await waitForBatchedUpdates(); - await measureRenders(, {scenario}); -}); - -test('[ReportActionCompose] should press add emoji button', async () => { - const scenario = async () => { - // Query for the emoji button - const hintEmojiButtonText = Localize.translateLocal('reportActionCompose.emoji'); - const emojiButton = await screen.findByLabelText(hintEmojiButtonText); + const createButton = await screen.findByLabelText(hintAttachmentButtonText); - fireEvent.press(emojiButton); + fireEvent.press(createButton, mockEvent); }; await waitForBatchedUpdates(); @@ -177,37 +138,3 @@ test('[ReportActionCompose] should press send message button', async () => { await waitForBatchedUpdates(); await measureRenders(, {scenario}); }); - -test('[ReportActionCompose] press add attachment button', async () => { - const scenario = async () => { - const hintAddAttachmentButtonText = Localize.translateLocal('reportActionCompose.addAttachment'); - - const addAttachmentButton = await screen.findByLabelText(hintAddAttachmentButtonText); - fireEvent.press(addAttachmentButton, mockEvent); - }; - - await waitForBatchedUpdates(); - await measureRenders(, {scenario}); -}); - -test('[ReportActionCompose] should press split bill button', async () => { - const scenario = async () => { - const hintSplitBillButtonText = Localize.translateLocal('iou.splitExpense'); - const splitBillButton = await screen.findByLabelText(hintSplitBillButtonText); - fireEvent.press(splitBillButton, mockEvent); - }; - - await waitForBatchedUpdates(); - await measureRenders(, {scenario}); -}); - -test('[ReportActionCompose] should press assign task button', async () => { - const scenario = async () => { - const hintAssignTaskButtonText = Localize.translateLocal('newTaskPage.assignTask'); - const assignTaskButton = await screen.findByLabelText(hintAssignTaskButtonText); - fireEvent.press(assignTaskButton, mockEvent); - }; - - await waitForBatchedUpdates(); - await measureRenders(, {scenario}); -}); diff --git a/tests/perf-test/ReportActionsList.perf-test.tsx b/tests/perf-test/ReportActionsList.perf-test.tsx index 4b2a114392c5..f7debc510832 100644 --- a/tests/perf-test/ReportActionsList.perf-test.tsx +++ b/tests/perf-test/ReportActionsList.perf-test.tsx @@ -1,4 +1,4 @@ -import {fireEvent, screen} from '@testing-library/react-native'; +import {screen} from '@testing-library/react-native'; import type {ComponentType} from 'react'; import Onyx from 'react-native-onyx'; import {measureRenders} from 'reassure'; @@ -7,12 +7,10 @@ import type Navigation from '@libs/Navigation/Navigation'; import ComposeProviders from '@src/components/ComposeProviders'; import {LocaleContextProvider} from '@src/components/LocaleContextProvider'; import OnyxProvider from '@src/components/OnyxProvider'; -import * as Localize from '@src/libs/Localize'; import ONYXKEYS from '@src/ONYXKEYS'; import ReportActionsList from '@src/pages/home/report/ReportActionsList'; import {ReportAttachmentsProvider} from '@src/pages/home/report/ReportAttachmentsContext'; import {ActionListContext, ReactionListContext} from '@src/pages/home/ReportScreenContext'; -import variables from '@src/styles/variables'; import type {PersonalDetailsList} from '@src/types/onyx'; import createRandomReportAction from '../utils/collections/reportActions'; import * as LHNTestUtilsModule from '../utils/LHNTestUtils'; @@ -117,50 +115,3 @@ test('[ReportActionsList] should render ReportActionsList with 500 reportActions await measureRenders(, {scenario}); }); - -test('[ReportActionsList] should render list items', async () => { - const scenario = async () => { - const hintText = Localize.translateLocal('accessibilityHints.chatMessage'); - await screen.findAllByLabelText(hintText); - }; - - await waitForBatchedUpdates(); - - Onyx.multiSet({ - [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtilsModule.fakePersonalDetails, - }); - - await measureRenders(, {scenario}); -}); - -test('[ReportActionsList] should scroll through list of items', async () => { - const eventData = { - nativeEvent: { - contentOffset: { - y: variables.optionRowHeight * 5, - }, - contentSize: { - // Dimensions of the scrollable content - height: variables.optionRowHeight * 10, - width: 100, - }, - layoutMeasurement: { - // Dimensions of the device - height: variables.optionRowHeight * 5, - width: 100, - }, - }, - }; - - const scenario = async () => { - const reportActionsList = await screen.findByTestId('report-actions-list'); - fireEvent.scroll(reportActionsList, eventData); - }; - await waitForBatchedUpdates(); - - Onyx.multiSet({ - [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtilsModule.fakePersonalDetails, - }); - - await measureRenders(, {scenario}); -}); diff --git a/tests/perf-test/ReportScreen.perf-test.tsx b/tests/perf-test/ReportScreen.perf-test.tsx deleted file mode 100644 index 550b6adabc36..000000000000 --- a/tests/perf-test/ReportScreen.perf-test.tsx +++ /dev/null @@ -1,322 +0,0 @@ -import type {StackNavigationProp, StackScreenProps} from '@react-navigation/stack'; -import {screen, waitFor} from '@testing-library/react-native'; -import type {ComponentType} from 'react'; -import React from 'react'; -import type ReactNative from 'react-native'; -import {Dimensions, InteractionManager} from 'react-native'; -import Onyx from 'react-native-onyx'; -import type Animated from 'react-native-reanimated'; -import {measureRenders} from 'reassure'; -import type {WithNavigationFocusProps} from '@components/withNavigationFocus'; -import type Navigation from '@libs/Navigation/Navigation'; -import type {AuthScreensParamList} from '@libs/Navigation/types'; -import ComposeProviders from '@src/components/ComposeProviders'; -import DragAndDropProvider from '@src/components/DragAndDrop/Provider'; -import {LocaleContextProvider} from '@src/components/LocaleContextProvider'; -import OnyxProvider from '@src/components/OnyxProvider'; -import {CurrentReportIDContextProvider} from '@src/components/withCurrentReportID'; -import {KeyboardStateProvider} from '@src/components/withKeyboardState'; -import CONST from '@src/CONST'; -import ONYXKEYS from '@src/ONYXKEYS'; -import {ReportAttachmentsProvider} from '@src/pages/home/report/ReportAttachmentsContext'; -import ReportScreen from '@src/pages/home/ReportScreen'; -import type SCREENS from '@src/SCREENS'; -import type * as OnyxTypes from '@src/types/onyx'; -import type {ReportCollectionDataSet} from '@src/types/onyx/Report'; -import type {ReportActionsCollectionDataSet} from '@src/types/onyx/ReportAction'; -import createCollection from '../utils/collections/createCollection'; -import createPersonalDetails from '../utils/collections/personalDetails'; -import createRandomPolicy from '../utils/collections/policies'; -import createRandomReport from '../utils/collections/reports'; -import createAddListenerMock from '../utils/createAddListenerMock'; -import * as ReportTestUtils from '../utils/ReportTestUtils'; -import * as TestHelper from '../utils/TestHelper'; -import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; -import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; - -type ReportScreenWrapperProps = StackScreenProps; - -jest.mock('@src/libs/API', () => ({ - write: jest.fn(), - makeRequestWithSideEffects: jest.fn(), - read: jest.fn(), - paginate: jest.fn(), -})); - -jest.mock('react-native/Libraries/Interaction/InteractionManager', () => ({ - runAfterInteractions: () => ({ - cancel: jest.fn(), - }), - createInteractionHandle: jest.fn(), - clearInteractionHandle: jest.fn(), -})); - -jest.mock('react-native', () => { - const actualReactNative = jest.requireActual('react-native'); - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return { - ...actualReactNative, - Dimensions: { - ...actualReactNative.Dimensions, - addEventListener: jest.fn(), - }, - }; -}); - -jest.mock('react-native-reanimated', () => { - const actualNav = jest.requireActual('react-native-reanimated/mock'); - return { - ...actualNav, - useSharedValue: jest.fn, - useAnimatedStyle: jest.fn, - useAnimatedRef: jest.fn, - }; -}); - -jest.mock('@src/components/ConfirmedRoute.tsx'); - -jest.mock('@src/components/withNavigationFocus', () => (Component: ComponentType) => { - function WithNavigationFocus(props: Omit) { - return ( - - ); - } - - WithNavigationFocus.displayName = 'WithNavigationFocus'; - - return WithNavigationFocus; -}); - -jest.mock('@src/hooks/useEnvironment', () => - jest.fn(() => ({ - environment: 'development', - environmentURL: 'https://new.expensify.com', - isProduction: false, - isDevelopment: true, - })), -); - -jest.mock('@src/libs/Permissions', () => ({ - canUseLinkPreviews: jest.fn(() => true), - canUseDefaultRooms: jest.fn(() => true), - canUseNewSearchRouter: jest.fn(() => true), -})); - -jest.mock('@src/libs/Navigation/Navigation', () => ({ - isNavigationReady: jest.fn(() => Promise.resolve()), - isDisplayedInModal: jest.fn(() => false), -})); - -jest.mock('@react-navigation/native', () => { - const actualNav = jest.requireActual('@react-navigation/native'); - return { - ...actualNav, - useFocusEffect: jest.fn(), - useIsFocused: () => true, - useRoute: () => jest.fn(), - useNavigation: () => ({ - navigate: jest.fn(), - addListener: () => jest.fn(), - }), - useNavigationState: () => {}, - createNavigationContainerRef: jest.fn(), - }; -}); - -// mock PortalStateContext -jest.mock('@gorhom/portal'); - -beforeAll(() => - Onyx.init({ - keys: ONYXKEYS, - safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], - }), -); - -type MockListener = { - remove: jest.Mock; - callback?: () => void; -}; - -const mockListener: MockListener = {remove: jest.fn()}; -let mockCancel: jest.Mock; -let mockRunAfterInteractions: jest.Mock; - -beforeEach(() => { - global.fetch = TestHelper.getGlobalFetchMock(); - wrapOnyxWithWaitForBatchedUpdates(Onyx); - - // Reset mocks before each test - (Dimensions.addEventListener as jest.Mock).mockClear(); - mockListener.remove.mockClear(); - - // Mock the implementation of addEventListener to return the mockListener - (Dimensions.addEventListener as jest.Mock).mockImplementation((event: string, callback: () => void) => { - if (event === 'change') { - mockListener.callback = callback; - return mockListener; - } - return {remove: jest.fn()}; - }); - - // Mock the implementation of InteractionManager.runAfterInteractions - mockCancel = jest.fn(); - mockRunAfterInteractions = jest.fn().mockReturnValue({cancel: mockCancel}); - - jest.spyOn(InteractionManager, 'runAfterInteractions').mockImplementation(mockRunAfterInteractions); - - // Initialize the network key for OfflineWithFeedback - Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); - Onyx.clear().then(waitForBatchedUpdates); -}); - -const policies = createCollection( - (item: OnyxTypes.Policy) => `${ONYXKEYS.COLLECTION.POLICY}${item.id}`, - (index: number) => createRandomPolicy(index), - 10, -); - -const personalDetails = createCollection( - (item: OnyxTypes.PersonalDetails) => item.accountID, - (index: number) => createPersonalDetails(index), - 20, -); - -function ReportScreenWrapper(props: ReportScreenWrapperProps) { - return ( - - - - ); -} - -const report = {...createRandomReport(1), policyID: '1'}; -const reportActions = ReportTestUtils.getMockedReportActionsMap(1000); -const mockRoute = {params: {reportID: '1', reportActionID: ''}, key: 'Report', name: 'Report' as const}; - -test('[ReportScreen] should render ReportScreen', async () => { - const {addListener} = createAddListenerMock(); - const scenario = async () => { - // wrapp the screen with waitFor to wait for the screen to be rendered - await waitFor(async () => { - await screen.findByTestId(`report-screen-${report.reportID}`); - }); - }; - - const navigation = {addListener} as unknown as StackNavigationProp; - - await waitForBatchedUpdates(); - const reportCollectionDataSet: ReportCollectionDataSet = { - [`${ONYXKEYS.COLLECTION.REPORT}${mockRoute.params.reportID}`]: report, - }; - const reportActionsCollectionDataSet: ReportActionsCollectionDataSet = { - [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${mockRoute.params.reportID}`]: reportActions, - }; - - Onyx.multiSet({ - [ONYXKEYS.IS_SIDEBAR_LOADED]: true, - [ONYXKEYS.PERSONAL_DETAILS_LIST]: personalDetails, - [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - [`${ONYXKEYS.COLLECTION.POLICY}`]: policies, - [ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: true, - ...reportCollectionDataSet, - ...reportActionsCollectionDataSet, - }); - - await measureRenders( - , - {scenario}, - ); -}); - -test('[ReportScreen] should render composer', async () => { - const {addListener} = createAddListenerMock(); - const scenario = async () => { - await waitFor(async () => { - await screen.findByTestId('composer'); - }); - }; - - const navigation = {addListener} as unknown as StackNavigationProp; - - await waitForBatchedUpdates(); - - const reportCollectionDataSet: ReportCollectionDataSet = { - [`${ONYXKEYS.COLLECTION.REPORT}${mockRoute.params.reportID}`]: report, - }; - - const reportActionsCollectionDataSet: ReportActionsCollectionDataSet = { - [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${mockRoute.params.reportID}`]: reportActions, - }; - Onyx.multiSet({ - [ONYXKEYS.IS_SIDEBAR_LOADED]: true, - [ONYXKEYS.PERSONAL_DETAILS_LIST]: personalDetails, - [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - [`${ONYXKEYS.COLLECTION.POLICY}`]: policies, - [ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: true, - ...reportCollectionDataSet, - ...reportActionsCollectionDataSet, - }); - await measureRenders( - , - {scenario}, - ); -}); - -test('[ReportScreen] should render report list', async () => { - const {addListener} = createAddListenerMock(); - const scenario = async () => { - await waitFor(async () => { - await screen.findByTestId('report-actions-list'); - }); - }; - - const navigation = {addListener} as unknown as StackNavigationProp; - - await waitForBatchedUpdates(); - - const reportCollectionDataSet: ReportCollectionDataSet = { - [`${ONYXKEYS.COLLECTION.REPORT}${mockRoute.params.reportID}`]: report, - }; - - const reportActionsCollectionDataSet: ReportActionsCollectionDataSet = { - [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${mockRoute.params.reportID}`]: reportActions, - }; - - Onyx.multiSet({ - [ONYXKEYS.IS_SIDEBAR_LOADED]: true, - [ONYXKEYS.PERSONAL_DETAILS_LIST]: personalDetails, - [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - [`${ONYXKEYS.COLLECTION.POLICY}`]: policies, - [ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: true, - [ONYXKEYS.IS_LOADING_APP]: false, - [`${ONYXKEYS.COLLECTION.REPORT_METADATA}${mockRoute.params.reportID}`]: { - isLoadingInitialReportActions: false, - }, - ...reportCollectionDataSet, - ...reportActionsCollectionDataSet, - }); - - await measureRenders( - , - {scenario}, - ); -}); diff --git a/tests/perf-test/SearchRouter.perf-test.tsx b/tests/perf-test/SearchRouter.perf-test.tsx index e9154a36a9a1..e5a0b2e30533 100644 --- a/tests/perf-test/SearchRouter.perf-test.tsx +++ b/tests/perf-test/SearchRouter.perf-test.tsx @@ -5,9 +5,9 @@ import type {ComponentType} from 'react'; import Onyx from 'react-native-onyx'; import {measureRenders} from 'reassure'; import {LocaleContextProvider} from '@components/LocaleContextProvider'; -import OptionListContextProvider, {OptionsListContext} from '@components/OptionListContextProvider'; +import {OptionsListContext} from '@components/OptionListContextProvider'; import SearchRouter from '@components/Search/SearchRouter/SearchRouter'; -import {KeyboardStateProvider} from '@components/withKeyboardState'; +import SearchRouterInput from '@components/Search/SearchRouter/SearchRouterInput'; import type {WithNavigationFocusProps} from '@components/withNavigationFocus'; import {createOptionList} from '@libs/OptionsListUtils'; import ComposeProviders from '@src/components/ComposeProviders'; @@ -125,12 +125,15 @@ afterEach(() => { const mockOnClose = jest.fn(); -function SearchRouterWrapper() { +function SearchRouterInputWrapper() { + const [value, setValue] = React.useState(''); return ( - - - - + + setValue(searchTerm)} + isFullWidth={false} + /> ); } @@ -145,7 +148,7 @@ function SearchRouterWrapperWithCachedOptions() { ); } -test('[SearchRouter] should render chat list with cached options', async () => { +test('[SearchRouter] should render list with cached options', async () => { const scenario = async () => { await screen.findByTestId('SearchRouter'); }; @@ -164,9 +167,7 @@ test('[SearchRouter] should render chat list with cached options', async () => { test('[SearchRouter] should react to text input changes', async () => { const scenario = async () => { - await screen.findByTestId('SearchRouter'); - - const input = screen.getByTestId('search-router-text-input'); + const input = await screen.findByTestId('search-router-text-input'); fireEvent.changeText(input, 'Email Four'); fireEvent.changeText(input, 'Report'); fireEvent.changeText(input, 'Email Five'); @@ -181,5 +182,5 @@ test('[SearchRouter] should react to text input changes', async () => { [ONYXKEYS.IS_SEARCHING_FOR_REPORTS]: true, }), ) - .then(() => measureRenders(, {scenario})); + .then(() => measureRenders(, {scenario})); }); diff --git a/tests/perf-test/SidebarLinks.perf-test.tsx b/tests/perf-test/SidebarLinks.perf-test.tsx index d1942522af38..ad19888b47a3 100644 --- a/tests/perf-test/SidebarLinks.perf-test.tsx +++ b/tests/perf-test/SidebarLinks.perf-test.tsx @@ -1,7 +1,6 @@ import {fireEvent, screen, waitFor} from '@testing-library/react-native'; import Onyx from 'react-native-onyx'; import {measureRenders} from 'reassure'; -import variables from '@styles/variables'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import * as LHNTestUtils from '../utils/LHNTestUtils'; @@ -88,70 +87,6 @@ describe('SidebarLinks', () => { await measureRenders(, {scenario}); }); - test('[SidebarLinks] should render list itmes', async () => { - const scenario = async () => { - await waitFor(async () => { - /** - * Query for display names of participants [1, 2]. - * This will ensure that the sidebar renders a list of items. - */ - await screen.findAllByText('Email Two'); - }); - }; - - await waitForBatchedUpdates(); - - Onyx.multiSet({ - [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, - [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, - [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, - ...mockedResponseMap, - }); - - await measureRenders(, {scenario}); - }); - - test('[SidebarLinks] should scroll through the list of items ', async () => { - const scenario = async () => { - const eventData = { - nativeEvent: { - contentOffset: { - y: variables.optionRowHeight * 5, - }, - contentSize: { - // Dimensions of the scrollable content - height: variables.optionRowHeight * 10, - width: 100, - }, - layoutMeasurement: { - // Dimensions of the device - height: variables.optionRowHeight * 5, - width: 100, - }, - }, - }; - - await wrapInAct(async () => { - const lhnOptionsList = await screen.findByTestId('lhn-options-list'); - - fireEvent.scroll(lhnOptionsList, eventData); - }); - }; - - await waitForBatchedUpdates(); - - Onyx.multiSet({ - [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, - [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS], - [ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD, - [ONYXKEYS.IS_LOADING_REPORT_DATA]: false, - ...mockedResponseMap, - }); - - await measureRenders(, {scenario}); - }); - test('[SidebarLinks] should click on list item', async () => { const scenario = async () => { await wrapInAct(async () => {