0daf8cdaad
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Has been cancelled
Project CI / AI game creator shell Rust smoke (pull_request) Has been cancelled
Project CI / AI game creator shell Rust crates (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / AI game creator shell web tests (pull_request) Has been cancelled
- apps/ai-game-creator-shell/src/view/project-development/chat/DirectProjectChatView.tsx 状态条改为读 DirectProjectTurnStatus.displayBusy(本地命令在飞 ∪ 原生在跑),计时起点放宽到最新一个未结束回合:原先只认原生 turnRunning,而 turn.started 要等宿主应答返回才发出,模型首 token 之前那约十秒界面完全没有「正在处理」的交代 - apps/ai-game-creator-shell/src/view/project-development/chat/components/DirectProjectConversation/DirectProjectConversation.tsx 入参 nativeRunning 更名 turnInFlight 并同步注释 - apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx 块级 pre 与 code 补 whitespace-pre-wrap + break-words:窄面板里长行不再把消息拉宽、不再顶出横向滚动条 - apps/ai-game-creator-shell/src/components/ChatMarkdownMessage/index.tsx 新增 normalizeMarkdownFences:把粘在正文行里的 ``` 拆到独立行(模型常写成「…实现细节(game.js):```js」「… }```」),CommonMark 只认整行围栏,粘着的围栏会让正文被当成代码、或代码块不闭合把后续内容一起吞掉;整行/缩进围栏、行内代码、代码里的 ``` 与引用块 / 列表项开头的合法围栏都不受影响 - apps/ai-game-creator-shell/tests/ChatMarkdownMessage.test.tsx 新增粘住围栏三种场景与代码块换行契约用例 - apps/ai-game-creator-shell/tests/appSurface/chat-composer.suite.ts 补「turn.started 未到时卡片已出现且已计时」断言 - apps/ai-game-creator-shell/tests/directProjectProcessStatus.test.tsx 同步入参更名
76 lines
2.4 KiB
TypeScript
76 lines
2.4 KiB
TypeScript
// @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(
|
||
<DirectProjectConversation
|
||
turns={[]}
|
||
messagesRef={createRef<HTMLDivElement>()}
|
||
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(
|
||
<DirectProjectConversation
|
||
turns={[]}
|
||
messagesRef={createRef<HTMLDivElement>()}
|
||
historyHasMore={false}
|
||
turnInFlight={false}
|
||
activeTurnStartedAt={0}
|
||
onLoadEarlierHistory={() => undefined}
|
||
onScroll={() => undefined}
|
||
/>,
|
||
);
|
||
expect(spy).not.toHaveBeenCalled();
|
||
} finally {
|
||
spy.mockRestore();
|
||
vi.useRealTimers();
|
||
}
|
||
});
|