feat(FN-1906): merge fusion/fn-1906
This commit is contained in:
@@ -783,6 +783,24 @@ export function MissionManager({ isOpen, isInline = false, onClose, addToast, pr
|
|||||||
|
|
||||||
const handleMissionUpdated = (rawEvent: Event) => {
|
const handleMissionUpdated = (rawEvent: Event) => {
|
||||||
refreshHealth();
|
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.)
|
// Reload the selected mission detail to reflect updated mission state (autopilot, status, etc.)
|
||||||
if (selectedMission) {
|
if (selectedMission) {
|
||||||
void loadMissionDetail(selectedMission.id);
|
void loadMissionDetail(selectedMission.id);
|
||||||
|
|||||||
@@ -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 () => {
|
it("reloads selected mission detail when slice:updated SSE event arrives", async () => {
|
||||||
const fetchMock = createDetailFetchMock(mockMissionEvents);
|
const fetchMock = createDetailFetchMock(mockMissionEvents);
|
||||||
globalThis.fetch = fetchMock;
|
globalThis.fetch = fetchMock;
|
||||||
|
|||||||
Reference in New Issue
Block a user