fix: listen for named SSE events in progress panel
The backend sends named lifecycle events (event: instance.health_changed), but useEvents only set es.onmessage, which only receives unnamed message events. Add explicit addEventListener registrations for all lifecycle event types so the progress panel receives updates and completes. Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed).
This commit is contained in:
@@ -14,10 +14,13 @@ const mockedCreateEventSource = vi.mocked(createEventSource);
|
||||
const mockedProbeEventStreamStatus = vi.mocked(probeEventStreamStatus);
|
||||
|
||||
describe("useEvents", () => {
|
||||
let mockEs: EventSource;
|
||||
let mockEs: EventSource & {
|
||||
emit: (type: string, data: string) => void;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true });
|
||||
const listeners = new Map<string, Set<(e: MessageEvent) => void>>();
|
||||
mockEs = {
|
||||
close: vi.fn(),
|
||||
onopen: null,
|
||||
@@ -27,7 +30,17 @@ describe("useEvents", () => {
|
||||
return EventSource.OPEN;
|
||||
},
|
||||
url: "http://localhost:8000/events/stream",
|
||||
} as unknown as EventSource;
|
||||
addEventListener: vi.fn((type: string, handler: (e: MessageEvent) => void) => {
|
||||
if (!listeners.has(type)) listeners.set(type, new Set());
|
||||
listeners.get(type)!.add(handler);
|
||||
}),
|
||||
removeEventListener: vi.fn((type: string, handler: (e: MessageEvent) => void) => {
|
||||
listeners.get(type)?.delete(handler);
|
||||
}),
|
||||
emit: (type: string, data: string) => {
|
||||
listeners.get(type)?.forEach((handler) => handler({ data } as MessageEvent));
|
||||
},
|
||||
} as unknown as EventSource & { emit: (type: string, data: string) => void };
|
||||
mockedCreateEventSource.mockReturnValue(mockEs);
|
||||
mockedProbeEventStreamStatus.mockResolvedValue(null);
|
||||
});
|
||||
@@ -56,9 +69,7 @@ describe("useEvents", () => {
|
||||
};
|
||||
|
||||
act(() => {
|
||||
mockEs.onmessage?.({
|
||||
data: JSON.stringify(payload),
|
||||
} as MessageEvent);
|
||||
mockEs.emit("instance.started", JSON.stringify(payload));
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
|
||||
@@ -12,6 +12,16 @@ export interface UseEventsReturn {
|
||||
const MAX_DELAY = 30000;
|
||||
const BASE_DELAY = 1000;
|
||||
|
||||
const LIFECYCLE_EVENT_TYPES = [
|
||||
"instance.created",
|
||||
"instance.started",
|
||||
"instance.stopped",
|
||||
"instance.restarted",
|
||||
"instance.deleted",
|
||||
"instance.error",
|
||||
"instance.health_changed",
|
||||
] as const;
|
||||
|
||||
export function useEvents(): UseEventsReturn {
|
||||
const [events, setEvents] = useState<InstanceEventPayload[]>([]);
|
||||
const [connected, setConnected] = useState(false);
|
||||
@@ -39,7 +49,7 @@ export function useEvents(): UseEventsReturn {
|
||||
setReconnectCount(0);
|
||||
};
|
||||
|
||||
es.onmessage = (e) => {
|
||||
const handleEventMessage = (e: MessageEvent) => {
|
||||
if (!isMountedRef.current) return;
|
||||
try {
|
||||
const payload: InstanceEventPayload = JSON.parse(e.data);
|
||||
@@ -49,6 +59,11 @@ export function useEvents(): UseEventsReturn {
|
||||
}
|
||||
};
|
||||
|
||||
es.addEventListener("message", handleEventMessage);
|
||||
for (const eventType of LIFECYCLE_EVENT_TYPES) {
|
||||
es.addEventListener(eventType, handleEventMessage);
|
||||
}
|
||||
|
||||
es.onerror = () => {
|
||||
if (!isMountedRef.current) return;
|
||||
setConnected(false);
|
||||
|
||||
Reference in New Issue
Block a user