import { afterEach, beforeEach, describe, expect, it, rs } from "@rstest/core"; import { cleanup, fireEvent, render, screen, waitFor, within, } from "@testing-library/react"; import { useState } from "react"; import { ModelPicker, ModelPickerContent, ModelPickerTrigger, } from "@/components/workspace/model-picker-content"; import { useAuth } from "@/core/auth/AuthProvider"; import { type Model } from "@/core/models/types"; import { useModelFavorites } from "@/core/models/use-model-favorites"; rs.mock("@/core/auth/AuthProvider", () => ({ useAuth: rs.fn(), })); rs.mock("@/core/models/use-model-favorites", () => ({ useModelFavorites: rs.fn(), })); rs.mock("@/core/i18n/hooks", () => ({ useI18n: () => ({ locale: "en-US", changeLocale: rs.fn(), t: { modelPicker: { title: "Choose a model", favorites: "Favorites", otherModels: "Other models", noModels: "No models available", favoriteModel: (displayName: string, name: string) => `Favorite ${displayName} (${name})`, sessionOnly: "Favorites are stored for this session only.", }, }, }), })); const MODELS: readonly Model[] = [ { id: "one", name: "provider/alpha", model: "alpha-api", display_name: "Shared label", description: "Fast general model", }, { id: "two", name: ' provider/"beta" ', model: "beta-api", display_name: "Shared label", description: "Careful reasoning model", }, { id: "three", name: "provider/gamma", model: "gamma-api", display_name: "Gamma", description: null, }, ]; const mockedUseAuth = rs.mocked(useAuth); const mockedUseModelFavorites = rs.mocked(useModelFavorites); const setFavorite = rs.fn(); let authUser: { id: string } | null; let authLoading: boolean; let favoriteNames: readonly string[]; let persistence: "local" | "memory"; function installHookState() { mockedUseAuth.mockImplementation( () => ({ user: authUser, isAuthenticated: authUser !== null, isLoading: authLoading, logout: rs.fn(), refreshUser: rs.fn(), applyUser: rs.fn(), }) as ReturnType, ); mockedUseModelFavorites.mockImplementation((userId) => ({ names: userId === null ? [] : favoriteNames, persistence: userId === null ? "memory" : persistence, canEdit: userId !== null, setFavorite, })); } interface PickerHarnessProps { models?: readonly Model[]; selectedModelName?: string; onModelSelect?: (name: string) => void; initiallyOpen?: boolean; } function StatefulPicker({ models = MODELS, selectedModelName = MODELS[0]?.name, onModelSelect = () => undefined, initiallyOpen = true, }: PickerHarnessProps) { const [open, setOpen] = useState(initiallyOpen); return ( ); } function ControlledPicker({ open, models = MODELS, selectedModelName = MODELS[0]?.name, onModelSelect = () => undefined, }: PickerHarnessProps & { open: boolean }) { return ( ); } function modelButtons() { return Array.from( document.querySelectorAll( 'button[data-model-picker-option="true"]', ), ); } function favoriteButton(model: Model) { return screen.getByRole("button", { name: `Favorite ${model.display_name} (${model.name})`, }); } beforeEach(() => { authUser = { id: "alice" }; authLoading = false; favoriteNames = [MODELS[1]!.name]; persistence = "local"; setFavorite.mockReset(); installHookState(); }); afterEach(() => { cleanup(); rs.restoreAllMocks(); }); describe("ModelPickerContent anchored selection", () => { it("opens without a modal overlay and exposes inline favorite actions", async () => { render(); const dialog = await screen.findByRole("dialog", { name: "Choose a model", }); expect(document.querySelector('[data-slot="dialog-overlay"]')).toBeNull(); expect(dialog.className).toContain("w-72"); expect(screen.queryByRole("searchbox")).toBeNull(); expect(screen.queryByText("Fast general model")).toBeNull(); expect(favoriteButton(MODELS[0]!)).not.toBeNull(); expect( screen.queryByRole("button", { name: "Manage favorites" }), ).toBeNull(); expect(screen.queryByRole("button", { name: "Done" })).toBeNull(); }); it("renders favorites first and preserves API order inside each group", async () => { render(); const favorites = await screen.findByRole("group", { name: "Favorites" }); const others = screen.getByRole("group", { name: "Other models" }); expect( within(favorites).getByText("beta-api").closest("li"), ).not.toBeNull(); expect( within(others) .getAllByRole("listitem") .map((row) => row.textContent), ).toEqual([ expect.stringContaining("alpha-api"), expect.stringContaining("gamma-api"), ]); }); it("omits the empty favorites heading without filtering the model list", async () => { favoriteNames = []; render(); await screen.findByRole("dialog"); expect(screen.queryByText("Favorites")).toBeNull(); expect(screen.getByText("Other models")).not.toBeNull(); expect(modelButtons()).toHaveLength(MODELS.length); }); it("shows the no-model state", async () => { render(); expect(await screen.findByText("No models available")).not.toBeNull(); }); it("selects duplicate-label models by their untouched names", async () => { const onModelSelect = rs.fn(); render(); await screen.findByRole("dialog"); const shared = modelButtons().filter((button) => button.textContent?.includes("Shared label"), ); fireEvent.click(shared[0]!); fireEvent.click(shared[1]!); expect(onModelSelect.mock.calls).toEqual([ [MODELS[1]!.name], [MODELS[0]!.name], ]); }); it("marks the current model without nesting the favorite button", async () => { render(); const current = await screen.findByRole("button", { name: `Shared label (${MODELS[1]!.name})`, }); expect(current.getAttribute("aria-current")).toBe("true"); expect(current.getAttribute("data-current-model")).toBe("true"); expect(within(current).queryByRole("button")).toBeNull(); expect(current.parentElement?.contains(favoriteButton(MODELS[1]!))).toBe( true, ); }); it("focuses the current model and moves between rows with arrows", async () => { render(); const current = await screen.findByRole("button", { name: `Shared label (${MODELS[0]!.name})`, }); const gamma = screen.getByRole("button", { name: `Gamma (${MODELS[2]!.name})`, }); await waitFor(() => expect(document.activeElement).toBe(current)); fireEvent.keyDown(current, { key: "ArrowDown" }); expect(document.activeElement).toBe(gamma); fireEvent.keyDown(gamma, { key: "ArrowUp" }); expect(document.activeElement).toBe(current); }); it("moves from favorite stars to adjacent model rows with arrows", async () => { render(); const betaStar = await screen.findByRole("button", { name: `Favorite ${MODELS[1]!.display_name} (${MODELS[1]!.name})`, }); const alpha = screen.getByRole("button", { name: `Shared label (${MODELS[0]!.name})`, }); const gamma = screen.getByRole("button", { name: `Gamma (${MODELS[2]!.name})`, }); betaStar.focus(); fireEvent.keyDown(betaStar, { key: "ArrowDown" }); expect(document.activeElement).toBe(alpha); betaStar.focus(); fireEvent.keyDown(betaStar, { key: "ArrowUp" }); expect(document.activeElement).toBe(gamma); }); }); describe("ModelPickerContent favorite actions", () => { it("updates a favorite without selecting a model or closing the picker", async () => { const onModelSelect = rs.fn(); render(); const betaStar = await screen.findByRole("button", { name: `Favorite ${MODELS[1]!.display_name} (${MODELS[1]!.name})`, }); expect(betaStar.getAttribute("aria-pressed")).toBe("true"); expect(betaStar.className).toContain("size-8"); fireEvent.click(betaStar); expect(setFavorite).toHaveBeenCalledWith(MODELS[1]!.name, false); expect(onModelSelect).not.toHaveBeenCalled(); expect(screen.getByRole("dialog")).not.toBeNull(); }); it("restores focus to the same star after favorite regrouping", async () => { favoriteNames = []; const { rerender } = render(); const betaStar = await screen.findByRole("button", { name: `Favorite ${MODELS[1]!.display_name} (${MODELS[1]!.name})`, }); betaStar.focus(); fireEvent.click(betaStar); favoriteNames = [MODELS[1]!.name]; rerender(); await waitFor(() => expect(document.activeElement).toBe(favoriteButton(MODELS[1]!)), ); }); it("preserves the user's focus during an external favorite regroup", async () => { favoriteNames = []; const { rerender } = render( , ); const beta = await screen.findByRole("button", { name: `Shared label (${MODELS[1]!.name})`, }); beta.focus(); expect(document.activeElement).toBe(beta); favoriteNames = [MODELS[1]!.name]; rerender(); await waitFor(() => expect(document.activeElement).toBe( screen.getByRole("button", { name: `Shared label (${MODELS[1]!.name})`, }), ), ); favoriteButton(MODELS[1]!).focus(); favoriteNames = []; rerender(); await waitFor(() => expect(document.activeElement).toBe(favoriteButton(MODELS[1]!)), ); }); it("hides stars when signed out and disables them during auth refresh", async () => { authUser = null; const { rerender } = render(); await screen.findByRole("dialog"); expect( screen.queryByRole("button", { name: /Favorite Shared label/ }), ).toBeNull(); authUser = { id: "alice" }; authLoading = true; rerender(); expect(favoriteButton(MODELS[0]!).hasAttribute("disabled")).toBe(true); }); it("revalidates loading and a same-reference catalog in the latest handler", async () => { const mutableModels = [...MODELS]; const { rerender } = render( , ); const alphaStar = await screen.findByRole("button", { name: `Favorite ${MODELS[0]!.display_name} (${MODELS[0]!.name})`, }); authLoading = true; rerender(); alphaStar.removeAttribute("disabled"); fireEvent.click(alphaStar); expect(setFavorite).not.toHaveBeenCalled(); authLoading = false; rerender(); const connectedStar = favoriteButton(MODELS[0]!); mutableModels.splice(0, 1); fireEvent.click(connectedStar); expect(setFavorite).not.toHaveBeenCalled(); }); it("only announces persistence when storage falls back to memory", async () => { const { rerender } = render(); await screen.findByRole("dialog"); expect(screen.queryByRole("status")).toBeNull(); persistence = "memory"; rerender(); expect(screen.getByRole("status").textContent).toBe( "Favorites are stored for this session only.", ); }); }); describe("ModelPickerContent popover lifecycle", () => { it("focuses the current model on every closed-to-open edge", async () => { const { rerender } = render( , ); const current = await screen.findByRole("button", { name: `Gamma (${MODELS[2]!.name})`, }); await waitFor(() => expect(document.activeElement).toBe(current)); rerender( , ); screen.getByRole("button", { name: "Current model" }).focus(); rerender(); await waitFor(() => expect(document.activeElement).toBe( screen.getByRole("button", { name: `Gamma (${MODELS[2]!.name})`, }), ), ); }); it("closes on Escape and returns focus to the trigger", async () => { render(); await screen.findByRole("dialog"); fireEvent.keyDown(document, { key: "Escape" }); await waitFor(() => expect(screen.queryByRole("dialog")).toBeNull()); expect(document.activeElement).toBe( screen.getByRole("button", { name: "Current model" }), ); }); });