Review round 1 of the refusal contract: it claimed more than it held. - The other reason carriers get their Literal too (BlockedUrl, ClipError, FolderError, the playlist and sign-in refusal helpers), so the type checker refuses a reason passed by variable or an unknown literal, and the contract reads one shape. - A literal reason in a detail dict built anywhere counts as a reason of its code; a code the scan cannot read, a refused() whose reason it cannot read, and a new reason passed by variable each fail the contract until listed with what holds them. - A sign-in refusal key the backend no longer sends is caught too. - localizeDetail's numbers (download cap in GB, upload cap in bytes) and the shared-sentence fallbacks are tested in both languages. - One Literal parser in the contract; a stale comment fixed.
81 lines
3.3 KiB
TypeScript
81 lines
3.3 KiB
TypeScript
// @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"));
|
|
});
|
|
});
|