f2002e1d21
Co-Authored-By: Claude Code <noreply@anthropic.com>
187 lines
6.3 KiB
JavaScript
187 lines
6.3 KiB
JavaScript
/**
|
||
* UI06 B0:管理后台「改动前」截图脚本。
|
||
* 目标:测试服务器上当前部署的后台(= HEAD 的 admin 产物),1440×1000 全页截图,
|
||
* 命名与 admin-preview 示例编号对齐,存入同目录 before/。
|
||
* 用法:ADMIN_PW=<密码> node capture-before.cjs [--out before] [--base http://219.157.255.213:32005]
|
||
* 凭据不写入本文件;仅浏览器会话内存。
|
||
*/
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const { chromium } = require(
|
||
require.resolve('@playwright/test', { paths: [path.resolve(__dirname, '../../../apps/admin')] })
|
||
);
|
||
|
||
const args = process.argv.slice(2);
|
||
const argOf = (name, def) => {
|
||
const i = args.indexOf(name);
|
||
return i >= 0 ? args[i + 1] : def;
|
||
};
|
||
const BASE = argOf('--base', 'http://219.157.255.213:32005');
|
||
const OUT = path.join(__dirname, argOf('--out', 'before'));
|
||
const ACCOUNT = process.env.ADMIN_ACCOUNT || 'admin';
|
||
const PASSWORD = process.env.ADMIN_PW;
|
||
if (!PASSWORD) {
|
||
console.error('missing ADMIN_PW env');
|
||
process.exit(1);
|
||
}
|
||
|
||
const notes = [];
|
||
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
||
|
||
(async () => {
|
||
fs.mkdirSync(OUT, { recursive: true });
|
||
const browser = await chromium.launch({ headless: true });
|
||
const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
|
||
const errors = [];
|
||
page.on('pageerror', (e) => errors.push(e.message));
|
||
|
||
const shot = async (name) => {
|
||
await sleep(1200); // 等数据渲染;无动画依赖
|
||
await page.screenshot({ path: path.join(OUT, name + '.png'), fullPage: true });
|
||
};
|
||
const gotoHash = async (hash) => {
|
||
await page.goto(`${BASE}/admin/#${hash}`);
|
||
await page.waitForLoadState('networkidle').catch(() => {});
|
||
await sleep(800);
|
||
};
|
||
|
||
// 01 登录页(未登录态)
|
||
await gotoHash('/auth/login');
|
||
await shot('01-login');
|
||
|
||
// 登录
|
||
await page.fill('input[autocomplete="username"]', ACCOUNT);
|
||
await page.fill('input[autocomplete="current-password"]', PASSWORD);
|
||
await page.click('.auth-right-wrap .el-button--primary');
|
||
await page.waitForURL((u) => !u.href.includes('/auth/login'), { timeout: 15000 });
|
||
await sleep(1200);
|
||
|
||
// 02 相册列表
|
||
await gotoHash('/albums/manage');
|
||
await shot('02-albums');
|
||
|
||
// 进入第一个相册详情(03–07、15、16 的宿主)——UI06 起默认卡片视图(点封面),表格模式保留 album-detail-btn
|
||
const detailBtn = page.locator('[data-test="album-detail-btn"]').first();
|
||
const coverBtn = page.locator('[data-test^="album-cover-"]').first();
|
||
const entry = (await detailBtn.count()) ? detailBtn : coverBtn;
|
||
if (await entry.count()) {
|
||
await entry.click();
|
||
await page.waitForURL('**/albums/detail/**');
|
||
await sleep(1200);
|
||
await shot('03-photos'); // photos 为默认页签(UI06 起默认照片网格)
|
||
|
||
// 04–07 各页签
|
||
for (const [name, tab] of [
|
||
['04-members', 'members'],
|
||
['05-categories', 'categories'],
|
||
['06-watermark', 'watermark'],
|
||
['07-share', 'share'],
|
||
]) {
|
||
const tabEl = page.locator(`#tab-${tab}`);
|
||
if (await tabEl.count()) {
|
||
await tabEl.click();
|
||
await sleep(900);
|
||
await shot(name);
|
||
} else {
|
||
notes.push(`${name}: tab #tab-${tab} not found`);
|
||
}
|
||
}
|
||
|
||
// 09 编辑对话框(详情页入口)
|
||
const editBtn = page.locator('[data-test="album-detail-edit"]').first();
|
||
if (await editBtn.count()) {
|
||
// 回到 photos 页签,避免页签状态干扰
|
||
const photosTab = page.locator('#tab-photos');
|
||
if (await photosTab.count()) await photosTab.click().catch(() => {});
|
||
await sleep(400);
|
||
await editBtn.click();
|
||
await sleep(900);
|
||
await shot('09-edit');
|
||
await page.keyboard.press('Escape');
|
||
await sleep(400);
|
||
} else {
|
||
notes.push('09-edit: album-detail-edit not found');
|
||
}
|
||
|
||
// 15 品牌资产(现状:详情卡内 banner 上传区,未独立成页签)——整页含该区
|
||
await shot('15-branding');
|
||
notes.push('15-branding: before 状态 banner 位于详情卡内,未独立页签');
|
||
// 16 照片大图(现状:表格视图,无大图预览)
|
||
await shot('16-photo-viewer');
|
||
notes.push('16-photo-viewer: before 状态无大图预览,以照片表格代替');
|
||
} else {
|
||
notes.push('03-07/15/16: 无相册数据,未截详情系');
|
||
}
|
||
|
||
// 08 新建对话框(列表页入口)
|
||
await gotoHash('/albums/manage');
|
||
const createBtn = page.locator('[data-test="album-create-btn"]').first();
|
||
if (await createBtn.count()) {
|
||
await createBtn.click();
|
||
await sleep(900);
|
||
await shot('08-create');
|
||
await page.keyboard.press('Escape');
|
||
await sleep(300);
|
||
} else {
|
||
notes.push('08-create: album-create-btn not found');
|
||
}
|
||
|
||
// 10–12 治理页
|
||
await gotoHash('/system/users');
|
||
await shot('10-users');
|
||
await gotoHash('/system/user-sync');
|
||
await shot('11-user-sync');
|
||
// 20 同步批次失败明细(现状:点历史行打开抽屉;无失败批次则记录)
|
||
const failRow = page.locator('[data-test^="sync-run"]').first();
|
||
if (await failRow.count()) {
|
||
await failRow.click().catch(() => {});
|
||
await sleep(900);
|
||
await shot('20-sync-detail');
|
||
} else {
|
||
await shot('20-sync-detail');
|
||
notes.push('20-sync-detail: 未找到可点历史行,截页面原状');
|
||
}
|
||
await gotoHash('/system/usage');
|
||
await shot('12-usage');
|
||
|
||
// 13 展示位列表 + 14 配置对话框
|
||
await gotoHash('/system/embed-slots');
|
||
await shot('13-embed-slots');
|
||
const slotEdit = page.locator('[data-test="slot-edit-btn"]').first();
|
||
if (await slotEdit.count()) {
|
||
await slotEdit.click();
|
||
await sleep(900);
|
||
await shot('14-embed-editor');
|
||
await page.keyboard.press('Escape');
|
||
await sleep(300);
|
||
} else {
|
||
notes.push('14-embed-editor: slot-edit-btn not found');
|
||
}
|
||
|
||
// 17–19 异常页
|
||
await gotoHash('/403');
|
||
await shot('17-403');
|
||
await gotoHash('/ui06-nonexistent');
|
||
await shot('18-404');
|
||
await gotoHash('/500');
|
||
await shot('19-500');
|
||
|
||
const result = {
|
||
date: new Date().toISOString(),
|
||
base: BASE,
|
||
viewport: '1440x1000',
|
||
errors,
|
||
notes,
|
||
};
|
||
fs.writeFileSync(
|
||
path.join(OUT, 'capture.json'),
|
||
JSON.stringify(result, null, 2)
|
||
);
|
||
console.log(JSON.stringify(result, null, 2));
|
||
await browser.close();
|
||
if (errors.length) process.exitCode = 1;
|
||
})().catch((e) => {
|
||
console.error(e);
|
||
process.exit(1);
|
||
});
|