Files
Genarrative/apps/ai-game-creator-shell/tests/directProjectTurnStatus.test.ts
T
k88936 e221543947
Project CI / AI game creator shell Rust crates (pull_request) Successful in 1m24s
Project CI / Backend tests (pull_request) Failing after 14s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m8s
Project CI / Frontend tests (pull_request) Successful in 1m57s
Project CI / Repository checks (pull_request) Failing after 25s
Project CI / AI game creator shell web tests (pull_request) Successful in 1m27s
Project CI / Native shell tests (pull_request) Successful in 5m47s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 8m38s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 8m57s
终止钮按 canStopTurn 显示:队列非空不再顶出终止按钮
- `useDirectProjectTurnStatus` 新增派生判据 `canStopTurn`(`nativeRunning || commandInFlight`)与共享的
  `canStopDirectProjectTurn`,写清它与 `displayBusy` 的分工:前者回答"终止这一脚踢给谁",后者回答"界面能不能声称空闲"
- composer 的发送钮 / 终止钮按 `canStopTurn` 换;`busy` 继续管模型 / 附件 / 语音这些与当前这轮冲突的控件
- `cancelTurn` 的受理判据改用同一个函数:显示与受理同源,不再出现"按钮在、点了说没有回合"
- `displayBusy` 保持三者并集不变(卡片仍要早于首个 token 显示"正在处理")
- 测试:`directProjectTurnStatus` 补 `canStopTurn` 用例;appSurface 补"只有待发消息、没有回合在跑时不显示终止钮"(撤掉修复会红)
2026-09-30 13:11:45 +08:00

145 lines
4.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import {
canStopDirectProjectTurn,
deriveDirectProjectTurnStatus,
} from '../src/view/project-development/chat/controller/useDirectProjectTurnStatus';
import type { DirectPendingTurn } from '../src/view/project-development/chat/conversation/directPendingTurns';
import type { DirectChatTurn } from '../src/view/project-development/chat/conversation/directTurnPresentation';
const turn = (key: string, state: DirectChatTurn['state']): DirectChatTurn => ({
key,
users: [],
process: [],
finals: [],
state,
startedAt: 1_800_000_000_000,
endedAt: 0,
});
/** 待发消息:这里只关心条数,内容与 chip 文案无关。 */
const pending = (clientTurnId: string): DirectPendingTurn => ({
clientTurnId,
userItem: {
type: 'message',
role: 'user',
id: `direct-codex:${clientTurnId}:user`,
content: [{ type: 'input_text', text: clientTurnId }],
},
at: 1,
});
describe('DirectProject 回合状态派生', () => {
it('displayBusy 是原生真相、IPC 在飞与待发消息条数的并集', () => {
expect(
deriveDirectProjectTurnStatus({
turnRunning: true,
commandInFlight: false,
pendingTurns: [],
turns: [],
}).displayBusy,
).toBe(true);
expect(
deriveDirectProjectTurnStatus({
turnRunning: false,
commandInFlight: true,
pendingTurns: [],
turns: [],
}).displayBusy,
).toBe(true);
// 队列非空也是忙:还有一条待发消息在宿主手里,这时不能假装空闲。
expect(
deriveDirectProjectTurnStatus({
turnRunning: false,
commandInFlight: false,
pendingTurns: [pending('client-1')],
turns: [],
}).displayBusy,
).toBe(true);
expect(
deriveDirectProjectTurnStatus({
turnRunning: false,
commandInFlight: false,
pendingTurns: [],
turns: [],
}).displayBusy,
).toBe(false);
});
it('三个来源各自独立暴露,不被并集吃掉', () => {
const status = deriveDirectProjectTurnStatus({
turnRunning: true,
commandInFlight: true,
pendingTurns: [pending('client-1'), pending('client-2')],
turns: [],
});
expect(status.nativeRunning).toBe(true);
expect(status.commandInFlight).toBe(true);
expect(status.pendingCount).toBe(2);
});
it('canStopTurn 只认"有作用对象"的两种状态:队列非空不算', () => {
// 队列非空仍然是 displayBusy(界面不假装空闲),但它不是终止的作用对象——终止钮不该出现,
// 否则点了只会报「当前没有正在运行的回合」。
const queuedOnly = deriveDirectProjectTurnStatus({
turnRunning: false,
commandInFlight: false,
pendingTurns: [pending('client-1')],
turns: [],
});
expect(queuedOnly.displayBusy).toBe(true);
expect(queuedOnly.canStopTurn).toBe(false);
// 宿主已经放行 / 命令还在飞:这两段里有东西可终止。
expect(
canStopDirectProjectTurn({ turnRunning: true, commandInFlight: false }),
).toBe(true);
expect(
canStopDirectProjectTurn({ turnRunning: false, commandInFlight: true }),
).toBe(true);
// 真空闲:终止钮与发送钮各自归位。
expect(
canStopDirectProjectTurn({ turnRunning: false, commandInFlight: false }),
).toBe(false);
expect(
deriveDirectProjectTurnStatus({
turnRunning: false,
commandInFlight: true,
pendingTurns: [],
turns: [],
}).canStopTurn,
).toBe(true);
});
it('latestTurnState 取最新一轮的两态;没有回合时为 null', () => {
expect(
deriveDirectProjectTurnStatus({
turnRunning: false,
commandInFlight: false,
pendingTurns: [],
turns: [turn('u1', 'finished'), turn('u2', 'running')],
}).latestTurnState,
).toBe('running');
expect(
deriveDirectProjectTurnStatus({
turnRunning: false,
commandInFlight: false,
pendingTurns: [],
turns: [],
}).latestTurnState,
).toBeNull();
});
it('IPC 在飞不等于原生在跑', () => {
const status = deriveDirectProjectTurnStatus({
turnRunning: false,
commandInFlight: true,
pendingTurns: [],
turns: [turn('u1', 'finished')],
});
expect(status.nativeRunning).toBe(false);
expect(status.commandInFlight).toBe(true);
expect(status.latestTurnState).toBe('finished');
});
});