test: a list key waits for the list's registration, not only its rows
gate / frontend (push) Successful in 6m5s
gate / backend (push) Successful in 7m10s

The server gate's frontend job went red on 0.96.0: PlexPlaylistView's j/Delete
test pressed j after the rows painted but before useListNav's passive effect
registered the list, so the key did nothing on the slower runner. Every test
that presses a list key after an async load now waits for [data-nav-list] on
its list first (PlexPlaylistView, Playlists, Home, PlexDiscover). Checked
against a 30 ms delayed registration: the old tests fail, the new ones pass.
This commit is contained in:
2026-09-27 23:01:57 +02:00
parent ffcea0cb07
commit 0f9ed7cdbf
4 changed files with 20 additions and 7 deletions
@@ -96,6 +96,10 @@ const toast = () =>
.map((c) => c[0] as { message: string; action?: { label: string; onClick: () => void } })
.filter((n) => n.message?.startsWith("plex.playlist.removedToast"))
.pop();
// The list's keys are live once its effect has registered it (`data-nav-list`), a passive effect that
// can run AFTER the rows are on screen: a key pressed on a slow machine in between did nothing (CI flake).
const keysLiveOn = (el: Element) =>
waitFor(() => expect(el.closest("[data-nav-list]")).not.toBeNull());
const pressKey = (key: string) =>
act(() => {
(document.activeElement ?? document.body).dispatchEvent(
@@ -254,7 +258,7 @@ describe("PlexPlaylistView · reordering without a drag (WCAG 2.5.7)", () => {
describe("PlexPlaylistView · the keyboard", () => {
it("walks the rows with j and takes out the walked one on Delete", async () => {
mount([movie("1"), movie("2"), movie("3")]);
await screen.findByText("Movie 3");
await keysLiveOn(await screen.findByText("Movie 3"));
vi.spyOn(api, "plexPlaylistRemoveItem").mockResolvedValue({} as never);
pressKey("j");
pressKey("j");
@@ -268,7 +272,7 @@ describe("PlexPlaylistView · the keyboard", () => {
it("walks an open show's episodes after its header, and Delete on the header takes the show", async () => {
mount([ep("e1", 1, 1), ep("e2", 1, 2), movie("9")]);
fireEvent.click(await screen.findByText("The Show"));
await screen.findByText("Ep e2");
await keysLiveOn(await screen.findByText("Ep e2"));
pressKey("j");
expect(document.activeElement).toBe(rowOf("The Show"));
pressKey("j");