@@ -54,6 +54,11 @@ describe('AudioPlayingService', () => {
5454 expect ( service ) . toBeTruthy ( ) ;
5555 } ) ;
5656
57+ function createMockSource ( ) {
58+ return jasmine . createSpyObj (
59+ 'AudioBufferSourceNode' , [ 'stop' , 'start' , 'connect' ] ) ;
60+ }
61+
5762 describe ( 'playAudio' , ( ) => {
5863 it ( 'should not play audio if the buffer is empty' , ( ) => {
5964 spyOn < any > ( service , 'playPCM' ) . and . callThrough ( ) ;
@@ -75,5 +80,79 @@ describe('AudioPlayingService', () => {
7580 service . playAudio ( buffer ) ;
7681 expect ( ( service as any ) . playPCM ) . toHaveBeenCalledWith ( combinedBuffer ) ;
7782 } ) ;
83+
84+ it ( 'should schedule audio sources correctly' , ( ) => {
85+ const buffer = [ new Uint8Array ( [ 1 , 2 ] ) ] ;
86+ const mockSource = createMockSource ( ) ;
87+ mockAudioContext . createBufferSource . and . returnValue ( mockSource ) ;
88+
89+ service . playAudio ( buffer ) ;
90+
91+ expect ( mockAudioContext . createBufferSource ) . toHaveBeenCalled ( ) ;
92+ expect ( mockSource . start ) . toHaveBeenCalled ( ) ;
93+ } ) ;
94+
95+ it ( 'removes source from schedule after playback finishes' , ( ) => {
96+ const mockSource = createMockSource ( ) ;
97+ mockAudioContext . createBufferSource . and . returnValue ( mockSource ) ;
98+ const scheduledAudioSources =
99+ ( service as any ) . scheduledAudioSources as Set < any > ;
100+
101+ service . playAudio ( [ new Uint8Array ( [ 1 ] ) ] ) ;
102+
103+ // Assert that the source was added and the onended handler was attached.
104+ expect ( scheduledAudioSources . has ( mockSource ) ) . toBeTrue ( ) ;
105+ expect ( ( mockSource as any ) . onended ) . toEqual ( jasmine . any ( Function ) ) ;
106+
107+ // Simulate the audio finishing by calling the onended handler.
108+ if ( ( mockSource as any ) . onended ) {
109+ ( mockSource as any ) . onended ( ) ;
110+ }
111+
112+ // Assert that the source was removed from the set.
113+ expect ( scheduledAudioSources . has ( mockSource ) ) . toBeFalse ( ) ;
114+ } ) ;
115+ } ) ;
116+
117+ describe ( 'stopAudio' , ( ) => {
118+ it ( 'should stop all scheduled sources' , ( ) => {
119+ const mockSource1 = createMockSource ( ) ;
120+ const mockSource2 = createMockSource ( ) ;
121+
122+ mockAudioContext . createBufferSource . and . returnValues (
123+ mockSource1 , mockSource2 ) ;
124+
125+ // Play two audio clips
126+ service . playAudio ( [ new Uint8Array ( [ 1 ] ) ] ) ;
127+ service . playAudio ( [ new Uint8Array ( [ 2 ] ) ] ) ;
128+
129+ service . stopAudio ( ) ;
130+
131+ expect ( mockSource1 . stop ) . toHaveBeenCalled ( ) ;
132+ expect ( mockSource2 . stop ) . toHaveBeenCalled ( ) ;
133+ } ) ;
134+
135+ it ( 'should reset scheduling time so next audio plays immediately' , ( ) => {
136+ const mockSource1 = createMockSource ( ) ;
137+ const mockSource2 = createMockSource ( ) ;
138+ mockAudioContext . createBufferSource . and . returnValues (
139+ mockSource1 , mockSource2 ) ;
140+
141+ // Play a long audio clip (duration 1s from mock)
142+ service . playAudio ( [ new Uint8Array ( [ 1 ] ) ] ) ;
143+
144+ // Advance time slightly
145+ mockAudioContext . currentTime = 0.5 ;
146+
147+ // Stop audio
148+ service . stopAudio ( ) ;
149+
150+ // Play another clip
151+ service . playAudio ( [ new Uint8Array ( [ 2 ] ) ] ) ;
152+
153+ // The second clip should start at current time (0.5), not after the first
154+ // clip (1.0)
155+ expect ( mockSource2 . start ) . toHaveBeenCalledWith ( 0.5 ) ;
156+ } ) ;
78157 } ) ;
79158} ) ;
0 commit comments