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

This commit is contained in:
gsxdsm
2026-04-16 08:53:06 -07:00
parent e0f1ec5bd7
commit caa03ad752
2 changed files with 54 additions and 0 deletions

View File

@@ -783,6 +783,24 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
const handleMissionUpdated = (rawEvent: Event) => {
refreshHealth();
// Update mission status in the list to keep badges in sync
const messageEvent = rawEvent as MessageEvent<string>;
if (messageEvent.data) {
try {
const updatedMission = JSON.parse(messageEvent.data);
if (updatedMission?.id) {
setMissions((prev) =>
prev.map((m) =>
m.id === updatedMission.id ? { ...m, ...updatedMission } : m
)
);
}
} catch {
// ignore invalid payloads
}
}
// Reload the selected mission detail to reflect updated mission state (autopilot, status, etc.)
if (selectedMission) {
void loadMissionDetail(selectedMission.id);

View File

@@ -911,6 +911,42 @@ describe("MissionManager", () => {
});
});
it("updates mission status badge when mission:updated SSE event arrives", async () => {
globalThis.fetch = createFetchMock();
globalThis.EventSource = MockEventSource as unknown as typeof globalThis.EventSource;
render(<MissionManager isOpen={true} onClose={vi.fn()} addToast={vi.fn()} />);
// Wait for initial render — M-001 has status "planning"
await waitFor(() => {
expect(screen.getByText("Build Auth System")).toBeDefined();
});
// Verify initial status badge shows "planning"
const missionItem = screen.getByText("Build Auth System").closest(".mission-list__item");
expect(missionItem).toBeDefined();
const planningBadges = missionItem!.querySelectorAll(".mission-status-badge");
expect([...planningBadges].some((b) => b.textContent === "planning")).toBe(true);
// Emit mission:updated SSE event changing M-001 to "active"
await act(async () => {
for (const source of MockEventSource.instances) {
source.emit("mission:updated", {
id: "M-001",
title: "Build Auth System",
status: "active",
});
}
});
// Verify the badge now shows "active" instead of "planning"
await waitFor(() => {
const updatedBadges = missionItem!.querySelectorAll(".mission-status-badge");
expect([...updatedBadges].some((b) => b.textContent === "active")).toBe(true);
expect([...updatedBadges].some((b) => b.textContent === "planning")).toBe(false);
});
});
it("reloads selected mission detail when slice:updated SSE event arrives", async () => {
const fetchMock = createDetailFetchMock(mockMissionEvents);
globalThis.fetch = fetchMock;