test(plex): cover create write-through + the 409->nameTaken mapping (F-F)

Backend: create-collection happy path (write-through + editable) and the
create resync-failure 422 (honest reconcile detail), mirroring the rename/remove
tests. Frontend: extract notifyCollectionEditError (the 409->nameTaken vs
generic-fallback mapping that was inline in the New-collection create()) and
unit-test it; wire create() through it.
This commit is contained in:
2026-08-23 08:08:34 +02:00
parent 7caa2636e4
commit 81b7660232
4 changed files with 106 additions and 16 deletions
+31
View File
@@ -0,0 +1,31 @@
// @vitest-environment jsdom
//
// jsdom (not node): importing ../api pulls in i18n, which reads navigator.language at module load.
import { describe, it, expect, vi, beforeEach } from "vitest";
// Spy on the toast sink so we can assert exactly what each error maps to.
vi.mock("./notifications", () => ({ notify: vi.fn(() => 0) }));
import { HttpError } from "./api";
import { notify } from "./notifications";
import { notifyCollectionEditError } from "./actionErrors";
const notifyMock = vi.mocked(notify);
beforeEach(() => notifyMock.mockClear());
describe("notifyCollectionEditError", () => {
it("maps a backend 409 to the friendly nameTaken message (not the raw server detail)", () => {
notifyCollectionEditError(new HttpError(409, "already exists"), "EDIT_FAILED", "NAME_TAKEN");
expect(notifyMock).toHaveBeenCalledWith({ level: "error", message: "NAME_TAKEN" });
});
it("falls back to the server detail for a non-409 HttpError", () => {
notifyCollectionEditError(new HttpError(500, "server boom"), "EDIT_FAILED", "NAME_TAKEN");
expect(notifyMock).toHaveBeenCalledWith({ level: "error", message: "server boom" });
});
it("falls back to the generic edit-failed message for a non-HttpError", () => {
notifyCollectionEditError(new Error("network"), "EDIT_FAILED", "NAME_TAKEN");
expect(notifyMock).toHaveBeenCalledWith({ level: "error", message: "EDIT_FAILED" });
});
});
+19
View File
@@ -12,3 +12,22 @@ export function notifyActionError(err: unknown, fallbackMessage: string): void {
const detail = err instanceof HttpError ? err.detail : null;
notify({ level: "error", message: detail || fallbackMessage });
}
/**
* Collection write errors. The backend enforces per-library name uniqueness and returns 409 on a
* duplicate; localize that specific case to the friendly `nameTaken` copy (notifyActionError would
* otherwise surface the server's raw English detail). Everything else falls back to the generic
* edit-failed handling (which still prefers the server's reason when present). Both args are already
* translated so this stays i18n-agnostic and unit-testable.
*/
export function notifyCollectionEditError(
err: unknown,
editFailedMessage: string,
nameTakenMessage: string,
): void {
if (err instanceof HttpError && err.status === 409) {
notify({ level: "error", message: nameTakenMessage });
return;
}
notifyActionError(err, editFailedMessage);
}