Files
MailUI4Agents/client/electron/test/manual/inbox-group-verify.mjs

169 lines
7.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 收件箱分组 + 授权独立列表的实测验收。
*
* 起因:生产库里一个会话独占 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);