@@ -547,3 +547,247 @@ describe('ChatView - Auto Approval Tests', () => {
547547 } )
548548 } )
549549} )
550+
551+ describe ( 'ChatView - Sound Playing Tests' , ( ) => {
552+ beforeEach ( ( ) => {
553+ jest . clearAllMocks ( )
554+ } )
555+
556+ it ( 'does not play sound for auto-approved browser actions' , async ( ) => {
557+ render (
558+ < ExtensionStateContextProvider >
559+ < ChatView
560+ isHidden = { false }
561+ showAnnouncement = { false }
562+ hideAnnouncement = { ( ) => { } }
563+ showHistoryView = { ( ) => { } }
564+ />
565+ </ ExtensionStateContextProvider >
566+ )
567+
568+ // First hydrate state with initial task and streaming
569+ mockPostMessage ( {
570+ alwaysAllowBrowser : true ,
571+ clineMessages : [
572+ {
573+ type : 'say' ,
574+ say : 'task' ,
575+ ts : Date . now ( ) - 2000 ,
576+ text : 'Initial task'
577+ } ,
578+ {
579+ type : 'say' ,
580+ say : 'api_req_started' ,
581+ ts : Date . now ( ) - 1000 ,
582+ text : JSON . stringify ( { } ) ,
583+ partial : true
584+ }
585+ ]
586+ } )
587+
588+ // Then send the browser action ask message (streaming finished)
589+ mockPostMessage ( {
590+ alwaysAllowBrowser : true ,
591+ clineMessages : [
592+ {
593+ type : 'say' ,
594+ say : 'task' ,
595+ ts : Date . now ( ) - 2000 ,
596+ text : 'Initial task'
597+ } ,
598+ {
599+ type : 'ask' ,
600+ ask : 'browser_action_launch' ,
601+ ts : Date . now ( ) ,
602+ text : JSON . stringify ( { action : 'launch' , url : 'http://example.com' } ) ,
603+ partial : false
604+ }
605+ ]
606+ } )
607+
608+ // Verify no sound was played
609+ expect ( vscode . postMessage ) . not . toHaveBeenCalledWith ( {
610+ type : 'playSound' ,
611+ audioType : expect . any ( String )
612+ } )
613+ } )
614+
615+ it ( 'plays notification sound for non-auto-approved browser actions' , async ( ) => {
616+ render (
617+ < ExtensionStateContextProvider >
618+ < ChatView
619+ isHidden = { false }
620+ showAnnouncement = { false }
621+ hideAnnouncement = { ( ) => { } }
622+ showHistoryView = { ( ) => { } }
623+ />
624+ </ ExtensionStateContextProvider >
625+ )
626+
627+ // First hydrate state with initial task and streaming
628+ mockPostMessage ( {
629+ alwaysAllowBrowser : false ,
630+ clineMessages : [
631+ {
632+ type : 'say' ,
633+ say : 'task' ,
634+ ts : Date . now ( ) - 2000 ,
635+ text : 'Initial task'
636+ } ,
637+ {
638+ type : 'say' ,
639+ say : 'api_req_started' ,
640+ ts : Date . now ( ) - 1000 ,
641+ text : JSON . stringify ( { } ) ,
642+ partial : true
643+ }
644+ ]
645+ } )
646+
647+ // Then send the browser action ask message (streaming finished)
648+ mockPostMessage ( {
649+ alwaysAllowBrowser : false ,
650+ clineMessages : [
651+ {
652+ type : 'say' ,
653+ say : 'task' ,
654+ ts : Date . now ( ) - 2000 ,
655+ text : 'Initial task'
656+ } ,
657+ {
658+ type : 'ask' ,
659+ ask : 'browser_action_launch' ,
660+ ts : Date . now ( ) ,
661+ text : JSON . stringify ( { action : 'launch' , url : 'http://example.com' } ) ,
662+ partial : false
663+ }
664+ ]
665+ } )
666+
667+ // Verify notification sound was played
668+ await waitFor ( ( ) => {
669+ expect ( vscode . postMessage ) . toHaveBeenCalledWith ( {
670+ type : 'playSound' ,
671+ audioType : 'notification'
672+ } )
673+ } )
674+ } )
675+
676+ it ( 'plays celebration sound for completion results' , async ( ) => {
677+ render (
678+ < ExtensionStateContextProvider >
679+ < ChatView
680+ isHidden = { false }
681+ showAnnouncement = { false }
682+ hideAnnouncement = { ( ) => { } }
683+ showHistoryView = { ( ) => { } }
684+ />
685+ </ ExtensionStateContextProvider >
686+ )
687+
688+ // First hydrate state with initial task and streaming
689+ mockPostMessage ( {
690+ clineMessages : [
691+ {
692+ type : 'say' ,
693+ say : 'task' ,
694+ ts : Date . now ( ) - 2000 ,
695+ text : 'Initial task'
696+ } ,
697+ {
698+ type : 'say' ,
699+ say : 'api_req_started' ,
700+ ts : Date . now ( ) - 1000 ,
701+ text : JSON . stringify ( { } ) ,
702+ partial : true
703+ }
704+ ]
705+ } )
706+
707+ // Then send the completion result message (streaming finished)
708+ mockPostMessage ( {
709+ clineMessages : [
710+ {
711+ type : 'say' ,
712+ say : 'task' ,
713+ ts : Date . now ( ) - 2000 ,
714+ text : 'Initial task'
715+ } ,
716+ {
717+ type : 'ask' ,
718+ ask : 'completion_result' ,
719+ ts : Date . now ( ) ,
720+ text : 'Task completed successfully' ,
721+ partial : false
722+ }
723+ ]
724+ } )
725+
726+ // Verify celebration sound was played
727+ await waitFor ( ( ) => {
728+ expect ( vscode . postMessage ) . toHaveBeenCalledWith ( {
729+ type : 'playSound' ,
730+ audioType : 'celebration'
731+ } )
732+ } )
733+ } )
734+
735+ it ( 'plays progress_loop sound for api failures' , async ( ) => {
736+ render (
737+ < ExtensionStateContextProvider >
738+ < ChatView
739+ isHidden = { false }
740+ showAnnouncement = { false }
741+ hideAnnouncement = { ( ) => { } }
742+ showHistoryView = { ( ) => { } }
743+ />
744+ </ ExtensionStateContextProvider >
745+ )
746+
747+ // First hydrate state with initial task and streaming
748+ mockPostMessage ( {
749+ clineMessages : [
750+ {
751+ type : 'say' ,
752+ say : 'task' ,
753+ ts : Date . now ( ) - 2000 ,
754+ text : 'Initial task'
755+ } ,
756+ {
757+ type : 'say' ,
758+ say : 'api_req_started' ,
759+ ts : Date . now ( ) - 1000 ,
760+ text : JSON . stringify ( { } ) ,
761+ partial : true
762+ }
763+ ]
764+ } )
765+
766+ // Then send the api failure message (streaming finished)
767+ mockPostMessage ( {
768+ clineMessages : [
769+ {
770+ type : 'say' ,
771+ say : 'task' ,
772+ ts : Date . now ( ) - 2000 ,
773+ text : 'Initial task'
774+ } ,
775+ {
776+ type : 'ask' ,
777+ ask : 'api_req_failed' ,
778+ ts : Date . now ( ) ,
779+ text : 'API request failed' ,
780+ partial : false
781+ }
782+ ]
783+ } )
784+
785+ // Verify progress_loop sound was played
786+ await waitFor ( ( ) => {
787+ expect ( vscode . postMessage ) . toHaveBeenCalledWith ( {
788+ type : 'playSound' ,
789+ audioType : 'progress_loop'
790+ } )
791+ } )
792+ } )
793+ } )
0 commit comments