11import React from 'react' ;
22import { render , screen , fireEvent , act } from '@testing-library/react-native' ;
33import type { LLMStore } from '../store/llmStore' ;
4+ import type { Attachment } from '../hooks/useAttachment' ;
5+ import type { PermissionStatus } from 'react-native-audio-api' ;
46
57// ── mocks ─────────────────────────────────────────────────────────────────────
68
@@ -27,7 +29,7 @@ jest.mock('../store/llmStore', () => ({
2729} ) ) ;
2830
2931const mockUseAttachment = {
30- attachments : [ ] as any [ ] ,
32+ attachments : [ ] as Attachment [ ] ,
3133 sheetRef : { current : null } ,
3234 pickFromLibrary : jest . fn ( ) ,
3335 pickFromCamera : jest . fn ( ) ,
@@ -49,7 +51,12 @@ jest.mock('../components/bottomSheets/AttachmentSheet', () => {
4951 onPickFromCamera,
5052 onPickDocument,
5153 isVisionModel,
52- } : any ) => (
54+ } : {
55+ onPickFromLibrary : ( ) => void ;
56+ onPickFromCamera : ( ) => void ;
57+ onPickDocument : ( ) => void ;
58+ isVisionModel : boolean ;
59+ } ) => (
5360 < View testID = "attachment-sheet" >
5461 { isVisionModel && (
5562 < >
@@ -73,7 +80,13 @@ jest.mock('../components/bottomSheets/AttachmentSheet', () => {
7380
7481jest . mock ( '../components/chat-screen/AttachmentThumbnail' , ( ) => {
7582 const { View, TouchableOpacity, Text } = require ( 'react-native' ) ;
76- return ( { attachment, onRemove } : any ) => (
83+ return ( {
84+ attachment,
85+ onRemove,
86+ } : {
87+ attachment : Attachment ;
88+ onRemove : ( ) => void ;
89+ } ) => (
7790 < View testID = { `attachment-thumb-${ attachment . id } ` } >
7891 < Text > { attachment . name || attachment . uri } </ Text >
7992 < TouchableOpacity
@@ -87,8 +100,14 @@ jest.mock('../components/chat-screen/AttachmentThumbnail', () => {
87100} ) ;
88101
89102jest . mock ( '../components/chat-screen/ChatSpeechInput' , ( ) => {
90- const { View, TouchableOpacity, Text } = require ( 'react-native' ) ;
91- return ( { onSubmit, onCancel } : any ) => (
103+ const { View, TouchableOpacity } = require ( 'react-native' ) ;
104+ return ( {
105+ onSubmit,
106+ onCancel,
107+ } : {
108+ onSubmit : ( transcript : string ) => void ;
109+ onCancel : ( ) => void ;
110+ } ) => (
92111 < View testID = "speech-input" >
93112 < TouchableOpacity
94113 testID = "speech-submit"
@@ -101,7 +120,7 @@ jest.mock('../components/chat-screen/ChatSpeechInput', () => {
101120
102121jest . mock ( '../components/chat-screen/PromptSuggestions' , ( ) => {
103122 const { TouchableOpacity, Text } = require ( 'react-native' ) ;
104- return ( { onSelectPrompt } : any ) => (
123+ return ( { onSelectPrompt } : { onSelectPrompt : ( prompt : string ) => void } ) => (
105124 < TouchableOpacity
106125 testID = "prompt-suggestion"
107126 onPress = { ( ) => onSelectPrompt ( 'Suggested prompt' ) }
@@ -124,7 +143,18 @@ jest.mock('../components/chat-screen/ChatBarActions', () => {
124143 onThinkingToggle,
125144 thinkingEnabled,
126145 onAttach,
127- } : any ) => (
146+ } : {
147+ userInput : string ;
148+ hasAttachments : boolean ;
149+ onSend : ( ) => void ;
150+ isGenerating : boolean ;
151+ isProcessingPrompt : boolean ;
152+ onInterrupt : ( ) => void ;
153+ onSpeechInput : ( ) => void ;
154+ onThinkingToggle : ( ) => void ;
155+ thinkingEnabled : boolean ;
156+ onAttach : ( ) => void ;
157+ } ) => (
128158 < View testID = "chat-bar-actions" >
129159 < TouchableOpacity testID = "attach-btn" onPress = { onAttach } >
130160 < Text > +</ Text >
@@ -185,7 +215,7 @@ const defaultProps = {
185215 onSelectModel : jest . fn ( ) ,
186216 onSelectPrompt : jest . fn ( ) ,
187217 model : downloadedModel ,
188- scrollRef : { current : null } as any ,
218+ scrollRef : { current : null } ,
189219 isAtBottom : true ,
190220 isVisionModel : false ,
191221 thinkingEnabled : false ,
@@ -218,7 +248,7 @@ beforeEach(() => {
218248 jest . spyOn ( console , 'warn' ) . mockImplementation ( ( ) => { } ) ;
219249 // Default: permission granted
220250 mockAudioManager . requestRecordingPermissions . mockResolvedValue (
221- 'Granted' as any
251+ 'Granted' as PermissionStatus
222252 ) ;
223253} ) ;
224254
@@ -357,7 +387,7 @@ describe('speech input', () => {
357387
358388 it ( 'shows toast and stays in text mode when microphone permission is denied' , async ( ) => {
359389 mockAudioManager . requestRecordingPermissions . mockResolvedValue (
360- 'Denied' as any
390+ 'Denied' as PermissionStatus
361391 ) ;
362392 renderBar ( ) ;
363393 await act ( async ( ) => {
@@ -430,7 +460,7 @@ describe('speech input', () => {
430460 // Override speech mock to submit empty string
431461 jest . mock ( '../components/chat-screen/ChatSpeechInput' , ( ) => {
432462 const { View, TouchableOpacity } = require ( 'react-native' ) ;
433- return ( { onSubmit } : any ) => (
463+ return ( { onSubmit } : { onSubmit : ( transcript : string ) => void } ) => (
434464 < View testID = "speech-input" >
435465 < TouchableOpacity
436466 testID = "speech-submit"
0 commit comments