fix(FN-2198): sync agent heartbeat config UI with live runtime updates
- Refresh AgentDetailView on agent:updated SSE events while preserving local unsaved config edits - Resync heartbeat and budget form state from latest runtime config when agent data changes - Centralize heartbeat interval defaults/formatting in shared utilities and reuse them in AgentsView selectors - Add dashboard tests for default heartbeat hints, unset runtime fallback behavior, and custom interval options
This commit is contained in:
@@ -4,6 +4,7 @@ import userEvent from "@testing-library/user-event";
|
||||
import "@testing-library/jest-dom";
|
||||
import { AgentDetailView } from "../AgentDetailView";
|
||||
import type { AgentCapability, AgentDetail } from "../../api";
|
||||
import { DEFAULT_HEARTBEAT_INTERVAL_MS } from "../../utils/heartbeatIntervals";
|
||||
|
||||
// Mock the API functions
|
||||
vi.mock("../../api", () => ({
|
||||
@@ -1122,6 +1123,27 @@ describe("AgentDetailView", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("shows shared system default hint for heartbeat interval", async () => {
|
||||
mockFetchAgent.mockResolvedValue(createMockAgent({ metadata: {} }));
|
||||
|
||||
const user = userEvent.setup();
|
||||
render(
|
||||
<AgentDetailView
|
||||
agentId="agent-001"
|
||||
onClose={vi.fn()}
|
||||
addToast={vi.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
await navigateToSettings(user);
|
||||
|
||||
const heartbeatInput = await screen.findByLabelText("Heartbeat Interval (ms)");
|
||||
expect(heartbeatInput).toHaveAttribute("placeholder", String(DEFAULT_HEARTBEAT_INTERVAL_MS));
|
||||
expect(
|
||||
screen.getByText(`How often heartbeats are checked. Leave empty for system default (${DEFAULT_HEARTBEAT_INTERVAL_MS}ms / 1h).`),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("pre-fills heartbeat fields from agent runtimeConfig", async () => {
|
||||
mockFetchAgent.mockResolvedValue(createMockAgent({
|
||||
runtimeConfig: {
|
||||
|
||||
@@ -181,6 +181,25 @@ describe("AgentsView", () => {
|
||||
expect(screen.getByDisplayValue("30s")).toBeTruthy();
|
||||
});
|
||||
|
||||
it("uses the system default heartbeat interval when runtime config is unset", async () => {
|
||||
mockFetchAgents.mockResolvedValue([
|
||||
{
|
||||
...mockAgents[1],
|
||||
runtimeConfig: {},
|
||||
},
|
||||
]);
|
||||
|
||||
render(<AgentsView addToast={mockAddToast} />);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByLabelText("Set heartbeat interval for Test Agent 2")).toBeTruthy();
|
||||
});
|
||||
|
||||
const intervalSelect = screen.getByLabelText("Set heartbeat interval for Test Agent 2") as HTMLSelectElement;
|
||||
expect(intervalSelect.value).toBe("3600000");
|
||||
expect(intervalSelect.options[intervalSelect.selectedIndex]?.text).toBe("1h");
|
||||
});
|
||||
|
||||
it("updates agent heartbeat interval from preset dropdown", async () => {
|
||||
render(<AgentsView addToast={mockAddToast} />);
|
||||
|
||||
@@ -202,7 +221,7 @@ describe("AgentsView", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("maps non-preset heartbeat interval to closest preset", async () => {
|
||||
it("shows a custom heartbeat option when configured interval is not a preset", async () => {
|
||||
mockFetchAgents.mockResolvedValue([
|
||||
{
|
||||
...mockAgents[1],
|
||||
@@ -217,8 +236,9 @@ describe("AgentsView", () => {
|
||||
});
|
||||
|
||||
const intervalSelect = screen.getByLabelText("Set heartbeat interval for Test Agent 2") as HTMLSelectElement;
|
||||
expect(intervalSelect.value).toBe("60000");
|
||||
expect(screen.getAllByText("1m").length).toBeGreaterThan(0);
|
||||
expect(intervalSelect.value).toBe("65000");
|
||||
expect(intervalSelect.options[intervalSelect.selectedIndex]?.text).toBe("1m (custom)");
|
||||
expect(screen.getByRole("option", { name: "1m (custom)" })).toBeTruthy();
|
||||
});
|
||||
|
||||
it("shows refresh button", async () => {
|
||||
|
||||
Reference in New Issue
Block a user