From 2ab42df0c8e347b47824f1ffc8b3d454f55e5c3c Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 29 Oct 2025 17:05:04 +0100 Subject: [PATCH] Fix audio player seek bar position (#31127) * fix(audio): stop clock when the source audio reaches the end * test(audio): add test for playback when audio source ended --- src/audio/Playback.ts | 6 +++--- test/unit-tests/audio/Playback-test.ts | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/audio/Playback.ts b/src/audio/Playback.ts index 356e41ca84..7cb5943129 100644 --- a/src/audio/Playback.ts +++ b/src/audio/Playback.ts @@ -202,6 +202,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte private onPlaybackEnd = async (): Promise => { await this.context.suspend(); this.emit(PlaybackState.Stopped); + this.clock.flagStop(); }; public async play(): Promise { @@ -248,9 +249,8 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte this.emit(PlaybackState.Paused); } - public async stop(): Promise { - await this.onPlaybackEnd(); - this.clock.flagStop(); + public stop(): Promise { + return this.onPlaybackEnd(); } public async toggle(): Promise { diff --git a/test/unit-tests/audio/Playback-test.ts b/test/unit-tests/audio/Playback-test.ts index 7f14685f45..9732132d6d 100644 --- a/test/unit-tests/audio/Playback-test.ts +++ b/test/unit-tests/audio/Playback-test.ts @@ -46,6 +46,7 @@ describe("Playback", () => { beforeEach(() => { jest.spyOn(logger, "error").mockRestore(); + mockAudioBufferSourceNode.addEventListener.mockClear(); mockAudioBuffer.getChannelData.mockClear().mockReturnValue(mockChannelData); mockAudioContext.decodeAudioData.mockReset().mockResolvedValue(mockAudioBuffer); mockAudioContext.resume.mockClear().mockResolvedValue(undefined); @@ -105,6 +106,26 @@ describe("Playback", () => { expect(playback.currentState).toEqual(PlaybackState.Stopped); }); + it("stop when audio source ended", async () => { + const buffer = new ArrayBuffer(8); + const playback = new Playback(buffer); + await playback.prepare(); + await playback.play(); + + // Simulate the audio source ending by calling the 'ended' event listener + const endedListener = mockAudioBufferSourceNode.addEventListener.mock.calls.find( + (call) => call[0] === "ended", + )[1]; + await endedListener(); + + // AudioContext should be suspended + expect(mockAudioContext.suspend).toHaveBeenCalled(); + // Playback state should be Stopped + expect(playback.currentState).toEqual(PlaybackState.Stopped); + // Clock should be reset to 0 + expect(playback.timeSeconds).toEqual(0); + }); + describe("prepare()", () => { it("decodes audio data when not greater than 5mb", async () => { const buffer = new ArrayBuffer(8);