人生进阶指南如何用 Playwright 烟测覆盖中英文页面并检查 375px 与 1280px 布局
《人生进阶指南》是一个 VitePress 静态站点,中文主线与英文对应页共存,页面数量由导航驱动。每次改动导航、新增章节或调整页面结构后,需要一条命令级的方式确认:所有中文页和英文页都能渲染出正确的一级标题,并且内容在 1280px 桌面视口和 375px 移动视口下都正常工作。仓库用 Playwright 烟测完成这件事,入口是 npm run test:smoke,配置在 playwright.config.mjs,用例在 tests/site.spec.mjs。本文说明这套烟测的覆盖范围如何生成、两个视口项目如何生效,以及如何运行并判断结果。
前置条件:Node 24 与锁定依赖
站点运行有明确的版本约束,安装依赖也走锁定文件,这两步是跑烟测的前置:
- Node.js 24。版本约束写在
.nvmrc、.node-version和 package.json 的engines(>=24 <25)中,可用nvm use切到对应版本(见 CONTRIBUTING.md)。 - 首次安装用
npm ci,严格按 lock file 安装,其中包含@playwright/test1.62.1(devDependency)。
nvm use
npm ci
MAINTENANCE.md 的“环境与命令”一节把 npm ci 列为首次安装步骤,端到端测试统一用 npm run test:smoke 触发。
两个视口项目:1280px 与 375px 在配置中如何生效
playwright.config.mjs 通过 projects 定义两套浏览器环境,同一条用例会分别在两个项目中各跑一次:
projects: [
{
name: "desktop-chromium",
use: {
...devices["Desktop Chrome"],
viewport: { width: 1280, height: 800 },
},
},
{
name: "mobile-chromium",
use: {
...devices["iPhone 13"],
browserName: "chromium",
viewport: { width: 375, height: 812 },
},
},
],
desktop-chromium基于devices["Desktop Chrome"]并把视口固定为 1280x800;mobile-chromium借用devices["iPhone 13"]的设备描述(移动 UA 等),但浏览器本身仍是chromium,视口固定为 375x812。
全局 use 还指定 browserName: "chromium",并在非 CI 环境叠加 channel: "chrome",即本地运行时借用本机 Chrome;CI 环境则直接跑 Playwright 自带的 chromium。trace: "on-first-retry" 表示只有重试时才保存 trace。
CI 与本地的行为差异同样在配置里写明:
- CI(
process.env.CI存在时):retries: 2、forbidOnly: true(禁止.only残留)、reporter 为html+list; - 本地:无重试,reporter 只有
list。
webServer 让测试自带站点,不需要手工起服务:
webServer: {
command: "npm run docs:build && npm run docs:preview -- --host 127.0.0.1 --port 4173",
url: "http://127.0.0.1:4173/up/",
reuseExistingServer: !process.env.CI,
timeout: 120_000,
},
也就是说,每次触发烟测时会先执行 npm run docs:build 做生产构建,再用 vitepress preview 在 127.0.0.1:4173 上起预览,站点基础路径是 /up/(与 baseURL: "http://127.0.0.1:4173/up/" 对应)。构建加预览的整体等待上限是 120 秒。注意 reuseExistingServer 仅在非 CI 生效:本地如果 4173 端口已有服务会被直接复用,CI 则强制重建。
中英文页面覆盖从哪里来
test suite 的覆盖 不是手写死的路由表,而是从导航唯一来源 docs/.vitepress/navigation.mjs 动态生成:
- 用例文件导入
zhNavigation、enNavigation、toSidebar三个导出; headingFromSource对每个导航条目的source读docs/下的 Markdown,用正则取出第一级标题(^# ...)。取不到时直接抛出导航 source 缺少一级标题: <source>,测试在收集阶段就失败;routesFromNavigation把中文和英文导航的所有条目压平成[route, heading]对,link === "/"归一为"./",其余加上./前缀;- 对每一对生成一条参数化用例:
for (const [route, heading] of routes) {
test(`${route} renders`, async ({ page }) => {
await page.goto(route);
await expect(
page.getByRole("heading", { level: 1, name: new RegExp(escapeRegExp(heading)) }),
).toBeVisible();
await expect(page.locator("main")).toBeVisible();
});
}
这条用例的判定标准是:页面能打开、源文件里的一级标题以 H1 形式可见、main 元素可见。由于中文导航产生 ./threads/... 这类路由、英文导航产生 ./en/... 这类路由,新增一个导航条目就会自动多出一条中英文(或单语)渲染断言,删除条目则反之——MAINTENANCE.md 也明确“烟测会从导航 source 自动生成中英文页面覆盖”。
同一文件里还有一组结构性断言,约束导航本身而不是页面渲染,例如:
- “开始 / Start Here” 前三个条目依次是 README、阅读指南、序章;
- 中文导航第二到第七组必须是“第一部:打开输入”到“后记”六段,英文对应
Part I到Afterword; - “工具箱 / 旧文归档 / 词表”三组在侧边栏默认折叠(
toSidebar(...)后collapsed === true),前七组默认展开。
移动视口的断言与桌面视口不同,体现在 English chrome uses English labels and author metadata 这条用例里:当 testInfo.project.name === "mobile-chromium" 时,断言页面出现 Menu 和 On this page 两个按钮;桌面项目下则断言 Change language 按钮可见。这就是 375px 布局被具体检查的方式——不是截图比对,而是移动菜单控件在窄视口下必须出现。
用例中还有与布局相邻的专项检查:旧 hash 路由 ./#/threads/part-1/1-understanding 重定向一次后落到干净路径并显示 H1;代表性页面(首页、英文首页、projects、口语篇、写作篇、创业篇、我的故事等中英各页)里的本地图片全部 complete 且 alt 非空;assets/session.json 这类私会话资产访问必须返回 404。
运行烟测并判断结果
最短主路径就一条命令:
npm run test:smoke
它在 package.json 中等价于 playwright test。执行时 Playwright 会按 webServer 配置先构建再预览站点,然后按 fullyParallel: true 并行执行全部用例,每条用例在 desktop-chromium 和 mobile-chromium 两个项目中各跑一遍。
判断方式按文档给出的条件:
- 本地运行时 reporter 是
list,全部用例通过即为完成;失败时可看test-results/下的失败诊断(trace 在首次重试时生成)。 - CI 运行时额外输出 HTML 报告到
playwright-report/,失败重试 2 次。 - MAINTENANCE.md 说明
test-results/和playwright-report/只保存失败诊断与 HTML 报告,属于生成文件而非书稿内容,已被 Git 和 Markdown lint 忽略,测试失败后可以安全清理后重跑。 - 在发布门禁中,烟测是最后一道:按顺序执行
npm run sync、npm run check、npm run docs:build、npm run test:smoke,检查生成文件差异后再提交(见 MAINTENANCE.md 的“章节发布门禁”与“发布流程”)。
排查与限制
- 首次运行偏慢:
webServer每次先跑完整docs:build再起 preview,整体等待上限 120 秒;本地若已有一个构建好的 4173 预览服务,reuseExistingServer: !process.env.CI会让本地直接复用,CI 则每次重建。复用意味着本地结果可能来自旧构建,需要全新产物验证时先停掉已有服务再跑。 - 本地需要 Chrome:非 CI 环境叠加
channel: "chrome",即借本机 Chrome 执行;CI 无此叠加,用 Playwright 自带 chromium。文档未提供浏览器安装命令,仓库只规定npm ci这条安装路径。 - 导航与源文件不一致会提前失败:
headingFromSource在读不到 source 的一级标题时直接抛错,这类失败发生在用例收集阶段,提示语直接指出缺失的source文件名;MAINTENANCE.md 说明同步脚本会在npm run check:navigation阶段检查source文件是否存在,两者互为补充。 forbidOnly只在 CI 生效:本地误留test.only不会报错,CI 会直接拒绝,提交前自查。- 布局变更的佐证材料:CONTRIBUTING.md 建议布局类改动的 PR 附 375px 与 1280px 截图,恰好对应两个测试项目的视口宽度。
- 烟测验证的是渲染、导航结构与关键交互(搜索、语言切换、重定向、图片加载、键盘焦点等),不做像素级比对;
build-revision元数据断言依赖GITHUB_SHA或BUILD_REVISION环境变量,本地运行时预期值为"local"。
下一步若烟测全绿,按 MAINTENANCE.md 的发布流程继续:在拉取请求的 site-preview 构建产物中检查站点,合并到 master 后由 GitHub Pages Actions 部署,部署后再核对中文首页、英文首页、代表性章节、搜索、语言切换和旧 hash 跳转。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



