test(游戏共创): 族谱验收脚本补网页端「改造这个作品」入口断言
- scripts/check-game-distribution-lineage-e2e.mjs 新增 5 条浏览器断言,插入位置在族谱树断言之后、
软删除母版之前(母版一删该入口即消失,顺序不能反):
· 已公开且授权开放的母版详情页出现「改造这个作品」入口
· 展开面板:作品 ID 等于母版;可复制作品链接指向 /games/detail?id=<母版>;面板给出
「打开陶泥儿客户端 → 首页『从平台作品开始创作』→ 粘贴」引导,并给出成品包只能作为
可玩参考与素材、不能直接再次发布的口径
· 点「复制作品 ID」后只断言可观察 UI 反馈(按钮变为「作品 ID 已复制」):真实剪贴板内容
需要额外权限,不在自动化范围,属人工验收项
· 对照断言:已公开但授权为 forbidden 的作品详情页不出现该入口(也不出现「登录后可改造」)
· 展开态截图存临时目录并打印路径
- 入口文案常量做成可覆盖(E2E_REMIX_*),取值对齐 GameDetailPage.tsx:430-490(Commit 392cb3bb0)
- 删除后回看族谱改为导航到先前记录的 lineage URL(原实现用 page.reload,新增入口步骤会先切走页面)
- 实测:本脚本 99 项 99 PASS / 0 FAIL / 0 SKIP;复跑 check:game-distribution-fork-authorization-e2e
48/48 未回归
This commit is contained in:
@@ -69,6 +69,15 @@ const LINEAGE_ROOT_LABEL = process.env.E2E_LINEAGE_LABEL_ROOT ?? '母版';
|
||||
const LINEAGE_ANCHOR_LABEL = process.env.E2E_LINEAGE_LABEL_ANCHOR ?? '当前作品';
|
||||
const LINEAGE_PARENT_UNAVAILABLE =
|
||||
process.env.E2E_LINEAGE_LABEL_PARENT_UNAVAILABLE ?? '原作品已不可用';
|
||||
// 详情页「改造这个作品」入口(392cb3bb0):标签与文案取自 GameDetailPage.tsx:430-490。
|
||||
const REMIX_ENTRY_LABEL = process.env.E2E_REMIX_ENTRY_LABEL ?? '改造这个作品';
|
||||
const REMIX_LOGIN_LABEL = process.env.E2E_REMIX_LOGIN_LABEL ?? '登录后可改造';
|
||||
const REMIX_STEPS_TEXT = process.env.E2E_REMIX_STEPS_TEXT ?? '打开陶泥儿客户端';
|
||||
const REMIX_NOTE_TEXT = process.env.E2E_REMIX_NOTE_TEXT ?? '不能直接再次发布';
|
||||
const REMIX_COPY_ID_LABEL =
|
||||
process.env.E2E_REMIX_COPY_ID_LABEL ?? '复制作品 ID';
|
||||
const REMIX_COPIED_ID_LABEL =
|
||||
process.env.E2E_REMIX_COPIED_ID_LABEL ?? '作品 ID 已复制';
|
||||
|
||||
// 节点键集合由实现钉死(shared-contracts/src/game_distribution.rs:262-281),多一个键就失败。
|
||||
const LINEAGE_NODE_KEYS = [
|
||||
@@ -520,6 +529,7 @@ async function runBrowserStep({
|
||||
parentId,
|
||||
parentTitle,
|
||||
parentAuthorName,
|
||||
forbiddenGameId,
|
||||
deleteParent,
|
||||
}) {
|
||||
if (SKIP_BROWSER) {
|
||||
@@ -615,6 +625,113 @@ async function runBrowserStep({
|
||||
);
|
||||
await page.screenshot({ path: beforeShot, fullPage: true });
|
||||
check('浏览器视觉:删除前族谱已截图', true, `截图路径=${beforeShot}`);
|
||||
const lineageUrl = page.url();
|
||||
|
||||
// 详情页「改造这个作品」(392cb3bb0):触发条件 = 已公开 + 授权非禁止(GameDetailPage.tsx:350)。
|
||||
await page.goto(`${WEB}/games/detail?id=${parentId}`, {
|
||||
waitUntil: 'commit',
|
||||
timeout: 120_000,
|
||||
});
|
||||
const remixEntry = page.getByRole('button', {
|
||||
name: REMIX_ENTRY_LABEL,
|
||||
exact: true,
|
||||
});
|
||||
const remixVisible = await visible(remixEntry, 60_000);
|
||||
check(
|
||||
'浏览器视觉:已公开且授权开放的母版详情页出现「改造这个作品」入口',
|
||||
remixVisible,
|
||||
`parent=${parentId} label=${REMIX_ENTRY_LABEL}`,
|
||||
);
|
||||
|
||||
if (remixVisible) {
|
||||
await remixEntry.click();
|
||||
const panel = page.locator('#game-fork-remix-panel');
|
||||
check(
|
||||
'浏览器视觉:展开后出现获取方式面板(引导去客户端粘贴)',
|
||||
(await visible(panel)) &&
|
||||
(await visible(page.getByText(REMIX_STEPS_TEXT, { exact: false }))),
|
||||
'panel=#game-fork-remix-panel',
|
||||
);
|
||||
const codes = panel.locator('.game-fork-remix-value code');
|
||||
const idText = (
|
||||
await codes
|
||||
.nth(0)
|
||||
.innerText()
|
||||
.catch(() => '')
|
||||
).trim();
|
||||
const linkText = (
|
||||
await codes
|
||||
.nth(1)
|
||||
.innerText()
|
||||
.catch(() => '')
|
||||
).trim();
|
||||
check(
|
||||
'浏览器视觉:面板显示的作品 ID 等于母版',
|
||||
idText === parentId,
|
||||
`id=${idText} 期望=${parentId}`,
|
||||
);
|
||||
check(
|
||||
'浏览器视觉:面板显示的可复制作品链接指向该作品',
|
||||
linkText.includes(`/games/detail?id=${parentId}`),
|
||||
`link=${linkText}`,
|
||||
);
|
||||
check(
|
||||
'浏览器视觉:面板给出「成品包只能作为参考且不能直接再次发布」的口径',
|
||||
await visible(page.getByText(REMIX_NOTE_TEXT, { exact: false })),
|
||||
`note 包含 ${REMIX_NOTE_TEXT}`,
|
||||
);
|
||||
|
||||
const remixShot = path.join(screenshotDir, 'game-remix-panel.png');
|
||||
await page.screenshot({ path: remixShot, fullPage: true });
|
||||
check(
|
||||
'浏览器视觉:改造入口展开态已截图',
|
||||
true,
|
||||
`截图路径=${remixShot}`,
|
||||
);
|
||||
|
||||
// 复制反馈只看 UI(读系统剪贴板需要额外权限,属人工验收项)。
|
||||
const copyId = page.getByRole('button', {
|
||||
name: REMIX_COPY_ID_LABEL,
|
||||
exact: true,
|
||||
});
|
||||
const copyIdVisible = await visible(copyId, 15_000);
|
||||
check(
|
||||
'浏览器视觉:复制作品 ID 按钮可见',
|
||||
copyIdVisible,
|
||||
`label=${REMIX_COPY_ID_LABEL}`,
|
||||
);
|
||||
if (copyIdVisible) {
|
||||
await copyId.click();
|
||||
check(
|
||||
'浏览器视觉:点「复制作品 ID」后按钮出现已复制反馈',
|
||||
await visible(
|
||||
page.getByRole('button', {
|
||||
name: REMIX_COPIED_ID_LABEL,
|
||||
exact: true,
|
||||
}),
|
||||
15_000,
|
||||
),
|
||||
`期望按钮名=${REMIX_COPIED_ID_LABEL}(未校验真实剪贴板内容)`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 对照:已公开但授权仍为 forbidden 的作品不得出现该入口(也不得出现未登录态文案)。
|
||||
await page.goto(`${WEB}/games/detail?id=${forbiddenGameId}`, {
|
||||
waitUntil: 'commit',
|
||||
timeout: 120_000,
|
||||
});
|
||||
await page.waitForTimeout(1500);
|
||||
check(
|
||||
'浏览器视觉:授权 forbidden 的作品详情页没有「改造这个作品」入口',
|
||||
(await page
|
||||
.getByRole('button', { name: REMIX_ENTRY_LABEL, exact: true })
|
||||
.count()) === 0 &&
|
||||
(await page
|
||||
.getByRole('button', { name: REMIX_LOGIN_LABEL, exact: true })
|
||||
.count()) === 0,
|
||||
`forbidden=${forbiddenGameId}`,
|
||||
);
|
||||
|
||||
// 父作品软删除后:树仍在,但父节点内容不得再出现。
|
||||
const deleted = await deleteParent();
|
||||
@@ -623,7 +740,7 @@ async function runBrowserStep({
|
||||
deleted?.status === 200,
|
||||
brief(deleted),
|
||||
);
|
||||
await page.reload({ waitUntil: 'commit', timeout: 120_000 });
|
||||
await page.goto(lineageUrl, { waitUntil: 'commit', timeout: 120_000 });
|
||||
check(
|
||||
'浏览器视觉:父作品不可用后族谱页显示降级文案',
|
||||
await visible(page.getByText(LINEAGE_PARENT_UNAVAILABLE), 60_000),
|
||||
@@ -1102,6 +1219,7 @@ async function main() {
|
||||
parentId,
|
||||
parentTitle,
|
||||
parentAuthorName: authorDisplayName,
|
||||
forbiddenGameId: blockedId,
|
||||
deleteParent,
|
||||
});
|
||||
if (SKIP_BROWSER) {
|
||||
|
||||
Reference in New Issue
Block a user