eb90244182
Settings moves from a tab bar to /settings/<section>: a left nav grouped General / Agents / Routing / Git / Projects (a select on narrow screens), a search box over every section (Ctrl/Cmd+F, Enter opens the first match), and row primitives. Each AI tool has its page under agents/<id>, the AI Tools modal becomes the Agents page, Pricing shows every vendor, Git → Worktrees sets which untracked worktrees the sidebar lists, and each project gets a page with its own worktree override and Forget all. ?tool= and #ai-tools links redirect to the path form.
62 lines
2.7 KiB
TypeScript
62 lines
2.7 KiB
TypeScript
/**
|
|
* @file AppearanceSettings.test.tsx
|
|
* @description Tests for the Settings → Appearance section: the mode switch and the per-mode theme cards write through useTheme to `<html>`.
|
|
* @author Nguyễn Ngọc Trí Vĩ <vinnt@smartgift.vn>
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach } from "vitest";
|
|
import { render, screen } from "@testing-library/react";
|
|
import userEvent from "@testing-library/user-event";
|
|
import { AppearanceSettings } from "../AppearanceSettings";
|
|
|
|
const root = document.documentElement;
|
|
|
|
describe("AppearanceSettings", () => {
|
|
beforeEach(() => {
|
|
localStorage.clear();
|
|
root.classList.remove("dark");
|
|
root.removeAttribute("data-theme");
|
|
root.removeAttribute("data-mode");
|
|
});
|
|
|
|
it("renders every theme of both modes, defaults selected", () => {
|
|
render(<AppearanceSettings />);
|
|
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", "Apple Light", "IntelliJ Light", "Canvas"]) {
|
|
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(<AppearanceSettings />);
|
|
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(<AppearanceSettings />);
|
|
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(<AppearanceSettings />);
|
|
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();
|
|
});
|
|
});
|