feat(FN-2076): merge fusion/fn-2076

This commit is contained in:
gsxdsm
2026-04-18 20:16:03 -07:00
parent ba58095165
commit 77fa635345
2 changed files with 78 additions and 17 deletions

View File

@@ -1,5 +1,5 @@
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen, waitFor, fireEvent, act } from "@testing-library/react";
import { render, screen, waitFor, fireEvent, act, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import type { PlanningQuestion } from "@fusion/core";
import { MissionInterviewModal } from "../MissionInterviewModal";
@@ -90,6 +90,17 @@ describe("MissionInterviewModal", () => {
streamHandlers = undefined;
vi.spyOn(window, "confirm").mockReturnValue(true);
Object.defineProperty(window, "matchMedia", {
writable: true,
value: vi.fn().mockImplementation((query: string) => ({
matches: query === "(prefers-color-scheme: dark)",
media: query,
onchange: null,
addEventListener: vi.fn(),
removeEventListener: vi.fn(),
dispatchEvent: vi.fn(),
})),
});
mockStartMissionInterview.mockResolvedValue({ sessionId: "mission-session-1" });
mockRespondToMissionInterview.mockResolvedValue({ type: "question", data: sampleQuestionSingle });
@@ -482,11 +493,8 @@ describe("MissionInterviewModal", () => {
});
});
it.skip("rolls back local state on updateGlobalSettings failure", async () => {
// This test is skipped because the component does not automatically call updateGlobalSettings
// during render - it only calls it when the user interacts with the favorite toggles.
// The test was incorrectly written expecting automatic behavior that doesn't exist.
mockUpdateGlobalSettings.mockRejectedValueOnce(new Error("Network error"));
it("rolls back local favorite state when updateGlobalSettings fails", async () => {
vi.mocked(api.updateGlobalSettings).mockRejectedValueOnce(new Error("Network error"));
renderModal();
@@ -494,9 +502,21 @@ describe("MissionInterviewModal", () => {
expect(mockFetchModels).toHaveBeenCalled();
});
// The toggle should have been attempted
// After error, local state should be rolled back (verifiable by checking state doesn't include the failed toggle)
expect(mockUpdateGlobalSettings).toHaveBeenCalled();
fireEvent.click(screen.getByRole("button", { name: "Planning Model" }));
await waitFor(() => {
expect(document.body.querySelector('[data-testid="model-combobox-portal"]')).not.toBeNull();
});
const portal = document.body.querySelector('[data-testid="model-combobox-portal"]')!;
fireEvent.click(within(portal).getByRole("button", { name: "Remove anthropic from favorites" }));
await waitFor(() => {
expect(api.updateGlobalSettings).toHaveBeenCalled();
});
const portalAfterRollback = document.body.querySelector('[data-testid="model-combobox-portal"]')!;
expect(within(portalAfterRollback).getByRole("button", { name: "Remove anthropic from favorites" })).toBeTruthy();
});
});
});

View File

@@ -26,7 +26,23 @@ vi.mock("../SkillMultiselect", () => ({
// Mock CustomModelDropdown to simplify interaction testing
vi.mock("../CustomModelDropdown", () => ({
CustomModelDropdown: ({ value, onChange, label }: { value: string; onChange: (v: string) => void; label: string }) => (
CustomModelDropdown: ({
value,
onChange,
label,
favoriteProviders = [],
favoriteModels = [],
onToggleFavorite,
onToggleModelFavorite,
}: {
value: string;
onChange: (v: string) => void;
label: string;
favoriteProviders?: string[];
favoriteModels?: string[];
onToggleFavorite?: (provider: string) => void;
onToggleModelFavorite?: (modelId: string) => void;
}) => (
<div data-testid="custom-model-dropdown">
<span data-testid="dropdown-label">{label}</span>
<span data-testid="dropdown-value">{value}</span>
@@ -42,6 +58,24 @@ vi.mock("../CustomModelDropdown", () => ({
>
Use default
</button>
{favoriteProviders.map((provider) => (
<button
key={`fav-provider-${provider}`}
aria-label={`Remove ${provider} from favorites`}
onClick={() => onToggleFavorite?.(provider)}
>
{provider}
</button>
))}
{favoriteModels.map((model) => (
<button
key={`fav-model-${model}`}
aria-label={`Remove ${model} from favorites`}
onClick={() => onToggleModelFavorite?.(model)}
>
{model}
</button>
))}
</div>
),
}));
@@ -1138,11 +1172,8 @@ describe("NewAgentDialog", () => {
});
});
it.skip("rolls back local state on updateGlobalSettings failure", async () => {
// This test is skipped because the component does not automatically call updateGlobalSettings
// during render - it only calls it when the user interacts with the favorite toggles.
// The test was incorrectly written expecting automatic behavior that doesn't exist.
mockUpdateGlobalSettings.mockRejectedValueOnce(new Error("Network error"));
it("rolls back local favorite state when updateGlobalSettings fails", async () => {
vi.mocked(apiModule.updateGlobalSettings).mockRejectedValueOnce(new Error("Network error"));
render(
<NewAgentDialog isOpen={true} onClose={mockOnClose} onCreated={mockOnCreated} />,
@@ -1150,8 +1181,18 @@ describe("NewAgentDialog", () => {
await waitFor(() => expect(mockFetchModels).toHaveBeenCalledOnce());
// The toggle should have been attempted
expect(mockUpdateGlobalSettings).toHaveBeenCalled();
const user = userEvent.setup();
const nameInput = screen.getByLabelText(/Name/);
await user.type(nameInput, "Test Agent");
await user.click(screen.getByText("Next"));
fireEvent.click(screen.getByRole("button", { name: "Remove anthropic from favorites" }));
await waitFor(() => {
expect(apiModule.updateGlobalSettings).toHaveBeenCalled();
});
expect(screen.getByRole("button", { name: "Remove anthropic from favorites" })).toBeTruthy();
});
});