Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions src/renderer/src/components/HttpCaptureStep.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ export function HttpCaptureStep({ onVerified, onPrepare, paused = false }: {

useEffect(() => { onVerified?.(verified) }, [verified, onVerified])

// A rejected start is this card's to report until the proxy says something
// newer. A status read that finds it merely stopped knows nothing about why,
// and taking it whole erased the failure within one 3 s poll.
const takeStatus = (st: ManagedProxyStatus): void =>
setStatus((prev) => (prev.state === 'failed' && prev.error && st.state === 'stopped' ? prev : st))

const check = async (): Promise<void> => {
const [pf, st, cfg] = await Promise.all([
window.redlog.runtime.preflight().catch(() => null),
Expand All @@ -172,7 +178,7 @@ export function HttpCaptureStep({ onVerified, onPrepare, paused = false }: {
const mitm = pf.checks.find((c) => c.id === 'mitmdump')
setMitmMissing(mitm ? !mitm.found : false)
}
if (st) setStatus(st)
if (st) takeStatus(st)
if (cfg) setConfig(cfg as Record<string, unknown>)
}

Expand All @@ -192,7 +198,7 @@ export function HttpCaptureStep({ onVerified, onPrepare, paused = false }: {
// request. Same cadence as the app-wide toggle.
useEffect(() => {
const timer = setInterval(() => {
window.redlog.httpCapture.status().then(setStatus).catch(() => { /* keep the last known state */ })
window.redlog.httpCapture.status().then(takeStatus).catch(() => { /* keep the last known state */ })
}, 3_000)
return () => clearInterval(timer)
}, [])
Expand Down
18 changes: 18 additions & 0 deletions test/first-run-record-terminal.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,24 @@ describe('first run: HTTP(S) card reports its own failures (UI/UX audit F6/F7)',
expect(screen.getByTestId('first-run-http-restart')).not.toBeNull()
})

// The card also polls the proxy's status every 3 s. The poll's answer knows
// nothing about this start, so taking it whole erased the failure: on a
// slow runner the poll landed before the assertion above, and the operator
// saw the message for at most three seconds.
it('keeps a rejected start\'s failure through the next status poll', async () => {
vi.useFakeTimers({ shouldAdvanceTime: true })
install()
bridge.proxyStart.mockRejectedValueOnce(new Error('port 8080 in use'))
draw()
fireEvent.click(await screen.findByRole('button', { name: '重新啟動 HTTP 擷取' }, { timeout: 5000 }))
const card = await screen.findByTestId('first-run-http')
await waitFor(() => expect(card.textContent).toContain('port 8080 in use'))
const polls = bridge.proxyStatus.mock.calls.length
await act(async () => { await vi.advanceTimersByTimeAsync(3_100) })
expect(bridge.proxyStatus.mock.calls.length).toBeGreaterThan(polls)
expect(card.textContent).toContain('port 8080 in use')
})

// The terminal-proxy switch moved to Settings ▸ Browser, which owns every
// other proxy setting; a global behaviour switch does not belong on the
// screen where capture is being proved for the first time.
Expand Down
5 changes: 4 additions & 1 deletion test/terminal-session-rotation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,10 @@ describe('project recording rotation without killing the pane', () => {
expect(detachTerminalRecordings()).toBe(10)
closeDB(); resetCausesResolver(); current = 'B'; open('B')
expect(resumeTerminalRecordings()).toBe(10)
expect(performance.now() - started).toBeLessThan(1000)
// A rotation that waited would wait for `sleep 99`. Five seconds still
// tells that apart, where one second failed on a loaded Windows runner
// (1205 ms) while the rotation itself did nothing wrong.
expect(performance.now() - started).toBeLessThan(5000)
expect(listTerminals()).toHaveLength(10)
expect(fake.panes.every(pane => pane.kill.mock.calls.length === 0)).toBe(true)
expect(rows().filter(row => row.data.subtype === 'session_start').every(row => row.data.rotationPaneCount === 10)).toBe(true)
Expand Down
Loading