/** * @file AppearanceSettings.test.tsx * @description Tests for the Settings → Appearance section: the language switch, and the mode switch and the per-mode theme cards writing through useTheme to ``. * @author Nguyễn Ngọc Trí Vĩ */ import { describe, it, expect, beforeEach, vi } from "vitest"; import { act, render, screen, within } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import i18n from "../../../i18n"; import { AppearanceSettings } from "../AppearanceSettings"; vi.mock("../PetSettings", () => ({ PetSettings: () =>
})); const root = document.documentElement; describe("AppearanceSettings", () => { beforeEach(() => { localStorage.clear(); root.classList.remove("dark"); root.removeAttribute("data-theme"); root.removeAttribute("data-mode"); }); it("switches the interface language and holds the pet block", async () => { const user = userEvent.setup(); render(); expect(screen.getByTestId("pet-settings").closest("section")).toHaveAttribute("id", "pet"); const group = screen.getByRole("radiogroup", { name: "Interface language" }); expect(within(group).getByRole("radio", { name: "English" })).toHaveAttribute( "aria-checked", "true" ); await user.click(within(group).getByRole("radio", { name: "Tiếng Việt" })); expect(i18n.language).toBe("vi"); await act(async () => { await i18n.changeLanguage("en"); }); }); it("renders every theme of both modes, defaults selected", () => { render(); const pressed = screen .getAllByRole("button", { pressed: true }) .map((b) => b.getAttribute("aria-label") ?? b.textContent); expect(pressed).toHaveLength(2); // both Default cards expect(screen.getByRole("radio", { name: /Dark/ })).toHaveAttribute("aria-checked", "true"); for (const name of [ "Darcula", "Apple Dark", "Orca Dark", "Apple Light", "IntelliJ Light", "Canvas", "Orca Light", ]) { expect(screen.getByText(name)).toBeInTheDocument(); } }); it("picking a dark theme applies it at once; a light pick waits for light mode", async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole("button", { name: /Darcula/ })); expect(root.dataset.theme).toBe("darcula"); await user.click(screen.getByRole("button", { name: /Canvas/ })); expect(root.dataset.theme).toBe("darcula"); await user.click(screen.getByRole("radio", { name: /^Light$/ })); expect(root.classList.contains("dark")).toBe(false); expect(root.dataset.theme).toBe("canvas"); expect(screen.getByRole("button", { name: /Canvas/ })).toHaveAttribute("aria-pressed", "true"); }); it("the System mode is checked after choosing it", async () => { const user = userEvent.setup(); render(); await user.click(screen.getByRole("radio", { name: /System/ })); expect(screen.getByRole("radio", { name: /System/ })).toHaveAttribute("aria-checked", "true"); expect(localStorage.getItem("theme")).toBe("system"); }); it("each swatch scopes its own mode and theme", () => { const { container } = render(); expect(container.querySelector('.dark[data-theme="darcula"]')).not.toBeNull(); expect(container.querySelector('[data-mode="light"][data-theme="canvas"]')).not.toBeNull(); expect(container.querySelector('[data-mode="light"]:not([data-theme])')).not.toBeNull(); }); });