From d92ac93c64a382ccf28dce1027efdaf5ac9956e7 Mon Sep 17 00:00:00 2001 From: beefchimi Date: Fri, 22 Dec 2023 16:14:42 -0500 Subject: [PATCH] fix up and move down stack --- docs/api.md | 4 ++++ src/Stack.ts | 7 ++---- src/tests/Stack.test.ts | 47 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/docs/api.md b/docs/api.md index 1a383db..2056175 100644 --- a/docs/api.md +++ b/docs/api.md @@ -240,6 +240,10 @@ soundStack.activeEvents; // Possible `StackState` values are: `idle`, `loading`, `playing`. (event: 'state', listener: (current: StackState) => void) +// Event called whenever the `keys` property changes. This is useful +// to subscribe to changes in the internal “sound queue”. +(event: 'queue', listener: (newKeys: SoundId[], oldKeys: SoundId[]) => void) + // Event called whenever the `volume` property changes. (event: 'volume', listener: (level: number) => void) diff --git a/src/Stack.ts b/src/Stack.ts index 5f792fe..4042f93 100644 --- a/src/Stack.ts +++ b/src/Stack.ts @@ -201,17 +201,14 @@ export class Stack extends EmittenCommon { ({id}) => !outOfBoundsIds.includes(id), ); - outOfBounds.forEach((expiredSound) => { - expiredSound.stop(); - }); - + outOfBounds.forEach((expiredSound) => expiredSound.stop()); this.#setQueue(filteredQueue); return newSound; } #setQueue(value: Sound[]) { - const oldKeys = this._keys; + const oldKeys = [...this._keys]; const newKeys = value.map(({id}) => id); const identicalKeys = arrayShallowEquals(oldKeys, newKeys); diff --git a/src/tests/Stack.test.ts b/src/tests/Stack.test.ts index f7e5d3e..4ba6ef3 100644 --- a/src/tests/Stack.test.ts +++ b/src/tests/Stack.test.ts @@ -210,6 +210,28 @@ describe('Stack component', () => { expect(spySound3Stop).toBeCalled(); }); + it('emits `queue` event for each stopped Sound', async () => { + const spyQueue: StackEventMap['queue'] = vi.fn((_new, _old) => {}); + + mockStack.on('queue', spyQueue); + expect(spyQueue).not.toBeCalled(); + + await mockStack.prepare('One'); + await mockStack.prepare('Two'); + await mockStack.prepare('Three'); + + expect(spyQueue).toBeCalledTimes(3); + expect(spyQueue).toHaveBeenLastCalledWith( + ['One', 'Two', 'Three'], + ['One', 'Two'], + ); + + mockStack.stop(); + + expect(spyQueue).toBeCalledTimes(6); + expect(spyQueue).toHaveBeenLastCalledWith([], ['Three']); + }); + it('returns instance', async () => { await mockStack.prepare('Foo'); const instance = mockStack.stop(); @@ -293,6 +315,31 @@ describe('Stack component', () => { await expect(sound).resolves.toBeInstanceOf(Sound); await expect(sound).resolves.toHaveProperty('id', mockSoundId); }); + + it('emits `queue` event with new and old `keys`', async () => { + const mockSoundId1 = 'Foo'; + const mockSoundId2 = 'Bar'; + const spyQueue: StackEventMap['queue'] = vi.fn((_new, _old) => {}); + + mockStack.on('queue', spyQueue); + expect(spyQueue).not.toBeCalled(); + expect(mockStack.keys).toHaveLength(0); + + await mockStack.prepare(mockSoundId1); + + expect(spyQueue).toBeCalledTimes(1); + expect(spyQueue).toBeCalledWith([mockSoundId1], []); + expect(mockStack.keys).toHaveLength(1); + + await mockStack.prepare(mockSoundId2); + + expect(spyQueue).toBeCalledTimes(2); + expect(spyQueue).toBeCalledWith( + [mockSoundId1, mockSoundId2], + [mockSoundId1], + ); + expect(mockStack.keys).toHaveLength(2); + }); }); describe('#load()', () => {