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
- `useDirectProjectTurnStatus` 新增派生判据 `canStopTurn`(`nativeRunning || commandInFlight`)与共享的 `canStopDirectProjectTurn`,写清它与 `displayBusy` 的分工:前者回答"终止这一脚踢给谁",后者回答"界面能不能声称空闲" - composer 的发送钮 / 终止钮按 `canStopTurn` 换;`busy` 继续管模型 / 附件 / 语音这些与当前这轮冲突的控件 - `cancelTurn` 的受理判据改用同一个函数:显示与受理同源,不再出现"按钮在、点了说没有回合" - `displayBusy` 保持三者并集不变(卡片仍要早于首个 token 显示"正在处理") - 测试:`directProjectTurnStatus` 补 `canStopTurn` 用例;appSurface 补"只有待发消息、没有回合在跑时不显示终止钮"(撤掉修复会红)
145 lines
4.4 KiB
TypeScript
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');
|
|
});
|
|
});
|