title: 演習: フォームページのPOM化
演習: フォームページのPOM化
複雑なフォームページをPage Object化してみましょう。
対象ページ
会員登録フォームを想定します:
- 名前(テキスト)
- メールアドレス(テキスト)
- 性別(ラジオボタン)
- 興味のあるカテゴリ(チェックボックス複数)
- 都道府県(セレクトボックス)
- 利用規約同意(チェックボックス)
- 登録ボタン
演習: Page Objectを作成
以下の要件を満たすPage Objectを作成してください。
要件
- 各フォーム要素をLocatorとして定義
- フォーム全体を一度に入力できるメソッド
- 各項目を個別に入力できるメソッド
- 複数のチェックボックスを選択できるメソッド
解答例
クリックして解答を表示
// pages/RegistrationPage.ts
import { Page, Locator } from '@playwright/test';
interface RegistrationData {
name: string;
email: string;
gender: 'male' | 'female' | 'other';
categories: string[];
prefecture: string;
agreeToTerms: boolean;
}
export class RegistrationPage {
readonly page: Page;
readonly nameInput: Locator;
readonly emailInput: Locator;
readonly maleRadio: Locator;
readonly femaleRadio: Locator;
readonly otherRadio: Locator;
readonly prefectureSelect: Locator;
readonly termsCheckbox: Locator;
readonly submitButton: Locator;
readonly successMessage: Locator;
constructor(page: Page) {
this.page = page;
this.nameInput = page.getByRole('textbox', { name: '名前' });
this.emailInput = page.getByRole('textbox', { name: 'メールアドレス' });
this.maleRadio = page.getByRole('radio', { name: '男性' });
this.femaleRadio = page.getByRole('radio', { name: '女性' });
this.otherRadio = page.getByRole('radio', { name: 'その他' });
this.prefectureSelect = page.getByRole('combobox', { name: '都道府県' });
this.termsCheckbox = page.getByRole('checkbox', { name: '利用規約に同意' });
this.submitButton = page.getByRole('button', { name: '登録' });
this.successMessage = page.locator('.success-message');
}
async goto() {
await this.page.goto('/register');
}
// 名前を入力
async fillName(name: string) {
await this.nameInput.fill(name);
}
// メールを入力
async fillEmail(email: string) {
await this.emailInput.fill(email);
}
// 性別を選択
async selectGender(gender: 'male' | 'female' | 'other') {
const radioMap = {
male: this.maleRadio,
female: this.femaleRadio,
other: this.otherRadio,
};
await radioMap[gender].check();
}
// カテゴリを選択(複数)
async selectCategories(categories: string[]) {
for (const category of categories) {
await this.page.getByRole('checkbox', { name: category }).check();
}
}
// 都道府県を選択
async selectPrefecture(prefecture: string) {
await this.prefectureSelect.selectOption(prefecture);
}
// 利用規約に同意
async agreeToTerms() {
await this.termsCheckbox.check();
}
// フォームを送信
async submit() {
await this.submitButton.click();
}
// フォーム全体を入力
async fillForm(data: RegistrationData) {
await this.fillName(data.name);
await this.fillEmail(data.email);
await this.selectGender(data.gender);
await this.selectCategories(data.categories);
await this.selectPrefecture(data.prefecture);
if (data.agreeToTerms) {
await this.agreeToTerms();
}
}
// 登録を完了
async register(data: RegistrationData) {
await this.fillForm(data);
await this.submit();
}
}
テスト例
import { test, expect } from '@playwright/test';
import { RegistrationPage } from '../pages/RegistrationPage';
test('会員登録ができる', async ({ page }) => {
const registrationPage = new RegistrationPage(page);
await registrationPage.goto();
await registrationPage.register({
name: '田中太郎',
email: 'tanaka@example.com',
gender: 'male',
categories: ['スポーツ', '音楽'],
prefecture: '東京都',
agreeToTerms: true,
});
await expect(registrationPage.successMessage).toBeVisible();
});
💡 ポイント
- interfaceでフォームデータの型を定義すると使いやすい
- 個別入力と一括入力の両方のメソッドを用意する
- 複数選択は配列で受け取ってループで処理