Skip to content

Commit b68a0fd

Browse files
snowopsdevcodex
andauthored
fix(cms): refresh articles when short runs finish between polls (#90)
Co-Authored-By: Codex <noreply@openai.com>
1 parent bf30442 commit b68a0fd

2 files changed

Lines changed: 37 additions & 1 deletion

File tree

cms/src/components/ops/GlobalRunBar.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ export function GlobalRunBar() {
5656
seen.current = next ? { runId: next.runId, status: next.status } : null
5757
const wasActive = prev?.status === 'queued' || prev?.status === 'running'
5858
const nextActive = next?.status === 'queued' || next?.status === 'running'
59-
if (wasActive && next && !nextActive && prev?.runId === next.runId) router.refresh()
59+
// A short run can finish between idle polls (or before the first poll).
60+
// Refresh each newly observed completion even if we never saw it active.
61+
if (next && !nextActive && (wasActive || prev?.runId !== next.runId)) router.refresh()
6062
setRun(next)
6163
setNow(Date.now())
6264
return nextActive

cms/tests/int/runBar.int.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,37 @@ it('ignores a request that completes after unmount', async () => {
7676
})
7777
expect(mocks.refresh).not.toHaveBeenCalled()
7878
})
79+
80+
it('refreshes a run that starts and finishes between idle polls', async () => {
81+
mocks.poll.mockResolvedValueOnce(null).mockResolvedValue(run('succeeded'))
82+
render(React.createElement(GlobalRunBar))
83+
await act(() => vi.advanceTimersByTimeAsync(0))
84+
expect(mocks.refresh).not.toHaveBeenCalled()
85+
await act(() => vi.advanceTimersByTimeAsync(15000))
86+
expect(mocks.refresh).toHaveBeenCalledTimes(1)
87+
await act(() => vi.advanceTimersByTimeAsync(30000))
88+
expect(mocks.refresh).toHaveBeenCalledTimes(1)
89+
})
90+
91+
it('refreshes a different completed run without requiring an active observation', async () => {
92+
mocks.poll.mockResolvedValueOnce(run('succeeded')).mockResolvedValue({
93+
...run('succeeded'),
94+
runId: 'next-run',
95+
})
96+
render(React.createElement(GlobalRunBar))
97+
await act(() => vi.advanceTimersByTimeAsync(0))
98+
mocks.refresh.mockClear()
99+
await act(() => vi.advanceTimersByTimeAsync(15000))
100+
expect(mocks.refresh).toHaveBeenCalledTimes(1)
101+
await act(() => vi.advanceTimersByTimeAsync(15000))
102+
expect(mocks.refresh).toHaveBeenCalledTimes(1)
103+
})
104+
105+
it('refreshes once when a run completed before the first poll', async () => {
106+
mocks.poll.mockResolvedValue(run('succeeded'))
107+
render(React.createElement(GlobalRunBar))
108+
await act(() => vi.advanceTimersByTimeAsync(0))
109+
expect(mocks.refresh).toHaveBeenCalledTimes(1)
110+
await act(() => vi.advanceTimersByTimeAsync(30000))
111+
expect(mocks.refresh).toHaveBeenCalledTimes(1)
112+
})

0 commit comments

Comments
 (0)