E03第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

核心路径自动化

核心路径自动化 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

读完本课你能:跑通并读懂本站 19 项核心路径 e2e;用角色定位(getByRole)等稳定手段写不会频繁坏的定位器;让测试自带独立数据、自己清理,重复跑结果一致。

问题场景

你写了个脚本验证「答题后标记完成」:用 CSS 选择器找主按钮。第二天你调了下按钮样式换了个组件,脚本红了——功能明明没坏。改用文字定位,又发现文案里有个空格,匹配不上。e2e 的稳定性问题十有八九出在定位器和测试数据上,本课就修这两件事。

核心解释

本站的浏览器层检查是 scripts/e2e-core.mjs:无头 Chromium、viewport 1440×900,跑 19 项检查覆盖「首页 → 全局搜索 → 课程阅读 → 答题 → 标记完成 → 刷新恢复 → 复习中心 → 创建笔记 → 练习沙箱 → 移动端布局 → 设置页 → 清理」,每项用 record(name, ok, detail) 记录,结尾汇总并决定退出码:

// scripts/e2e-core.mjs(节选):汇总与退出码
const failed = results.filter((r) => !r.ok);
console.log(`\n==== E2E 结果:${results.length - failed.length}/${results.length} 通过 ====`);
if (failed.length > 0) process.exit(1);

稳定定位:按角色和可访问名称找,不按样式找。 按钮的 class 随重构变,但「它是一个按钮、叫『全局搜索』」是功能契约:

// 稳定:角色 + 名称(重构样式不影响)
await page.getByRole("button", { name: "全局搜索" }).click();
const searchInput = page.getByPlaceholder(/搜索课程/);
// 跳转用 URL 断言,不用猜等待时长
await page.waitForURL("**/courses/r08-state-snapshot");

独立测试数据:自造、带标记、自己清。 本站 e2e 新建笔记的标题带时间戳(E2E 测试笔记 ${Date.now() % 100000}),结尾用同一个标题找到并删除。这样重复跑不会撞上次的数据,也不会在真实学习数据里留垃圾。标记完成的检查同理:先看是不是「已完成」,没完成才点,两次运行结果一致。

运行前提:e2e 测的是构建产物——先 npm run build && npm run start(生产模式),再 node scripts/e2e-core.mjs。在 npm run dev 下跑,测到的是一个和部署形态不同的服务,结果不可信。

示例拆解

移动端三项检查展示了「用一个真实视口验证布局契约」:

// 360×800 视口:导航、抽屉、水平溢出
const mobile = await browser.newPage({ viewport: { width: 360, height: 800 } });
await mobile.goto(BASE + "/");
record("移动端导航按钮存在",
  await mobile.getByRole("button", { name: "打开导航菜单" }).isVisible());
await mobile.getByRole("button", { name: "打开导航菜单" }).click();
record("移动端抽屉打开",
  await mobile.getByRole("navigation", { name: "主导航" }).isVisible());
const overflow = await mobile.evaluate(
  () => document.documentElement.scrollWidth - document.documentElement.clientWidth
);
record("移动端无水平溢出", overflow <= 2, `溢出 ${overflow}px`);

关键点:scrollWidth - clientWidth 是水平溢出的客观定义,<= 2 容忍亚像素误差——断言带边界而不是 === 0;evaluate 跑的是浏览器上下文的 JS,适合这种「页面物理属性」断言;按钮没出现时用 isVisible().catch(() => false) 兜底,让结果是一条干净的 FAIL 而不是整个流程崩掉。

动手任务

  1. 本机 build + start 后完整跑一遍 e2e,记录 19 项的 PASS/FAIL 与总耗时。
  2. 临时改掉「新建笔记」按钮的名称(或 aria 标签),重跑 e2e,观察对应项如何 FAIL——这就是定位器契约在起作用,改回来。
  3. 给 e2e 加第 20 项检查(比如「全局搜索无结果时的空态提示」),用 getByRole 定位,跑通后保留。

常见错误

  • waitForTimeout(3000) 当同步手段:等多了浪费时间,等少了偶发红。能等信号(URL、元素出现)就不要等固定时长。
  • 测试间共享状态:第 3 项依赖第 2 项留下的数据,单独跑任何一项都可能挂——每项尽量自备前提。
  • 数据写死:固定标题「测试笔记」第二次运行就撞上第一次的残留;带时间戳或随机后缀,结束清理。
  • 在 dev 模式跑 e2e:开发模式的报错页、按需编译都和生产不同,结果不可信。

检查与复盘

  • 能说出 19 项覆盖的主路径顺序与每项对应的业务风险
  • 写定位器时默认 getByRole / 占位符 / 文本,class 选择器是最后手段
  • 测试数据自造自清,连续跑两遍结果一致

参考资料

检查题共 2 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1读代码中等

两段 Playwright 定位代码。核心路径 E2E(e2e-core.mjs)里,为什么写法二更可维护?

// 写法一
await page
  .locator('div:nth-child(3) > button')
  .click();

// 写法二
await page
  .getByRole('button', { name: '全局搜索' })
  .click();
Q2故障定位较难

bug 现场:E2E 脚本点击「全局搜索」后立即对输入框填词,本地几乎稳定通过,CI 上约三成失败,报 TimeoutError:输入框不可见。原因与根治修法是?

// e2e-core.mjs 片段
await page.getByRole('button', { name: '全局搜索' }).click();
// CI 上下一行偶发失败
await page.getByPlaceholder(/搜索课程/).fill('快照');

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...