fix(plex): a collection kept after a failed refresh holds its seed title

When Plex made the collection and only the read after it failed, the kept row had no members: the
title showed unticked in the collection it had just been added to, the collection empty, and the
name stayed in the field for a retry that could only be refused as taken. The seed's row now names
the collection (child_count 1), and the panel clears the field on that refusal as on success. Also
held: the panel's lines sit beside its list, and a keyboard Retry lands on the first row.
This commit is contained in:
2026-10-08 22:12:43 +02:00
parent 7541b1b016
commit 4eaa55385f
4 changed files with 53 additions and 6 deletions
+29 -3
View File
@@ -8,7 +8,7 @@ import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import { FloatingTree } from "@floating-ui/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { api, type PlexCard, type PlexCollection } from "../lib/api";
import { api, HttpError, type PlexCard, type PlexCollection } from "../lib/api";
import { qk } from "../lib/queryKeys";
vi.mock("react-i18next", async (importOriginal) => {
@@ -361,9 +361,35 @@ describe("a Plex title's menu — Add to collection", () => {
const alert = await screen.findByRole("alert", { hidden: true });
expect(alert.textContent).toContain("plex.collectionAdd.loadFailed");
expect(screen.queryByText("plex.collectionAdd.none")).toBeNull();
// A menu owns its items only: the line about the list sits beside it, not in it.
expect(document.querySelector("[role=menu]")).not.toBeNull();
expect(alert.closest("[role=menu]")).toBeNull();
list.mockResolvedValue({ collections: [coll("cB", "Beta", false)] });
fireEvent.click(await screen.findByText("common.retry"));
await checkRow("Beta");
// Retry from the keyboard: the focused button goes as the list loads, and focus must not be left
// on <body> with the menu open — it lands on the first row that came back.
const retry = (await screen.findByText("common.retry")).closest("button")!;
retry.focus();
fireEvent.click(retry);
const beta = await checkRow("Beta");
await vi.waitFor(() => expect(document.activeElement).toBe(beta));
});
it("clears the name when Plex made the collection and only the read after it failed", async () => {
vi.spyOn(api, "plexCollectionsFor").mockResolvedValue({ collections: [] });
const create = vi
.spyOn(api, "plexCreateCollection")
.mockRejectedValue(
new HttpError(422, "pending", { code: "refused", reason: "collection_pending_sync" }),
);
setup(card({}), {}, { admin: true });
rightClick();
fireEvent.click(item("plex.collectionAdd.row"));
const name = await screen.findByLabelText<HTMLInputElement>("plex.collection.namePlaceholder");
fireEvent.change(name, { target: { value: "Noir" } });
fireEvent.keyDown(name, { key: "Enter" });
await vi.waitFor(() => expect(create).toHaveBeenCalledTimes(1));
// It exists now: a second Enter on the kept name could only be refused as taken.
await vi.waitFor(() => expect(name.value).toBe(""));
});
it("steps from the name field into the rows with the arrows — rows that arrived after it opened", async () => {