Files
siftlode/frontend/src/components/plexCards.lang.dom.test.tsx
T
peter aac79ffa53 fix(i18n): numbers, units, dates and order in the UI language, and after a switch (E30 S3)
Every locale formatter takes the language as an argument. Under the React
Compiler a formatter that read the i18n singleton was memoized on its other
arguments, so a card kept "2 hr 4 min" after a switch to Hungarian; plexLang()
had the same flaw and left Plex queries in the old language. Components now
take the language from useTranslation, and the signature makes forgetting it
a type error.

- ratings, file sizes, stream stats: the language's decimal mark ("8,3")
- view and subscriber counts compact in the language ("1,2 E")
- seconds as the language's unit ("6 mp") in the sliders and the stats
- a person's birth and death dates as dates, not ISO strings
- A-Z lists by the shown name in the language's alphabet (compareText),
  the tag catalog re-sorted by its translated names
- lint local/locale-format (was date-format) also refuses Intl.NumberFormat
  outside lib/format.ts, localeCompare, and a component reading the language
  off the i18n singleton
2026-09-29 22:39:57 +02:00

72 lines
2.7 KiB
TypeScript

// @vitest-environment jsdom
//
// E30: what a Plex card says, in the reader's language. An episode in a poster rail stands as its show
// (the show's poster and name, the episode on the line under it), and a season Plex named generically
// ("Season 1", "Specials") is named from its number instead. i18n here echoes the key AND its values,
// so an assertion can see which words went into the line.
import { describe, it, expect, afterEach, vi } from "vitest";
import { render, screen, cleanup } from "@testing-library/react";
vi.mock("react-i18next", async (importOriginal) => {
const actual = await importOriginal<typeof import("react-i18next")>();
const t = (key: string, o?: Record<string, unknown>) =>
o ? `${key}(${Object.values(o).join("|")})` : key;
return { ...actual, useTranslation: () => ({ i18n: { language: "en" }, t }) };
});
import { PlexPosterCard, SeasonCard } from "./plexCards";
import type { PlexCard, PlexSeasonDetail } from "../lib/api";
afterEach(cleanup);
const watchedEpisode: PlexCard = {
id: "e5",
type: "episode",
title: "Visszaszámlálás",
thumb: "/api/plex/image/e5",
season_number: 2,
episode_number: 5,
show_title: "A 3-test-probléma",
show_id: "s9",
};
describe("an episode in a poster rail", () => {
it("stands as its show: the show's poster and name, the episode under it", () => {
render(<PlexPosterCard card={watchedEpisode} onOpen={vi.fn()} shelf />);
const img = document.querySelector("img");
expect(img?.getAttribute("src")).toBe("/api/plex/image/s9");
expect(screen.getByText("A 3-test-probléma")).toBeTruthy();
expect(screen.getByText("plex.episodeLine(2|5|Visszaszámlálás)")).toBeTruthy();
});
it("keeps its own landscape still where the rail is landscape, or the show is unknown", () => {
const { unmount } = render(<PlexPosterCard card={watchedEpisode} onOpen={vi.fn()} wide />);
expect(document.querySelector("img")?.getAttribute("src")).toBe("/api/plex/image/e5");
unmount();
render(<PlexPosterCard card={{ ...watchedEpisode, show_id: null }} onOpen={vi.fn()} />);
expect(document.querySelector("img")?.getAttribute("src")).toBe("/api/plex/image/e5");
expect(screen.getByText("Visszaszámlálás")).toBeTruthy();
});
});
const season = (title: string, n: number | null): PlexSeasonDetail => ({
id: "se",
season_number: n,
title,
thumb: "",
episode_count: 3,
status: "new",
episodes: [],
});
describe("a season's name", () => {
it.each([
["Season 1", 1, "plex.seasonTitle(1)"],
["Specials", 0, "plex.specials"],
["Book One: Water", 1, "Book One: Water"],
])("Plex's %j (season %s) reads %j", (title, n, shown) => {
render(<SeasonCard se={season(title, n)} onOpen={vi.fn()} />);
expect(screen.getByText(shown)).toBeTruthy();
});
});