// @vitest-environment jsdom import { cleanup, render } from '@testing-library/react'; import { act, createRef } from 'react'; import { afterEach, expect, test, vi } from 'vitest'; import { DirectProjectConversation } from '../src/view/project-development/chat/components/DirectProjectConversation/DirectProjectConversation'; afterEach(() => { cleanup(); }); const STARTED_AT = 1_800_000_000_000; /** * 读秒粒度:走对话侧唯一的 `useLiveNow`(`LIVE_TIMER_TICK_MS = 100`)。 * * 状态条的耗时文案不足一分钟保留一位小数,刷新就必须是 100ms——按 1 秒一跳时,用户看到的 * 是一块「带小数却一格一格跳」的表,像卡住不动。这里用假时钟钉住粒度:把时钟改回 1000ms * 时第二步即红。 */ test('运行中状态条的读秒按 100ms 刷新:不足一分钟的耗时以 0.1 秒递增', () => { vi.useFakeTimers(); try { vi.setSystemTime(STARTED_AT); const view = render( ()} historyHasMore={false} turnInFlight activeTurnStartedAt={STARTED_AT} onLoadEarlierHistory={() => undefined} onScroll={() => undefined} />, ); const elapsed = () => view.getByText(/^已耗时 /u).textContent; expect(elapsed()).toBe('已耗时 0.0秒'); act(() => { vi.advanceTimersByTime(100); }); expect(elapsed()).toBe('已耗时 0.1秒'); // 1 秒一跳的实现会停在上面的 0.1 秒:再走 400ms 必须继续往上加。 act(() => { vi.advanceTimersByTime(400); }); expect(elapsed()).toBe('已耗时 0.5秒'); } finally { vi.useRealTimers(); } }); /** 回合不在跑时不订阅时钟:否则空转的 tick 会持续重建状态条所在的那块视图。 */ test('没有运行中的回合时不订阅时钟', () => { vi.useFakeTimers(); const spy = vi.spyOn(globalThis, 'setInterval'); try { render( ()} historyHasMore={false} turnInFlight={false} activeTurnStartedAt={0} onLoadEarlierHistory={() => undefined} onScroll={() => undefined} />, ); expect(spy).not.toHaveBeenCalled(); } finally { spy.mockRestore(); vi.useRealTimers(); } });