Skip to content

Commit b646d91

Browse files
committed
RU-T46 Fixing issue with call creation map, perf fix for action. Livekit fixes.
1 parent 923226b commit b646d91

23 files changed

Lines changed: 589 additions & 274 deletions
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Mock for @livekit/react-native-webrtc
2+
export const RTCAudioSession = {
3+
configure: jest.fn().mockResolvedValue(undefined),
4+
setCategory: jest.fn().mockResolvedValue(undefined),
5+
setMode: jest.fn().mockResolvedValue(undefined),
6+
getActiveAudioSession: jest.fn().mockReturnValue(null),
7+
setActive: jest.fn().mockResolvedValue(undefined),
8+
};
9+
10+
export default {
11+
RTCAudioSession,
12+
};

expo-env.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
/// <reference types="expo/types" />
22

3-
// NOTE: This file should not be edited and should be in your git ignore
3+
// NOTE: This file should not be edited and should be in your git ignore

src/api/calls/calls.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ import { type CallExtraDataResult } from '@/models/v4/calls/callExtraDataResult'
33
import { type CallResult } from '@/models/v4/calls/callResult';
44
import { type SaveCallResult } from '@/models/v4/calls/saveCallResult';
55

6+
import { createCachedApiEndpoint } from '../common/cached-client';
67
import { createApiEndpoint } from '../common/client';
78

8-
const callsApi = createApiEndpoint('/Calls/GetActiveCalls');
9+
const callsApi = createCachedApiEndpoint('/Calls/GetActiveCalls', {
10+
ttl: 30 * 1000, // Cache for 30 seconds - calls can change frequently
11+
enabled: true,
12+
});
913
const getCallApi = createApiEndpoint('/Calls/GetCall');
1014
const getCallExtraDataApi = createApiEndpoint('/Calls/GetCallExtraData');
1115
const createCallApi = createApiEndpoint('/Calls/SaveCall');

src/components/calls/__tests__/call-files-modal.test.tsx

Lines changed: 35 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,29 @@ const defaultMockFiles = [
3737
},
3838
];
3939

40-
let mockStoreState: any = {
41-
callFiles: defaultMockFiles,
40+
// Create a single object that will be mutated - never reassign!
41+
const mockStoreState = {
42+
callFiles: defaultMockFiles as any,
4243
isLoadingFiles: false,
43-
errorFiles: null,
44+
errorFiles: null as string | null,
4445
fetchCallFiles: mockFetchCallFiles,
4546
clearFiles: mockClearFiles,
4647
};
4748

49+
// Helper function to update mock state without replacing the object
50+
const setMockStoreState = (updates: Partial<typeof mockStoreState>) => {
51+
Object.assign(mockStoreState, updates);
52+
};
53+
54+
// Reset mock state to defaults
55+
const resetMockStoreState = () => {
56+
mockStoreState.callFiles = defaultMockFiles;
57+
mockStoreState.isLoadingFiles = false;
58+
mockStoreState.errorFiles = null;
59+
mockStoreState.fetchCallFiles = mockFetchCallFiles;
60+
mockStoreState.clearFiles = mockClearFiles;
61+
};
62+
4863
jest.mock('@/stores/calls/detail-store', () => ({
4964
useCallDetailStore: () => mockStoreState,
5065
}));
@@ -301,13 +316,7 @@ describe('CallFilesModal', () => {
301316
beforeEach(() => {
302317
jest.clearAllMocks();
303318
// Reset to default state
304-
mockStoreState = {
305-
callFiles: defaultMockFiles,
306-
isLoadingFiles: false,
307-
errorFiles: null,
308-
fetchCallFiles: mockFetchCallFiles,
309-
clearFiles: mockClearFiles,
310-
};
319+
resetMockStoreState();
311320
});
312321

313322
it('renders correctly when closed', () => {
@@ -421,13 +430,11 @@ describe('CallFilesModal', () => {
421430
describe('Loading States', () => {
422431
beforeEach(() => {
423432
// Mock loading state
424-
mockStoreState = {
433+
setMockStoreState({
425434
callFiles: null,
426435
isLoadingFiles: true,
427436
errorFiles: null,
428-
fetchCallFiles: mockFetchCallFiles,
429-
clearFiles: mockClearFiles,
430-
};
437+
});
431438
});
432439

433440
it('displays loading spinner when fetching files', () => {
@@ -443,13 +450,11 @@ describe('CallFilesModal', () => {
443450
describe('Error States', () => {
444451
beforeEach(() => {
445452
// Mock error state
446-
mockStoreState = {
453+
setMockStoreState({
447454
callFiles: [],
448455
isLoadingFiles: false,
449456
errorFiles: 'Network error occurred',
450-
fetchCallFiles: mockFetchCallFiles,
451-
clearFiles: mockClearFiles,
452-
};
457+
});
453458
});
454459

455460
it('displays error message when file fetch fails', () => {
@@ -476,13 +481,11 @@ describe('CallFilesModal', () => {
476481
describe('Empty States', () => {
477482
beforeEach(() => {
478483
// Mock empty state
479-
mockStoreState = {
484+
setMockStoreState({
480485
callFiles: [],
481486
isLoadingFiles: false,
482487
errorFiles: null,
483-
fetchCallFiles: mockFetchCallFiles,
484-
clearFiles: mockClearFiles,
485-
};
488+
});
486489
});
487490

488491
it('displays empty state when no files available', () => {
@@ -503,12 +506,11 @@ describe('CallFilesModal', () => {
503506

504507
beforeEach(() => {
505508
// Reset to default state with files
506-
mockStoreState = {
509+
setMockStoreState({
507510
callFiles: defaultMockFiles,
508511
isLoadingFiles: false,
509512
errorFiles: null,
510-
fetchCallFiles: mockFetchCallFiles,
511-
};
513+
});
512514
});
513515

514516
it('downloads and shares file when clicked', async () => {
@@ -591,13 +593,7 @@ describe('CallFilesModal', () => {
591593
describe('File Format Utilities', () => {
592594
beforeEach(() => {
593595
// Reset to default state
594-
mockStoreState = {
595-
callFiles: defaultMockFiles,
596-
isLoadingFiles: false,
597-
errorFiles: null,
598-
fetchCallFiles: mockFetchCallFiles,
599-
clearFiles: mockClearFiles,
600-
};
596+
resetMockStoreState();
601597
});
602598

603599
it('formats file sizes correctly', () => {
@@ -625,13 +621,7 @@ describe('CallFilesModal', () => {
625621
beforeEach(() => {
626622
jest.clearAllMocks();
627623
// Reset to default state
628-
mockStoreState = {
629-
callFiles: defaultMockFiles,
630-
isLoadingFiles: false,
631-
errorFiles: null,
632-
fetchCallFiles: mockFetchCallFiles,
633-
clearFiles: mockClearFiles,
634-
};
624+
resetMockStoreState();
635625
});
636626

637627
it('should track analytics event when modal is opened', () => {
@@ -653,10 +643,9 @@ describe('CallFilesModal', () => {
653643
});
654644

655645
it('should track analytics event with loading state', () => {
656-
mockStoreState = {
657-
...mockStoreState,
646+
setMockStoreState({
658647
isLoadingFiles: true,
659-
};
648+
});
660649

661650
render(<CallFilesModal {...defaultProps} isOpen={true} callId="test-call-456" />);
662651

@@ -670,10 +659,9 @@ describe('CallFilesModal', () => {
670659
});
671660

672661
it('should track analytics event with error state', () => {
673-
mockStoreState = {
674-
...mockStoreState,
662+
setMockStoreState({
675663
errorFiles: 'Failed to load files',
676-
};
664+
});
677665

678666
render(<CallFilesModal {...defaultProps} isOpen={true} callId="test-call-error" />);
679667

@@ -687,10 +675,9 @@ describe('CallFilesModal', () => {
687675
});
688676

689677
it('should track analytics event with no files', () => {
690-
mockStoreState = {
691-
...mockStoreState,
678+
setMockStoreState({
692679
callFiles: [],
693-
};
680+
});
694681

695682
render(<CallFilesModal {...defaultProps} isOpen={true} callId="test-call-no-files" />);
696683

src/components/calls/__tests__/call-notes-modal-new.test.tsx

Lines changed: 101 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ import { useCallDetailStore } from '@/stores/calls/detail-store';
99
jest.mock('react-i18next');
1010
jest.mock('@/lib/auth');
1111
jest.mock('@/stores/calls/detail-store');
12+
jest.mock('@/hooks/use-analytics', () => ({
13+
useAnalytics: () => ({
14+
trackEvent: jest.fn(),
15+
}),
16+
}));
1217

1318
// Mock navigation
1419
jest.mock('@react-navigation/native', () => ({
@@ -25,10 +30,13 @@ jest.mock('nativewind', () => ({
2530
}));
2631

2732
// Mock lucide-react-native icons
28-
jest.mock('lucide-react-native', () => ({
29-
SearchIcon: 'SearchIcon',
30-
X: 'X',
31-
}));
33+
jest.mock('lucide-react-native', () => {
34+
const { View } = require('react-native');
35+
return {
36+
SearchIcon: (props: any) => <View testID="search-icon" {...props} />,
37+
X: (props: any) => <View testID="x-icon" {...props} />,
38+
};
39+
});
3240

3341
// Mock Loading component
3442
jest.mock('../../common/loading', () => ({
@@ -58,6 +66,10 @@ jest.mock('react-native-keyboard-controller', () => ({
5866
const { View } = require('react-native');
5967
return <View testID="keyboard-aware-scroll-view">{children}</View>;
6068
},
69+
KeyboardStickyView: ({ children }: any) => {
70+
const { View } = require('react-native');
71+
return <View testID="keyboard-sticky-view">{children}</View>;
72+
},
6173
}));
6274

6375
// Mock react-native-gesture-handler
@@ -94,10 +106,86 @@ jest.mock('@gorhom/bottom-sheet', () => {
94106
};
95107
});
96108

97-
// Mock lucide-react-native icons
98-
jest.mock('lucide-react-native', () => ({
99-
SearchIcon: 'SearchIcon',
100-
X: 'X',
109+
// Mock UI components
110+
jest.mock('../../ui/box', () => ({
111+
Box: ({ children, ...props }: any) => {
112+
const { View } = require('react-native');
113+
return <View {...props}>{children}</View>;
114+
},
115+
}));
116+
117+
jest.mock('../../ui/button', () => ({
118+
Button: ({ children, onPress, disabled, isDisabled, ...props }: any) => {
119+
const { TouchableOpacity } = require('react-native');
120+
const isButtonDisabled = disabled || isDisabled;
121+
return (
122+
<TouchableOpacity
123+
onPress={isButtonDisabled ? undefined : onPress}
124+
disabled={isButtonDisabled}
125+
{...props}
126+
>
127+
{children}
128+
</TouchableOpacity>
129+
);
130+
},
131+
ButtonText: ({ children, ...props }: any) => {
132+
const { Text } = require('react-native');
133+
return <Text {...props}>{children}</Text>;
134+
},
135+
}));
136+
137+
jest.mock('../../ui/heading', () => ({
138+
Heading: ({ children, ...props }: any) => {
139+
const { Text } = require('react-native');
140+
return <Text {...props}>{children}</Text>;
141+
},
142+
}));
143+
144+
jest.mock('../../ui/hstack', () => ({
145+
HStack: ({ children, ...props }: any) => {
146+
const { View } = require('react-native');
147+
return <View {...props}>{children}</View>;
148+
},
149+
}));
150+
151+
jest.mock('../../ui/vstack', () => ({
152+
VStack: ({ children, ...props }: any) => {
153+
const { View } = require('react-native');
154+
return <View {...props}>{children}</View>;
155+
},
156+
}));
157+
158+
jest.mock('../../ui/text', () => ({
159+
Text: ({ children, ...props }: any) => {
160+
const { Text: RNText } = require('react-native');
161+
return <RNText {...props}>{children}</RNText>;
162+
},
163+
}));
164+
165+
jest.mock('../../ui/input', () => ({
166+
Input: ({ children, ...props }: any) => {
167+
const { View } = require('react-native');
168+
return <View {...props}>{children}</View>;
169+
},
170+
InputField: ({ placeholder, value, onChangeText, ...props }: any) => {
171+
const { TextInput } = require('react-native');
172+
return <TextInput placeholder={placeholder} value={value} onChangeText={onChangeText} {...props} />;
173+
},
174+
InputSlot: ({ children, ...props }: any) => {
175+
const { View } = require('react-native');
176+
return <View {...props}>{children}</View>;
177+
},
178+
}));
179+
180+
jest.mock('../../ui/textarea', () => ({
181+
Textarea: ({ children, ...props }: any) => {
182+
const { View } = require('react-native');
183+
return <View {...props}>{children}</View>;
184+
},
185+
TextareaInput: ({ placeholder, value, onChangeText, ...props }: any) => {
186+
const { TextInput } = require('react-native');
187+
return <TextInput placeholder={placeholder} value={value} onChangeText={onChangeText} {...props} />;
188+
},
101189
}));
102190

103191
const mockUseTranslation = useTranslation as jest.MockedFunction<typeof useTranslation>;
@@ -146,6 +234,9 @@ describe('CallNotesModal', () => {
146234
'callNotes.searchPlaceholder': 'Search notes...',
147235
'callNotes.addNotePlaceholder': 'Add a note...',
148236
'callNotes.addNote': 'Add Note',
237+
'callNotes.noNotesFound': 'No notes found',
238+
'callNotes.addNoteLabel': 'Add a note',
239+
'common.cancel': 'Cancel',
149240
};
150241
return translations[key] || key;
151242
},
@@ -185,8 +276,8 @@ describe('CallNotesModal', () => {
185276
it('renders correctly when closed', () => {
186277
const { queryByText } = render(<CallNotesModal {...mockProps} isOpen={false} />);
187278

188-
// Bottom sheet should still render but with index -1 (closed)
189-
expect(queryByText('Call Notes')).toBeTruthy();
279+
// Component returns null when closed
280+
expect(queryByText('Call Notes')).toBeFalsy();
190281
});
191282

192283
it('handles search input correctly', () => {

0 commit comments

Comments
 (0)