用户反馈「我的页面点击进入后无法滑动」。 ## 根因:AccountPage 根本没有滚动容器 窄屏外壳是 `h-full flex flex-col overflow-hidden`,页面本身是 `flex-1 min-w-0 flex flex-col`,中间缺一层 `overflow-y-auto` —— 内容超出的部分**直接被裁**,滚不到也点不到。 实测 390px 下内容(资料 + 权限 + 改密码 + 密钥 + 退出)需 860px、容器只有 795px,「退出登录」按钮连同下面 65px 一起消失;1280x800 的桌面上同样看不到。 其余六个页面级组件(AdminUsersPage / MailView / ComposePage / ThreadView / ContactPanel / MailList)都有这一层,只有它漏了 —— 上一轮把退出登录搬进这页 之后内容变高,问题才显形。 ## 顺带:登录页与初始化页在矮屏滚不到底 卡片高约 371px,而 `h-full flex items-center` 在内容超高时让它上下**同时**溢出, 溢出到顶部那段是滚不到的(`scrollTop` 最小是 0)—— 实测 568x280(横屏手机, 或软键盘弹出后的可视高度)下「登录」按钮完全在视口外,光加 `overflow-y-auto` 也够不着。 改用卡片自己的 `my-auto` 而不是容器的 `items-center`:auto margin 在空间不足时 自动退化为 0,矮屏变成正常的顶对齐可滚布局,高屏仍然垂直居中 (390x844 与 1280x800 实测 centered=true,568x280 下滚到底能看到按钮)。 ## 两侧都加了检查 - 结构断言:七个页面级组件都必须含 `overflow-y-auto`; 登录/初始化页必须有 `my-auto` 且不用 `h-full ... items-center` - 实测脚本新增 `scrollHealth()`:每页都有滚动容器,且没有内容被 `overflow-hidden` 的父级裁掉 判据刻意是「**有**滚动容器」而不是「当前正在滚动」—— 内容暂时不够高时后者 为假,但页面是健康的;真正的 bug 是根本没有那一层。 ## 验证 窄屏实测 18 项 + 宽屏回归 5 项全通过;结构断言从 28 条扩到 37 条。 生产已部署。
手工浏览器实测脚本
不进 npm test —— 它们需要一个跑着的 Chromium 与一个活的 Gateway。
日常回归靠 ../narrow-layout.test.mjs(读源码验形态,无外部依赖)。
为什么两套都要
结构性断言守住「代码写成了什么形态」,量不出「按钮实际多大、点下去命中谁」。
窄屏那轮修复里最严重的一个 bug 是抽屉式侧栏(fixed ... z-50 铺满视口高度)
把底部导航最左那一项盖住 —— 按钮在那里、尺寸也够、md:hidden 之类的规则也
没写错,只有 elementFromPoint 才能发现它命中的是抽屉里的 SVG。
用法
# 窄屏:390px(iPhone 14 Pro)+ 320px(iPhone SE)
ADMIN_PW=<密码> npm run test:narrow
# 宽屏回归:窄屏修复不能把桌面改坏
ADMIN_PW=<密码> npm run test:wide
环境变量:
| 变量 | 默认 | 说明 |
|---|---|---|
ADMIN_PW |
无(必填) | 管理员密码 |
ADMIN_USER |
admin |
登录用户名 |
AGENTMAIL_URL |
https://mail.jianfgit.xyz |
目标地址 |
CDP_URL |
http://127.0.0.1:9222 |
浏览器 CDP 端点 |
PLAYWRIGHT |
/usr/lib/node_modules/playwright/index.mjs |
playwright 入口 |
浏览器用的是本机 systemd 托管的共享 Chromium(homeagent-browser.service),
通过 CDP 连上去开自己的标签页,用完关掉。没有它时先
systemctl start homeagent-browser。
文件
| 文件 | 作用 |
|---|---|
narrow-probe-helper.mjs |
连浏览器、登录、量盒子/溢出/命中区/命中测试 |
narrow-verify.mjs |
窄屏 13 项验收 |
wide-regression.mjs |
宽屏 5 项回归 |
narrow-probe-helper.mjs 里两个函数值得单独知道:
tapTargets(page, labels)—— 量.tap按钮的真实命中区(::after伪元素的尺寸)。.tap刻意不改变视觉尺寸,所以只看boundingBox会误判成偏小hitTest(page, selector)—— 每个元素点下去是否命中自己。遮挡类 bug 只能这样查
已知限制
headless Chromium 报告 hover: none,因此 .reveal(只在支持悬停的设备上隐藏)
在这里永远是可见的 —— 脚本只能验「触摸设备上可见」这一半,
「鼠标设备上隐藏」那一半靠 ../narrow-layout.test.mjs 检查 CSS 规则存在。
没有像素级视觉比对:字体差异下极脆,维护成本高于收益。