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
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:
@@ -796,7 +796,9 @@ describe("Home · keyboard", () => {
|
||||
],
|
||||
});
|
||||
mount();
|
||||
await screen.findByText("Comfort thing");
|
||||
// The rail's keys are live once its effect registers it, which can land after the cards paint.
|
||||
const first = await screen.findByText("Comfort thing");
|
||||
await waitFor(() => expect(first.closest("[data-nav-list]")).not.toBeNull());
|
||||
const titles = ["Comfort thing", "Pinned channel", "Second thing", "Third thing"];
|
||||
const seen: (string | undefined)[] = [];
|
||||
for (let i = 0; i < titles.length; i++) {
|
||||
|
||||
@@ -424,6 +424,10 @@ describe("Playlists — removing a row, and undoing it", () => {
|
||||
.map((c) => c[0] as { message: string; action?: { label: string; onClick: () => void } })
|
||||
.filter((n) => n.message?.startsWith("playlists.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(
|
||||
@@ -497,7 +501,7 @@ describe("Playlists — removing a row, and undoing it", () => {
|
||||
const lists = { 1: [vid("A", 0), vid("B", 0), vid("C", 0)] };
|
||||
serverRemoves(lists);
|
||||
mount(lists);
|
||||
await screen.findByRole("button", { name: "card.play — Video C" });
|
||||
await keysLiveOn(await screen.findByRole("button", { name: "card.play — Video C" }));
|
||||
pressKey("j");
|
||||
pressKey("j");
|
||||
expect(document.activeElement?.textContent).toContain("Video B");
|
||||
@@ -511,7 +515,7 @@ describe("Playlists — removing a row, and undoing it", () => {
|
||||
vi.spyOn(api, "removeFromPlaylist").mockResolvedValue(undefined as never);
|
||||
h.selectedId = WATCH_LATER;
|
||||
mount({ [WATCH_LATER]: [vid("A", 0), vid("B", 0)] });
|
||||
await screen.findByRole("button", { name: "card.play — Video B" });
|
||||
await keysLiveOn(await screen.findByRole("button", { name: "card.play — Video B" }));
|
||||
pressKey("j");
|
||||
pressKey("Delete");
|
||||
await act(async () => void (await new Promise((r) => setTimeout(r, 20))));
|
||||
@@ -617,7 +621,9 @@ describe("Playlists · a narrow table", () => {
|
||||
describe("Playlists — the rows are a keyboard list", () => {
|
||||
it("walks the rows with j and plays the focused one on Enter", async () => {
|
||||
mount({ 1: [vid("A", 0), vid("B", 0)] });
|
||||
await screen.findByRole("button", { name: "card.play — Video B" });
|
||||
const row = await screen.findByRole("button", { name: "card.play — Video B" });
|
||||
// The keys are live once the list's effect registers it, which can land after the rows paint.
|
||||
await waitFor(() => expect(row.closest("[data-nav-list]")).not.toBeNull());
|
||||
const press = (key: string) =>
|
||||
act(() => {
|
||||
(document.activeElement ?? document.body).dispatchEvent(
|
||||
|
||||
@@ -289,8 +289,9 @@ describe("PlexDiscover · keyboard", () => {
|
||||
);
|
||||
await screen.findAllByText("Random Pick");
|
||||
// Five rails (continue, recently watched, recently added, because-you-watched, surprise) + genres.
|
||||
// Registered by a passive effect, which can land after the cards paint.
|
||||
await waitFor(() => expect(document.querySelectorAll("[data-nav-list]")).toHaveLength(6));
|
||||
const lists = [...document.querySelectorAll("[data-nav-list]")];
|
||||
expect(lists.length).toBe(6);
|
||||
// …and each holds its cards as items: the genre tiles too, not only the posters.
|
||||
expect(lists.every((list) => list.querySelector("[data-nav-index]"))).toBe(true);
|
||||
const step = () => {
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user