Skip to content

Commit b7f466a

Browse files
authored
Merge pull request #52452 from callstack-internal/lhn-test-setup
[NoQA] Initial setup for LHN tests
2 parents 4e0bbeb + 47e511f commit b7f466a

File tree

3 files changed

+212
-2
lines changed

3 files changed

+212
-2
lines changed

tests/ui/LHNItemsPresence.tsx

+208
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
import {screen} from '@testing-library/react-native';
2+
import type {ComponentType} from 'react';
3+
import Onyx from 'react-native-onyx';
4+
import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails';
5+
import * as Localize from '@libs/Localize';
6+
import CONST from '@src/CONST';
7+
import ONYXKEYS from '@src/ONYXKEYS';
8+
import type {PersonalDetailsList} from '@src/types/onyx';
9+
import type {ReportCollectionDataSet} from '@src/types/onyx/Report';
10+
import * as LHNTestUtils from '../utils/LHNTestUtils';
11+
import * as TestHelper from '../utils/TestHelper';
12+
import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
13+
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
14+
import wrapOnyxWithWaitForBatchedUpdates from '../utils/wrapOnyxWithWaitForBatchedUpdates';
15+
16+
// Be sure to include the mocked permissions library, as some components that are rendered
17+
// during the test depend on its methods.
18+
jest.mock('@libs/Permissions');
19+
jest.mock('@src/hooks/useActiveWorkspaceFromNavigationState');
20+
21+
type LazyLoadLHNTestUtils = {
22+
fakePersonalDetails: PersonalDetailsList;
23+
};
24+
jest.mock('@components/withCurrentUserPersonalDetails', () => {
25+
// Lazy loading of LHNTestUtils
26+
const lazyLoadLHNTestUtils = () => require<LazyLoadLHNTestUtils>('../utils/LHNTestUtils');
27+
28+
return <TProps extends WithCurrentUserPersonalDetailsProps>(Component: ComponentType<TProps>) => {
29+
function WrappedComponent(props: Omit<TProps, keyof WithCurrentUserPersonalDetailsProps>) {
30+
const currentUserAccountID = 1;
31+
const LHNTestUtilsMock = lazyLoadLHNTestUtils(); // Load LHNTestUtils here
32+
33+
return (
34+
<Component
35+
// eslint-disable-next-line react/jsx-props-no-spreading
36+
{...(props as TProps)}
37+
currentUserPersonalDetails={LHNTestUtilsMock.fakePersonalDetails[currentUserAccountID]}
38+
/>
39+
);
40+
}
41+
42+
WrappedComponent.displayName = 'WrappedComponent';
43+
44+
return WrappedComponent;
45+
};
46+
});
47+
48+
const TEST_USER_ACCOUNT_ID = 1;
49+
const TEST_USER_LOGIN = 'test@test.com';
50+
const betas = [CONST.BETAS.DEFAULT_ROOMS];
51+
52+
const signUpWithTestUser = () => {
53+
TestHelper.signInWithTestUser(TEST_USER_ACCOUNT_ID, TEST_USER_LOGIN);
54+
};
55+
56+
const getOptionRows = () => {
57+
const hintText = Localize.translateLocal('accessibilityHints.navigatesToChat');
58+
return screen.queryAllByAccessibilityHint(hintText);
59+
};
60+
61+
const getDisplayNames = () => {
62+
const hintText = Localize.translateLocal('accessibilityHints.chatUserDisplayNames');
63+
return screen.queryAllByLabelText(hintText);
64+
};
65+
66+
// Reusable function to setup a mock report. Feel free to add more parameters as needed.
67+
const createReport = (isPinned = false, participants = [1, 2], messageCount = 1) => {
68+
return {
69+
...LHNTestUtils.getFakeReport(participants, messageCount),
70+
isPinned,
71+
};
72+
};
73+
74+
describe('SidebarLinksData', () => {
75+
beforeAll(() => {
76+
Onyx.init({
77+
keys: ONYXKEYS,
78+
safeEvictionKeys: [ONYXKEYS.COLLECTION.REPORT_ACTIONS],
79+
});
80+
});
81+
82+
// Helper to initialize common state
83+
const initializeState = async (reportData: ReportCollectionDataSet) => {
84+
await waitForBatchedUpdates();
85+
await Onyx.multiSet({
86+
[ONYXKEYS.NVP_PRIORITY_MODE]: CONST.PRIORITY_MODE.GSD,
87+
[ONYXKEYS.BETAS]: betas,
88+
[ONYXKEYS.PERSONAL_DETAILS_LIST]: LHNTestUtils.fakePersonalDetails,
89+
[ONYXKEYS.IS_LOADING_APP]: false,
90+
...reportData,
91+
});
92+
};
93+
94+
beforeEach(() => {
95+
wrapOnyxWithWaitForBatchedUpdates(Onyx);
96+
// Initialize the network key for OfflineWithFeedback
97+
Onyx.merge(ONYXKEYS.NETWORK, {isOffline: false});
98+
signUpWithTestUser();
99+
});
100+
101+
afterEach(async () => {
102+
await Onyx.clear();
103+
await waitForBatchedUpdatesWithAct();
104+
});
105+
106+
describe('Report that should be included in the LHN', () => {
107+
it('should display the current active report', async () => {
108+
// When the SidebarLinks are rendered without a specified report ID.
109+
LHNTestUtils.getDefaultRenderedSidebarLinks();
110+
const report = createReport();
111+
112+
// And the Onyx state is initialized with a report.
113+
await initializeState({
114+
[`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report,
115+
});
116+
117+
// Then no other reports should be displayed in the sidebar.
118+
expect(getOptionRows()).toHaveLength(0);
119+
120+
// When the SidebarLinks are rendered again with the current active report ID.
121+
LHNTestUtils.getDefaultRenderedSidebarLinks(report.reportID);
122+
123+
// Then the active report should be displayed as part of LHN,
124+
expect(getOptionRows()).toHaveLength(1);
125+
126+
// And the active report should be highlighted.
127+
// TODO add the proper assertion for the highlighted report.
128+
});
129+
130+
it('should display draft report', async () => {
131+
// When SidebarLinks are rendered initially.
132+
LHNTestUtils.getDefaultRenderedSidebarLinks();
133+
const draftReport = {
134+
...createReport(false, [1, 2], 0),
135+
writeCapability: CONST.REPORT.WRITE_CAPABILITIES.ALL,
136+
};
137+
138+
// And Onyx state is initialized with a draft report.
139+
await initializeState({
140+
[`${ONYXKEYS.COLLECTION.REPORT}${draftReport.reportID}`]: draftReport,
141+
});
142+
143+
await waitForBatchedUpdatesWithAct();
144+
145+
// And a draft message is added to the report.
146+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT_DRAFT_COMMENT}${draftReport.reportID}`, 'draft report message');
147+
148+
// Then the sidebar should display the draft report.
149+
expect(getDisplayNames()).toHaveLength(1);
150+
151+
// And the draft icon should be shown, indicating there is unsent content.
152+
expect(screen.getByTestId('Pencil Icon')).toBeOnTheScreen();
153+
});
154+
155+
it('should display pinned report', async () => {
156+
// When the SidebarLinks are rendered.
157+
LHNTestUtils.getDefaultRenderedSidebarLinks();
158+
const report = createReport(false);
159+
160+
// And the report is initialized in Onyx.
161+
await initializeState({
162+
[`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report,
163+
});
164+
165+
// Then the report should not appear in the sidebar as it is not pinned.
166+
expect(getOptionRows()).toHaveLength(0);
167+
await waitForBatchedUpdatesWithAct();
168+
169+
// When the report is marked as pinned.
170+
await Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`, {isPinned: true});
171+
172+
// Then the report should appear in the sidebar because it’s pinned.
173+
expect(getOptionRows()).toHaveLength(1);
174+
175+
// TODO add the proper assertion for the pinned report.
176+
});
177+
});
178+
179+
describe('Report that should NOT be included in the LHN', () => {
180+
it('should not display report with no participants', async () => {
181+
// When the SidebarLinks are rendered.
182+
LHNTestUtils.getDefaultRenderedSidebarLinks();
183+
const report = LHNTestUtils.getFakeReport([]);
184+
185+
// And a report with no participants is initialized in Onyx.
186+
await initializeState({
187+
[`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report,
188+
});
189+
190+
// Then the report should not appear in the sidebar.
191+
expect(getOptionRows()).toHaveLength(0);
192+
});
193+
194+
it('should not display empty chat', async () => {
195+
// When the SidebarLinks are rendered.
196+
LHNTestUtils.getDefaultRenderedSidebarLinks();
197+
const report = LHNTestUtils.getFakeReport([1, 2], 0);
198+
199+
// And a report with no messages is initialized in Onyx
200+
await initializeState({
201+
[`${ONYXKEYS.COLLECTION.REPORT}${report.reportID}`]: report,
202+
});
203+
204+
// Then the empty report should not appear in the sidebar.
205+
expect(getOptionRows()).toHaveLength(0);
206+
});
207+
});
208+
});

tests/ui/UnreadIndicatorsTest.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import waitForBatchedUpdates from '../utils/waitForBatchedUpdates';
2929
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
3030

3131
// We need a large timeout here as we are lazy loading React Navigation screens and this test is running against the entire mounted App
32-
jest.setTimeout(30000);
32+
jest.setTimeout(60000);
3333

3434
jest.mock('@react-navigation/native');
3535
jest.mock('../../src/libs/Notification/LocalNotification');

tests/utils/LHNTestUtils.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import SidebarLinksData from '@pages/home/sidebar/SidebarLinksData';
1616
import CONST from '@src/CONST';
1717
import type {PersonalDetailsList, Policy, Report, ReportAction} from '@src/types/onyx';
1818
import type ReportActionName from '@src/types/onyx/ReportActionName';
19+
import waitForBatchedUpdatesWithAct from './waitForBatchedUpdatesWithAct';
1920

2021
type MockedReportActionItemSingleProps = {
2122
/** Determines if the avatar is displayed as a subscript (positioned lower than normal) */
@@ -239,7 +240,7 @@ function getFakeAdvancedReportAction(actionName: ReportActionName = 'IOU', actor
239240

240241
function MockedSidebarLinks({currentReportID = ''}: MockedSidebarLinksProps) {
241242
return (
242-
<ComposeProviders components={[OnyxProvider, LocaleContextProvider, EnvironmentProvider, CurrentReportIDContextProvider]}>
243+
<ComposeProviders components={[OnyxProvider, LocaleContextProvider]}>
243244
{/*
244245
* Only required to make unit tests work, since we
245246
* explicitly pass the currentReportID in LHNTestUtils
@@ -276,6 +277,7 @@ function getDefaultRenderedSidebarLinks(currentReportID = '') {
276277
// and there are a lot of render warnings. It needs to be done like this because normally in
277278
// our app (App.js) is when the react application is wrapped in the context providers
278279
render(<MockedSidebarLinks currentReportID={currentReportID} />);
280+
return waitForBatchedUpdatesWithAct();
279281
} catch (error) {
280282
console.error(error);
281283
}

0 commit comments

Comments
 (0)