完善聊天 Markdown 测试兼容
调整消息断言以适配 Markdown 段落和列表结构 补充预览快捷键消息气泡查找工具 完善流式 Markdown 样式并同步组件测试
This commit is contained in:
@@ -57,21 +57,25 @@ const markdownComponents: Components = {
|
||||
h3: ({ children }) => (
|
||||
<h3 className="m-0 mt-3 text-base font-semibold first:mt-0">{children}</h3>
|
||||
),
|
||||
p: ({ children }) => <p className="m-0 break-words">{children}</p>,
|
||||
p: ({ children }) => (
|
||||
<p className="m-0 mt-2 break-words first:mt-0">{children}</p>
|
||||
),
|
||||
ul: ({ children }) => (
|
||||
<ul className="m-0 list-none space-y-1 pl-0">{children}</ul>
|
||||
<ul className="m-0 mt-2 list-none space-y-1 pl-0 first:mt-0">{children}</ul>
|
||||
),
|
||||
ol: ({ children }) => (
|
||||
<ol className="m-0 list-decimal space-y-1 pl-5">{children}</ol>
|
||||
<ol className="m-0 mt-2 list-decimal space-y-1 pl-5 first:mt-0">
|
||||
{children}
|
||||
</ol>
|
||||
),
|
||||
li: ({ children }) => <li className="break-words">- {children}</li>,
|
||||
blockquote: ({ children }) => (
|
||||
<blockquote className="m-0 border-l-2 border-(--platform-surface-border) pl-3 text-(--platform-text-soft)">
|
||||
<blockquote className="m-0 mt-2 border-l-2 border-(--platform-surface-border) pl-3 text-(--platform-text-soft) first:mt-0">
|
||||
{children}
|
||||
</blockquote>
|
||||
),
|
||||
pre: ({ children }) => (
|
||||
<pre className="m-0 max-w-full overflow-x-auto rounded-lg bg-black/6 p-3 text-xs leading-5">
|
||||
<pre className="m-0 mt-2 max-w-full overflow-x-auto rounded-lg bg-black/6 p-3 text-xs leading-5 first:mt-0">
|
||||
{children}
|
||||
</pre>
|
||||
),
|
||||
@@ -91,7 +95,7 @@ const markdownComponents: Components = {
|
||||
);
|
||||
},
|
||||
table: ({ children }) => (
|
||||
<div className="max-w-full overflow-x-auto">
|
||||
<div className="mt-2 max-w-full overflow-x-auto first:mt-0">
|
||||
<table className="min-w-full border-collapse text-left text-sm">
|
||||
{children}
|
||||
</table>
|
||||
@@ -108,7 +112,14 @@ const markdownComponents: Components = {
|
||||
</td>
|
||||
),
|
||||
hr: () => (
|
||||
<hr className="border-0 border-t border-(--platform-surface-border)" />
|
||||
<hr className="mt-2 border-0 border-t border-(--platform-surface-border)" />
|
||||
),
|
||||
};
|
||||
|
||||
const streamingMarkdownComponents: Components = {
|
||||
...markdownComponents,
|
||||
p: ({ children }) => (
|
||||
<p className="m-0 mt-2 break-words opacity-95 first:mt-0">{children}</p>
|
||||
),
|
||||
};
|
||||
|
||||
@@ -129,20 +140,15 @@ export function ChatMarkdownMessage({
|
||||
|
||||
return (
|
||||
<MarkdownErrorBoundary fallbackText={text}>
|
||||
<div
|
||||
className={`chat-markdown min-w-0 space-y-2 break-words leading-6${
|
||||
streaming ? ' opacity-95' : ''
|
||||
}`}
|
||||
data-streaming={streaming ? 'true' : undefined}
|
||||
<ReactMarkdown
|
||||
skipHtml
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={
|
||||
streaming ? streamingMarkdownComponents : markdownComponents
|
||||
}
|
||||
>
|
||||
<ReactMarkdown
|
||||
skipHtml
|
||||
remarkPlugins={[remarkGfm]}
|
||||
components={markdownComponents}
|
||||
>
|
||||
{escapeHtmlTagsForDisplay(text)}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
{escapeHtmlTagsForDisplay(text)}
|
||||
</ReactMarkdown>
|
||||
</MarkdownErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,6 @@ describe('ChatMarkdownMessage', () => {
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.querySelector('[data-streaming="true"]')).toBeTruthy();
|
||||
expect(container.textContent).toContain('正在生成');
|
||||
expect(container.textContent).toContain('结果');
|
||||
});
|
||||
|
||||
@@ -3805,7 +3805,13 @@ export function registerProjectCommandTests() {
|
||||
submitChat('/files');
|
||||
|
||||
expect(await screen.findByText(/本地项目文件:/)).not.toBeNull();
|
||||
expect(screen.getByText(/- game\//)).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(
|
||||
(_, element) =>
|
||||
element?.tagName === 'LI' &&
|
||||
element.textContent?.trim() === '- game/',
|
||||
),
|
||||
).not.toBeNull();
|
||||
expect(screen.getByText(/- game\/index\.html/)).not.toBeNull();
|
||||
expect(screen.getByText(/- assets\/uploads\/hero\.png/)).not.toBeNull();
|
||||
expect(invoke).toHaveBeenCalledWith('list_local_project_files', {
|
||||
|
||||
+49
-22
@@ -1,5 +1,6 @@
|
||||
import { expect, fireEvent, screen, submitChat, within } from '../../harness';
|
||||
import type { PreviewShortcutInvoke } from './invoke-mock';
|
||||
import { messageBubble } from './message-bubble';
|
||||
|
||||
export async function assertPlanningAndStatusShortcutFlow(
|
||||
invoke: PreviewShortcutInvoke,
|
||||
@@ -70,7 +71,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
};
|
||||
submitChat('/blockers');
|
||||
const blockerMessages = await screen.findAllByText(/当前阻塞项:/);
|
||||
const blockerMessage = blockerMessages[blockerMessages.length - 1];
|
||||
const blockerMessage = messageBubble(
|
||||
blockerMessages[blockerMessages.length - 1],
|
||||
);
|
||||
expect(blockerMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(blockerMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
@@ -170,7 +173,7 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
};
|
||||
submitChat('/ready');
|
||||
const readyMessages = await screen.findAllByText(/试玩就绪度:/);
|
||||
const readyMessage = readyMessages[readyMessages.length - 1];
|
||||
const readyMessage = messageBubble(readyMessages[readyMessages.length - 1]);
|
||||
expect(readyMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(readyMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
@@ -270,7 +273,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
};
|
||||
submitChat('/evidence');
|
||||
const evidenceMessages = await screen.findAllByText(/验证证据台账:/);
|
||||
const evidenceMessage = evidenceMessages[evidenceMessages.length - 1];
|
||||
const evidenceMessage = messageBubble(
|
||||
evidenceMessages[evidenceMessages.length - 1],
|
||||
);
|
||||
expect(evidenceMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(evidenceMessage.textContent).toContain(
|
||||
'Run trace:已有 run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
@@ -377,7 +382,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
};
|
||||
submitChat('/deps');
|
||||
const dependencyMessages = await screen.findAllByText(/任务依赖链:/);
|
||||
const dependencyMessage = dependencyMessages[dependencyMessages.length - 1];
|
||||
const dependencyMessage = messageBubble(
|
||||
dependencyMessages[dependencyMessages.length - 1],
|
||||
);
|
||||
expect(dependencyMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
);
|
||||
@@ -479,7 +486,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
};
|
||||
submitChat('/revise');
|
||||
const revisionMessages = await screen.findAllByText(/改版草稿:/);
|
||||
const revisionMessage = revisionMessages[revisionMessages.length - 1];
|
||||
const revisionMessage = messageBubble(
|
||||
revisionMessages[revisionMessages.length - 1],
|
||||
);
|
||||
expect(revisionMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(revisionMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
@@ -604,7 +613,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
};
|
||||
submitChat('/privacy');
|
||||
const privacyMessages = await screen.findAllByText(/隐私与导出边界:/);
|
||||
const privacyMessage = privacyMessages[privacyMessages.length - 1];
|
||||
const privacyMessage = messageBubble(
|
||||
privacyMessages[privacyMessages.length - 1],
|
||||
);
|
||||
expect(privacyMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(privacyMessage.textContent).toContain(
|
||||
'本地目录:/tmp/authorized-game',
|
||||
@@ -616,10 +627,10 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
'本地预览:未启动;仅限 127.0.0.1 本机访问',
|
||||
);
|
||||
expect(privacyMessage.textContent).toContain(
|
||||
'试玩包:尚未导出;只应包含 game/**、assets/** 和 exports/README.md',
|
||||
'试玩包:尚未导出;只应包含 game/、assets/ 和 exports/README.md',
|
||||
);
|
||||
expect(privacyMessage.textContent).toContain(
|
||||
'内部文件:.agent/**、memory/**、日志、trace、配置和密钥不得进入试玩包',
|
||||
'内部文件:.agent/、memory/、日志、trace、配置和密钥不得进入试玩包',
|
||||
);
|
||||
expect(privacyMessage.textContent).toContain(
|
||||
'素材来源:2 个;上传 1 / 画板 1',
|
||||
@@ -697,7 +708,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
submitChat('/criteria');
|
||||
expect(await screen.findByText(/当前验收标准:/)).not.toBeNull();
|
||||
const criteriaMessages = screen.getAllByText(/当前验收标准:/);
|
||||
const criteriaMessage = criteriaMessages[criteriaMessages.length - 1];
|
||||
const criteriaMessage = messageBubble(
|
||||
criteriaMessages[criteriaMessages.length - 1],
|
||||
);
|
||||
expect(criteriaMessage.textContent).toContain(
|
||||
'ready:美术组 / Asset 生成首版美术素材(art-asset-plan)',
|
||||
);
|
||||
@@ -740,7 +753,7 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
submitChat('/groups');
|
||||
expect(await screen.findByText(/专业组进度:/)).not.toBeNull();
|
||||
const groupMessages = screen.getAllByText(/专业组进度:/);
|
||||
const groupMessage = groupMessages[groupMessages.length - 1];
|
||||
const groupMessage = messageBubble(groupMessages[groupMessages.length - 1]);
|
||||
expect(groupMessage.textContent).toContain(
|
||||
'美术组:完成 0/3 · active 0 · carry 0 · ready 1',
|
||||
);
|
||||
@@ -793,7 +806,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
submitChat('/balance');
|
||||
expect(await screen.findByText(/数值状态:/)).not.toBeNull();
|
||||
const balanceMessages = screen.getAllByText(/数值状态:/);
|
||||
const balanceMessage = balanceMessages[balanceMessages.length - 1];
|
||||
const balanceMessage = messageBubble(
|
||||
balanceMessages[balanceMessages.length - 1],
|
||||
);
|
||||
expect(balanceMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(balanceMessage.textContent).toContain(
|
||||
'balance-director:Director 确定数值口径 · 待处理',
|
||||
@@ -874,7 +889,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
submitChat('/budget');
|
||||
expect(await screen.findByText(/运行预算:/)).not.toBeNull();
|
||||
const budgetMessages = screen.getAllByText(/运行预算:/);
|
||||
const budgetMessage = budgetMessages[budgetMessages.length - 1];
|
||||
const budgetMessage = messageBubble(
|
||||
budgetMessages[budgetMessages.length - 1],
|
||||
);
|
||||
expect(budgetMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
);
|
||||
@@ -923,7 +940,7 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
submitChat('/qa');
|
||||
expect(await screen.findByText(/质量检查:/)).not.toBeNull();
|
||||
const qaMessages = screen.getAllByText(/质量检查:/);
|
||||
const qaMessage = qaMessages[qaMessages.length - 1];
|
||||
const qaMessage = messageBubble(qaMessages[qaMessages.length - 1]);
|
||||
expect(qaMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
);
|
||||
@@ -988,7 +1005,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
submitChat('/changes');
|
||||
expect(await screen.findByText(/最近变更:/)).not.toBeNull();
|
||||
const changeMessages = screen.getAllByText(/最近变更:/);
|
||||
const changeMessage = changeMessages[changeMessages.length - 1];
|
||||
const changeMessage = messageBubble(
|
||||
changeMessages[changeMessages.length - 1],
|
||||
);
|
||||
expect(changeMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
);
|
||||
@@ -1064,7 +1083,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
submitChat('/review');
|
||||
expect(await screen.findByText(/评审状态:/)).not.toBeNull();
|
||||
const reviewMessages = screen.getAllByText(/评审状态:/);
|
||||
const reviewMessage = reviewMessages[reviewMessages.length - 1];
|
||||
const reviewMessage = messageBubble(
|
||||
reviewMessages[reviewMessages.length - 1],
|
||||
);
|
||||
expect(reviewMessage.textContent).toContain('Evaluator:通过');
|
||||
expect(reviewMessage.textContent).toContain('返工焦点:暂无');
|
||||
expect(reviewMessage.textContent).toContain(
|
||||
@@ -1150,7 +1171,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
submitChat('/timeline');
|
||||
expect(await screen.findByText(/项目时间线:/)).not.toBeNull();
|
||||
const timelineMessages = screen.getAllByText(/项目时间线:/);
|
||||
const timelineMessage = timelineMessages[timelineMessages.length - 1];
|
||||
const timelineMessage = messageBubble(
|
||||
timelineMessages[timelineMessages.length - 1],
|
||||
);
|
||||
expect(timelineMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done',
|
||||
);
|
||||
@@ -1209,7 +1232,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
submitChat('/handoff');
|
||||
expect(await screen.findByText(/项目交接:/)).not.toBeNull();
|
||||
const handoffMessages = screen.getAllByText(/项目交接:/);
|
||||
const handoffMessage = handoffMessages[handoffMessages.length - 1];
|
||||
const handoffMessage = messageBubble(
|
||||
handoffMessages[handoffMessages.length - 1],
|
||||
);
|
||||
expect(handoffMessage.textContent).toContain(
|
||||
'- Run:run-main-shortcut-trace',
|
||||
);
|
||||
@@ -1335,14 +1360,14 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
};
|
||||
submitChat('/todo');
|
||||
const todoMessages = await screen.findAllByText(/下一轮小步:/);
|
||||
const todoMessage = todoMessages[todoMessages.length - 1];
|
||||
const todoMessage = messageBubble(todoMessages[todoMessages.length - 1]);
|
||||
expect(todoMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(todoMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
);
|
||||
expect(todoMessage.textContent).toContain('编排下一步:preview');
|
||||
expect(todoMessage.textContent).toContain(
|
||||
'1. ready:美术组 / Asset 生成首版美术素材(art-asset-plan) · 待处理 · 验收:角色、场景、UI 和动画需求已映射到画板或本地资产,且至少一张首版核心素材图已生成并登记',
|
||||
'ready:美术组 / Asset 生成首版美术素材(art-asset-plan) · 待处理 · 验收:角色、场景、UI 和动画需求已映射到画板或本地资产,且至少一张首版核心素材图已生成并登记',
|
||||
);
|
||||
expect(todoMessage.textContent).toContain(
|
||||
'边界:只整理下一步;不读取任务文件;不启动 run;不修改项目',
|
||||
@@ -1428,7 +1453,7 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
};
|
||||
submitChat('/plan');
|
||||
const planMessages = await screen.findAllByText(/下一轮分工计划:/);
|
||||
const planMessage = planMessages[planMessages.length - 1];
|
||||
const planMessage = messageBubble(planMessages[planMessages.length - 1]);
|
||||
expect(planMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(planMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
@@ -1533,7 +1558,7 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
};
|
||||
submitChat('/guide');
|
||||
const guideMessages = await screen.findAllByText(/使用导引:/);
|
||||
const guideMessage = guideMessages[guideMessages.length - 1];
|
||||
const guideMessage = messageBubble(guideMessages[guideMessages.length - 1]);
|
||||
expect(guideMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(guideMessage.textContent).toContain('当前阶段:可导出');
|
||||
expect(guideMessage.textContent).toContain(
|
||||
@@ -1627,7 +1652,9 @@ export async function assertPlanningAndStatusShortcutFlow(
|
||||
};
|
||||
submitChat('/progress');
|
||||
const progressMessages = await screen.findAllByText(/项目进度:/);
|
||||
const progressMessage = progressMessages[progressMessages.length - 1];
|
||||
const progressMessage = messageBubble(
|
||||
progressMessages[progressMessages.length - 1],
|
||||
);
|
||||
expect(progressMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(progressMessage.textContent).toContain('当前阶段:已生成');
|
||||
expect(progressMessage.textContent).toMatch(
|
||||
|
||||
+43
-23
@@ -1,5 +1,6 @@
|
||||
import { expect, fireEvent, screen, submitChat, within } from '../../harness';
|
||||
import type { PreviewShortcutInvoke } from './invoke-mock';
|
||||
import { messageBubble } from './message-bubble';
|
||||
|
||||
export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
invoke: PreviewShortcutInvoke,
|
||||
@@ -168,7 +169,9 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/listing');
|
||||
const listingMessages = await screen.findAllByText(/作品页草稿:/);
|
||||
const listingMessage = listingMessages[listingMessages.length - 1];
|
||||
const listingMessage = messageBubble(
|
||||
listingMessages[listingMessages.length - 1],
|
||||
);
|
||||
expect(listingMessage.textContent).toContain('标题:未命名游戏原型');
|
||||
expect(listingMessage.textContent).toContain(
|
||||
'一句话卖点:做一个厨房弹幕游戏',
|
||||
@@ -260,7 +263,9 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
).length;
|
||||
submitChat('/playtest');
|
||||
const playtestMessages = await screen.findAllByText(/试玩状态:/);
|
||||
const playtestMessage = playtestMessages[playtestMessages.length - 1];
|
||||
const playtestMessage = messageBubble(
|
||||
playtestMessages[playtestMessages.length - 1],
|
||||
);
|
||||
expect(playtestMessage.textContent).toContain(
|
||||
'原型:最近 run 已通过 run-main-shortcut-trace',
|
||||
);
|
||||
@@ -327,26 +332,26 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/test-plan');
|
||||
const testPlanMessages = await screen.findAllByText(/手动测试计划:/);
|
||||
const testPlanMessage = testPlanMessages[testPlanMessages.length - 1];
|
||||
const testPlanMessage = messageBubble(
|
||||
testPlanMessages[testPlanMessages.length - 1],
|
||||
);
|
||||
expect(testPlanMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(testPlanMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(testPlanMessage.textContent).toContain(
|
||||
'当前证据:最近 run 已通过 run-main-shortcut-trace;预览 未启动;入口 未见 trace 产物',
|
||||
);
|
||||
expect(testPlanMessage.textContent).toContain(
|
||||
'1. 启动预览:/run 后确认首屏不空白',
|
||||
'启动预览:/run 后确认首屏不空白',
|
||||
);
|
||||
expect(testPlanMessage.textContent).toContain(
|
||||
'2. 30 秒理解:目标、操作、得分/失败和重开可见',
|
||||
'30 秒理解:目标、操作、得分/失败和重开可见',
|
||||
);
|
||||
expect(testPlanMessage.textContent).toContain(
|
||||
'3. 输入验证:键盘/点击/触屏至少一种可完成核心动作',
|
||||
'输入验证:键盘/点击/触屏至少一种可完成核心动作',
|
||||
);
|
||||
expect(testPlanMessage.textContent).toContain('结局验证:胜利或失败后可重开');
|
||||
expect(testPlanMessage.textContent).toContain(
|
||||
'4. 结局验证:胜利或失败后可重开',
|
||||
);
|
||||
expect(testPlanMessage.textContent).toContain(
|
||||
'5. 回归检查:/mobile;/accessibility;/performance;/audio',
|
||||
'回归检查:/mobile;/accessibility;/performance;/audio',
|
||||
);
|
||||
expect(testPlanMessage.textContent).toContain(
|
||||
'preview-readiness:程序组 / Preview 执行静态自检 · 待处理',
|
||||
@@ -430,7 +435,9 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/audience');
|
||||
const audienceMessages = await screen.findAllByText(/首批试玩对象:/);
|
||||
const audienceMessage = audienceMessages[audienceMessages.length - 1];
|
||||
const audienceMessage = messageBubble(
|
||||
audienceMessages[audienceMessages.length - 1],
|
||||
);
|
||||
expect(audienceMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(audienceMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(audienceMessage.textContent).toContain(
|
||||
@@ -543,7 +550,9 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/invite');
|
||||
const inviteMessages = await screen.findAllByText(/试玩邀请:/);
|
||||
const inviteMessage = inviteMessages[inviteMessages.length - 1];
|
||||
const inviteMessage = messageBubble(
|
||||
inviteMessages[inviteMessages.length - 1],
|
||||
);
|
||||
expect(inviteMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(inviteMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(inviteMessage.textContent).toContain(
|
||||
@@ -721,7 +730,9 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/survey');
|
||||
const surveyMessages = await screen.findAllByText(/试玩问卷:/);
|
||||
const surveyMessage = surveyMessages[surveyMessages.length - 1];
|
||||
const surveyMessage = messageBubble(
|
||||
surveyMessages[surveyMessages.length - 1],
|
||||
);
|
||||
expect(surveyMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(surveyMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(surveyMessage.textContent).toContain(
|
||||
@@ -828,7 +839,7 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/cover');
|
||||
const coverMessages = await screen.findAllByText(/封面与缩略图:/);
|
||||
const coverMessage = coverMessages[coverMessages.length - 1];
|
||||
const coverMessage = messageBubble(coverMessages[coverMessages.length - 1]);
|
||||
expect(coverMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(coverMessage.textContent).toContain(
|
||||
'当前状态:最近 run 已通过 run-main-shortcut-trace;预览 未启动',
|
||||
@@ -937,7 +948,9 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/screenshots');
|
||||
const screenshotMessages = await screen.findAllByText(/宣传截图:/);
|
||||
const screenshotMessage = screenshotMessages[screenshotMessages.length - 1];
|
||||
const screenshotMessage = messageBubble(
|
||||
screenshotMessages[screenshotMessages.length - 1],
|
||||
);
|
||||
expect(screenshotMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(screenshotMessage.textContent).toContain(
|
||||
'当前状态:最近 run 已通过 run-main-shortcut-trace;预览 未启动',
|
||||
@@ -1046,7 +1059,9 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/trailer');
|
||||
const trailerMessages = await screen.findAllByText(/试玩短视频:/);
|
||||
const trailerMessage = trailerMessages[trailerMessages.length - 1];
|
||||
const trailerMessage = messageBubble(
|
||||
trailerMessages[trailerMessages.length - 1],
|
||||
);
|
||||
expect(trailerMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(trailerMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(trailerMessage.textContent).toContain(
|
||||
@@ -1156,7 +1171,7 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/faq');
|
||||
const faqMessages = await screen.findAllByText(/试玩 FAQ:/);
|
||||
const faqMessage = faqMessages[faqMessages.length - 1];
|
||||
const faqMessage = messageBubble(faqMessages[faqMessages.length - 1]);
|
||||
expect(faqMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(faqMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(faqMessage.textContent).toContain(
|
||||
@@ -1261,7 +1276,7 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/post');
|
||||
const postMessages = await screen.findAllByText(/社区发布文案:/);
|
||||
const postMessage = postMessages[postMessages.length - 1];
|
||||
const postMessage = messageBubble(postMessages[postMessages.length - 1]);
|
||||
expect(postMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(postMessage.textContent).toContain('一句话:做一个厨房弹幕游戏');
|
||||
expect(postMessage.textContent).toContain(
|
||||
@@ -1372,7 +1387,7 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/store');
|
||||
const storeMessages = await screen.findAllByText(/上架资料:/);
|
||||
const storeMessage = storeMessages[storeMessages.length - 1];
|
||||
const storeMessage = messageBubble(storeMessages[storeMessages.length - 1]);
|
||||
expect(storeMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(storeMessage.textContent).toContain('一句话:做一个厨房弹幕游戏');
|
||||
expect(storeMessage.textContent).toContain(
|
||||
@@ -1482,7 +1497,9 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/media-kit');
|
||||
const mediaKitMessages = await screen.findAllByText(/媒体资料包:/);
|
||||
const mediaKitMessage = mediaKitMessages[mediaKitMessages.length - 1];
|
||||
const mediaKitMessage = messageBubble(
|
||||
mediaKitMessages[mediaKitMessages.length - 1],
|
||||
);
|
||||
expect(mediaKitMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(mediaKitMessage.textContent).toContain('一句话:做一个厨房弹幕游戏');
|
||||
expect(mediaKitMessage.textContent).toContain(
|
||||
@@ -1589,8 +1606,9 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/release-notes');
|
||||
const releaseNotesMessages = await screen.findAllByText(/试玩更新说明:/);
|
||||
const releaseNotesMessage =
|
||||
releaseNotesMessages[releaseNotesMessages.length - 1];
|
||||
const releaseNotesMessage = messageBubble(
|
||||
releaseNotesMessages[releaseNotesMessages.length - 1],
|
||||
);
|
||||
expect(releaseNotesMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(releaseNotesMessage.textContent).toContain(
|
||||
'一句话:做一个厨房弹幕游戏',
|
||||
@@ -1707,7 +1725,9 @@ export async function assertPlaytestAndReleaseShortcutFlow(
|
||||
};
|
||||
submitChat('/known-issues');
|
||||
const knownIssueMessages = await screen.findAllByText(/已知问题清单:/);
|
||||
const knownIssueMessage = knownIssueMessages[knownIssueMessages.length - 1];
|
||||
const knownIssueMessage = messageBubble(
|
||||
knownIssueMessages[knownIssueMessages.length - 1],
|
||||
);
|
||||
expect(knownIssueMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(knownIssueMessage.textContent).toContain(
|
||||
'当前状态:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed;预览 未启动',
|
||||
|
||||
+60
-29
@@ -1,5 +1,6 @@
|
||||
import { expect, fireEvent, screen, submitChat, within } from '../../harness';
|
||||
import type { PreviewShortcutInvoke } from './invoke-mock';
|
||||
import { messageBubble } from './message-bubble';
|
||||
|
||||
export async function assertProjectAndDesignShortcutFlow(
|
||||
invoke: PreviewShortcutInvoke,
|
||||
@@ -40,17 +41,31 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
expect(screen.queryByText(/art-secret/)).toBeNull();
|
||||
|
||||
submitChat('/agents');
|
||||
expect(await screen.findByText('Agent 状态:')).not.toBeNull();
|
||||
expect(
|
||||
await screen.findByText(
|
||||
/Agent 状态:[\s\S]*美术组 \/ Asset · 生成首版美术素材 · 待处理[\s\S]*LLM:已配置 · art-model/,
|
||||
(_, element) =>
|
||||
element?.tagName === 'LI' &&
|
||||
element.textContent?.includes(
|
||||
'美术组 / Asset · 生成首版美术素材 · 待处理',
|
||||
) === true,
|
||||
),
|
||||
).not.toBeNull();
|
||||
expect(
|
||||
await screen.findByText(
|
||||
(_, element) =>
|
||||
element?.tagName === 'LI' &&
|
||||
element.textContent?.includes('LLM:已配置 · art-model') === true,
|
||||
),
|
||||
).not.toBeNull();
|
||||
submitChat('/agent-conversations');
|
||||
expect(
|
||||
await screen.findByText(
|
||||
/Agent 对话读取命令:[\s\S]*美术组 \/ Asset · 生成首版美术素材:\/read \.agent\/conversations\/agents\/art-asset-plan\.jsonl/,
|
||||
),
|
||||
).not.toBeNull();
|
||||
const agentConversationMessage =
|
||||
await screen.findByText('Agent 对话读取命令:');
|
||||
const agentConversationBubble = agentConversationMessage.closest('.message');
|
||||
expect(agentConversationBubble).not.toBeNull();
|
||||
expect(agentConversationBubble?.textContent).toContain(
|
||||
'美术组 / Asset · 生成首版美术素材:/read .agent/conversations/agents/art-asset-plan.jsonl',
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '读取拆解创作方向对话' }));
|
||||
expect(screen.getByLabelText('创作想法')).toHaveProperty(
|
||||
'value',
|
||||
@@ -63,11 +78,13 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
}),
|
||||
);
|
||||
submitChat('/agent-memories');
|
||||
expect(
|
||||
await screen.findByText(
|
||||
/Agent 私有记忆读取命令:[\s\S]*美术组 \/ Asset · 生成首版美术素材:\/read memory\/agents\/art\/asset\.md/,
|
||||
),
|
||||
).not.toBeNull();
|
||||
const agentMemoryMessage =
|
||||
await screen.findByText('Agent 私有记忆读取命令:');
|
||||
const agentMemoryBubble = agentMemoryMessage.closest('.message');
|
||||
expect(agentMemoryBubble).not.toBeNull();
|
||||
expect(agentMemoryBubble?.textContent).toContain(
|
||||
'美术组 / Asset · 生成首版美术素材:/read memory/agents/art/asset.md',
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '读取拆解创作方向记忆' }));
|
||||
expect(screen.getByLabelText('创作想法')).toHaveProperty(
|
||||
'value',
|
||||
@@ -129,7 +146,7 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
submitChat('/goal');
|
||||
expect(await screen.findByText(/创作目标:/)).not.toBeNull();
|
||||
const goalMessages = screen.getAllByText(/创作目标:/);
|
||||
const goalMessage = goalMessages[goalMessages.length - 1];
|
||||
const goalMessage = messageBubble(goalMessages[goalMessages.length - 1]);
|
||||
expect(goalMessage.textContent).toContain('Manifest:做一个厨房弹幕游戏');
|
||||
expect(goalMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
@@ -187,7 +204,7 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/spec');
|
||||
const specMessages = await screen.findAllByText(/创作规格包:/);
|
||||
const specMessage = specMessages[specMessages.length - 1];
|
||||
const specMessage = messageBubble(specMessages[specMessages.length - 1]);
|
||||
expect(specMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(specMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(specMessage.textContent).toContain(
|
||||
@@ -287,7 +304,7 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
submitChat('/mvp');
|
||||
expect(await screen.findByText(/MVP 范围:/)).not.toBeNull();
|
||||
const mvpMessages = screen.getAllByText(/MVP 范围:/);
|
||||
const mvpMessage = mvpMessages[mvpMessages.length - 1];
|
||||
const mvpMessage = messageBubble(mvpMessages[mvpMessages.length - 1]);
|
||||
expect(mvpMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(mvpMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(mvpMessage.textContent).toContain(
|
||||
@@ -383,7 +400,7 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/pitch');
|
||||
const pitchMessages = await screen.findAllByText(/试玩定位:/);
|
||||
const pitchMessage = pitchMessages[pitchMessages.length - 1];
|
||||
const pitchMessage = messageBubble(pitchMessages[pitchMessages.length - 1]);
|
||||
expect(pitchMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(pitchMessage.textContent).toContain('一句话:做一个厨房弹幕游戏');
|
||||
expect(pitchMessage.textContent).toContain(
|
||||
@@ -478,7 +495,7 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/demo');
|
||||
const demoMessages = await screen.findAllByText(/试玩讲解稿:/);
|
||||
const demoMessage = demoMessages[demoMessages.length - 1];
|
||||
const demoMessage = messageBubble(demoMessages[demoMessages.length - 1]);
|
||||
expect(demoMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(demoMessage.textContent).toContain(
|
||||
'30 秒开场:这是《未命名游戏原型》,目标是做一个厨房弹幕游戏',
|
||||
@@ -578,7 +595,9 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
submitChat('/rules');
|
||||
expect(await screen.findByText(/玩法操作:/)).not.toBeNull();
|
||||
const controlMessages = screen.getAllByText(/玩法操作:/);
|
||||
const controlMessage = controlMessages[controlMessages.length - 1];
|
||||
const controlMessage = messageBubble(
|
||||
controlMessages[controlMessages.length - 1],
|
||||
);
|
||||
expect(controlMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(controlMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(controlMessage.textContent).toContain(
|
||||
@@ -682,7 +701,9 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/tutorial');
|
||||
const tutorialMessages = await screen.findAllByText(/新手引导:/);
|
||||
const tutorialMessage = tutorialMessages[tutorialMessages.length - 1];
|
||||
const tutorialMessage = messageBubble(
|
||||
tutorialMessages[tutorialMessages.length - 1],
|
||||
);
|
||||
expect(tutorialMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(tutorialMessage.textContent).toContain('首屏目标:做一个厨房弹幕游戏');
|
||||
expect(tutorialMessage.textContent).toContain(
|
||||
@@ -785,7 +806,9 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/mobile');
|
||||
const mobileMessages = await screen.findAllByText(/移动试玩:/);
|
||||
const mobileMessage = mobileMessages[mobileMessages.length - 1];
|
||||
const mobileMessage = messageBubble(
|
||||
mobileMessages[mobileMessages.length - 1],
|
||||
);
|
||||
expect(mobileMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(mobileMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(mobileMessage.textContent).toContain(
|
||||
@@ -894,8 +917,9 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/compatibility');
|
||||
const compatibilityMessages = await screen.findAllByText(/兼容性说明:/);
|
||||
const compatibilityMessage =
|
||||
compatibilityMessages[compatibilityMessages.length - 1];
|
||||
const compatibilityMessage = messageBubble(
|
||||
compatibilityMessages[compatibilityMessages.length - 1],
|
||||
);
|
||||
expect(compatibilityMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(compatibilityMessage.textContent).toContain(
|
||||
'目标:做一个厨房弹幕游戏',
|
||||
@@ -1009,8 +1033,9 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/accessibility');
|
||||
const accessibilityMessages = await screen.findAllByText(/可读性与无障碍:/);
|
||||
const accessibilityMessage =
|
||||
accessibilityMessages[accessibilityMessages.length - 1];
|
||||
const accessibilityMessage = messageBubble(
|
||||
accessibilityMessages[accessibilityMessages.length - 1],
|
||||
);
|
||||
expect(accessibilityMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(accessibilityMessage.textContent).toContain(
|
||||
'目标:做一个厨房弹幕游戏',
|
||||
@@ -1132,8 +1157,9 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/localization');
|
||||
const localizationMessages = await screen.findAllByText(/本地化与文案:/);
|
||||
const localizationMessage =
|
||||
localizationMessages[localizationMessages.length - 1];
|
||||
const localizationMessage = messageBubble(
|
||||
localizationMessages[localizationMessages.length - 1],
|
||||
);
|
||||
expect(localizationMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(localizationMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(localizationMessage.textContent).toContain(
|
||||
@@ -1262,8 +1288,9 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/performance');
|
||||
const performanceMessages = await screen.findAllByText(/性能与加载:/);
|
||||
const performanceMessage =
|
||||
performanceMessages[performanceMessages.length - 1];
|
||||
const performanceMessage = messageBubble(
|
||||
performanceMessages[performanceMessages.length - 1],
|
||||
);
|
||||
expect(performanceMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(performanceMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(performanceMessage.textContent).toContain(
|
||||
@@ -1379,7 +1406,9 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/polish');
|
||||
const polishMessages = await screen.findAllByText(/试玩前打磨:/);
|
||||
const polishMessage = polishMessages[polishMessages.length - 1];
|
||||
const polishMessage = messageBubble(
|
||||
polishMessages[polishMessages.length - 1],
|
||||
);
|
||||
expect(polishMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(polishMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(polishMessage.textContent).toContain(
|
||||
@@ -1491,7 +1520,9 @@ export async function assertProjectAndDesignShortcutFlow(
|
||||
};
|
||||
submitChat('/credits');
|
||||
const creditMessages = await screen.findAllByText(/素材署名:/);
|
||||
const creditMessage = creditMessages[creditMessages.length - 1];
|
||||
const creditMessage = messageBubble(
|
||||
creditMessages[creditMessages.length - 1],
|
||||
);
|
||||
expect(creditMessage.textContent).toContain('当前资产:2 个');
|
||||
expect(creditMessage.textContent).toContain('来源分布:上传 1 / 画板 1');
|
||||
expect(creditMessage.textContent).toContain(
|
||||
|
||||
+18
-17
@@ -7,6 +7,7 @@ import {
|
||||
within,
|
||||
} from '../../harness';
|
||||
import type { PreviewShortcutInvoke } from './invoke-mock';
|
||||
import { messageBubble } from './message-bubble';
|
||||
|
||||
export async function assertProjectToolsAndPreviewFlow(
|
||||
invoke: PreviewShortcutInvoke,
|
||||
@@ -36,7 +37,9 @@ export async function assertProjectToolsAndPreviewFlow(
|
||||
};
|
||||
submitChat('/feedback');
|
||||
const feedbackMessages = await screen.findAllByText(/试玩反馈:/);
|
||||
const feedbackMessage = feedbackMessages[feedbackMessages.length - 1];
|
||||
const feedbackMessage = messageBubble(
|
||||
feedbackMessages[feedbackMessages.length - 1],
|
||||
);
|
||||
expect(feedbackMessage.textContent).toContain(
|
||||
'Run:run-main-shortcut-trace · passed / done · 1/3 轮 · evaluator-passed',
|
||||
);
|
||||
@@ -125,7 +128,9 @@ export async function assertProjectToolsAndPreviewFlow(
|
||||
};
|
||||
submitChat('/retention');
|
||||
const retentionMessages = await screen.findAllByText(/复玩观察:/);
|
||||
const retentionMessage = retentionMessages[retentionMessages.length - 1];
|
||||
const retentionMessage = messageBubble(
|
||||
retentionMessages[retentionMessages.length - 1],
|
||||
);
|
||||
expect(retentionMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(retentionMessage.textContent).toContain('目标:做一个厨房弹幕游戏');
|
||||
expect(retentionMessage.textContent).toContain(
|
||||
@@ -233,7 +238,7 @@ export async function assertProjectToolsAndPreviewFlow(
|
||||
};
|
||||
submitChat('/share');
|
||||
const shareMessages = await screen.findAllByText(/试玩交付:/);
|
||||
const shareMessage = shareMessages[shareMessages.length - 1];
|
||||
const shareMessage = messageBubble(shareMessages[shareMessages.length - 1]);
|
||||
expect(shareMessage.textContent).toContain('项目:未命名游戏原型');
|
||||
expect(shareMessage.textContent).toContain('目录:/tmp/authorized-game');
|
||||
expect(shareMessage.textContent).toContain(
|
||||
@@ -438,11 +443,11 @@ export async function assertProjectToolsAndPreviewFlow(
|
||||
expect(composerInput).toHaveProperty('value', '/read game/index.html');
|
||||
await waitFor(() => expect(document.activeElement).toBe(composerInput));
|
||||
submitChat('/run-artifacts');
|
||||
expect(
|
||||
await screen.findByText(
|
||||
/最近 Run 产物读取命令:[\s\S]*exports\/README\.md · 128B · fnv1a64:exports:\/read exports\/README\.md/,
|
||||
),
|
||||
).not.toBeNull();
|
||||
const runArtifactsHeading =
|
||||
await screen.findByText('最近 Run 产物读取命令:');
|
||||
expect(messageBubble(runArtifactsHeading).textContent).toContain(
|
||||
'exports/README.md · 128B · fnv1a64:exports:/read exports/README.md',
|
||||
);
|
||||
fireEvent.click(screen.getByRole('button', { name: '读取首个 Run 产物' }));
|
||||
expect(composerInput).toHaveProperty('value', '/read exports/README.md');
|
||||
expect(invoke).not.toHaveBeenCalledWith(
|
||||
@@ -457,8 +462,9 @@ export async function assertProjectToolsAndPreviewFlow(
|
||||
submitChat('/passes');
|
||||
const passArtifactMessages =
|
||||
await screen.findAllByText(/Agent 轮次产物读取命令:/);
|
||||
const passArtifactMessage =
|
||||
passArtifactMessages[passArtifactMessages.length - 1];
|
||||
const passArtifactMessage = messageBubble(
|
||||
passArtifactMessages[passArtifactMessages.length - 1],
|
||||
);
|
||||
expect(passArtifactMessage.textContent).toContain(
|
||||
'.agent/passes/pass-1/agenda.md · 96B · fnv1a64:agenda',
|
||||
);
|
||||
@@ -564,13 +570,8 @@ export async function assertProjectToolsAndPreviewFlow(
|
||||
expect(screen.getByText(/下一步:设计实现组 \/ Director/)).not.toBeNull();
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Trace' }));
|
||||
expect(
|
||||
await screen.findByText(
|
||||
(content) =>
|
||||
content.trimStart().startsWith('Run:run-main-shortcut-trace') &&
|
||||
content.includes('产物快照:'),
|
||||
),
|
||||
).not.toBeNull();
|
||||
const traceHeading = await screen.findByText(/Run:run-main-shortcut-trace/);
|
||||
expect(messageBubble(traceHeading).textContent).toContain('产物快照:');
|
||||
expect(screen.getByText(/产物快照:/)).not.toBeNull();
|
||||
expect(
|
||||
screen.getByText(/exports\/README\.md · fnv1a64:exports/),
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
export function messageBubble(element: Element): HTMLElement {
|
||||
const bubble = element.closest<HTMLElement>('.message');
|
||||
if (!bubble) {
|
||||
throw new Error('Expected chat message bubble');
|
||||
}
|
||||
return bubble;
|
||||
}
|
||||
Reference in New Issue
Block a user