1- import { render , screen } from '@testing-library/react-native' ;
1+ import { fireEvent , render , screen } from '@testing-library/react-native' ;
22import React from 'react' ;
33
44import { type PersonnelInfoResultData } from '@/models/v4/personnel/personnelInfoResultData' ;
@@ -9,57 +9,48 @@ import { RoleAssignmentItem } from '../role-assignment-item';
99// Mock react-i18next
1010jest . mock ( 'react-i18next' , ( ) => ( {
1111 useTranslation : ( ) => ( {
12- t : ( key : string , defaultValue ?: string ) => defaultValue || key ,
12+ t : ( key : string , defaultValue ?: string | Record < string , string > ) => {
13+ if ( typeof defaultValue === 'string' ) return defaultValue ;
14+ if ( typeof defaultValue === 'object' && defaultValue . defaultValue ) return defaultValue . defaultValue ;
15+ return key ;
16+ } ,
1317 } ) ,
1418} ) ) ;
1519
16- // Mock the Select components
17- jest . mock ( '@/components/ui/select' , ( ) => ( {
18- Select : ( { children, selectedValue, onValueChange } : any ) => {
19- const { View } = require ( 'react-native' ) ;
20+ // Mock nativewind
21+ jest . mock ( 'nativewind' , ( ) => ( {
22+ useColorScheme : ( ) => ( { colorScheme : 'light' } ) ,
23+ cssInterop : jest . fn ( ) ,
24+ } ) ) ;
25+
26+ // Mock the RoleUserSelectionModal to simplify testing
27+ let mockModalOnSelectUser : ( ( userId ?: string ) => void ) | undefined ;
28+ jest . mock ( '../role-user-selection-modal' , ( ) => ( {
29+ RoleUserSelectionModal : ( { isOpen, onClose, roleName, selectedUserId, users, onSelectUser } : any ) => {
30+ const { View, Text, TouchableOpacity } = require ( 'react-native' ) ;
31+ mockModalOnSelectUser = onSelectUser ;
32+ if ( ! isOpen ) return null ;
2033 return (
21- < View testID = "select" onPress = { ( ) => onValueChange && onValueChange ( 'user1' ) } >
22- { children }
34+ < View testID = "user-selection-modal" >
35+ < Text testID = "modal-role-name" > { roleName } </ Text >
36+ < TouchableOpacity testID = "select-unassigned" onPress = { ( ) => { onSelectUser ( undefined ) ; onClose ( ) ; } } >
37+ < Text > Unassigned</ Text >
38+ </ TouchableOpacity >
39+ { users . map ( ( user : any ) => (
40+ < TouchableOpacity key = { user . UserId } testID = { `select-user-${ user . UserId } ` } onPress = { ( ) => { onSelectUser ( user . UserId ) ; onClose ( ) ; } } >
41+ < Text > { `${ user . FirstName } ${ user . LastName } ` } </ Text >
42+ </ TouchableOpacity >
43+ ) ) }
2344 </ View >
2445 ) ;
2546 } ,
26- SelectTrigger : ( { children } : any ) => {
27- const { View } = require ( 'react-native' ) ;
28- return < View testID = "select-trigger" > { children } </ View > ;
29- } ,
30- SelectInput : ( { value, placeholder } : any ) => {
31- const { Text } = require ( 'react-native' ) ;
32- return < Text testID = "select-input" > { value || placeholder } </ Text > ;
33- } ,
34- SelectIcon : ( ) => {
35- const { View } = require ( 'react-native' ) ;
36- return < View testID = "select-icon" /> ;
37- } ,
38- SelectPortal : ( { children } : any ) => children ,
39- SelectBackdrop : ( ) => {
40- const { View } = require ( 'react-native' ) ;
41- return < View testID = "select-backdrop" /> ;
42- } ,
43- SelectContent : ( { children } : any ) => {
44- const { View } = require ( 'react-native' ) ;
45- return < View testID = "select-content" > { children } </ View > ;
46- } ,
47- SelectDragIndicatorWrapper : ( { children } : any ) => children ,
48- SelectDragIndicator : ( ) => {
49- const { View } = require ( 'react-native' ) ;
50- return < View testID = "select-drag-indicator" /> ;
51- } ,
52- SelectItem : ( { label, value } : any ) => {
53- const { Text } = require ( 'react-native' ) ;
54- return < Text testID = { `select-item-${ value } ` } > { label } </ Text > ;
55- } ,
5647} ) ) ;
5748
58- // Mock other UI components
49+ // Mock UI components
5950jest . mock ( '@/components/ui/text' , ( ) => ( {
60- Text : ( { children, className } : any ) => {
51+ Text : ( { children, className, testID } : any ) => {
6152 const { Text } = require ( 'react-native' ) ;
62- return < Text testID = " text" > { children } </ Text > ;
53+ return < Text testID = { testID || ' text' } > { children } </ Text > ;
6354 } ,
6455} ) ) ;
6556
@@ -70,6 +61,13 @@ jest.mock('@/components/ui/vstack', () => ({
7061 } ,
7162} ) ) ;
7263
64+ jest . mock ( '@/components/ui/hstack' , ( ) => ( {
65+ HStack : ( { children } : any ) => {
66+ const { View } = require ( 'react-native' ) ;
67+ return < View testID = "hstack" > { children } </ View > ;
68+ } ,
69+ } ) ) ;
70+
7371describe ( 'RoleAssignmentItem' , ( ) => {
7472 const mockOnAssignUser = jest . fn ( ) ;
7573
@@ -88,19 +86,19 @@ describe('RoleAssignmentItem', () => {
8886 DepartmentId : 'dept1' ,
8987 IdentificationNumber : '' ,
9088 MobilePhone : '' ,
91- GroupId : '' ,
92- GroupName : '' ,
89+ GroupId : 'group1 ' ,
90+ GroupName : 'Engine 1 ' ,
9391 StatusId : '' ,
94- Status : '' ,
95- StatusColor : '' ,
92+ Status : 'Available ' ,
93+ StatusColor : '#00ff00 ' ,
9694 StatusTimestamp : '' ,
9795 StatusDestinationId : '' ,
9896 StatusDestinationName : '' ,
9997 StaffingId : '' ,
100- Staffing : '' ,
101- StaffingColor : '' ,
98+ Staffing : 'On Shift ' ,
99+ StaffingColor : '#0000ff ' ,
102100 StaffingTimestamp : '' ,
103- Roles : [ ] ,
101+ Roles : [ 'Firefighter' ] ,
104102 } ,
105103 {
106104 UserId : 'user2' ,
@@ -128,6 +126,7 @@ describe('RoleAssignmentItem', () => {
128126
129127 beforeEach ( ( ) => {
130128 jest . clearAllMocks ( ) ;
129+ mockModalOnSelectUser = undefined ;
131130 } ) ;
132131
133132 it ( 'renders the role name' , ( ) => {
@@ -143,20 +142,36 @@ describe('RoleAssignmentItem', () => {
143142 expect ( screen . getByText ( 'Captain' ) ) . toBeTruthy ( ) ;
144143 } ) ;
145144
146- it ( 'displays placeholder when no user is assigned' , ( ) => {
145+ it ( 'displays "Unassigned" when no user is assigned' , ( ) => {
146+ render (
147+ < RoleAssignmentItem
148+ role = { mockRole }
149+ availableUsers = { mockUsers }
150+ onAssignUser = { mockOnAssignUser }
151+ currentAssignments = { [ ] }
152+ />
153+ ) ;
154+
155+ expect ( screen . getByText ( 'Unassigned' ) ) . toBeTruthy ( ) ;
156+ } ) ;
157+
158+ it ( 'displays assigned user name inline when user is assigned' , ( ) => {
159+ const assignedUser = mockUsers [ 0 ] ;
160+
147161 render (
148162 < RoleAssignmentItem
149163 role = { mockRole }
164+ assignedUser = { assignedUser }
150165 availableUsers = { mockUsers }
151166 onAssignUser = { mockOnAssignUser }
152167 currentAssignments = { [ ] }
153168 />
154169 ) ;
155170
156- expect ( screen . getByText ( 'Select user ' ) ) . toBeTruthy ( ) ;
171+ expect ( screen . getByText ( 'John Doe ' ) ) . toBeTruthy ( ) ;
157172 } ) ;
158173
159- it ( 'displays assigned user name when user is assigned ' , ( ) => {
174+ it ( 'displays user details inline (group, status, staffing) ' , ( ) => {
160175 const assignedUser = mockUsers [ 0 ] ;
161176
162177 render (
@@ -169,12 +184,35 @@ describe('RoleAssignmentItem', () => {
169184 />
170185 ) ;
171186
172- expect ( screen . getAllByText ( 'John Doe' ) ) . toHaveLength ( 2 ) ; // One in input, one in options
187+ expect ( screen . getByText ( 'Engine 1' ) ) . toBeTruthy ( ) ;
188+ expect ( screen . getByText ( 'Available' ) ) . toBeTruthy ( ) ;
189+ expect ( screen . getByText ( 'On Shift' ) ) . toBeTruthy ( ) ;
173190 } ) ;
174191
175- it ( 'filters out users assigned to other roles' , ( ) => {
192+ it ( 'opens selection modal on tap' , ( ) => {
193+ render (
194+ < RoleAssignmentItem
195+ role = { mockRole }
196+ availableUsers = { mockUsers }
197+ onAssignUser = { mockOnAssignUser }
198+ currentAssignments = { [ ] }
199+ />
200+ ) ;
201+
202+ // Modal should not be visible initially
203+ expect ( screen . queryByTestId ( 'user-selection-modal' ) ) . toBeNull ( ) ;
204+
205+ // Tap the item
206+ fireEvent . press ( screen . getByTestId ( 'role-assignment-role1' ) ) ;
207+
208+ // Modal should now be visible
209+ expect ( screen . getByTestId ( 'user-selection-modal' ) ) . toBeTruthy ( ) ;
210+ expect ( screen . getByTestId ( 'modal-role-name' ) ) . toBeTruthy ( ) ;
211+ } ) ;
212+
213+ it ( 'shows all users including those assigned to other roles in the modal' , ( ) => {
176214 const currentAssignments = [
177- { roleId : 'role2' , userId : 'user2' } , // user2 is assigned to a different role
215+ { roleId : 'role2' , userId : 'user2' , roleName : 'Engineer' } , // user2 is assigned to a different role
178216 ] ;
179217
180218 render (
@@ -186,19 +224,21 @@ describe('RoleAssignmentItem', () => {
186224 />
187225 ) ;
188226
189- // Should show unassigned option
190- expect ( screen . getByTestId ( 'select-item-' ) ) . toBeTruthy ( ) ;
191- // Should show user1 (not assigned to other roles)
192- expect ( screen . getByTestId ( 'select-item-user1' ) ) . toBeTruthy ( ) ;
193- // Should NOT show user2 (assigned to other role)
194- expect ( screen . queryByTestId ( 'select-item-user2' ) ) . toBeNull ( ) ;
227+ // Open modal
228+ fireEvent . press ( screen . getByTestId ( 'role-assignment-role1' ) ) ;
229+
230+ // Should show both users (no filtering)
231+ expect ( screen . getByTestId ( 'select-user-user1' ) ) . toBeTruthy ( ) ;
232+ expect ( screen . getByTestId ( 'select-user-user2' ) ) . toBeTruthy ( ) ;
233+ // Unassigned option should always be present
234+ expect ( screen . getByTestId ( 'select-unassigned' ) ) . toBeTruthy ( ) ;
195235 } ) ;
196236
197- it ( 'includes user assigned to the same role in available users ' , ( ) => {
237+ it ( 'shows all users in the modal including those assigned to same and other roles ' , ( ) => {
198238 const assignedUser = mockUsers [ 0 ] ;
199239 const currentAssignments = [
200- { roleId : 'role1' , userId : 'user1' } , // user1 is assigned to this role
201- { roleId : 'role2' , userId : 'user2' } , // user2 is assigned to a different role
240+ { roleId : 'role1' , userId : 'user1' , roleName : 'Captain' } , // user1 is assigned to this role
241+ { roleId : 'role2' , userId : 'user2' , roleName : 'Engineer' } , // user2 is assigned to a different role
202242 ] ;
203243
204244 render (
@@ -211,15 +251,15 @@ describe('RoleAssignmentItem', () => {
211251 />
212252 ) ;
213253
214- // Should show assigned user name
215- expect ( screen . getAllByText ( 'John Doe ') ) . toHaveLength ( 2 ) ; // One in input, one in options
216- // Should show user1 in options (assigned to this role)
217- expect ( screen . getByTestId ( 'select-item-user1' ) ) . toBeTruthy ( ) ;
218- // Should NOT show user2 (assigned to other role)
219- expect ( screen . queryByTestId ( 'select-item -user2' ) ) . toBeNull ( ) ;
254+ // Open modal
255+ fireEvent . press ( screen . getByTestId ( 'role-assignment-role1 ') ) ;
256+
257+ // Should show both users (no filtering, all users visible)
258+ expect ( screen . getByTestId ( 'select-user-user1' ) ) . toBeTruthy ( ) ;
259+ expect ( screen . getByTestId ( 'select-user -user2' ) ) . toBeTruthy ( ) ;
220260 } ) ;
221261
222- it ( 'shows unassigned option ' , ( ) => {
262+ it ( 'calls onAssignUser when selecting a user in the modal ' , ( ) => {
223263 render (
224264 < RoleAssignmentItem
225265 role = { mockRole }
@@ -229,7 +269,48 @@ describe('RoleAssignmentItem', () => {
229269 />
230270 ) ;
231271
232- expect ( screen . getByTestId ( 'select-item-' ) ) . toBeTruthy ( ) ;
233- expect ( screen . getByText ( 'Unassigned' ) ) . toBeTruthy ( ) ;
272+ // Open modal
273+ fireEvent . press ( screen . getByTestId ( 'role-assignment-role1' ) ) ;
274+
275+ // Select user1
276+ fireEvent . press ( screen . getByTestId ( 'select-user-user1' ) ) ;
277+
278+ expect ( mockOnAssignUser ) . toHaveBeenCalledWith ( 'user1' ) ;
279+ } ) ;
280+
281+ it ( 'calls onAssignUser with undefined when selecting Unassigned' , ( ) => {
282+ const assignedUser = mockUsers [ 0 ] ;
283+
284+ render (
285+ < RoleAssignmentItem
286+ role = { mockRole }
287+ assignedUser = { assignedUser }
288+ availableUsers = { mockUsers }
289+ onAssignUser = { mockOnAssignUser }
290+ currentAssignments = { [ ] }
291+ />
292+ ) ;
293+
294+ // Open modal
295+ fireEvent . press ( screen . getByTestId ( 'role-assignment-role1' ) ) ;
296+
297+ // Select Unassigned
298+ fireEvent . press ( screen . getByTestId ( 'select-unassigned' ) ) ;
299+
300+ expect ( mockOnAssignUser ) . toHaveBeenCalledWith ( undefined ) ;
301+ } ) ;
302+
303+ it ( 'has proper accessibility role on the pressable' , ( ) => {
304+ render (
305+ < RoleAssignmentItem
306+ role = { mockRole }
307+ availableUsers = { mockUsers }
308+ onAssignUser = { mockOnAssignUser }
309+ currentAssignments = { [ ] }
310+ />
311+ ) ;
312+
313+ const pressable = screen . getByTestId ( 'role-assignment-role1' ) ;
314+ expect ( pressable . props . accessibilityRole ) . toBe ( 'button' ) ;
234315 } ) ;
235316} ) ;
0 commit comments