1e17d2c852
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Successful in 7m44s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 7m57s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 5m44s
Project CI / Backend tests (pull_request) Failing after 16s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 5m53s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 2m21s
Project CI / Repository checks (pull_request) Failing after 12s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m27s
Project CI / Frontend tests (pull_request) Successful in 7m4s
Project CI / AI game creator shell web tests (pull_request) Successful in 6m22s
Project CI / Native shell tests (pull_request) Successful in 10m13s
AI 润色回包与原文相同时给出明确提示,不再落下点了等于没点的「恢复原文」假入口 润色「与原文相同」的判据改为规范化之后要写回宿主的文本,回包被长度上限截回原文同样算没变化 共享聊天输入区显示润色提示,并让提示随用户手改提示词失效 快速编辑未提交草稿改按资源路径归属,收起面板或被旁路重投影后都不再静默丢掉 恢复入口按路径与本会话草稿对号,并区分「本会话未提交」与原生账本条目 账本读取失败时入口仍报出本会话草稿条数,「继续编辑」重开面板回填提示词与 @ 引用 派生资源名预检接入快速编辑与角色动画两条提交路径,早于正规化写盘 名称预检自身抛错时回写面板失败态,不再留下「点了没反应」的提交按钮 浮层关闭判定改在捕获阶段冻结,避免浮层内按钮卸载自己时整块面板被自己人收掉 恢复入口动作按钮收进统一容器,补移动端纵向排布与禁用态样式 空草稿表改工厂函数,跨语言对表用例按模块路径解析 Rust 源码 补回归用例:旁路重投影后的草稿、原样回包提示、提交前名称拦截、草稿表读写与跨卡隔离 同步 decision-log 与画布验收待办的进展说明
131 lines
5.0 KiB
TypeScript
131 lines
5.0 KiB
TypeScript
// @vitest-environment jsdom
|
||
import {
|
||
cleanup,
|
||
fireEvent,
|
||
render,
|
||
screen,
|
||
waitFor,
|
||
} from '@testing-library/react';
|
||
import { useState } from 'react';
|
||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||
|
||
import { ResourcePromptPolishSlot } from '../src/features/resource-canvas/ResourcePromptPolishSlot';
|
||
|
||
type TauriInvoke = (
|
||
command: string,
|
||
args?: Record<string, unknown>,
|
||
) => Promise<unknown>;
|
||
|
||
/**
|
||
* 平台侧润色的桩:`ResourcePromptPolishSlot` 走 `usePromptPolish` 的默认实现
|
||
* (`polish_local_project_prompt`),所以这里和生成素材面板的既有用例一样,
|
||
* 用 `window.__TAURI__` 打桩,不碰真实 Provider。
|
||
*/
|
||
function installPolishInvoke(result: string) {
|
||
const invoke = vi.fn(async (command: string) => {
|
||
if (command !== 'polish_local_project_prompt') {
|
||
throw new Error(`unexpected invoke ${command}`);
|
||
}
|
||
return result;
|
||
});
|
||
(
|
||
window as unknown as { __TAURI__?: { core?: { invoke: TauriInvoke } } }
|
||
).__TAURI__ = { core: { invoke } };
|
||
return invoke;
|
||
}
|
||
|
||
afterEach(() => {
|
||
cleanup();
|
||
delete (window as unknown as { __TAURI__?: unknown }).__TAURI__;
|
||
});
|
||
|
||
/**
|
||
* 宿主侧的最小接线:slot 本身是受控的,提示词文本存在宿主里
|
||
* (与 AGC 快速编辑面板 / 生成素材弹窗同一形状),`output` 只是把宿主状态显示出来。
|
||
*/
|
||
function PolishSlotHarness({ prompt }: { prompt: string }) {
|
||
const [text, setText] = useState(prompt);
|
||
return (
|
||
<>
|
||
<ResourcePromptPolishSlot
|
||
subject="图片素材的快速编辑提示词"
|
||
editKind="image-reference"
|
||
prompt={text}
|
||
applyPrompt={setText}
|
||
/>
|
||
{/* 刻意用 span:`<output>` 自带 status 角色,会和 slot 的状态行撞名。 */}
|
||
<span aria-label="宿主提示词">{text}</span>
|
||
{/* 宿主输入框里的手改:与 AGC 面板里改提示词同一条路径(改的是宿主状态)。 */}
|
||
<button type="button" onClick={() => setText('把角色头发改成蓝色')}>
|
||
模拟用户手改
|
||
</button>
|
||
</>
|
||
);
|
||
}
|
||
|
||
describe('ResourcePromptPolishSlot', () => {
|
||
test('回包与原文相同:状态行说清「未做修改」,也不给「恢复原文」假入口', async () => {
|
||
const invoke = installPolishInvoke('把角色头发改成红色');
|
||
render(<PolishSlotHarness prompt="把角色头发改成红色" />);
|
||
|
||
fireEvent.click(screen.getByRole('button', { name: 'AI 润色' }));
|
||
|
||
// 验收项 AGC-004 的用户可见面:必须有一条说得清的反馈,而不是静默什么都不发生。
|
||
await waitFor(() =>
|
||
expect(screen.getByRole('status').textContent).toBe(
|
||
'AI 润色结果与原文相同,未做修改',
|
||
),
|
||
);
|
||
expect(invoke).toHaveBeenCalledWith('polish_local_project_prompt', {
|
||
prompt: '把角色头发改成红色',
|
||
context: expect.stringContaining('图片素材的快速编辑提示词'),
|
||
});
|
||
// 文本没变,也就没有可回退的变化:不渲染「恢复原文」。
|
||
expect(screen.getByLabelText('宿主提示词').textContent).toBe(
|
||
'把角色头发改成红色',
|
||
);
|
||
expect(screen.queryByRole('button', { name: '恢复原文' })).toBeNull();
|
||
});
|
||
|
||
test('用户接着手改提示词:上一轮的提示收起,宿主文本就是用户写的那份', async () => {
|
||
installPolishInvoke('把角色头发改成红色');
|
||
render(<PolishSlotHarness prompt="把角色头发改成红色" />);
|
||
|
||
fireEvent.click(screen.getByRole('button', { name: 'AI 润色' }));
|
||
await waitFor(() =>
|
||
expect(screen.getByRole('status').textContent).toBe(
|
||
'AI 润色结果与原文相同,未做修改',
|
||
),
|
||
);
|
||
|
||
// 宿主文本被用户改写:上一轮「与原文相同」的结论不再描述当前这段话,提示收起,
|
||
// 免得用户把上一轮的结论当成对现在这段文本的判断。
|
||
fireEvent.click(screen.getByRole('button', { name: '模拟用户手改' }));
|
||
|
||
await waitFor(() => expect(screen.queryByRole('status')).toBeNull());
|
||
expect(screen.getByLabelText('宿主提示词').textContent).toBe(
|
||
'把角色头发改成蓝色',
|
||
);
|
||
});
|
||
|
||
test('回包不同:「恢复原文」把原输入回填给宿主,并收起自己与提示', async () => {
|
||
installPolishInvoke('把角色头发设定改为亮红色');
|
||
render(<PolishSlotHarness prompt="把角色头发改成红色" />);
|
||
|
||
fireEvent.click(screen.getByRole('button', { name: 'AI 润色' }));
|
||
const restore = await screen.findByRole('button', { name: '恢复原文' });
|
||
expect(screen.getByLabelText('宿主提示词').textContent).toBe(
|
||
'把角色头发设定改为亮红色',
|
||
);
|
||
|
||
fireEvent.click(restore);
|
||
|
||
// 验收项 AGC-005 的动作面:恢复原文 = 回填最初原文,快照被消费掉,按钮与提示一起收干净。
|
||
expect(screen.getByLabelText('宿主提示词').textContent).toBe(
|
||
'把角色头发改成红色',
|
||
);
|
||
expect(screen.queryByRole('button', { name: '恢复原文' })).toBeNull();
|
||
expect(screen.queryByRole('status')).toBeNull();
|
||
});
|
||
});
|