// @vitest-environment jsdom // // A server refusal said through the records (`refusalCodes`), in both languages, by the real i18n — // one case per record shape, so moving the dispatch into data changed what no reader sees. import { afterEach, describe, expect, it } from "vitest"; import i18n from "../../i18n"; import { formatBytes } from "../format"; import { localizeDetail } from "./refusalText"; afterEach(async () => { await i18n.changeLanguage("en"); }); describe.each(["en", "hu"])("localizeDetail (%s)", (lng) => { const say = async (d: unknown) => { await i18n.changeLanguage(lng); return localizeDetail(d); }; it("names an edit refused for a file that is not video or audio", async () => { const said = await say({ code: "edit", reason: "not_media" }); expect(said).toBe(i18n.t("downloads.errors.edit.not_media")); expect(said).not.toBe(i18n.t("downloads.errors.edit.generic")); }); it("says a family's reason, and its own fallback for a reason it does not know", async () => { expect(await say({ code: "playlist_move", reason: "same_playlist" })).toBe( i18n.t("playlists.moveErrors.same_playlist"), ); expect(await say({ code: "playlist_move", reason: "nope" })).toBe( i18n.t("playlists.moveFailed"), ); }); it("reads a sign-in refusal's one-line form", async () => { expect(await say({ code: "login", reason: "suspended" })).toBe( i18n.t("welcome.auth.blocked.suspended.short"), ); }); it("puts a refusal's numbers into its sentence", async () => { expect(await say({ code: "quota", reason: "max_jobs", limit: 7 })).toContain("7"); expect(await say({ code: "reset", reason: "weak_password", min: 12 })).toContain("12"); expect(await say({ code: "refused", reason: "view_name_taken", name: "Esti" })).toContain( "Esti", ); }); it("puts a download cap in GB and an upload cap in bytes, in the reader's number format", async () => { expect(await say({ code: "quota", reason: "max_bytes", limit: 1.5 * 2 ** 30 })).toContain( lng === "hu" ? "1,5" : "1.5", ); const upload = await say({ code: "upload_too_large", limit: 5_242_880 }); expect(upload).toContain(formatBytes(5_242_880, lng)); expect(upload).not.toBe(i18n.t("downloads.errors.upload.tooLarge", { size: "" })); }); it("says a shared-sentence code's own fallback for a reason it does not know", async () => { expect(await say({ code: "quota", reason: "nope" })).toBe( i18n.t("downloads.errors.quota.generic"), ); expect(await say({ code: "collection", reason: "nope" })).toBe( i18n.t("plex.collection.editFailed"), ); }); it("says a shared-sentence code's fallback, and a fixed code's one sentence", async () => { expect(await say({ code: "reset", reason: "invalid" })).toBe( i18n.t("welcome.auth.deadLink.short"), ); expect(await say({ code: "sync_cooldown" })).toBe(i18n.t("errors.syncCooldown")); }); it("reads a code no screen shows, or one it does not know, as the generic failure", async () => { expect(await say({ code: "bad_cursor" })).toBe(i18n.t("errors.generic")); expect(await say({ code: "never_sent" })).toBe(i18n.t("errors.generic")); expect(await say({ code: "constructor" })).toBe(i18n.t("errors.generic")); expect(await say(null)).toBe(i18n.t("errors.generic")); }); });