关于 create-pom
用于生成 Playwright 端到端测试 TypeScript Page Object Model 类的 Skill,指导开发者检查现有 POM、继承基础页面并验证结果。
主要功能
- 01检查是否已有页面对象
- 02继承共享基础页面
- 03遵循选择器优先级指南
- 04用 JSDoc 文档化公有方法
- 05验证 TypeScript 编译
适用场景
- 01为新的应用页面创建 POM
- 02为现有 POM 添加行为
- 03使页面对象符合测试约定
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ agentmantis/ test-skills --skill create-pom安装后在 Claude Code 中输入/create-pom确认已加载。
附带文件references/pom-template.md
详细介绍
创建页面对象模型
按照所有 E2E 测试约定,为指定页面生成一个 POM 类。
工作流
- 确定页面名称:根据用户的请求确定(例如,“Settings” →
settings.page.ts) - 检查是否已存在 POM:检查
e2e/poms/中是否已有该页面的 POM。如果有,则在现有 POM 中添加方法——不要为同一页面创建第二个 POM - 确定基类页面类:在
e2e/poms/base.page.ts中查找并继承该类 - 阅读应用中的目标页面,以了解其元素和交互
- 生成 POM 文件,并遵循下方的模板
- 验证文件编译时没有 TypeScript 错误
规则
- 一个页面 = 一个 POM——每个不同的页面/视图都只能有一个 POM 文件
- 所有 POM 都必须继承基类页面类——检查
e2e/poms/base.page.ts中的抽象类 - 实现
setUp()和tearDown()——从基类继承 - 可复用的辅助方法应放在
BasePage中——如果某个辅助方法可用于多个 POM(例如,waitForToast、dismissModal),它必须放在BasePage中,而不是派生 POM 中。派生 POM 只能包含特定于页面的行为。如果你发现自己要在第二个 POM 中编写相同的辅助方法,请立即将它移到BasePage - 绝不要在 POM 中引用另一个 POM——如果测试需要操作多个页面,应由 spec 文件协调各个 POM
- 为每个公共方法编写详细的 JSDoc——逐步描述方法的功能,并添加
@param标记 - 按以下优先级顺序使用选择器:
getByRole()>getByLabel()>getByText()>getByPlaceholder()>locator()
POM 结构
使用注释横幅将每个 POM 清晰地划分为以下部分:
import { Page, Locator, expect } from '@playwright/test';
// TODO: Import your project's base page class
import { BasePage } from './base.page';
export class FeaturePage extends BasePage {
constructor(page: Page) {
super(page);
}
// ==========================================
// LIFECYCLE (from BasePage)
// ==========================================
/**
* Navigates to the feature page and cleans up any stale data
* left by previous failed test runs.
*
* Steps:
* 1. Navigates to /feature via direct URL.
* 2. Waits for the page heading to be visible.
* 3. Deletes any items matching the test data pattern.
*/
async setUp(): Promise<void> {
// TODO: Implement navigation and cleanup
}
/**
* Cleans up any data created during the test suite.
*
* Steps:
* 1. Navigates to /feature.
* 2. Deletes all items created by this test run.
*/
async tearDown(): Promise<void> {
// TODO: Implement cleanup
}
// ==========================================
// LOCATORS
// ==========================================
/** The main heading of the feature page. */
get heading(): Locator {
return this.page.getByRole('heading', { name: 'Feature Name' });
}
/** The "Create" button that opens the creation modal. */
get createButton(): Locator {
return this.page.getByRole('button', { name: /create/i });
}
// TODO: Add locators for all interactive elements on this page
// ==========================================
// NAVIGATION
// ==========================================
/**
* Navigates directly to the feature page via URL.
*
* Steps:
* 1. Calls page.goto('/feature').
* 2. Asserts the page heading is visible.
* 3. Asserts the URL contains '/feature'.
*/
async navigateToPage(): Promise<void> {
await this.page.goto('/feature');
await expect(this.heading).toBeVisible();
await expect(this.page).toHaveURL(/\/feature/);
}
// ==========================================
// VERIFICATION
// ==========================================
/**
* Verifies an item appears in the list with the expected name.
*
* Steps:
* 1. Locates the item row by name text.
* 2. Asserts the row is visible.
*
* @param name - The expected item name
*/
async verifyItemExists(name: string): Promise<void> {
await expect(
this.page.getByRole('row', { name: new RegExp(name, 'i') })
).toBeVisible();
}
// ==========================================
// CREATE
// ==========================================
/**
* Creates a new item via the creation modal.
*
* Steps:
* 1. Clicks the "Create" button to open the modal.
* 2. Fills in the name field.
* 3. Clicks "Submit" and waits for the modal to close.
*
* @param name - Display name for the new item
*/
async createItem(name: string): Promise<void> {
await this.createButton.click();
const modal = this.page.getByRole('dialog');
await modal.getByLabel('Name').fill(name);
await modal.getByRole('button', { name: 'Submit' }).click();
await expect(modal).toBeHidden();
}
// ==========================================
// EDIT
// ==========================================
// TODO: Add edit methods
// ==========================================
// DELETE
// ==========================================
// TODO: Add delete methods
}
JSDoc 要求
每个公共方法都必须包含 JSDoc 注释,其中包含:
- 摘要行——说明方法的功能
- 步骤块——以编号列表说明调用时会发生什么
@param标记——为每个参数添加@returns标记——如果方法返回值,则添加
这点至关重要,因为 LLM 会阅读这些注释,了解可执行的操作,并据此生成测试 spec。
/**
* Updates an existing item's name and value.
*
* Steps:
* 1. Locates the item row by its current name.
* 2. Clicks the "Edit" button within that row.
* 3. Clears and fills the name field with the new name.
* 4. Clears and fills the value field with the new value.
* 5. Clicks "Save" and waits for the edit modal to close.
* 6. Waits for the success toast notification to appear.
*
* @param currentName - The item's current display name (used to locate the row)
* @param newName - The new display name
* @param newValue - The new value
*/
async editItem(currentName: string, newName: string, newValue: string): Promise<void> {
命名
- 文件:
e2e/poms/{feature}.page.ts(使用小写字母和 kebab-case 格式的 feature 名称) - 类:
{Feature}Page(PascalCase) - feature 名称必须与对应的 spec 和 test-data 文件一致
完整的样板代码请参阅 references/pom-template.md。
评论
支持 Markdown