fix(ui,i18n): E30 review round 1 — audit rows in the reader's language, one HU name per thing

- audit log: a job run named from its result data (nested phases too), a skipped run says so, a
  secret's reset reads as a reset, choices named as Configuration names them, interval / batch /
  counts localized; the inbox shares the same jobRunText
- HU: remux is "Közvetlen közvetítés" (it shared "Közvetlen lejátszás" with direct play); backfill is
  behúzás, enrich is dúsítás, collection is kollekció, the allow-list is fehérlista; the last
  "jobnak" / "transzkódolás"; a test holds the options of one choice to distinct names
- contract: every skipped / reason word a job returns is named in both languages
- person page: the bio wait is timed from the open and a failing ask stops the polling; ClampedText
  (ui) shared with the collection page, whose short summary lost its dead More
- a playlist names season 0 the Specials; the channel re-slot compares in the reader's alphabet; an
  invite's date is in the admin's language; the language-ordered lists are asked again once a
  language switch is saved (saveLanguage)
- poster grids' column minimum in rem; lint refuses minmax(Npx,…)
This commit is contained in:
2026-09-30 03:26:58 +02:00
parent ab963c81d2
commit fa55e01ef0
46 changed files with 624 additions and 187 deletions
@@ -0,0 +1,47 @@
// @vitest-environment jsdom
//
// A clamped text offers More only when it is actually cut (E30 S6 on the person page, shared with the
// collection page in the E30 review, where a one-line summary carried a More that did nothing).
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
import { ClampedText } from "./ClampedText";
beforeAll(() => {
class Stub {
observe() {}
unobserve() {}
disconnect() {}
takeRecords() {
return [];
}
}
globalThis.ResizeObserver ??= Stub as unknown as typeof ResizeObserver;
});
afterEach(cleanup);
const view = (text: string) => (
<ClampedText text={text} lines={3} moreLabel="More" lessLabel="Less" />
);
describe("ClampedText", () => {
it("offers no toggle for a text that fits", () => {
render(view("Short."));
expect(screen.queryByText("More")).toBeNull(); // jsdom: nothing is cut
});
it("offers More for a cut text, and Less once open", () => {
const tall = vi.spyOn(HTMLElement.prototype, "scrollHeight", "get").mockReturnValue(400);
const box = vi.spyOn(HTMLElement.prototype, "clientHeight", "get").mockReturnValue(60);
try {
render(view("A long text.\n\nOn and on."));
const p = screen.getByText(/A long text/);
expect(p.className).toContain("line-clamp-3");
fireEvent.click(screen.getByText("More"));
expect(screen.getByText("Less").getAttribute("aria-expanded")).toBe("true");
expect(p.className).not.toContain("line-clamp-3");
} finally {
tall.mockRestore();
box.mockRestore();
}
});
});