diff --git a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js index a254ca8b0d83..a1286f39fe66 100644 --- a/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js +++ b/src/pages/home/report/ReportActionCompose/ComposerWithSuggestions.js @@ -551,6 +551,7 @@ function ComposerWithSuggestions({ setIsFullComposerAvailable={setIsFullComposerAvailable} isComposerFullSize={isComposerFullSize} value={value} + testID="composer" numberOfLines={numberOfLines} onNumberOfLinesChange={updateNumberOfLines} shouldCalculateCaretPosition diff --git a/src/pages/home/report/ReportActionsList.js b/src/pages/home/report/ReportActionsList.js index e26aae7be7c5..2bd969364e31 100644 --- a/src/pages/home/report/ReportActionsList.js +++ b/src/pages/home/report/ReportActionsList.js @@ -415,6 +415,7 @@ function ReportActionsList({ diff --git a/tests/perf-test/ReportActionsList.perf-test.js b/tests/perf-test/ReportActionsList.perf-test.js index f4dcd969923f..991ca82da1b6 100644 --- a/tests/perf-test/ReportActionsList.perf-test.js +++ b/tests/perf-test/ReportActionsList.perf-test.js @@ -13,6 +13,7 @@ import {ActionListContext, ReactionListContext} from '../../src/pages/home/Repor import variables from '../../src/styles/variables'; import * as LHNTestUtils from '../utils/LHNTestUtils'; import PusherHelper from '../utils/PusherHelper'; +import * as ReportTestUtils from '../utils/ReportTestUtils'; import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; @@ -58,7 +59,7 @@ afterAll(() => { const mockOnLayout = jest.fn(); const mockOnScroll = jest.fn(); -const mockLoadMoreChats = jest.fn(); +const mockLoadChats = jest.fn(); const mockRef = {current: null}; // Initialize the network key for OfflineWithFeedback @@ -74,49 +75,6 @@ afterEach(() => { PusherHelper.teardown(); }); -const getFakeReportAction = (index) => ({ - actionName: 'ADDCOMMENT', - actorAccountID: index, - automatic: false, - avatar: '', - created: '2023-09-12 16:27:35.124', - isAttachment: true, - isFirstItem: false, - lastModified: '2021-07-14T15:00:00Z', - message: [ - { - html: 'hey', - isDelatedParentAction: false, - isEdited: false, - reactions: [], - text: 'test', - type: 'TEXT', - whisperedTo: [], - }, - ], - originalMessage: { - html: 'hey', - lastModified: '2021-07-14T15:00:00Z', - }, - pendingAction: null, - person: [ - { - type: 'TEXT', - style: 'strong', - text: 'email@test.com', - }, - ], - previousReportActionID: '1', - reportActionID: index.toString(), - reportActionTimestamp: 1696243169753, - sequenceNumber: 2, - shouldShow: true, - timestamp: 1696243169, - whisperedToAccountIDs: [], -}); - -const getMockedSortedReportActions = (length = 100) => Array.from({length}, (__, i) => getFakeReportAction(i)); - const currentUserAccountID = 5; function ReportActionsListWrapper() { @@ -125,11 +83,13 @@ function ReportActionsListWrapper() { diff --git a/tests/perf-test/ReportScreen.perf-test.js b/tests/perf-test/ReportScreen.perf-test.js new file mode 100644 index 000000000000..578546cb4679 --- /dev/null +++ b/tests/perf-test/ReportScreen.perf-test.js @@ -0,0 +1,225 @@ +import {fireEvent, screen} from '@testing-library/react-native'; +import React from 'react'; +import Onyx from 'react-native-onyx'; +import {measurePerformance} from 'reassure'; +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 {WindowDimensionsProvider} from '../../src/components/withWindowDimensions'; +import CONST from '../../src/CONST'; +import * as Localize from '../../src/libs/Localize'; +import ONYXKEYS from '../../src/ONYXKEYS'; +import {ReportAttachmentsProvider} from '../../src/pages/home/report/ReportAttachmentsContext'; +import ReportScreen from '../../src/pages/home/ReportScreen'; +import * as LHNTestUtils from '../utils/LHNTestUtils'; +import PusherHelper from '../utils/PusherHelper'; +import * as ReportTestUtils from '../utils/ReportTestUtils'; +import * as TestHelper from '../utils/TestHelper'; +import waitForBatchedUpdates from '../utils/waitForBatchedUpdates'; +import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates'; + +jest.setTimeout(60000); + +jest.mock('react-native-reanimated', () => ({ + ...jest.requireActual('react-native-reanimated/mock'), + useSharedValue: jest.fn, + useAnimatedStyle: jest.fn, + useAnimatedRef: jest.fn, +})); + +jest.mock('../../src/components/withNavigationFocus', () => (Component) => { + function WithNavigationFocus(props) { + 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', () => ({ + canUseTasks: jest.fn(() => true), + canUseLinkPreviews: jest.fn(() => true), +})); + +jest.mock('../../src/libs/Navigation/Navigation'); + +const mockedNavigate = jest.fn(); +jest.mock('@react-navigation/native', () => { + const actualNav = jest.requireActual('@react-navigation/native'); + return { + ...actualNav, + useFocusEffect: jest.fn(), + useIsFocused: () => ({ + navigate: mockedNavigate, + }), + useRoute: () => jest.fn(), + useNavigation: () => ({ + navigate: jest.fn(), + addListener: () => jest.fn(), + }), + createNavigationContainerRef: jest.fn(), + }; +}); + +// mock PortalStateContext +jest.mock('@gorhom/portal'); + +beforeAll(() => + Onyx.init({ + keys: ONYXKEYS, + safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS], + registerStorageEventListener: () => {}, + }), +); + +// Initialize the network key for OfflineWithFeedback +beforeEach(() => { + global.fetch = TestHelper.getGlobalFetchMock(); + wrapOnyxWithWaitForBatchedUpdates(Onyx); + Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false}); +}); + +// Clear out Onyx after each test so that each test starts with a clean state +afterEach(() => { + Onyx.clear(); + PusherHelper.teardown(); +}); + +function ReportScreenWrapper(args) { + return ( + + + + ); +} + +test('should render ReportScreen with composer interactions', () => { + const scenario = async () => { + // Query for the report list + await screen.findByTestId('report-actions-list'); + + // Query for the composer + const composer = await screen.findByTestId('composer'); + expect(composer).toBeDefined(); + + // Type in the composer + fireEvent.changeText(composer, 'Test message'); + + const hintSendButtonText = Localize.translateLocal('common.send'); + + // Query for the send button + const sendButton = await screen.findByLabelText(hintSendButtonText); + expect(sendButton).toBeDefined(); + + // Click on the send button + fireEvent.press(sendButton); + + const hintHeaderText = Localize.translateLocal('common.back'); + + // Query for the header + const header = await screen.findByLabelText(hintHeaderText); + expect(header).toBeDefined(); + }; + + const policy = { + policyID: 1, + name: 'Testing Policy', + }; + + const report = LHNTestUtils.getFakeReport(); + const reportActions = ReportTestUtils.getMockedReportsMap(1000); + const mockRoute = {params: {reportID: '1'}}; + + return waitForBatchedUpdates() + .then(() => + Onyx.multiSet({ + [ONYXKEYS.IS_SIDEBAR_LOADED]: true, + [`${ONYXKEYS.COLLECTION.REPORT}${mockRoute.params.reportID}`]: report, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${mockRoute.params.reportID}`]: reportActions, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS], + [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + [`${ONYXKEYS.COLLECTION.REPORT_METADATA}${mockRoute.params.reportID}`]: { + isLoadingReportActions: false, + }, + }), + ) + .then(() => measurePerformance(, {scenario})); +}); + +test('should press of the report item', () => { + const scenario = async () => { + // Query for the report list + await screen.findByTestId('report-actions-list'); + + // Query for the composer + await screen.findByTestId('composer'); + + const hintReportPreviewText = Localize.translateLocal('iou.viewDetails'); + + // Query for report preview buttons + const reportPreviewButtons = await screen.findAllByLabelText(hintReportPreviewText); + + expect(reportPreviewButtons.length).toBeDefined(); + + // click on the report preview button + fireEvent.press(reportPreviewButtons[0]); + }; + + const policy = { + policyID: 123, + name: 'Testing Policy', + }; + + const report = LHNTestUtils.getFakeReport(); + const reportActions = ReportTestUtils.getMockedReportsMap(1000); + const mockRoute = {params: {reportID: '2'}}; + + return waitForBatchedUpdates() + .then(() => + Onyx.multiSet({ + [ONYXKEYS.IS_SIDEBAR_LOADED]: true, + [`${ONYXKEYS.COLLECTION.REPORT}${mockRoute.params.reportID}`]: report, + [`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${mockRoute.params.reportID}`]: reportActions, + [ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails, + [ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS, CONST.BETAS.POLICY_ROOMS], + [`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy, + [`${ONYXKEYS.COLLECTION.REPORT_METADATA}${mockRoute.params.reportID}`]: { + isLoadingReportActions: false, + }, + }), + ) + .then(() => measurePerformance(, {scenario})); +}); diff --git a/tests/utils/ReportTestUtils.js b/tests/utils/ReportTestUtils.js new file mode 100644 index 000000000000..48e5ebfaa56d --- /dev/null +++ b/tests/utils/ReportTestUtils.js @@ -0,0 +1,61 @@ +import _ from 'underscore'; + +const actionNames = ['ADDCOMMENT', 'IOU', 'REPORTPREVIEW']; + +const getFakeReportAction = (index, actionName) => ({ + actionName, + actorAccountID: index, + automatic: false, + avatar: '', + created: '2023-09-12 16:27:35.124', + isAttachment: true, + isFirstItem: false, + lastModified: '2021-07-14T15:00:00Z', + message: [ + { + html: 'hey', + isDelatedParentAction: false, + isEdited: false, + reactions: [], + text: 'test', + type: 'TEXT', + whisperedTo: [], + }, + ], + originalMessage: { + html: 'hey', + lastModified: '2021-07-14T15:00:00Z', + // IOUReportID: index, + linkedReportID: index.toString(), + }, + pendingAction: null, + person: [ + { + type: 'TEXT', + style: 'strong', + text: 'email@test.com', + }, + ], + previousReportActionID: '1', + reportActionID: index.toString(), + reportActionTimestamp: 1696243169753, + sequenceNumber: 0, + shouldShow: true, + timestamp: 1696243169, + whisperedToAccountIDs: [], +}); + +const getMockedSortedReportActions = (length = 100) => Array.from({length}, (__, i) => getFakeReportAction(i)); + +const getMockedReportsMap = (length = 100) => { + const mockReports = Array.from({length}, (__, i) => { + const reportID = i + 1; + const actionName = i === 0 ? 'CREATED' : actionNames[i % actionNames.length]; + const reportAction = getFakeReportAction(reportID, actionName); + + return {[reportID]: reportAction}; + }); + return _.assign({}, ...mockReports); +}; + +export {getFakeReportAction, getMockedSortedReportActions, getMockedReportsMap};