Files
fusion/packages/dashboard/app/hooks/__tests__/useFavorites.test.ts
gsxdsm 321ec691bd feat(FN-1203): extract App orchestration into focused hooks
- Extract modal manager, app settings, deep-link handling, favorites, and auth onboarding into dedicated hooks
- Rewire App.tsx to consume the new hooks and reduce component-level state/effect complexity
- Preserve favorite-toggle error toast behavior in the updated favorites wiring
- Add hook-level test coverage for modal manager, app settings, deep links, favorites, and auth onboarding flows
2026-04-08 05:53:24 -07:00

97 lines
2.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest";
import { act, renderHook, waitFor } from "@testing-library/react";
import { useFavorites } from "../useFavorites";
import * as api from "../../api";
vi.mock("../../api", () => ({
fetchModels: vi.fn(),
updateGlobalSettings: vi.fn(),
}));
const mockFetchModels = vi.mocked(api.fetchModels);
const mockUpdateGlobalSettings = vi.mocked(api.updateGlobalSettings);
describe("useFavorites", () => {
beforeEach(() => {
vi.clearAllMocks();
mockFetchModels.mockResolvedValue({
models: [
{
provider: "anthropic",
id: "claude-sonnet-4-5",
name: "Claude Sonnet 4.5",
reasoning: true,
contextWindow: 200000,
},
],
favoriteProviders: ["openai"],
favoriteModels: ["gpt-4o"],
});
mockUpdateGlobalSettings.mockResolvedValue({} as never);
});
it("loads available models and favorites from API", async () => {
const { result } = renderHook(() => useFavorites());
await waitFor(() => {
expect(result.current.availableModels).toHaveLength(1);
expect(result.current.favoriteProviders).toEqual(["openai"]);
expect(result.current.favoriteModels).toEqual(["gpt-4o"]);
});
expect(mockFetchModels).toHaveBeenCalledTimes(1);
});
it("optimistically toggles provider favorite and persists settings", async () => {
const { result } = renderHook(() => useFavorites());
await waitFor(() => {
expect(result.current.favoriteProviders).toEqual(["openai"]);
});
await act(async () => {
await result.current.toggleFavoriteProvider("anthropic");
});
expect(result.current.favoriteProviders).toEqual(["anthropic", "openai"]);
expect(mockUpdateGlobalSettings).toHaveBeenCalledWith({
favoriteProviders: ["anthropic", "openai"],
favoriteModels: ["gpt-4o"],
});
});
it("rolls back provider favorite on update failure", async () => {
mockUpdateGlobalSettings.mockRejectedValueOnce(new Error("network"));
const { result } = renderHook(() => useFavorites());
await waitFor(() => {
expect(result.current.favoriteProviders).toEqual(["openai"]);
});
await act(async () => {
await expect(result.current.toggleFavoriteProvider("anthropic")).rejects.toThrow("network");
});
expect(result.current.favoriteProviders).toEqual(["openai"]);
});
it("rolls back model favorite on update failure", async () => {
mockUpdateGlobalSettings.mockRejectedValueOnce(new Error("network"));
const { result } = renderHook(() => useFavorites());
await waitFor(() => {
expect(result.current.favoriteModels).toEqual(["gpt-4o"]);
});
await act(async () => {
await expect(result.current.toggleFavoriteModel("claude-sonnet-4-5")).rejects.toThrow("network");
});
expect(result.current.favoriteModels).toEqual(["gpt-4o"]);
});
});