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
72 lines
2.7 KiB
TypeScript
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();
|
|
});
|
|
});
|