Page Object 提取

当同一段流程出现在多个 spec 里时,只要这段流程的 UI 有变化,你就得逐个修改每个 spec。Hover 会把重复的流程抽取成一个共享的 Page Object,让这类改动变成改一个文件就搞定

它生成什么

一个位于 pages/<Name>.ts 的 Page Object,外加一个 fixtures.ts 入口。各 spec 不再把步骤内联重复,而是使用这个 fixture 并调用对应方法:

import { test, expect } from './fixtures';

test('checkout after adding to cart', async ({ cartPage, page }) => {
  await cartPage.addItemAndCheckout('Blue Widget', 2);
  await expect(page.getByRole('heading', { name: /order confirmed/i })).toBeVisible();
});
__vibe_tests__/
├── pages/CartPage.ts   # 被抽取出来的 Page Object
├── fixtures.ts         # 以 fixture 形式暴露它
└── checkout.spec.ts    # 使用 await cartPage.x() + 从 './fixtures' 导入

流程中会变化的数据值(商品名、数量)会变成方法参数

什么时候触发

test_appPhase 5,crystallize 完成之后,Hover 会用 detect_shared_flows 扫描你的 spec。如果某个非登录流程在多个 spec 中重复出现,agent 会征询你的意见是否要抽取。你同意后,extract_page_objects 会生成 Page Object 并把这些 spec 折叠到它上面。整个抽取过程是确定性的——没有 LLM 参与写代码。

登录被有意排除:它已经由 auth-as-fixture 处理,本来就不会在每个测试里重跑——参见 登录与凭据

诚实地说说适用范围

它的价值随测试套件规模而放大。如果套件很小、各流程互不相同,那么正确的结果就是没什么可抽取的——根本没有共享的非登录流程可以复用。真正见效的是大型套件:多个 spec 在做各自的事情之前,都要先走一遍相同的非登录入口流程。

依然是纯 Playwright

Page Object 和 fixture 都是 Playwright 自带的原语。折叠后的 spec 在 CI 里用 npx playwright test 运行,回路中没有 agent,也没有 Hover 运行时。