@@ -537,6 +537,14 @@ describe("Arpeggio Widget", () => {
537537
538538 expect ( arpeggio . _playing ) . toBe ( false ) ;
539539 } ) ;
540+
541+ test ( "returns immediately if not playing" , ( ) => {
542+ arpeggio . _playing = false ;
543+ const triggerSpy = jest . spyOn ( activityMock . logo . synth , "trigger" ) ;
544+ arpeggio . __playNote ( 0 ) ;
545+ expect ( triggerSpy ) . not . toHaveBeenCalled ( ) ;
546+ triggerSpy . mockRestore ( ) ;
547+ } ) ;
540548 } ) ;
541549
542550 // --- __playCell fallback Tests ---
@@ -616,4 +624,73 @@ describe("Arpeggio Widget", () => {
616624 expect ( cell2 . style . backgroundColor ) . toBe ( "black" ) ;
617625 } ) ;
618626 } ) ;
627+
628+ // --- Playback Teardown Tests ---
629+ describe ( "playback teardown and stop behavior" , ( ) => {
630+ beforeEach ( ( ) => {
631+ jest . useFakeTimers ( ) ;
632+ arpeggio . notesToPlay = [ [ "C4" , 1 ] ] ;
633+ arpeggio . init ( activityMock ) ;
634+ } ) ;
635+
636+ afterEach ( ( ) => {
637+ jest . useRealTimers ( ) ;
638+ } ) ;
639+
640+ test ( "widgetWindow.onclose stops playback and clears timeout" , ( ) => {
641+ // Start playing
642+ const cell = document . getElementById ( "2,1" ) ;
643+ cell . onclick ( { target : cell } ) ;
644+ arpeggio . playButton . onclick ( ) ;
645+ expect ( arpeggio . _playing ) . toBe ( true ) ;
646+ expect ( arpeggio . _playTimeout ) . not . toBeNull ( ) ;
647+
648+ const timeoutSpy = jest . spyOn ( global , "clearTimeout" ) ;
649+
650+ // Close window
651+ arpeggio . widgetWindow . onclose ( ) ;
652+
653+ expect ( arpeggio . _playing ) . toBe ( false ) ;
654+ expect ( arpeggio . _playTimeout ) . toBeNull ( ) ;
655+ expect ( timeoutSpy ) . toHaveBeenCalled ( ) ;
656+ expect ( activityMock . logo . synth . stop ) . toHaveBeenCalled ( ) ;
657+ expect ( mockWidgetWindow . destroy ) . toHaveBeenCalled ( ) ;
658+
659+ timeoutSpy . mockRestore ( ) ;
660+ } ) ;
661+
662+ test ( "clicking play button when playing stops playback and clears timeout" , ( ) => {
663+ const cell = document . getElementById ( "2,1" ) ;
664+ cell . onclick ( { target : cell } ) ;
665+ arpeggio . playButton . onclick ( ) ;
666+ expect ( arpeggio . _playing ) . toBe ( true ) ;
667+ expect ( arpeggio . _playTimeout ) . not . toBeNull ( ) ;
668+
669+ const timeoutSpy = jest . spyOn ( global , "clearTimeout" ) ;
670+
671+ // Click to stop
672+ arpeggio . playButton . onclick ( ) ;
673+
674+ expect ( arpeggio . _playing ) . toBe ( false ) ;
675+ expect ( arpeggio . _playTimeout ) . toBeNull ( ) ;
676+ expect ( timeoutSpy ) . toHaveBeenCalled ( ) ;
677+ expect ( activityMock . logo . synth . stop ) . toHaveBeenCalled ( ) ;
678+
679+ timeoutSpy . mockRestore ( ) ;
680+ } ) ;
681+
682+ test ( "_clear stops playback, clears timeout, and resets play button state" , ( ) => {
683+ const cell = document . getElementById ( "2,1" ) ;
684+ cell . onclick ( { target : cell } ) ;
685+ arpeggio . playButton . onclick ( ) ;
686+ expect ( arpeggio . _playing ) . toBe ( true ) ;
687+
688+ // Clear widget
689+ arpeggio . _clear ( ) ;
690+
691+ expect ( arpeggio . _playing ) . toBe ( false ) ;
692+ expect ( arpeggio . _playTimeout ) . toBeNull ( ) ;
693+ expect ( activityMock . logo . synth . stop ) . toHaveBeenCalled ( ) ;
694+ } ) ;
695+ } ) ;
619696} ) ;
0 commit comments