feat(workflow-bar): processing-only elapsed timer + optimistic phase paint on resume

Timer now accumulates only while the harness works, pausing while a gate awaits
the reviewer and freezing when finalized (keyed per session, in-memory). Resume
now colours the re-entry phase immediately from the manifest's phase (1..8),
mirroring the F1 paint for new questions.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 14:13:02 +02:00
co-authored by Claude Opus 4.8
parent e4ada07dab
commit 56ed8b29bd
5 changed files with 91 additions and 49 deletions
+23 -12
View File
@@ -1,17 +1,28 @@
import { render, screen } from "@testing-library/react";
import { render, screen, act } from "@testing-library/react";
import { ElapsedTimer } from "./ElapsedTimer";
test("renders nothing without a start time", () => {
const { container } = render(<ElapsedTimer startedAt={null} />);
expect(container).toBeEmptyDOMElement();
test("starts at zero", () => {
render(<ElapsedTimer running={false} />);
expect(screen.getByText("0m 0s")).toBeInTheDocument();
});
test("frozen elapsed when stoppedAt is set (2m 5s)", () => {
render(
<ElapsedTimer
startedAt="2026-07-03T10:00:00.000Z"
stoppedAt="2026-07-03T10:02:05.000Z"
/>,
);
expect(screen.getByText("2m 5s")).toBeInTheDocument();
test("accumulates while running, pauses when not, then resumes", () => {
vi.useFakeTimers();
try {
const { rerender } = render(<ElapsedTimer running />);
act(() => vi.advanceTimersByTime(3000));
expect(screen.getByText("0m 3s")).toBeInTheDocument();
// Gate awaiting the reviewer: processing time must NOT advance.
rerender(<ElapsedTimer running={false} />);
act(() => vi.advanceTimersByTime(10000));
expect(screen.getByText("0m 3s")).toBeInTheDocument();
// Reviewer answered, processing resumes.
rerender(<ElapsedTimer running />);
act(() => vi.advanceTimersByTime(2000));
expect(screen.getByText("0m 5s")).toBeInTheDocument();
} finally {
vi.useRealTimers();
}
});