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
This commit is contained in:
Florian Duros 2025-10-29 17:05:04 +01:00 committed by GitHub
parent a9993aef85
commit 2ab42df0c8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 24 additions and 3 deletions

View File

@ -202,6 +202,7 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
private onPlaybackEnd = async (): Promise<void> => { private onPlaybackEnd = async (): Promise<void> => {
await this.context.suspend(); await this.context.suspend();
this.emit(PlaybackState.Stopped); this.emit(PlaybackState.Stopped);
this.clock.flagStop();
}; };
public async play(): Promise<void> { public async play(): Promise<void> {
@ -248,9 +249,8 @@ export class Playback extends EventEmitter implements IDestroyable, PlaybackInte
this.emit(PlaybackState.Paused); this.emit(PlaybackState.Paused);
} }
public async stop(): Promise<void> { public stop(): Promise<void> {
await this.onPlaybackEnd(); return this.onPlaybackEnd();
this.clock.flagStop();
} }
public async toggle(): Promise<void> { public async toggle(): Promise<void> {

View File

@ -46,6 +46,7 @@ describe("Playback", () => {
beforeEach(() => { beforeEach(() => {
jest.spyOn(logger, "error").mockRestore(); jest.spyOn(logger, "error").mockRestore();
mockAudioBufferSourceNode.addEventListener.mockClear();
mockAudioBuffer.getChannelData.mockClear().mockReturnValue(mockChannelData); mockAudioBuffer.getChannelData.mockClear().mockReturnValue(mockChannelData);
mockAudioContext.decodeAudioData.mockReset().mockResolvedValue(mockAudioBuffer); mockAudioContext.decodeAudioData.mockReset().mockResolvedValue(mockAudioBuffer);
mockAudioContext.resume.mockClear().mockResolvedValue(undefined); mockAudioContext.resume.mockClear().mockResolvedValue(undefined);
@ -105,6 +106,26 @@ describe("Playback", () => {
expect(playback.currentState).toEqual(PlaybackState.Stopped); 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()", () => { describe("prepare()", () => {
it("decodes audio data when not greater than 5mb", async () => { it("decodes audio data when not greater than 5mb", async () => {
const buffer = new ArrayBuffer(8); const buffer = new ArrayBuffer(8);