File size: 2,419 Bytes
afe44d6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { subscribeProgress, type ProgressState } from "@/lib/progress";
class MockEventSource {
url: string;
onmessage: ((m: { data: string }) => void) | null = null;
closed = false;
static last: MockEventSource;
constructor(url: string) {
this.url = url;
MockEventSource.last = this;
}
emit(data: object) {
this.onmessage?.({ data: JSON.stringify(data) });
}
close() {
this.closed = true;
}
}
beforeEach(() => {
vi.stubGlobal("EventSource", MockEventSource);
});
afterEach(() => {
vi.unstubAllGlobals();
});
describe("subscribeProgress", () => {
it("emits running on start", () => {
const states: ProgressState[] = [];
subscribeProgress((s) => states.push(s));
MockEventSource.last.emit({
type: "start", elapsed_s: 0, kind: "dialog", total_turns: 3, turn: 0,
});
expect(states[0]).toMatchObject({ phase: "running", kind: "dialog", total: 3 });
});
it("updates turn on turn_complete", () => {
const states: ProgressState[] = [];
subscribeProgress((s) => states.push(s));
MockEventSource.last.emit({
type: "start", elapsed_s: 0, kind: "dialog", total_turns: 3, turn: 0,
});
MockEventSource.last.emit({
type: "turn_complete", elapsed_s: 1.2, kind: "dialog", total_turns: 3, turn: 2,
});
const last = states[states.length - 1];
expect(last).toMatchObject({ phase: "running", turn: 2, total: 3 });
});
it("transitions to done then idle", async () => {
vi.useFakeTimers();
const states: ProgressState[] = [];
subscribeProgress((s) => states.push(s));
MockEventSource.last.emit({ type: "done", elapsed_s: 4.5 });
expect(states[states.length - 1]).toMatchObject({ phase: "done", elapsedS: 4.5 });
vi.advanceTimersByTime(1100);
expect(states[states.length - 1]).toMatchObject({ phase: "idle" });
vi.useRealTimers();
});
it("emits error", () => {
const states: ProgressState[] = [];
subscribeProgress((s) => states.push(s));
MockEventSource.last.emit({
type: "error", elapsed_s: 2, message: "boom",
});
expect(states[states.length - 1]).toMatchObject({ phase: "error", message: "boom" });
});
it("close() shuts down EventSource", () => {
const close = subscribeProgress(() => {});
close();
expect(MockEventSource.last.closed).toBe(true);
});
});
|