Skip to content

Commit 3f4e1f4

Browse files
committed
test(player): reproduce stale play rejection race
1 parent 7bba2de commit 3f4e1f4

1 file changed

Lines changed: 37 additions & 0 deletions

File tree

apps/desktop/src/features/workspace/RehearsalPlayer.media-lifecycle.test.tsx

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,43 @@ describe("RehearsalPlayer media lifecycle", () => {
7878
expect(screen.queryByTestId("rehearsal-loop-audio-error")).toBeNull();
7979
});
8080

81+
it("ignores a stale play rejection after playback has resumed", async () => {
82+
installAudioBoundary();
83+
let rejectFirstPlay: ((reason?: unknown) => void) | undefined;
84+
const play = vi
85+
.spyOn(HTMLMediaElement.prototype, "play")
86+
.mockImplementationOnce(
87+
() =>
88+
new Promise<void>((_resolve, reject) => {
89+
rejectFirstPlay = reject;
90+
}),
91+
)
92+
.mockResolvedValue(undefined);
93+
renderPlayableSong();
94+
95+
fireEvent.click(
96+
screen.getByRole("button", { name: /Start the count-in/i }),
97+
);
98+
fireEvent.click(screen.getByRole("button", { name: /Pause/i }));
99+
fireEvent.click(screen.getByRole("button", { name: /Resume/i }));
100+
expect(play).toHaveBeenCalledTimes(2);
101+
expect(screen.getByTestId("rehearsal-loop-next-action")).toHaveTextContent(
102+
/Count in/i,
103+
);
104+
105+
await act(async () => {
106+
const staleInterruption = new Error("The earlier play request was interrupted");
107+
staleInterruption.name = "AbortError";
108+
rejectFirstPlay?.(staleInterruption);
109+
await Promise.resolve();
110+
});
111+
112+
expect(screen.getByTestId("rehearsal-loop-next-action")).toHaveTextContent(
113+
/Count in/i,
114+
);
115+
expect(screen.queryByTestId("rehearsal-loop-audio-error")).toBeNull();
116+
});
117+
81118
it("restarts a selected loop when the media ends at the section boundary", () => {
82119
vi.useFakeTimers();
83120
installAudioBoundary();

0 commit comments

Comments
 (0)