test(FN-4306): complete Step 4 — add capacity banner gating coverage
Fusion-Task-Id: FN-4306 Fusion-Task-Lineage: b0ab9b60-de02-40a7-ad8b-0ca18c98e16c
This commit is contained in:
@@ -23,10 +23,19 @@ const defaultSettings: Settings = {
|
||||
worktreeInitCommand: "",
|
||||
testCommand: "",
|
||||
buildCommand: "",
|
||||
capacityRiskBannerEnabled: false,
|
||||
capacityRiskTodoThreshold: 20,
|
||||
experimentalFeatures: { insights: true, skillsView: true, agentsView: true, memoryView: true, evalsView: true },
|
||||
};
|
||||
|
||||
|
||||
const mockAgentStats = {
|
||||
activeCount: 0,
|
||||
busyCount: 0,
|
||||
pausedCount: 0,
|
||||
todoTaskCount: 0,
|
||||
idleNonEphemeralCount: 1,
|
||||
};
|
||||
|
||||
const mockSubscribeSse = vi.fn((..._args: any[]) => vi.fn());
|
||||
|
||||
vi.mock("../../sse-bus", () => ({
|
||||
@@ -55,6 +64,7 @@ vi.mock("../../api", async (importOriginal) => {
|
||||
fetchModels: vi.fn(() => Promise.resolve({ models: [], favoriteProviders: [], favoriteModels: [] })),
|
||||
fetchGitRemotes: vi.fn(() => Promise.resolve([])),
|
||||
fetchAgents: vi.fn(() => Promise.resolve([])),
|
||||
fetchAgentStats: vi.fn(() => Promise.resolve({ ...mockAgentStats })),
|
||||
fetchTaskDetail: vi.fn((id: string) => Promise.resolve({ id, title: `Task ${id}` })),
|
||||
fetchUnreadCount: vi.fn(() => Promise.resolve({ unreadCount: 0 })),
|
||||
fetchDashboardHealth: vi.fn(() => Promise.resolve({
|
||||
@@ -660,6 +670,8 @@ beforeEach(() => {
|
||||
});
|
||||
mockUseViewportMode.mockReset();
|
||||
mockUseViewportMode.mockReturnValue("desktop");
|
||||
mockAgentStats.todoTaskCount = 0;
|
||||
mockAgentStats.idleNonEphemeralCount = 1;
|
||||
});
|
||||
|
||||
describe("FN-4250 FileBrowserProvider coverage", () => {
|
||||
@@ -685,6 +697,45 @@ describe("FN-4250 FileBrowserProvider coverage", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Capacity risk banner gating", () => {
|
||||
it("does not render banner when capacityRiskBannerEnabled is false", async () => {
|
||||
mockAgentStats.todoTaskCount = 21;
|
||||
mockAgentStats.idleNonEphemeralCount = 0;
|
||||
vi.mocked(fetchSettings).mockResolvedValueOnce({
|
||||
...defaultSettings,
|
||||
capacityRiskBannerEnabled: false,
|
||||
capacityRiskTodoThreshold: 20,
|
||||
});
|
||||
|
||||
render(<App />);
|
||||
await waitForAppShell();
|
||||
|
||||
expect(screen.queryByText(/Capacity risk:/i)).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("renders and dismisses banner when enabled", async () => {
|
||||
mockAgentStats.todoTaskCount = 21;
|
||||
mockAgentStats.idleNonEphemeralCount = 0;
|
||||
vi.mocked(fetchSettings).mockResolvedValueOnce({
|
||||
...defaultSettings,
|
||||
capacityRiskBannerEnabled: true,
|
||||
capacityRiskTodoThreshold: 20,
|
||||
});
|
||||
|
||||
render(<App />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Capacity risk:/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /dismiss capacity warning/i }));
|
||||
|
||||
expect(localStorage.getItem(scopedKey("kb-capacity-risk-banner-dismissed", DEFAULT_PROJECT_ID))).toBe("true");
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText(/Capacity risk:/i)).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("App backend-unreachable first-run flow", () => {
|
||||
it("renders backend connection error page instead of setup wizard when projects fetch fails during first-run", async () => {
|
||||
mockProjectsState.projects = [];
|
||||
|
||||
@@ -30,6 +30,7 @@ describe("useAppSettings", () => {
|
||||
taskStuckTimeoutMs: 600000,
|
||||
staleHighFanoutBlockerAgeThresholdMs: 7200000,
|
||||
showQuickChatFAB: false,
|
||||
capacityRiskBannerEnabled: false,
|
||||
} as never);
|
||||
|
||||
mockUpdateSettings.mockResolvedValue({} as never);
|
||||
@@ -49,6 +50,8 @@ describe("useAppSettings", () => {
|
||||
expect(result.current.taskStuckTimeoutMs).toBe(600000);
|
||||
expect(result.current.staleHighFanoutBlockerAgeThresholdMs).toBe(7200000);
|
||||
expect(result.current.showQuickChatFAB).toBe(false);
|
||||
expect(result.current.capacityRiskBannerEnabled).toBe(false);
|
||||
expect(result.current.capacityRiskTodoThreshold).toBe(20);
|
||||
});
|
||||
|
||||
expect(mockFetchConfig).toHaveBeenCalledWith("proj_123");
|
||||
@@ -117,6 +120,26 @@ describe("useAppSettings", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("propagates capacity risk settings from fetchSettings", async () => {
|
||||
mockFetchSettings.mockResolvedValueOnce({
|
||||
autoMerge: false,
|
||||
globalPause: true,
|
||||
enginePaused: false,
|
||||
prAuthAvailable: true,
|
||||
taskStuckTimeoutMs: 600000,
|
||||
showQuickChatFAB: false,
|
||||
capacityRiskBannerEnabled: true,
|
||||
capacityRiskTodoThreshold: 30,
|
||||
} as never);
|
||||
|
||||
const { result } = renderHook(() => useAppSettings("proj_123"));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.capacityRiskBannerEnabled).toBe(true);
|
||||
expect(result.current.capacityRiskTodoThreshold).toBe(30);
|
||||
});
|
||||
});
|
||||
|
||||
it("refresh() re-fetches and updates state", async () => {
|
||||
const { result } = renderHook(() => useAppSettings("proj_123"));
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ export const PROJECT_STORAGE_KEYS: string[] = [
|
||||
"kb-chat-active-session",
|
||||
"kb-dashboard-working-branch-filter",
|
||||
"kb-dashboard-base-branch-filter",
|
||||
"kb-capacity-risk-banner-dismissed",
|
||||
"kb-files-line-numbers",
|
||||
"fusion-plugin-dependency-graph:positions",
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user