@@ -1062,6 +1062,33 @@ describe("Paste functionality", () => {
10621062} ) ;
10631063
10641064describe ( "PromptInputSpeechButton" , ( ) => {
1065+ let mockRecognition : any ;
1066+
1067+ beforeEach ( ( ) => {
1068+ // Mock SpeechRecognition API
1069+ mockRecognition = {
1070+ start : vi . fn ( ) ,
1071+ stop : vi . fn ( ) ,
1072+ continuous : false ,
1073+ interimResults : false ,
1074+ lang : "" ,
1075+ onstart : null ,
1076+ onend : null ,
1077+ onresult : null ,
1078+ onerror : null ,
1079+ } ;
1080+
1081+ // @ts -expect-error - Mocking browser API
1082+ global . window . SpeechRecognition = vi . fn ( ( ) => mockRecognition ) ;
1083+ } ) ;
1084+
1085+ afterEach ( ( ) => {
1086+ // @ts -expect-error - Cleaning up mock
1087+ delete global . window . SpeechRecognition ;
1088+ // @ts -expect-error - Cleaning up mock
1089+ delete global . window . webkitSpeechRecognition ;
1090+ } ) ;
1091+
10651092 it ( "renders button component" , ( ) => {
10661093 const { container } = render ( < PromptInputSpeechButton /> ) ;
10671094 expect ( container . querySelector ( "button" ) ) . toBeInTheDocument ( ) ;
@@ -1079,6 +1106,93 @@ describe("PromptInputSpeechButton", () => {
10791106 render ( < PromptInputSpeechButton data-testid = "speech-btn" /> ) ;
10801107 expect ( screen . getByTestId ( "speech-btn" ) ) . toBeInTheDocument ( ) ;
10811108 } ) ;
1109+
1110+ it ( "initializes speech recognition when available" , ( ) => {
1111+ render ( < PromptInputSpeechButton /> ) ;
1112+ expect ( global . window . SpeechRecognition ) . toHaveBeenCalled ( ) ;
1113+ expect ( mockRecognition . continuous ) . toBe ( true ) ;
1114+ expect ( mockRecognition . interimResults ) . toBe ( true ) ;
1115+ expect ( mockRecognition . lang ) . toBe ( "en-US" ) ;
1116+ } ) ;
1117+
1118+ it ( "toggles listening when clicked" , async ( ) => {
1119+ const user = userEvent . setup ( ) ;
1120+ const { container } = render ( < PromptInputSpeechButton /> ) ;
1121+ const button = container . querySelector ( "button" ) ;
1122+
1123+ if ( button ) {
1124+ await user . click ( button ) ;
1125+ expect ( mockRecognition . start ) . toHaveBeenCalled ( ) ;
1126+ }
1127+ } ) ;
1128+
1129+ it ( "stops recognition when clicked while listening" , async ( ) => {
1130+ const user = userEvent . setup ( ) ;
1131+ const { container } = render ( < PromptInputSpeechButton /> ) ;
1132+ const button = container . querySelector ( "button" ) ;
1133+
1134+ if ( button ) {
1135+ // Start listening
1136+ await user . click ( button ) ;
1137+ mockRecognition . onstart ?.( ) ;
1138+
1139+ // Stop listening
1140+ await user . click ( button ) ;
1141+ expect ( mockRecognition . stop ) . toHaveBeenCalled ( ) ;
1142+ }
1143+ } ) ;
1144+
1145+ it ( "cleans up recognition on unmount" , ( ) => {
1146+ const { unmount } = render ( < PromptInputSpeechButton /> ) ;
1147+ unmount ( ) ;
1148+ expect ( mockRecognition . stop ) . toHaveBeenCalled ( ) ;
1149+ } ) ;
1150+
1151+ it ( "sets up speech recognition result handler" , ( ) => {
1152+ render (
1153+ < PromptInput onSubmit = { vi . fn ( ) } >
1154+ < PromptInputBody >
1155+ < PromptInputTextarea />
1156+ < PromptInputSpeechButton />
1157+ </ PromptInputBody >
1158+ </ PromptInput >
1159+ ) ;
1160+
1161+ // Verify onresult handler is set up
1162+ expect ( mockRecognition . onresult ) . toBeDefined ( ) ;
1163+ expect ( typeof mockRecognition . onresult ) . toBe ( "function" ) ;
1164+ } ) ;
1165+
1166+ it ( "sets up all speech recognition event handlers" , ( ) => {
1167+ render ( < PromptInputSpeechButton /> ) ;
1168+
1169+ expect ( mockRecognition . onstart ) . toBeDefined ( ) ;
1170+ expect ( mockRecognition . onend ) . toBeDefined ( ) ;
1171+ expect ( mockRecognition . onresult ) . toBeDefined ( ) ;
1172+ expect ( mockRecognition . onerror ) . toBeDefined ( ) ;
1173+ } ) ;
1174+
1175+ it ( "handles speech recognition error" , ( ) => {
1176+ const consoleError = vi . spyOn ( console , "error" ) . mockImplementation ( ( ) => { } ) ;
1177+
1178+ render ( < PromptInputSpeechButton /> ) ;
1179+
1180+ // Simulate error
1181+ mockRecognition . onerror ?.( { error : "network" } ) ;
1182+
1183+ expect ( consoleError ) . toHaveBeenCalledWith ( "Speech recognition error:" , "network" ) ;
1184+ consoleError . mockRestore ( ) ;
1185+ } ) ;
1186+
1187+ it ( "is disabled when speech recognition not available" , ( ) => {
1188+ // @ts -expect-error - Removing mock
1189+ delete global . window . SpeechRecognition ;
1190+
1191+ const { container } = render ( < PromptInputSpeechButton /> ) ;
1192+ const button = container . querySelector ( "button" ) ;
1193+
1194+ expect ( button ) . toBeDisabled ( ) ;
1195+ } ) ;
10821196} ) ;
10831197
10841198describe ( "PromptInputAttachment" , ( ) => {
0 commit comments