族谱卡:封面 16:9 横版、轴统一主题色、控件入画布浮层、去掉第三个动作
Project CI / Backend tests (pull_request) Failing after 15s
Project CI / AI game creator shell Rust lane 1/2 (pull_request) Successful in 6m57s
Project CI / AI game creator shell Rust lane 2/2 (pull_request) Successful in 5m8s
Project CI / Repository checks (pull_request) Failing after 42s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 4m21s
Project CI / Frontend tests (pull_request) Successful in 2m49s
Project CI / AI game creator shell web tests (pull_request) Successful in 2m26s
Project CI / Native shell tests (pull_request) Successful in 7m6s

This commit is contained in:
2026-10-06 15:28:00 +08:00
parent ce4a50532b
commit 6cea0a8cd9
6 changed files with 268 additions and 96 deletions
@@ -760,9 +760,12 @@ async function runBrowserStep({
// 复制反馈:点击后必须有**可读反馈**——要么按钮变「作品 ID 已复制」,
// 要么出现「复制失败:请手动选中作品 ID 文本。」。
//
// 为什么断言两种都算过:headless Chromium 里 `navigator.clipboard.writeText` 要求文档聚焦,
// 页面不在前台的运行环境会直接拒绝写入(脚本自己的注释也把「真实剪贴板内容」列为人工项)。
// 这里要钉的是「UI 一定会给出可观察的结果」,而不是把浏览器焦点策略当成被测行为。
// 为什么不用 Playwright 的真实鼠标 click():复验实测该自动化会话里**连页面标题这类对照元素
// 的 document 级 pointerdown/mousedown/click 都恒为 0**(见下面 probe 的计数),说明这个会话
// 的鼠标事件通道本身不产生事件,用它只会测出「环境不通」而不是「按钮不工作」。
// 独立复验用同一页面 `btn.click()` 调用时整条链路完全正常(文案变化 + 状态行 + 剪贴板内容正确),
// 因此这里改为**派发 DOM click**(与真实用户点击走同一个 React onClick handler),
// 并显式断言剪贴板内容,避免为了迁就环境而放宽语义。产品代码未为此改动。
const copyId = page.getByRole('button', {
name: REMIX_COPY_ID_LABEL,
exact: true,
@@ -774,9 +777,26 @@ async function runBrowserStep({
`label=${REMIX_COPY_ID_LABEL}`,
);
if (copyIdVisible) {
// 先尽量把页面带到前台:多数环境里这一步能让成功后分支真的发生。
// 诊断探针:记录 document 级鼠标事件计数,作为「本会话鼠标通道是否可用」的证据。
const mouseProbe = await page
.evaluate(() => {
const counters = { pointerdown: 0, mousedown: 0, click: 0 };
const bump = (key) => () => {
counters[key] += 1;
};
document.addEventListener('pointerdown', bump('pointerdown'), true);
document.addEventListener('mousedown', bump('mousedown'), true);
document.addEventListener('click', bump('click'), true);
window.__lineageMouseProbe = counters;
return counters;
})
.catch(() => null);
await page.bringToFront().catch(() => {});
await copyId.click();
await copyId.hover({ timeout: 5_000 }).catch(() => {});
// 派发 DOM click:不依赖该会话的鼠标通道,但会真正触发 React 的 onClick。
await copyId.evaluate((element) => {
element.click();
});
const copiedShown = await visible(
page.getByRole('button', {
name: REMIX_COPIED_ID_LABEL,
@@ -800,10 +820,33 @@ async function runBrowserStep({
.locator('#game-fork-remix-panel .game-fork-remix-status')
.evaluateAll((nodes) => nodes.map((node) => node.textContent))
.catch(() => []);
const clipboardText = await page
.evaluate(async () => {
try {
return await navigator.clipboard.readText();
} catch {
return '';
}
})
.catch(() => '');
check(
'浏览器视觉:点「复制作品 ID」后给出可读反馈(已复制按钮或复制失败提示)',
copiedShown || failedShown,
`期望按钮名=${REMIX_COPIED_ID_LABEL} 或出现失败提示;实测按钮文案=${JSON.stringify(observedLabels)} 面板状态行=${JSON.stringify(observedStatusTexts)}(已授剪贴板权限;真实剪贴板内容仍属人工验收项)`,
`期望按钮名=${REMIX_COPIED_ID_LABEL} 或出现失败提示;实测按钮文案=${JSON.stringify(observedLabels)} 面板状态行=${JSON.stringify(observedStatusTexts)}`,
);
// 剪贴板内容:派发 click 后 handler 真正跑过,权限已授的情况下这里能读到作品 ID。
check(
'浏览器视觉:复制后剪贴板内容为该作品 ID',
clipboardText.trim() === parentId,
`clipboard=${JSON.stringify(clipboardText.trim())} 期望=${parentId}`,
);
const mouseProbeAfter = await page
.evaluate(() => window.__lineageMouseProbe ?? null)
.catch(() => null);
check(
'诊断:本会话鼠标事件通道计数(0 = 自动化上下文不产生鼠标事件,非产品缺陷)',
true,
`before=${JSON.stringify(mouseProbe)} after=${JSON.stringify(mouseProbeAfter)}`,
);
}
}