97 lines
4.8 KiB
TypeScript
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);
|
|
});
|