Files
EvoScientist-WebUI/src/app/hooks/useThreads.running.test.ts
T
m4 cbc343963c feat(webui): show a running indicator on busy threads in the list
Three mechanisms, merged in ThreadList: a 10s heartbeat polling the new
?status=busy filter (out-of-band runs: scheduled tasks, other clients),
a module-level running store fed by useChat's own run lifecycle (zero
latency for this client's runs), and a spinner replacing the status dot
while either source marks the thread busy. The BFF list route now
validates and forwards the status filter, which also unbreaks the
previously ignored status dropdown.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-31 09:57:08 +08:00

39 lines
1.2 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
getRunningThreadIds,
setThreadRunning,
subscribeRunningThreads,
} from "./useThreads";
describe("running thread store", () => {
it("tracks running threads and notifies subscribers", () => {
const listener = vi.fn();
const unsubscribe = subscribeRunningThreads(listener);
setThreadRunning("thread-a", true);
expect(getRunningThreadIds().has("thread-a")).toBe(true);
expect(listener).toHaveBeenCalledTimes(1);
setThreadRunning("thread-a", false);
expect(getRunningThreadIds().has("thread-a")).toBe(false);
expect(listener).toHaveBeenCalledTimes(2);
unsubscribe();
setThreadRunning("thread-b", true);
expect(listener).toHaveBeenCalledTimes(2);
setThreadRunning("thread-b", false);
});
it("keeps snapshot identity stable for useSyncExternalStore", () => {
const before = getRunningThreadIds();
// Re-asserting the same state must not swap the snapshot (no-op writes).
setThreadRunning("thread-c", true);
const during = getRunningThreadIds();
expect(during).not.toBe(before);
setThreadRunning("thread-c", true);
expect(getRunningThreadIds()).toBe(during);
setThreadRunning("thread-c", false);
});
});