Merge branch 'fix/progress-panel-sse-listeners' into dev

This commit is contained in:
Developer
2026-06-13 09:30:45 +00:00
2 changed files with 32 additions and 6 deletions
+16 -5
View File
@@ -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(() => {
+16 -1
View File
@@ -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);