fix: model replay review cancellation
This commit is contained in:
@@ -109,4 +109,20 @@ describe("demoTimelineReducer", () => {
|
||||
expect(restarted.mode).toBe("replay");
|
||||
expect(restarted.appliedCount).toBe(0);
|
||||
});
|
||||
|
||||
it("cancels review as a terminal non-autoplay phase", () => {
|
||||
const reviewing = {
|
||||
...initialDemoTimelineState,
|
||||
mode: "replay" as const,
|
||||
phase: "review" as const,
|
||||
events: [event(0, "interrupt")],
|
||||
appliedCount: 1,
|
||||
autoplay: false,
|
||||
};
|
||||
const cancelled = demoTimelineReducer(reviewing, { type: "cancel_review" });
|
||||
|
||||
expect(cancelled.phase).toBe("cancelled");
|
||||
expect(cancelled.autoplay).toBe(false);
|
||||
expect(cancelled.appliedCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,7 @@ export type DemoTimelinePhase =
|
||||
| "running"
|
||||
| "paused"
|
||||
| "review"
|
||||
| "cancelled"
|
||||
| "completed"
|
||||
| "failed";
|
||||
|
||||
@@ -35,6 +36,7 @@ export type DemoTimelineAction =
|
||||
| { readonly type: "pause" }
|
||||
| { readonly type: "play" }
|
||||
| { readonly type: "continue_review" }
|
||||
| { readonly type: "cancel_review" }
|
||||
| { readonly type: "fail"; readonly message: string; readonly event?: DemoEvent }
|
||||
| { readonly type: "restart" }
|
||||
| {
|
||||
@@ -102,6 +104,10 @@ export const demoTimelineReducer = (
|
||||
return state.phase === "review"
|
||||
? { ...state, phase: "running", autoplay: true }
|
||||
: state;
|
||||
case "cancel_review":
|
||||
return state.phase === "review"
|
||||
? { ...state, phase: "cancelled", autoplay: false }
|
||||
: state;
|
||||
case "fail":
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -395,6 +395,23 @@ describe("useDemoTimeline", () => {
|
||||
expect(result.current.trace?.frames.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("replay cancellation stops without consuming submitted branch", async () => {
|
||||
vi.useFakeTimers();
|
||||
const { result } = renderHook(() => useDemoTimeline(null, vi.fn()));
|
||||
act(() => result.current.setMode("replay"));
|
||||
act(() => result.current.start());
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await act(async () => vi.advanceTimersByTimeAsync(900));
|
||||
}
|
||||
|
||||
await act(async () => result.current.cancelReview("Cancelled."));
|
||||
await act(async () => vi.advanceTimersByTimeAsync(1800));
|
||||
|
||||
expect(result.current.state.phase).toBe("cancelled");
|
||||
expect(result.current.state.events[result.current.state.appliedCount - 1]?.stage).toBe("interrupt");
|
||||
expect(result.current.output).toBeNull();
|
||||
});
|
||||
|
||||
it("missingDeploymentMessage shows when live mode with null target", () => {
|
||||
const { result } = renderHook(() => useDemoTimeline(null, vi.fn()));
|
||||
expect(result.current.missingDeploymentMessage).toContain("Not connected");
|
||||
|
||||
@@ -292,8 +292,8 @@ export const useDemoTimeline = (
|
||||
comment,
|
||||
outcome: "cancelled",
|
||||
};
|
||||
dispatch({ type: "continue_review" });
|
||||
}, []);
|
||||
dispatch({ type: state.mode === "live" ? "continue_review" : "cancel_review" });
|
||||
}, [state.mode]);
|
||||
|
||||
const restart = useCallback(() => {
|
||||
resetRuntime();
|
||||
|
||||
Reference in New Issue
Block a user