Skip to content

Commit

Permalink
Merge pull request Expensify#35882 from callstack-internal/fix/reassu…
Browse files Browse the repository at this point in the history
…re-fix-report-screen-flaky-test

[NO QA] ReportSceen perf test refactor
  • Loading branch information
mountiny authored Feb 9, 2024
2 parents b9aada8 + 4eece91 commit 85c990e
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 79 deletions.
109 changes: 35 additions & 74 deletions tests/perf-test/ReportScreen.perf-test.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {act, fireEvent, screen} from '@testing-library/react-native';
import {fireEvent, screen, waitFor} from '@testing-library/react-native';
import React from 'react';
import Onyx from 'react-native-onyx';
import {measurePerformance} from 'reassure';
import _ from 'underscore';
import ComposeProviders from '../../src/components/ComposeProviders';
import DragAndDropProvider from '../../src/components/DragAndDrop/Provider';
import {LocaleContextProvider} from '../../src/components/LocaleContextProvider';
Expand All @@ -15,7 +14,10 @@ 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 createCollection from '../utils/collections/createCollection';
import createPersonalDetails from '../utils/collections/personalDetails';
import createRandomPolicy from '../utils/collections/policies';
import createRandomReport from '../utils/collections/reports';
import PusherHelper from '../utils/PusherHelper';
import * as ReportTestUtils from '../utils/ReportTestUtils';
import * as TestHelper from '../utils/TestHelper';
Expand Down Expand Up @@ -56,6 +58,7 @@ jest.mock('../../src/hooks/useEnvironment', () =>

jest.mock('../../src/libs/Permissions', () => ({
canUseLinkPreviews: jest.fn(() => true),
canUseDefaultRooms: jest.fn(() => true),
}));
jest.mock('../../src/hooks/usePermissions.ts');

Expand Down Expand Up @@ -103,32 +106,17 @@ afterEach(() => {
PusherHelper.teardown();
});

/**
* 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.
*
* P.S: This can't be moved to a utils file because Jest wants any external function to stay in the scope.
*
* @returns {Object} An object with two functions: triggerTransitionEnd and addListener
*/
const createAddListenerMock = () => {
const transitionEndListeners = [];
const triggerTransitionEnd = () => {
transitionEndListeners.forEach((transitionEndListener) => transitionEndListener());
};

const addListener = jest.fn().mockImplementation((listener, callback) => {
if (listener === 'transitionEnd') {
transitionEndListeners.push(callback);
}
return () => {
_.filter(transitionEndListeners, (cb) => cb !== callback);
};
});
const policies = createCollection(
(item) => `${ONYXKEYS.COLLECTION.POLICY}${item.id}`,
(index) => createRandomPolicy(index),
10,
);

return {triggerTransitionEnd, addListener};
};
const personalDetails = createCollection(
(item) => item.accountID,
(index) => createPersonalDetails(index),
20,
);

function ReportScreenWrapper(args) {
return (
Expand All @@ -152,8 +140,12 @@ function ReportScreenWrapper(args) {
);
}

test.skip('[ReportScreen] should render ReportScreen with composer interactions', () => {
const {triggerTransitionEnd, addListener} = createAddListenerMock();
const report = {...createRandomReport(1), policyID: '1'};
const reportActions = ReportTestUtils.getMockedReportActionsMap(500);
const mockRoute = {params: {reportID: '1'}};

test('[ReportScreen] should render ReportScreen with composer interactions', () => {
const {triggerTransitionEnd, addListener} = TestHelper.createAddListenerMock();
const scenario = async () => {
/**
* First make sure ReportScreen is mounted, so that we can trigger
Expand All @@ -163,11 +155,7 @@ test.skip('[ReportScreen] should render ReportScreen with composer interactions'
* before the ReportScreen is mounted, and the test will fail.
*/
await screen.findByTestId('ReportScreen');

await act(triggerTransitionEnd);

// Query for the report list
await screen.findByTestId('report-actions-list');
await waitFor(triggerTransitionEnd);

// Query for the composer
const composer = await screen.findByTestId('composer');
Expand All @@ -189,15 +177,6 @@ test.skip('[ReportScreen] should render ReportScreen with composer interactions'
await screen.findByLabelText(hintHeaderText);
};

const policy = {
policyID: 1,
name: 'Testing Policy',
};

const report = LHNTestUtils.getFakeReport();
const reportActions = ReportTestUtils.getMockedReportActionsMap(1000);
const mockRoute = {params: {reportID: '1'}};

const navigation = {addListener};

return waitForBatchedUpdates()
Expand All @@ -206,12 +185,10 @@ test.skip('[ReportScreen] should render ReportScreen with composer interactions'
[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.PERSONAL_DETAILS_LIST]: personalDetails,
[ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS],
[`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy,
[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${mockRoute.params.reportID}`]: {
isLoadingReportActions: false,
},
[`${ONYXKEYS.COLLECTION.POLICY}`]: policies,
[ONYXKEYS.SHOULD_SHOW_COMPOSE_INPUT]: true,
}),
)
.then(() =>
Expand All @@ -225,8 +202,8 @@ test.skip('[ReportScreen] should render ReportScreen with composer interactions'
);
});

test.skip('[ReportScreen] should press of the report item', () => {
const {triggerTransitionEnd, addListener} = createAddListenerMock();
test('[ReportScreen] should press of the report item', () => {
const {triggerTransitionEnd, addListener} = TestHelper.createAddListenerMock();
const scenario = async () => {
/**
* First make sure ReportScreen is mounted, so that we can trigger
Expand All @@ -237,32 +214,19 @@ test.skip('[ReportScreen] should press of the report item', () => {
*/
await screen.findByTestId('ReportScreen');

await act(triggerTransitionEnd);
await waitFor(triggerTransitionEnd);

// 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);
const hintText = Localize.translateLocal('accessibilityHints.chatMessage');

// click on the report preview button
fireEvent.press(reportPreviewButtons[0]);
};
// Query for the list of items
const reportItems = await screen.findAllByLabelText(hintText);

const policy = {
policyID: 123,
name: 'Testing Policy',
fireEvent.press(reportItems[0], 'onLongPress');
};

const report = LHNTestUtils.getFakeReport();
const reportActions = ReportTestUtils.getMockedReportActionsMap(1000);
const mockRoute = {params: {reportID: '2'}};

const navigation = {addListener};

return waitForBatchedUpdates()
Expand All @@ -271,12 +235,9 @@ test.skip('[ReportScreen] should press of the report item', () => {
[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.PERSONAL_DETAILS_LIST]: personalDetails,
[ONYXKEYS.BETAS]: [CONST.BETAS.DEFAULT_ROOMS],
[`${ONYXKEYS.COLLECTION.POLICY}${policy.policyID}`]: policy,
[`${ONYXKEYS.COLLECTION.REPORT_METADATA}${mockRoute.params.reportID}`]: {
isLoadingReportActions: false,
},
[`${ONYXKEYS.COLLECTION.POLICY}`]: policies,
}),
)
.then(() =>
Expand Down
9 changes: 8 additions & 1 deletion tests/utils/ReportTestUtils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import _ from 'underscore';
import createRandomReportAction from './collections/reportActions';

const actionNames = ['ADDCOMMENT', 'IOU', 'REPORTPREVIEW', 'CLOSED'];

Expand Down Expand Up @@ -51,7 +52,13 @@ const getMockedReportActionsMap = (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);
const reportAction = {
...createRandomReportAction(reportID),
actionName,
originalMessage: {
linkedReportID: reportID.toString(),
},
};

return {[reportID]: reportAction};
});
Expand Down
2 changes: 2 additions & 0 deletions tests/utils/TestHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,8 @@ function assertFormDataMatchesObject(formData, obj) {

/**
* 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.
*
* @returns {Object} An object with two functions: triggerTransitionEnd and addListener
*/
Expand Down
18 changes: 14 additions & 4 deletions tests/utils/collections/reportActions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {rand, randAggregation, randBoolean, randPastDate, randWord} from '@ngneat/falso';
import {rand, randAggregation, randBoolean, randWord} from '@ngneat/falso';
import {format} from 'date-fns';
import CONST from '@src/CONST';
import type {ReportAction} from '@src/types/onyx';

Expand All @@ -17,6 +18,15 @@ const flattenActionNamesValues = (actionNames: any) => {
return result;
};

const getRandomDate = (): string => {
const randomTimestamp = Math.random() * new Date().getTime();
const randomDate = new Date(randomTimestamp);

const formattedDate = format(randomDate, CONST.DATE.FNS_DB_FORMAT_STRING);

return formattedDate;
};

export default function createRandomReportAction(index: number): ReportAction {
return {
// we need to add any here because of the way we are generating random values
Expand All @@ -32,7 +42,7 @@ export default function createRandomReportAction(index: number): ReportAction {
text: randWord(),
},
],
created: randPastDate().toISOString(),
created: getRandomDate(),
message: [
{
type: randWord(),
Expand All @@ -57,13 +67,13 @@ export default function createRandomReportAction(index: number): ReportAction {
],
originalMessage: {
html: randWord(),
type: rand(Object.values(CONST.IOU.REPORT_ACTION_TYPE)),
lastModified: getRandomDate(),
},
whisperedToAccountIDs: randAggregation(),
avatar: randWord(),
automatic: randBoolean(),
shouldShow: randBoolean(),
lastModified: randPastDate().toISOString(),
lastModified: getRandomDate(),
pendingAction: rand(Object.values(CONST.RED_BRICK_ROAD_PENDING_ACTION)),
delegateAccountID: index,
errors: {},
Expand Down

0 comments on commit 85c990e

Please sign in to comment.