Files
jobtrackingapp/job-tracker-ui/src/i18n-provider.test.tsx
T

97 lines
4.8 KiB
TypeScript

import React from "react";
import "@testing-library/jest-dom";
import { act, fireEvent, render, screen, waitFor, within } from "@testing-library/react";
import { api } from "./api";
import { I18nProvider, useI18n } from "./i18n/I18nProvider";
import { ToastProvider } from "./toast";
function Probe() {
const { language, setLanguage, hydrateLanguage, t } = useI18n();
return (
<div>
<output aria-label="language">{language}</output>
<output aria-label="settings-label">{t("settings")}</output>
<button onClick={() => setLanguage("en")}>English</button>
<button onClick={() => setLanguage("nb")}>Norsk</button>
<button onClick={() => hydrateLanguage("nb-NO")}>Restore server preference</button>
</div>
);
}
beforeEach(() => {
jest.clearAllMocks();
window.localStorage.clear();
});
test("migrates the legacy Norwegian code and persists Bokmål using nb-NO", async () => {
window.localStorage.setItem("uiLanguage", "no");
render(<I18nProvider><Probe /></I18nProvider>);
expect(screen.getByLabelText("language")).toHaveTextContent("nb");
expect(screen.getByLabelText("settings-label")).toHaveTextContent("Innstillinger");
expect(document.documentElement.lang).toBe("nb-NO");
fireEvent.click(screen.getByRole("button", { name: "English" }));
expect(screen.getByLabelText("settings-label")).toHaveTextContent("Settings");
await waitFor(() => expect(api.put).toHaveBeenCalledWith("/auth/preferences/language", { language: "en" }));
});
test("hydrates an authenticated server preference without writing it back", () => {
render(<I18nProvider><Probe /></I18nProvider>);
fireEvent.click(screen.getByRole("button", { name: /Restore server preference/i }));
expect(screen.getByLabelText("language")).toHaveTextContent("nb");
expect(window.localStorage.getItem("uiLanguage")).toBe("nb");
expect(api.put).not.toHaveBeenCalled();
});
test.each([
["Norsk", "nb", "nb-NO", "Språket er endret på denne enheten, men kunne ikke lagres på kontoen din.", "Prøv igjen"],
["English", "en", "en", "The language changed on this device, but your account preference could not be saved.", "Retry"],
])("a failed account language save keeps the local choice and offers a localized retry: %s", async (button, local, remote, message, retry) => {
(api.put as jest.Mock).mockRejectedValueOnce({ response: { status: 500, data: "Internal database detail" } });
render(<ToastProvider><I18nProvider><Probe /></I18nProvider></ToastProvider>);
fireEvent.click(screen.getByRole("button", { name: button }));
expect(screen.getByLabelText("language")).toHaveTextContent(local);
expect(window.localStorage.getItem("uiLanguage")).toBe(local);
expect(document.documentElement.lang).toBe(remote);
const alert = await screen.findByRole("alert");
expect(alert).toHaveTextContent(message);
expect(alert).not.toHaveTextContent("Internal database detail");
expect(api.put).toHaveBeenCalledTimes(1);
expect(api.put).toHaveBeenLastCalledWith("/auth/preferences/language", { language: remote });
fireEvent.click(within(alert).getByRole("button", { name: retry }));
await waitFor(() => expect(api.put).toHaveBeenCalledTimes(2));
expect(api.put).toHaveBeenLastCalledWith("/auth/preferences/language", { language: remote });
await waitFor(() => expect(screen.queryByRole("alert")).not.toBeInTheDocument());
expect(screen.getByLabelText("language")).toHaveTextContent(local);
});
test("a delayed language-save failure cannot offer a retry for a newer successful choice", async () => {
let rejectOld!: (error: Error) => void;
(api.put as jest.Mock).mockImplementationOnce(() => new Promise((_, reject) => { rejectOld = reject; }));
render(<ToastProvider><I18nProvider><Probe /></I18nProvider></ToastProvider>);
fireEvent.click(screen.getByRole("button", { name: "Norsk" }));
fireEvent.click(screen.getByRole("button", { name: "English" }));
await act(async () => { rejectOld(new Error("offline")); });
expect(screen.queryByRole("alert")).not.toBeInTheDocument();
expect(screen.getByLabelText("language")).toHaveTextContent("en");
expect(api.put).toHaveBeenCalledTimes(2);
});
test("a previously delivered language retry cannot reverse a newer choice", async () => {
(api.put as jest.Mock).mockRejectedValueOnce(new Error("offline"));
render(<ToastProvider><I18nProvider><Probe /></I18nProvider></ToastProvider>);
fireEvent.click(screen.getByRole("button", { name: "Norsk" }));
const alert = await screen.findByRole("alert");
const retry = within(alert).getByRole("button", { name: "Prøv igjen" });
fireEvent.click(screen.getByRole("button", { name: "English" }));
fireEvent.click(retry);
expect(screen.getByLabelText("language")).toHaveTextContent("en");
expect(window.localStorage.getItem("uiLanguage")).toBe("en");
expect(api.put).toHaveBeenCalledTimes(2);
});