You've already forked agentic-coding-workflow
fix(web): localStorage safety, default fetch timeout, visibility-aware polling
This commit is contained in:
@@ -58,4 +58,79 @@ describe('api client', () => {
|
||||
expect(ex.body.code).toBe('invalid_input')
|
||||
}
|
||||
})
|
||||
|
||||
it('schedules a default 30s timeout and forwards abort to fetch', async () => {
|
||||
// Replace setTimeout with a stub that captures the scheduled callback,
|
||||
// so we can assert the default-timeout scheduling and trigger it manually
|
||||
// without waiting wall-clock 30 seconds.
|
||||
const captured: Array<{ cb: () => void; ms: number }> = []
|
||||
const realSetTimeout = globalThis.setTimeout
|
||||
vi.stubGlobal('setTimeout', ((cb: () => void, ms?: number, ...args: unknown[]) => {
|
||||
if (ms === 30_000) {
|
||||
captured.push({ cb, ms })
|
||||
return 0 as unknown as ReturnType<typeof setTimeout>
|
||||
}
|
||||
return realSetTimeout(cb, ms, ...args)
|
||||
}) as typeof setTimeout)
|
||||
|
||||
try {
|
||||
const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(
|
||||
(_input, init) =>
|
||||
new Promise((_resolve, reject) => {
|
||||
const sig = init?.signal as AbortSignal | undefined
|
||||
sig?.addEventListener('abort', () => {
|
||||
const err = new Error('aborted')
|
||||
err.name = 'AbortError'
|
||||
reject(err)
|
||||
})
|
||||
}),
|
||||
)
|
||||
|
||||
let rejected: unknown = null
|
||||
const promise = request('/api/test').catch((e) => {
|
||||
rejected = e
|
||||
})
|
||||
|
||||
// Allow the request to register its setTimeout + start the fetch.
|
||||
await Promise.resolve()
|
||||
|
||||
expect(captured.length).toBe(1)
|
||||
expect(captured[0].ms).toBe(30_000)
|
||||
|
||||
// Fire the captured timeout callback to simulate elapsed 30s.
|
||||
captured[0].cb()
|
||||
await promise
|
||||
|
||||
expect(rejected).not.toBeNull()
|
||||
const init = fetchMock.mock.calls[0][1] as RequestInit
|
||||
expect((init.signal as AbortSignal).aborted).toBe(true)
|
||||
} finally {
|
||||
vi.unstubAllGlobals()
|
||||
}
|
||||
})
|
||||
|
||||
it('forwards a caller-supplied signal abort to the fetch', async () => {
|
||||
const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(
|
||||
(_input, init) =>
|
||||
new Promise((_resolve, reject) => {
|
||||
const sig = init?.signal as AbortSignal | undefined
|
||||
if (sig) {
|
||||
sig.addEventListener('abort', () => {
|
||||
const err = new Error('aborted')
|
||||
err.name = 'AbortError'
|
||||
reject(err)
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
|
||||
const ctl = new AbortController()
|
||||
const promise = request('/api/test', { signal: ctl.signal })
|
||||
const settled = expect(promise).rejects.toThrow()
|
||||
ctl.abort()
|
||||
await settled
|
||||
|
||||
const init = fetchMock.mock.calls[0][1] as RequestInit
|
||||
expect((init.signal as AbortSignal).aborted).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user