修复资源界面测试并清理退役策划残留
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 5m57s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 5m26s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m10s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 5m21s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m15s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m32s
Project CI / Backend tests (pull_request) Successful in 9m36s
Project CI / Repository checks (pull_request) Successful in 9m13s
Project CI / Frontend tests (pull_request) Successful in 9m42s
Project CI / Native shell tests (pull_request) Successful in 11m55s
Project CI / AI game creator shell web tests (pull_request) Successful in 7m59s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 5m57s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 5m26s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m10s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 5m21s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m15s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m32s
Project CI / Backend tests (pull_request) Successful in 9m36s
Project CI / Repository checks (pull_request) Successful in 9m13s
Project CI / Frontend tests (pull_request) Successful in 9m42s
Project CI / Native shell tests (pull_request) Successful in 11m55s
Project CI / AI game creator shell web tests (pull_request) Successful in 7m59s
更新资源卡片断言与 Lexical 提示词输入,恢复生成任务异步状态及轮询收尾验证。 删除旧策划别名和不可达错误映射,更新阶段审批注释。 保留旧项目无报错新开 Design 会话并继续的行为。 资源界面修复后完整界面测试 448 通过、17 跳过;审查清理后 Runtime 模型 27 项及 Design Agent 4 项测试通过,类型和配置检查通过。
This commit is contained in:
@@ -562,7 +562,6 @@ export function App({
|
||||
!supervisorChatOnly &&
|
||||
!planningStartMode &&
|
||||
!designAgentActive;
|
||||
const planningLaneActive = designAgentActive;
|
||||
const [devMode] = useState(() =>
|
||||
projectSupervisorOnly ? false : isDeveloperMode(),
|
||||
);
|
||||
@@ -1641,7 +1640,7 @@ export function App({
|
||||
if (
|
||||
!canSubscribeTauriEvents() ||
|
||||
directCodexProductRuntime ||
|
||||
planningLaneActive
|
||||
designAgentActive
|
||||
) {
|
||||
return;
|
||||
}
|
||||
@@ -1744,7 +1743,7 @@ export function App({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
directCodexProductRuntime,
|
||||
planningLaneActive,
|
||||
designAgentActive,
|
||||
refreshManifest,
|
||||
updateProjectSupervisorResponseStream,
|
||||
updateProjectSupervisorRuntime,
|
||||
@@ -1869,7 +1868,7 @@ export function App({
|
||||
if (
|
||||
!invoke ||
|
||||
directCodexProductRuntime ||
|
||||
planningLaneActive ||
|
||||
designAgentActive ||
|
||||
!nextProjectPath ||
|
||||
!sessionId ||
|
||||
!trackedRunId ||
|
||||
@@ -1965,7 +1964,7 @@ export function App({
|
||||
}, [
|
||||
localProject?.projectPath,
|
||||
directCodexProductRuntime,
|
||||
planningLaneActive,
|
||||
designAgentActive,
|
||||
projectSupervisorSessionId,
|
||||
projectSupervisorRuntime?.phase,
|
||||
projectSupervisorRuntime?.runId,
|
||||
@@ -1980,7 +1979,7 @@ export function App({
|
||||
if (
|
||||
!projectSupervisorOnly ||
|
||||
directCodexProductRuntime ||
|
||||
planningLaneActive ||
|
||||
designAgentActive ||
|
||||
!invoke ||
|
||||
!nextProjectPath ||
|
||||
!supervisorRunId
|
||||
@@ -2061,7 +2060,7 @@ export function App({
|
||||
}, [
|
||||
localProject?.projectPath,
|
||||
directCodexProductRuntime,
|
||||
planningLaneActive,
|
||||
designAgentActive,
|
||||
projectSupervisorOnly,
|
||||
projectSupervisorRuntime?.phase,
|
||||
projectSupervisorRuntime?.runId,
|
||||
@@ -2073,7 +2072,7 @@ export function App({
|
||||
(!supervisorChatOnly &&
|
||||
!(
|
||||
projectSupervisorOnly &&
|
||||
(directCodexProductRuntime || planningLaneActive)
|
||||
(directCodexProductRuntime || designAgentActive)
|
||||
)) ||
|
||||
!supervisorChatShouldFollowLatestRef.current
|
||||
) {
|
||||
@@ -2090,7 +2089,7 @@ export function App({
|
||||
projectSupervisorRuntimeError,
|
||||
directThreadChat,
|
||||
directCodexProductRuntime,
|
||||
planningLaneActive,
|
||||
designAgentActive,
|
||||
projectSupervisorOnly,
|
||||
supervisorChatOnly,
|
||||
]);
|
||||
|
||||
@@ -2052,13 +2052,6 @@ export function projectRuntimeVisibleError(
|
||||
) {
|
||||
return `${subject} 保存运行记录失败,请检查项目目录后重试`;
|
||||
}
|
||||
if (
|
||||
normalized.includes('planning_invalid') ||
|
||||
normalized.includes('gdd 结构无效') ||
|
||||
normalized.includes('策划输出格式')
|
||||
) {
|
||||
return `${subject} 输出格式不符合当前 GDD 结构,请重试`;
|
||||
}
|
||||
const containsInternalDiagnostics =
|
||||
normalized.includes('agentllm.') ||
|
||||
/(?:^|[\s::])kind=/.test(normalized) ||
|
||||
|
||||
@@ -2846,7 +2846,7 @@ export default function ProjectDevelopmentView({
|
||||
[activeVersionId, manifest],
|
||||
);
|
||||
// 做方案在拿到第一个产物之前,左侧的资源区域是空的(文档 0 项、项目版本 0 项),而澄清
|
||||
// 问答、决定卡和 GDD 审批全挤在右边那一列里。这段时间先把画布收掉让对话铺满,一旦有第
|
||||
// 问答和阶段审批都在右侧对话列中。这段时间先把画布收掉让对话铺满,一旦有第
|
||||
// 一个已登记资源就自动恢复双栏。用 CSS 收而不是不渲染:画布里的页签、缩放和选中状态都
|
||||
// 留着,恢复双栏时不会被重置。
|
||||
const conversationOnlyWorkbench = planningStartMode && resources.length === 0;
|
||||
|
||||
@@ -19,6 +19,10 @@ import { normalizeProjectResourceGraph } from '../../src/view/project-developmen
|
||||
import { ResourceDependencyOverlay } from '../../src/view/project-development/ResourceDependencyOverlay';
|
||||
import type { ProjectAgentResultSummary } from '../../src/view/project-development/resourceProjectionModel';
|
||||
import { projectResourcesFromReadModels } from '../../src/view/project-development/resourceProjectionModel';
|
||||
import {
|
||||
generationPromptText,
|
||||
typeGenerationPrompt,
|
||||
} from '../resourceGenerationPromptTestUtils';
|
||||
import {
|
||||
act,
|
||||
agentRuntimeUserInputRequest,
|
||||
@@ -345,9 +349,9 @@ async function submitBottomToolbarPanel(
|
||||
fireEvent.click(screen.getByRole('button', { name: label }));
|
||||
}
|
||||
const panel = await screen.findByRole('dialog', { name: label });
|
||||
fireEvent.change(within(panel).getByLabelText('生成提示词'), {
|
||||
target: { value: options.prompt },
|
||||
});
|
||||
// 提示词输入区是与聊天同一份 `@` 引用输入区(Lexical):jsdom 没有可用 Selection,
|
||||
// 浏览器输入事件不会落字,只能走编辑器更新(见 resourceGenerationPromptTestUtils)。
|
||||
await typeGenerationPrompt(panel, options.prompt);
|
||||
fireEvent.click(within(panel).getByRole('button', { name: label }));
|
||||
// 成功路径由宿主卸载面板:等它消失,下一个入口才不会撞上残留的浮层。
|
||||
await waitFor(() =>
|
||||
@@ -2978,7 +2982,17 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
await showResourcePage('角色与对象');
|
||||
let heroDetailButton = await findResourceSelectButton('hero.png');
|
||||
const heroCard = heroDetailButton.closest('.game-resource-card');
|
||||
expect(heroCard?.textContent).not.toContain('hero.png');
|
||||
// 卡面名称就是正式资源名(manifest `localPath` 的文件名,生成时来自 assetName、
|
||||
// 重命名时同步改写),长名由 CSS 省略、完整名挂在实际命中指针的整卡选中按钮 `title` 上。
|
||||
const heroName = heroCard?.querySelector('.game-resource-card-name');
|
||||
expect(heroName?.textContent).toBe('hero.png');
|
||||
expect(heroName?.getAttribute('data-resource-name')).toBe('hero.png');
|
||||
expect(
|
||||
heroCard
|
||||
?.querySelector('.game-resource-card-select')
|
||||
?.getAttribute('title'),
|
||||
).toBe('hero.png');
|
||||
// 卡面仍然只给"名称 + 类型",不铺完整路径与来源这类详细文本。
|
||||
expect(heroCard?.textContent).not.toContain('assets/hero.png');
|
||||
expect(heroCard?.textContent).not.toContain('Agent 生成');
|
||||
// 角标显示的是**资源类型**(功能分类),等于它所在的画布栏目;不再是"图片"这类媒体类型。
|
||||
@@ -3002,13 +3016,21 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
});
|
||||
await showResourcePage('待归类');
|
||||
act(() => observer.triggerVisible());
|
||||
const documentSummary = await screen.findByText(/这是安全的卡片正文摘要。/);
|
||||
// 卡面正文按扩展名走文档分支(媒体类型),但角标是资源类型:`design-document` 不在
|
||||
// canonical 目录里 → 落「待归类」,与它所在的栏目一致(这正是改前"标着文档、却在待归类栏"的分叉)。
|
||||
// 文档卡卡面**不铺正文**:只显示居中图标 + 正式资源名,正文只在独立的文档详情浮层里读。
|
||||
const documentCard = (
|
||||
await findResourceSelectButton('design.md')
|
||||
).closest<HTMLElement>('.game-resource-card');
|
||||
expect(
|
||||
documentSummary
|
||||
.closest('.game-resource-card')
|
||||
?.querySelector('[data-resource-type="待归类"]'),
|
||||
documentCard?.querySelector('.game-resource-card-name')?.textContent,
|
||||
).toBe('design.md');
|
||||
expect(
|
||||
documentCard?.querySelector('.game-resource-card-document-visual'),
|
||||
).not.toBeNull();
|
||||
expect(documentCard?.textContent).not.toContain('这是安全的卡片正文摘要。');
|
||||
// 角标仍是资源类型:`design-document` 不在 canonical 目录里 → 落「待归类」,
|
||||
// 与它所在的栏目一致(这正是改前"标着文档、却在待归类栏"的分叉)。
|
||||
expect(
|
||||
documentCard?.querySelector('[data-resource-type="待归类"]'),
|
||||
).not.toBeNull();
|
||||
await showResourcePage('角色与对象');
|
||||
expect(
|
||||
@@ -4249,9 +4271,9 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
expect(screen.queryByRole('toolbar', { name: '图片工具栏' })).toBeNull();
|
||||
});
|
||||
expect(
|
||||
screen
|
||||
.queryAllByTitle('选中资源')
|
||||
.some((card) => card.getAttribute('aria-pressed') === 'true'),
|
||||
Array.from(
|
||||
document.querySelectorAll<HTMLElement>('.game-resource-card-select'),
|
||||
).some((card) => card.getAttribute('aria-pressed') === 'true'),
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
@@ -12212,9 +12234,7 @@ export function registerProjectAgentStatusTests() {
|
||||
fireEvent.change(within(firstPanel).getByLabelText('素材名称'), {
|
||||
target: { value: '第一条设计图' },
|
||||
});
|
||||
fireEvent.change(within(firstPanel).getByLabelText('生成提示词'), {
|
||||
target: { value: '第一条界面' },
|
||||
});
|
||||
await typeGenerationPrompt(firstPanel, '第一条界面');
|
||||
fireEvent.click(
|
||||
within(firstPanel).getByRole('button', { name: '生成 UI 设计图' }),
|
||||
);
|
||||
@@ -12225,8 +12245,15 @@ export function registerProjectAgentStatusTests() {
|
||||
// 2) 面板关掉之后任务仍在「生成任务」面板里,阶段文案来自后端记录。
|
||||
const taskPanel = await screen.findByRole('region', { name: '生成任务' });
|
||||
expect(within(taskPanel).getByText('第一条设计图')).not.toBeNull();
|
||||
expect(within(taskPanel).getByText('正在生成。')).not.toBeNull();
|
||||
expect(within(taskPanel).getByText('生成中')).not.toBeNull();
|
||||
/**
|
||||
* 阶段文案与徽标要**等**后端记录并进来:刚提交时任务还停在本地队列的「排队中。」,
|
||||
* 记录合并发生在第一轮账本轮询(间隔 2s)之后。同步断言在这里读到的会是本地阶段,
|
||||
* 在整套连跑时必然抢跑(隔离单跑时那一拍刚好落在窗口内,所以只在全量里红)。
|
||||
*/
|
||||
await waitFor(() => {
|
||||
expect(within(taskPanel).getByText('正在生成。')).not.toBeNull();
|
||||
expect(within(taskPanel).getByText('生成中')).not.toBeNull();
|
||||
});
|
||||
// 非模态:没有全屏遮罩、没有 aria-modal。
|
||||
expect(taskPanel.getAttribute('aria-modal')).toBeNull();
|
||||
expect(document.querySelector('[aria-modal="true"]')).toBeNull();
|
||||
@@ -12239,9 +12266,7 @@ export function registerProjectAgentStatusTests() {
|
||||
fireEvent.change(within(secondPanel).getByLabelText('素材名称'), {
|
||||
target: { value: '第二条设计图' },
|
||||
});
|
||||
fireEvent.change(within(secondPanel).getByLabelText('生成提示词'), {
|
||||
target: { value: '第二条界面' },
|
||||
});
|
||||
await typeGenerationPrompt(secondPanel, '第二条界面');
|
||||
fireEvent.click(
|
||||
within(secondPanel).getByRole('button', { name: '生成 UI 设计图' }),
|
||||
);
|
||||
@@ -12286,6 +12311,28 @@ export function registerProjectAgentStatusTests() {
|
||||
expect(
|
||||
calls.filter((call) => call.command === 'get_local_game_manifest').length,
|
||||
).toBeGreaterThanOrEqual(manifestReadsBefore + 2);
|
||||
|
||||
/**
|
||||
* 5) 等轮询真的退出:两个 mock 任务都已收口 completed,再连等两个 poll 周期
|
||||
* (间隔 2s),账本读计数不再增长才算这条异步链跑完。
|
||||
*
|
||||
* 宿主读账本用的是「调用时」的 `window.__TAURI__`,而 afterEach 会把它删掉、下一个用例
|
||||
* 再装自己的 mock:留下一条在途/在睡的轮询,下一轮醒来就会打进**下一个用例**的 spy
|
||||
* (实测就是 `does not open a preview before a local project is initialized` 那条变红,
|
||||
* 且看到的 projectPath 是本案的)。所以排空必须发生在本用例内部,而不是靠全局 harness 掩盖。
|
||||
*/
|
||||
const ledgerReads = () =>
|
||||
calls.filter(
|
||||
(call) => call.command === 'list_local_project_asset_generations',
|
||||
).length;
|
||||
await act(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2_200));
|
||||
});
|
||||
const ledgerReadsAfterFirstQuietPeriod = ledgerReads();
|
||||
await act(async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 2_200));
|
||||
});
|
||||
expect(ledgerReads()).toBe(ledgerReadsAfterFirstQuietPeriod);
|
||||
}, 30_000);
|
||||
|
||||
/**
|
||||
@@ -12799,9 +12846,7 @@ export function registerProjectAgentStatusTests() {
|
||||
fireEvent.change(within(panel).getByLabelText('素材名称'), {
|
||||
target: { value: '待提交设计图' },
|
||||
});
|
||||
fireEvent.change(within(panel).getByLabelText('生成提示词'), {
|
||||
target: { value: '主界面与背包页' },
|
||||
});
|
||||
await typeGenerationPrompt(panel, '主界面与背包页');
|
||||
return panel;
|
||||
}
|
||||
|
||||
@@ -12848,10 +12893,9 @@ export function registerProjectAgentStatusTests() {
|
||||
'项目权限策略拒绝执行:canvas.asset_generate',
|
||||
),
|
||||
);
|
||||
expect(
|
||||
(within(reopened).getByLabelText('生成提示词') as HTMLTextAreaElement)
|
||||
.value,
|
||||
).toBe('主界面与背包页');
|
||||
await waitFor(() =>
|
||||
expect(generationPromptText(reopened)).toBe('主界面与背包页'),
|
||||
);
|
||||
expect(
|
||||
(within(reopened).getByLabelText('素材名称') as HTMLInputElement).value,
|
||||
).toBe('待提交设计图');
|
||||
|
||||
Reference in New Issue
Block a user