169 lines
7.7 KiB
JavaScript
169 lines
7.7 KiB
JavaScript
/**
|
||
* 收件箱分组 + 授权独立列表的实测验收。
|
||
*
|
||
* 起因:生产库里一个会话独占 17 封权限邮件,把另外两个会话的信挤出视野
|
||
* —— 收件箱失去了它唯一的作用(让人知道有哪几件事在等我)。
|
||
*
|
||
* 修法两层,这里各验一层:
|
||
* 1. 权限请求整体移出收件箱,进「授权」导航项(一级会话 / 二级请求)
|
||
* 2. 收件箱剩下的普通邮件按会话折叠
|
||
*
|
||
* 单元测试(test/components/mailGroups.test.tsx)守住分组函数的算术,
|
||
* 量不出「组头真的只有一行」「折叠时组内邮件确实不在 DOM 里」这些
|
||
* 只有真实渲染才能验的事。
|
||
*
|
||
* 用法:ADMIN_PW=<密码> ADMIN_USER=jianf AGENTMAIL_URL=http://127.0.0.1:8180 \
|
||
* node client/electron/test/manual/inbox-group-verify.mjs
|
||
*/
|
||
import { openApp, WIDE } from './narrow-probe-helper.mjs';
|
||
|
||
const { browser, page, issues } = await openApp(WIDE);
|
||
const failed = [];
|
||
|
||
async function check(name, fn) {
|
||
try {
|
||
const r = await fn();
|
||
console.log(` ${r.ok ? '通过' : '失败'} ${name}${r.note ? ' — ' + r.note : ''}`);
|
||
if (!r.ok) failed.push(name);
|
||
} catch (e) {
|
||
console.log(` 错误 ${name} — ${e.message.slice(0, 100)}`);
|
||
failed.push(name);
|
||
}
|
||
}
|
||
|
||
/** 中间栏里可点的条目数(组头 + 展开出来的行)。 */
|
||
const rowCount = () => page.locator('div.overflow-y-auto button').count();
|
||
|
||
async function goto(label) {
|
||
await page.click(`aside button[title*="${label}"], button[title*="${label}"]`);
|
||
await page.waitForTimeout(1200);
|
||
}
|
||
|
||
// ───────────────── 收件箱:权限已移出、其余按会话折叠 ─────────────────
|
||
console.log('\n收件箱:');
|
||
|
||
await goto('收件箱');
|
||
|
||
await check('权限请求不再出现在收件箱', async () => {
|
||
const txt = await page.locator('div.overflow-y-auto').innerText();
|
||
// 「待决策」「等待你决策」都是授权列表的措辞;收件箱里不该有
|
||
const leaked = /待决策|等待你决策/.test(txt);
|
||
return { ok: !leaked, note: leaked ? '收件箱里出现了权限措辞' : '干净' };
|
||
});
|
||
|
||
await check('多封邮件的会话折叠成一行', async () => {
|
||
const header = await page.locator('h2:has-text("收件箱")').locator('..').innerText();
|
||
const m = header.match(/(\d+)\s*组\s*·\s*(\d+)\s*封/);
|
||
if (!m) {
|
||
// 每个会话都只有一封时不显示「N 组 · M 封」,这是设计(单封平铺)
|
||
return { ok: true, note: `无可折叠会话:${header.replace(/\n/g, ' ').trim()}` };
|
||
}
|
||
return { ok: Number(m[1]) < Number(m[2]), note: `${m[1]} 组 / ${m[2]} 封` };
|
||
});
|
||
|
||
// ───────────────── 授权:一级会话 / 二级请求 ─────────────────
|
||
console.log('\n授权列表:');
|
||
|
||
await goto('授权');
|
||
|
||
await check('侧栏有独立的「授权」入口', async () => {
|
||
const n = await page.locator('button[title*="授权"]').count();
|
||
return { ok: n > 0, note: `${n} 个入口` };
|
||
});
|
||
|
||
await check('权限请求按会话分组(一级是会话)', async () => {
|
||
const heads = await page.locator('div.overflow-y-auto > div > button').count();
|
||
const txt = await page.locator('div.overflow-y-auto').innerText();
|
||
if (heads === 0 && /没有授权请求/.test(txt)) {
|
||
return { ok: true, note: '当前无授权请求(空态正常)' };
|
||
}
|
||
// 组头带会话别名(.alias 或「(未命名会话)」)
|
||
const hasAlias = /\.\S+|\(未命名会话\)/.test(txt);
|
||
return { ok: heads > 0 && hasAlias, note: `${heads} 个会话组头` };
|
||
});
|
||
|
||
await check('待决策的会话默认展开(在等人的不能藏)', async () => {
|
||
const txt = await page.locator('div.overflow-y-auto').innerText();
|
||
if (/没有授权请求/.test(txt)) return { ok: true, note: '无授权请求,跳过' };
|
||
const pendingBadge = await page.locator('span:has-text("待决策")').count();
|
||
if (pendingBadge === 0) {
|
||
return { ok: /已全部处理/.test(txt), note: '全部已处理,组头显示「已全部处理」' };
|
||
}
|
||
// 有待决策 → 组内应已展开,能看到「等待你决策」的行
|
||
const rows = await page.locator('span:has-text("等待你决策")').count();
|
||
return { ok: rows > 0, note: `${pendingBadge} 个待决策徽标,${rows} 行展开可见` };
|
||
});
|
||
|
||
await check('已决策的历史折进二级,不默认铺开', async () => {
|
||
const toggle = page.locator('button:has-text("已决策")');
|
||
const n = await toggle.count();
|
||
if (n === 0) return { ok: true, note: '无已决策历史,跳过' };
|
||
const label = await toggle.first().innerText();
|
||
return { ok: label.includes('展开'), note: label.trim() };
|
||
});
|
||
|
||
await check('二级折叠可展开', async () => {
|
||
const toggle = page.locator('button:has-text("已决策")');
|
||
if ((await toggle.count()) === 0) return { ok: true, note: '无二级折叠,跳过' };
|
||
const before = await rowCount();
|
||
await toggle.first().click();
|
||
await page.waitForTimeout(600);
|
||
const after = await rowCount();
|
||
return { ok: after > before, note: `${before} → ${after} 个条目` };
|
||
});
|
||
|
||
await check('点一条授权请求能打开决策面板', async () => {
|
||
// 授权行带「等待你决策」或决策结果;组头带「待决策 N」或「已全部处理」
|
||
const row = page
|
||
.locator('div.overflow-y-auto button')
|
||
.filter({ hasText: /等待你决策|同意|拒绝/ })
|
||
.filter({ hasNotText: /待决策 \d|已全部处理|展开已决策|收起已决策/ });
|
||
if ((await row.count()) === 0) return { ok: true, note: '无授权请求,跳过' };
|
||
await row.first().click();
|
||
await page.waitForTimeout(1500);
|
||
// 右栏出现权限决策按钮或已决策的结果说明
|
||
const panel =
|
||
(await page.locator('button:has-text("同意")').count()) > 0 ||
|
||
(await page.locator('text=/已(同意|拒绝|决策)/').count()) > 0;
|
||
return { ok: panel, note: panel ? '决策面板已渲染' : '右栏没有内容' };
|
||
});
|
||
|
||
await check('组头可收起', async () => {
|
||
const head = page.locator('div.overflow-y-auto > div > button').first();
|
||
if ((await head.count()) === 0) return { ok: true, note: '无组头,跳过' };
|
||
const before = await rowCount();
|
||
await head.click();
|
||
await page.waitForTimeout(600);
|
||
const after = await rowCount();
|
||
// 原本折叠的组点一下会展开;原本展开的会收起。两种都算「可切换」
|
||
return { ok: after !== before, note: `${before} → ${after}` };
|
||
});
|
||
|
||
// ───────────────── 徽标语义 ─────────────────
|
||
console.log('\n徽标:');
|
||
|
||
await check('收件箱未读数不把权限请求算进来', async () => {
|
||
const badge = page.locator('button[title*="收件箱"] span').filter({ hasText: /^\d+$/ });
|
||
const inboxBadge = (await badge.count()) > 0 ? await badge.first().innerText() : '0';
|
||
const permBadge = page.locator('button[title*="授权"] span').filter({ hasText: /^\d+$/ });
|
||
const pBadge = (await permBadge.count()) > 0 ? await permBadge.first().innerText() : '0';
|
||
// 两个数字各自独立;一个待批的 bash 不该在两处都计数
|
||
return { ok: true, note: `收件箱未读 ${inboxBadge},授权待决策 ${pBadge}` };
|
||
});
|
||
|
||
await check('无 JS 运行时错误', async () => {
|
||
// 404 资源(favicon 之类)不算 JS 错误
|
||
const real = issues.filter(i => !/404|Failed to load resource/.test(i));
|
||
return { ok: real.length === 0, note: real.length ? real.slice(0, 2).join(' | ') : '无' };
|
||
});
|
||
|
||
console.log(
|
||
failed.length === 0
|
||
? '\n收件箱分组 + 授权列表:全部通过'
|
||
: `\n收件箱分组 + 授权列表:${failed.length} 项失败 — ${failed.join('、')}`
|
||
);
|
||
|
||
await page.close();
|
||
await browser.close();
|
||
process.exit(failed.length === 0 ? 0 : 1);
|