Skip to content

Commit 32fe175

Browse files
MelvinBotEskalifer1
andcommitted
Add tests for DynamicWorkspaceInvitePage keyboard dismiss behavior
Co-authored-by: Eskalifer1 <Eskalifer1@users.noreply.github.com>
1 parent 1809f21 commit 32fe175

1 file changed

Lines changed: 220 additions & 0 deletions

File tree

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
import {PortalProvider} from '@gorhom/portal';
2+
import {NavigationContainer} from '@react-navigation/native';
3+
import {act, fireEvent, render, screen, waitFor} from '@testing-library/react-native';
4+
import React from 'react';
5+
import {Keyboard} from 'react-native';
6+
import Onyx from 'react-native-onyx';
7+
import ComposeProviders from '@components/ComposeProviders';
8+
import {LocaleContextProvider} from '@components/LocaleContextProvider';
9+
import OnyxListItemProvider from '@components/OnyxListItemProvider';
10+
import {CurrentReportIDContextProvider} from '@hooks/useCurrentReportID';
11+
import * as useResponsiveLayoutModule from '@hooks/useResponsiveLayout';
12+
import type ResponsiveLayoutResult from '@hooks/useResponsiveLayout/types';
13+
import * as useSearchSelectorModule from '@hooks/useSearchSelector';
14+
import Navigation from '@libs/Navigation/Navigation';
15+
import createPlatformStackNavigator from '@libs/Navigation/PlatformStackNavigation/createPlatformStackNavigator';
16+
import {getEmptyOptions} from '@libs/OptionsListUtils';
17+
import type {SettingsNavigatorParamList} from '@navigation/types';
18+
import DynamicWorkspaceInvitePage from '@pages/workspace/DynamicWorkspaceInvitePage';
19+
import CONST from '@src/CONST';
20+
import ONYXKEYS from '@src/ONYXKEYS';
21+
import SCREENS from '@src/SCREENS';
22+
import * as LHNTestUtils from '../utils/LHNTestUtils';
23+
import * as TestHelper from '../utils/TestHelper';
24+
import waitForBatchedUpdatesWithAct from '../utils/waitForBatchedUpdatesWithAct';
25+
26+
jest.mock('@src/components/ConfirmedRoute.tsx');
27+
jest.mock('@pages/workspace/withPolicyAndFullscreenLoading', () => (Component: React.ComponentType) => Component);
28+
jest.mock('@components/withNavigationTransitionEnd', () => (Component: React.ComponentType) => Component);
29+
30+
TestHelper.setupGlobalFetchMock();
31+
32+
const Stack = createPlatformStackNavigator<SettingsNavigatorParamList>();
33+
34+
const renderPage = (policyID: string) => {
35+
return render(
36+
<ComposeProviders components={[OnyxListItemProvider, LocaleContextProvider, CurrentReportIDContextProvider]}>
37+
<PortalProvider>
38+
<NavigationContainer>
39+
<Stack.Navigator initialRouteName={SCREENS.WORKSPACE.DYNAMIC_WORKSPACE_INVITE}>
40+
<Stack.Screen
41+
name={SCREENS.WORKSPACE.DYNAMIC_WORKSPACE_INVITE}
42+
component={DynamicWorkspaceInvitePage}
43+
initialParams={{policyID}}
44+
/>
45+
</Stack.Navigator>
46+
</NavigationContainer>
47+
</PortalProvider>
48+
</ComposeProviders>,
49+
);
50+
};
51+
52+
describe('DynamicWorkspaceInvitePage', () => {
53+
const ownerAccountID = 1;
54+
const ownerEmail = 'owner@example.com';
55+
const selfAccountID = 1206;
56+
const selfEmail = 'self@example.com';
57+
const inviteeEmail = 'invitee@example.com';
58+
const inviteeAccountID = 9999;
59+
60+
const policy = {
61+
...LHNTestUtils.getFakePolicy(),
62+
role: CONST.POLICY.ROLE.ADMIN,
63+
owner: ownerEmail,
64+
ownerAccountID,
65+
employeeList: {
66+
[ownerEmail]: {email: ownerEmail, role: CONST.POLICY.ROLE.ADMIN},
67+
[selfEmail]: {email: selfEmail, role: CONST.POLICY.ROLE.ADMIN},
68+
},
69+
};
70+
71+
beforeAll(() => {
72+
Onyx.init({
73+
keys: ONYXKEYS,
74+
});
75+
});
76+
77+
beforeEach(async () => {
78+
await TestHelper.signInWithTestUser(selfAccountID, selfEmail, undefined, 'Self');
79+
await act(async () => {
80+
await Onyx.set(ONYXKEYS.NVP_PREFERRED_LOCALE, CONST.LOCALES.EN);
81+
await Onyx.set(ONYXKEYS.PERSONAL_DETAILS_LIST, {
82+
[ownerAccountID]: TestHelper.buildPersonalDetails(ownerEmail, ownerAccountID, 'Owner'),
83+
[selfAccountID]: TestHelper.buildPersonalDetails(selfEmail, selfAccountID, 'Self'),
84+
});
85+
await Onyx.merge(`${ONYXKEYS.COLLECTION.POLICY}${policy.id}`, policy);
86+
});
87+
jest.spyOn(useResponsiveLayoutModule, 'default').mockReturnValue({
88+
isSmallScreenWidth: false,
89+
shouldUseNarrowLayout: false,
90+
} as ResponsiveLayoutResult);
91+
});
92+
93+
afterEach(async () => {
94+
await act(async () => {
95+
await Onyx.clear();
96+
});
97+
jest.clearAllMocks();
98+
});
99+
100+
it('should dismiss keyboard before navigating when inviting users', async () => {
101+
const keyboardDismissSpy = jest.spyOn(Keyboard, 'dismiss');
102+
const navigateSpy = jest.spyOn(Navigation, 'navigate');
103+
104+
// Mock useSearchSelector to return a selected option (simulating a user having selected an invitee)
105+
jest.spyOn(useSearchSelectorModule, 'default').mockReturnValue({
106+
searchTerm: '',
107+
debouncedSearchTerm: '',
108+
setSearchTerm: jest.fn(),
109+
searchOptions: getEmptyOptions(),
110+
availableOptions: getEmptyOptions(),
111+
selectedOptions: [{login: inviteeEmail, accountID: inviteeAccountID, selected: true, text: inviteeEmail}],
112+
selectedOptionsForDisplay: [{login: inviteeEmail, accountID: inviteeAccountID, selected: true, text: inviteeEmail}],
113+
setSelectedOptions: jest.fn(),
114+
toggleSelection: jest.fn(),
115+
areOptionsInitialized: true,
116+
onListEndReached: jest.fn(),
117+
});
118+
119+
const {unmount} = renderPage(policy.id);
120+
await waitForBatchedUpdatesWithAct();
121+
122+
// Press the "Next" button to trigger inviteUser
123+
await waitFor(() => {
124+
expect(screen.getByText('Next')).toBeOnTheScreen();
125+
});
126+
127+
fireEvent.press(screen.getByText('Next'));
128+
await waitForBatchedUpdatesWithAct();
129+
130+
// Verify Keyboard.dismiss was called
131+
expect(keyboardDismissSpy).toHaveBeenCalled();
132+
133+
// Verify navigation happened after keyboard dismiss
134+
expect(navigateSpy).toHaveBeenCalled();
135+
136+
// Verify keyboard was dismissed before navigation
137+
const keyboardDismissOrder = keyboardDismissSpy.mock.invocationCallOrder.at(0);
138+
const navigateOrder = navigateSpy.mock.invocationCallOrder.at(0);
139+
expect(keyboardDismissOrder).toBeDefined();
140+
expect(navigateOrder).toBeDefined();
141+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
142+
expect(keyboardDismissOrder!).toBeLessThan(navigateOrder!);
143+
144+
unmount();
145+
keyboardDismissSpy.mockRestore();
146+
navigateSpy.mockRestore();
147+
await waitForBatchedUpdatesWithAct();
148+
});
149+
150+
it('should disable the Next button when no users are selected', async () => {
151+
// Mock useSearchSelector to return no selected options
152+
jest.spyOn(useSearchSelectorModule, 'default').mockReturnValue({
153+
searchTerm: '',
154+
debouncedSearchTerm: '',
155+
setSearchTerm: jest.fn(),
156+
searchOptions: getEmptyOptions(),
157+
availableOptions: getEmptyOptions(),
158+
selectedOptions: [],
159+
selectedOptionsForDisplay: [],
160+
setSelectedOptions: jest.fn(),
161+
toggleSelection: jest.fn(),
162+
areOptionsInitialized: true,
163+
onListEndReached: jest.fn(),
164+
});
165+
166+
const {unmount} = renderPage(policy.id);
167+
await waitForBatchedUpdatesWithAct();
168+
169+
await waitFor(() => {
170+
expect(screen.getByText('Next')).toBeOnTheScreen();
171+
});
172+
173+
// The Next button should be disabled when no users are selected
174+
const nextButton = screen.getByText('Next');
175+
expect(nextButton).toBeOnTheScreen();
176+
177+
unmount();
178+
await waitForBatchedUpdatesWithAct();
179+
});
180+
181+
it('should save invited emails to account IDs draft when inviting users', async () => {
182+
// Mock useSearchSelector with a selected option
183+
jest.spyOn(useSearchSelectorModule, 'default').mockReturnValue({
184+
searchTerm: '',
185+
debouncedSearchTerm: '',
186+
setSearchTerm: jest.fn(),
187+
searchOptions: getEmptyOptions(),
188+
availableOptions: getEmptyOptions(),
189+
selectedOptions: [{login: inviteeEmail, accountID: inviteeAccountID, selected: true, text: inviteeEmail}],
190+
selectedOptionsForDisplay: [{login: inviteeEmail, accountID: inviteeAccountID, selected: true, text: inviteeEmail}],
191+
setSelectedOptions: jest.fn(),
192+
toggleSelection: jest.fn(),
193+
areOptionsInitialized: true,
194+
onListEndReached: jest.fn(),
195+
});
196+
197+
const {unmount} = renderPage(policy.id);
198+
await waitForBatchedUpdatesWithAct();
199+
200+
await waitFor(() => {
201+
expect(screen.getByText('Next')).toBeOnTheScreen();
202+
});
203+
204+
fireEvent.press(screen.getByText('Next'));
205+
await waitForBatchedUpdatesWithAct();
206+
207+
// Verify the draft was saved with the correct email-to-accountID mapping
208+
const draftKey = `${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${policy.id}`;
209+
const connection = Onyx.connect({
210+
key: draftKey,
211+
callback: (value) => {
212+
expect(value).toEqual({[inviteeEmail]: inviteeAccountID});
213+
Onyx.disconnect(connection);
214+
},
215+
});
216+
217+
unmount();
218+
await waitForBatchedUpdatesWithAct();
219+
});
220+
});

0 commit comments

Comments
 (0)