Skip to content

Commit 606e084

Browse files
committed
add test for ReportActionsView
1 parent 1a1ab2c commit 606e084

2 files changed

Lines changed: 279 additions & 1 deletion

File tree

src/components/ReportActionsSkeletonView/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ function ReportActionsSkeletonView({shouldAnimate = true, possibleVisibleContent
4545
);
4646
}
4747
}
48-
return <View>{skeletonViewLines}</View>;
48+
return <View testID="ReportActionsSkeletonView">{skeletonViewLines}</View>;
4949
}
5050

5151
ReportActionsSkeletonView.displayName = 'ReportActionsSkeletonView';

tests/ui/ReportActionsViewTest.tsx

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
import * as NativeNavigation from '@react-navigation/native';
2+
import {render, screen} from '@testing-library/react-native';
3+
import React from 'react';
4+
import Onyx from 'react-native-onyx';
5+
import type {OnyxEntry} from 'react-native-onyx';
6+
import useNetwork from '@hooks/useNetwork';
7+
import useOnyx from '@hooks/useOnyx';
8+
import useResponsiveLayout from '@hooks/useResponsiveLayout';
9+
import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport';
10+
import ReportActionsView from '@pages/home/report/ReportActionsView';
11+
import CONST from '@src/CONST';
12+
import ONYXKEYS from '@src/ONYXKEYS';
13+
import type * as OnyxTypes from '@src/types/onyx';
14+
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
15+
16+
jest.mock('@react-navigation/native');
17+
const mockUseIsFocused = jest.fn(() => true);
18+
const mockUseRoute = jest.fn(() => ({
19+
params: {},
20+
}));
21+
(NativeNavigation as any).useIsFocused = mockUseIsFocused;
22+
(NativeNavigation as any).useRoute = mockUseRoute;
23+
24+
jest.mock('@hooks/useNetwork', () => jest.fn());
25+
jest.mock('@hooks/useOnyx', () => jest.fn());
26+
jest.mock('@hooks/useResponsiveLayout', () => jest.fn());
27+
jest.mock('@hooks/useTransactionsAndViolationsForReport', () => jest.fn());
28+
29+
const mockUseNetwork = useNetwork as jest.MockedFunction<typeof useNetwork>;
30+
const mockUseOnyx = useOnyx as jest.MockedFunction<typeof useOnyx>;
31+
const mockUseResponsiveLayout = useResponsiveLayout as jest.MockedFunction<typeof useResponsiveLayout>;
32+
const mockUseTransactionsAndViolationsForReport = useTransactionsAndViolationsForReport as jest.MockedFunction<typeof useTransactionsAndViolationsForReport>;
33+
jest.mock('@hooks/useCopySelectionHelper', () => jest.fn());
34+
jest.mock('@hooks/useLoadReportActions', () =>
35+
jest.fn(() => ({
36+
loadOlderChats: jest.fn(),
37+
loadNewerChats: jest.fn(),
38+
})),
39+
);
40+
jest.mock('@hooks/usePrevious', () => jest.fn());
41+
42+
jest.mock('@pages/home/report/ReportActionsList', () =>
43+
jest.fn(({sortedReportActions}) => {
44+
if (sortedReportActions && sortedReportActions.length > 0) {
45+
return null; // Simulate normal content
46+
}
47+
return null;
48+
}),
49+
);
50+
jest.mock('@pages/home/report/UserTypingEventListener', () => jest.fn(() => null));
51+
52+
jest.mock('@libs/actions/Report', () => ({
53+
updateLoadingInitialReportAction: jest.fn(),
54+
}));
55+
56+
jest.mock('@libs/Performance', () => ({
57+
withRenderTrace: jest.fn(() => (Component: any) => Component),
58+
}));
59+
60+
// Mock report data
61+
const mockReport: OnyxTypes.Report = {
62+
reportID: '123',
63+
reportName: 'Test Report',
64+
chatReportID: '456',
65+
ownerAccountID: 123,
66+
lastVisibleActionCreated: '2023-01-01',
67+
total: 0,
68+
};
69+
70+
const mockReportActions: OnyxTypes.ReportAction[] = [
71+
{
72+
reportActionID: '1',
73+
actionName: CONST.REPORT.ACTIONS.TYPE.CREATED,
74+
created: '2023-01-01',
75+
actorAccountID: 123,
76+
message: [{type: 'COMMENT', html: 'Test message', text: 'Test message'}],
77+
originalMessage: {},
78+
shouldShow: true,
79+
person: [{type: 'TEXT', style: 'strong', text: 'Test User'}],
80+
pendingAction: null,
81+
errors: {},
82+
},
83+
{
84+
reportActionID: '2',
85+
actionName: CONST.REPORT.ACTIONS.TYPE.ADD_COMMENT,
86+
created: '2023-01-02',
87+
actorAccountID: 124,
88+
message: [{type: 'COMMENT', html: 'Another message', text: 'Another message'}],
89+
originalMessage: {},
90+
shouldShow: true,
91+
person: [{type: 'TEXT', style: 'strong', text: 'Another User'}],
92+
pendingAction: null,
93+
errors: {},
94+
},
95+
];
96+
97+
const renderReportActionsView = (
98+
props: Partial<{
99+
report: OnyxTypes.Report;
100+
reportActions?: OnyxTypes.ReportAction[];
101+
parentReportAction: OnyxEntry<OnyxTypes.ReportAction>;
102+
isLoadingInitialReportActions?: boolean;
103+
transactionThreadReportID?: string | null;
104+
hasNewerActions: boolean;
105+
hasOlderActions: boolean;
106+
}> = {},
107+
) => {
108+
const defaultProps = {
109+
report: mockReport,
110+
reportActions: mockReportActions,
111+
parentReportAction: null as unknown as OnyxEntry<OnyxTypes.ReportAction>,
112+
isLoadingInitialReportActions: false,
113+
hasNewerActions: false,
114+
hasOlderActions: false,
115+
...props,
116+
};
117+
118+
return render(<ReportActionsView {...defaultProps} />);
119+
};
120+
121+
describe('ReportActionsView', () => {
122+
beforeAll(() => {
123+
Onyx.init({
124+
keys: ONYXKEYS,
125+
});
126+
});
127+
128+
beforeEach(() => {
129+
jest.clearAllMocks();
130+
131+
mockUseResponsiveLayout.mockReturnValue({
132+
shouldUseNarrowLayout: false,
133+
isSmallScreenWidth: false,
134+
isInNarrowPaneModal: false,
135+
isExtraSmallScreenHeight: false,
136+
isMediumScreenWidth: false,
137+
isLargeScreenWidth: true,
138+
isExtraLargeScreenWidth: false,
139+
isExtraSmallScreenWidth: false,
140+
isSmallScreen: false,
141+
onboardingIsMediumOrLargerScreenWidth: true,
142+
});
143+
144+
mockUseTransactionsAndViolationsForReport.mockReturnValue({
145+
transactions: {},
146+
violations: {},
147+
});
148+
149+
mockUseOnyx.mockImplementation((key: string) => {
150+
if (key === ONYXKEYS.IS_LOADING_APP) {
151+
return [false, {status: 'loaded'}];
152+
}
153+
if (key === ONYXKEYS.ARE_TRANSLATIONS_LOADING) {
154+
return [false, {status: 'loaded'}];
155+
}
156+
if (key.includes('reportActions')) {
157+
return [[], {status: 'loaded'}];
158+
}
159+
if (key.includes('report')) {
160+
return [undefined, {status: 'loaded'}];
161+
}
162+
return [undefined, {status: 'loaded'}];
163+
});
164+
});
165+
166+
afterEach(async () => {
167+
await waitForBatchedUpdatesWithAct();
168+
await Onyx.clear();
169+
});
170+
171+
describe('Skeleton Loading States', () => {
172+
it('should show skeleton when shouldShowSkeletonForAppLoad is true (isLoadingApp is true and not offline)', () => {
173+
mockUseNetwork.mockReturnValue({
174+
isOffline: false,
175+
});
176+
177+
mockUseOnyx.mockImplementation((key: string) => {
178+
if (key === ONYXKEYS.IS_LOADING_APP) {
179+
return [true, {status: 'loaded'}];
180+
}
181+
if (key === ONYXKEYS.ARE_TRANSLATIONS_LOADING) {
182+
return [false, {status: 'loaded'}];
183+
}
184+
if (key.includes('reportActions')) {
185+
return [[], {status: 'loaded'}];
186+
}
187+
if (key.includes('report')) {
188+
return [undefined, {status: 'loaded'}];
189+
}
190+
return [undefined, {status: 'loaded'}];
191+
});
192+
193+
renderReportActionsView({
194+
reportActions: [], // Empty report actions to trigger isMissingReportActions condition
195+
});
196+
197+
expect(screen.getByTestId('ReportActionsSkeletonView')).toBeTruthy();
198+
});
199+
200+
it('should not show skeleton when shouldShowSkeletonForAppLoad is false (isLoadingApp is false)', () => {
201+
mockUseNetwork.mockReturnValue({
202+
isOffline: false,
203+
});
204+
205+
mockUseOnyx.mockImplementation((key: string) => {
206+
if (key === ONYXKEYS.IS_LOADING_APP) {
207+
return [false, {status: 'loaded'}];
208+
}
209+
if (key === ONYXKEYS.ARE_TRANSLATIONS_LOADING) {
210+
return [false, {status: 'loaded'}];
211+
}
212+
if (key.includes('reportActions')) {
213+
return [[], {status: 'loaded'}];
214+
}
215+
if (key.includes('report')) {
216+
return [undefined, {status: 'loaded'}];
217+
}
218+
return [undefined, {status: 'loaded'}];
219+
});
220+
221+
renderReportActionsView();
222+
223+
expect(screen.queryByTestId('ReportActionsSkeletonView')).toBeNull();
224+
});
225+
226+
it('should not show skeleton when shouldShowSkeletonForAppLoad is false (isOffline is true)', () => {
227+
mockUseNetwork.mockReturnValue({
228+
isOffline: true,
229+
});
230+
231+
mockUseOnyx.mockImplementation((key: string) => {
232+
if (key === ONYXKEYS.IS_LOADING_APP) {
233+
return [true, {status: 'loaded'}];
234+
}
235+
if (key === ONYXKEYS.ARE_TRANSLATIONS_LOADING) {
236+
return [false, {status: 'loaded'}];
237+
}
238+
if (key.includes('reportActions')) {
239+
return [[], {status: 'loaded'}];
240+
}
241+
if (key.includes('report')) {
242+
return [undefined, {status: 'loaded'}];
243+
}
244+
return [undefined, {status: 'loaded'}];
245+
});
246+
247+
renderReportActionsView();
248+
249+
expect(screen.queryByTestId('ReportActionsSkeletonView')).toBeNull();
250+
});
251+
252+
it('should not show skeleton when both isLoadingApp is false and isOffline is true', () => {
253+
mockUseNetwork.mockReturnValue({
254+
isOffline: true,
255+
});
256+
257+
mockUseOnyx.mockImplementation((key: string) => {
258+
if (key === ONYXKEYS.IS_LOADING_APP) {
259+
return [false, {status: 'loaded'}];
260+
}
261+
if (key === ONYXKEYS.ARE_TRANSLATIONS_LOADING) {
262+
return [false, {status: 'loaded'}];
263+
}
264+
if (key.includes('reportActions')) {
265+
return [[], {status: 'loaded'}];
266+
}
267+
if (key.includes('report')) {
268+
return [undefined, {status: 'loaded'}];
269+
}
270+
return [undefined, {status: 'loaded'}];
271+
});
272+
273+
renderReportActionsView();
274+
275+
expect(screen.queryByTestId('ReportActionsSkeletonView')).toBeNull();
276+
});
277+
});
278+
});

0 commit comments

Comments
 (0)