diff --git a/frontend/e2e/dashboard.spec.ts b/frontend/e2e/dashboard.spec.ts index bcbf826..c0c53ed 100644 --- a/frontend/e2e/dashboard.spec.ts +++ b/frontend/e2e/dashboard.spec.ts @@ -142,6 +142,13 @@ test('manages a task and reads a real run log', async ({ page, request }) => { }) await drawer.getByRole('button', { name: '关闭' }).click() + // 运行已结束时再打开日志,也要能回放(曾因 rAF 句柄未归零停在空态)。 + await page.getByRole('button', { name: '日志' }).click() + const replayDrawer = page.getByRole('dialog', { name: 'E2E echo' }) + await expect(replayDrawer).toBeVisible() + await expect(replayDrawer.getByText('e2e-output')).toBeVisible() + await replayDrawer.getByRole('button', { name: '关闭' }).click() + await page.getByRole('button', { name: '编辑' }).click() const editor = page.getByRole('dialog', { name: '编辑任务 [e2e_echo]', diff --git a/frontend/src/components/RunLogDrawer.tsx b/frontend/src/components/RunLogDrawer.tsx index 6a8702c..6b01b7e 100644 --- a/frontend/src/components/RunLogDrawer.tsx +++ b/frontend/src/components/RunLogDrawer.tsx @@ -409,6 +409,10 @@ function RunLogOutput({ return () => { if (frameRef.current) { cancelAnimationFrame(frameRef.current) + // StrictMode 会「挂载 → 清理 → 再挂载」,取消后必须把句柄归零, + // 否则下一次 scheduleRender 会误以为还有帧在排队而不再安排渲染, + // 已结束的 run(complete 在首帧就为 true)会一直停在「等待日志输出」。 + frameRef.current = 0 } } }, [])