Playwright ロケーター(Locators)

Locator(ロケーター)は Playwright の最も中心的な概念の 1 つであり、ページ上で要素を検索する方法を表します。

この章では、Playwright が推奨する特定方法を紹介します:ロール、テキスト、ラベル、プレースホルダーによる要素の特定です。


Locator とは

Locator は、Playwright がページ上で要素を検索するために使用する抽象オブジェクトです。

従来のものとdocument.querySelector()異なり、Playwright の Locator は自動待機と自動再試行の能力を持っています。

ページの内容が動的に変化する場合、Locator は一致する要素が見つかるかタイムアウトになるまで、要素の検索を繰り返し行います。

例

// Locator を作成します(この時点では要素を検索しません)
const button = page.getByRole('button', { name: '提交' });

// 操作を実行すると、Playwright はボタンが表示され操作可能な状態になるまで自動的に待機します
await button.click();

Locator の動作原理

Locator を作成するとき、Playwright は検索ルールを 1 つ記録するだけです。

あなたが呼び出すと.click()、.fill()などの操作、またはexpect(locator)アサーションを行うとき、Locator は初めて実際に要素の検索を開始します。

要素が見つからない場合、Locator は操作がタイムアウトになるまで再試行し続けます。


ロールによる特定:getByRole()

getByRole()Playwright が最も推奨する特定方法です。

要素のアクセシビリティロール(ARIA ロール)によって要素を検索します。これはユーザーの認識に最も近い方法です。

基本的な使い方

例

// ボタンを特定する——ロール "button" を使用
const submitBtn = page.getByRole('button', { name: '提交' });
await submitBtn.click();

// リンクを特定する——ロール "link" を使用
const homeLink = page.getByRole('link', { name: '首页' });
await homeLink.click();

// 見出しを特定する——ロール "heading" を使用
const mainTitle = page.getByRole('heading', { name: '欢迎使用 サンプル' });

// テキストボックスを特定する——ロール "textbox" を使用
const searchBox = page.getByRole('textbox', { name: '搜索' });
await searchBox.fill('Playwright');

一般的な ARIA ロール

Role対応する HTML 要素使用例
'button'<button>、role="button"getByRole('button', { name: '登录' })
'link'<a>getByRole('link', { name: '了解更多' })
'heading'<h1>~<h6>getByRole('heading', { name: 'サンプル 教程' })
'textbox'<input type="text">、<textarea>getByRole('textbox', { name: '用户名' })
'checkbox'<input type="checkbox">getByRole('checkbox', { name: '记住我' })
'radio'<input type="radio">getByRole('radio', { name: '男' })
'combobox'<select>getByRole('combobox', { name: '城市' })
'img'<img>getByRole('img', { name: 'Logo' })
'list'<ul>、<ol>getByRole('list')
'listitem'<li>getByRole('listitem')
'navigation'<nav>getByRole('navigation')
'table'<table>getByRole('table')

name オプション

nameオプションは、要素のアクセシブルネームによって範囲を絞り込みます。

アクセシブルネームの情報源には、要素のテキストコンテンツ、aria-label属性、関連する<label>などがあります。

例

// ボタンのテキストでマッチング
page.getByRole('button', { name: '登录' });

// aria-label でマッチング
page.getByRole('button', { name: 'ダイアログを閉じる' });

// 正規表現でマッチング
page.getByRole('link', { name: /サンプル|サンプル/ });

状態オプション

ほかにname,getByRoleさまざまな状態オプションもサポートしています:

例

// 選択済みのチェックボックスを特定する
page.getByRole('checkbox', { checked: true });

// 無効化されたボタンを特定する
page.getByRole('button', { disabled: true });

// 展開されたドロップダウンメニューを特定する
page.getByRole('button', { expanded: true });

// 選択されたオプションを特定する
page.getByRole('option', { selected: true });

// 複数の状態を組み合わせる
page.getByRole('checkbox', { name: '同意协议', checked: false });

テキストによる特定:getByText()

getByText()要素のテキストコンテンツによって特定します。

例

// テキストを完全一致でマッチング
page.getByText('欢迎访问 サンプル');

// 部分一致(デフォルト)
page.getByText('サンプル');

// 完全一致を強制する
page.getByText('サンプル', { exact: true });

// 正規表現でマッチング
page.getByText(/サンプル|サンプル/);

getByText一致するのは要素の完全なテキストコンテンツなので、getByText('サンプル', { exact: true })テキストコンテンツがちょうど "サンプル" である要素にのみ一致します。

デフォルトでは完全一致を行わないため、部分文字列の一致で十分です(例:getByText('サンプル')"欢迎访问 サンプル" に一致します)。


ラベルによる特定:getByLabel()

getByLabel()関連する~を特定するために使用する<label>フォームコントロール。

例

// HTML 構造:
// <label for="username">用户名</label>
// <input id="username" type="text" />

// label テキストで input を特定する
await page.getByLabel('用户名').fill('サンプル_user');

// HTML 構造:
// <label>密码 <input type="password" /></label>

// ネストされた label も有効です
await page.getByLabel('密码').fill('password123');

また{ exact: true }完全一致オプションもサポートしています。


プレースホルダーによる特定:getByPlaceholder()

getByPlaceholder()を使ってplaceholder属性で入力ボックスを特定します。

例

// HTML 構造:<input placeholder="请输入搜索关键词" />
await page.getByPlaceholder('请输入搜索关键词').fill('Playwright');

// HTML 構造:<textarea placeholder="あなたの考えを入力してください..."></textarea>
await page.getByPlaceholder('あなたの考えを聞かせて...').fill('サンプル 教程很棒!');

getByLabel vs getByPlaceholder の選択

方法適用シーン優先順位
getByLabel()<label> 要素がある場合優先的に使用
getByPlaceholder()label がないが、placeholder がある場合次に使用

getByRolePlaywright が最も推奨する特定方法です。ユーザーがロールを使って要素を認識する方法を模倣しているためです。次にgetByLabelとgetByPlaceholder。セマンティックな要素がない場合にのみ使用を検討しますgetByTestIdまたは CSS セレクター。


getByAltText() 画像の特定

getByAltText()画像のalt属性で特定する<img><img> 要素。

例

// HTML:<img src="logo.png" alt="サンプル Logo" />
await expect(page.getByAltText('サンプル Logo')).toBeVisible();

// 正規表現でマッチング
await expect(page.getByAltText(/logo/i)).toBeVisible();

getByTitle() タイトル属性による特定

getByTitle()要素のtitle属性で特定します。

例

// HTML:<button title="关闭当前页面">X</button>
await page.getByTitle('关闭当前页面').click();

getByAltTextとgetByTitle使用頻度は低く、要素に明確な alt または title 属性があり、他の方法では特定できない場合にのみ使用します。


getByTestId() テスト ID による特定

getByTestId()であるフォールバック方法、経由でdata-testid属性を使用して要素を特定します。

例

// HTML:<button data-testid="submit-btn">提交</button>
await page.getByTestId('submit-btn').click();

// 複数の一致を含む場合があります(例:リスト項目)
const items = page.getByTestId('todo-item');
await expect(items).toHaveCount(3);

testId 属性のカスタマイズ

プロジェクトで使用していない場合data-testid、次の場所でplaywright.config.ts属性名をカスタマイズできます:

例

// ファイルパス:playwright.config.ts
export default defineConfig({
  use: {
    // data-testid ではなく data-cy を使用(Cypress からの移行に合わせる)
    testIdAttribute: 'data-cy',
    // または id を testId として使用
    // testIdAttribute: 'id',
  },
});

page.locator() CSS と XPath による特定

page.locator()CSS セレクターと XPath に対応しており、より柔軟な代替手段です。

CSS セレクター

例

// CSS クラス名で
await page.locator('.submit-btn').click();

// ID で
await page.locator('#username').fill('サンプル_user');

// 属性で
await page.locator('[data-type="primary"]').click();

// タグ + クラス名の組み合わせで
await page.locator('button.primary').click();

// 親子関係で
await page.locator('nav a').first().click();

// 特定のテキストを含む CSS セレクター
await page.locator('button:has-text("提交")').click();

XPath による特定

例

// XPath を使用して特定
await page.locator('xpath=//button[@type="submit"]').click();

// テキストで特定
await page.locator('xpath=//h1[contains(text(), "サンプル")]').click();

CSS セレクターと XPath は最後の選択肢であり、優先して使用getByRole、getByTextなどのセマンティックなロケーター方法。CSS セレクターはスタイルのリファクタリングで無効になりやすく、XPath は可読性が低くパフォーマンスも遅いです。


Locator のチェーン操作

Playwright の Locator はチェーン呼び出しをサポートしており、現在の一致結果をさらに絞り込むことができます。

.first()、.last()、.nth()

例

// 最初に一致するリスト項目を取得
const firstItem = page.getByRole('listitem').first();

// 最後の項目を取得
const lastItem = page.getByRole('listitem').last();

// 2番目の項目を取得(インデックスは0から始まります)
const secondItem = page.getByRole('listitem').nth(1);

.filter() によるフィルタリング

例

// すべてのボタンから特定のテキストを含むものをフィルタリング
page.getByRole('button').filter({ hasText: '保存' });

// 特定のテキストを含まないものをフィルタリング
page.getByRole('listitem').filter({ hasNotText: '已删除' });

// 特定の子要素を含むものをフィルタリング
page.getByRole('listitem').filter({ has: page.getByRole('button') });

// 特定の子要素を含まないものをフィルタリング
page.getByRole('listitem').filter({ hasNot: page.getByTestId('badge') });

.locator() による子要素の特定

例

// まずリストコンテナを特定し、次にそのコンテナ内でボタンを探します
const list = page.getByTestId('todo-list');
const deleteBtn = list.locator('button.delete');
await deleteBtn.click();

// 複数レベルのネスト
page
  .getByRole('dialog')
  .locator('div.content')
  .locator('button')
  .filter({ hasText: '确认' })
  .click();

.and() と .or() の論理的な組み合わせ(1.63+)

例

// 2つの条件を同時に満たす
const btn = page.getByRole('button')
  .and(page.getByText('提交'));

// いずれかの条件を満たす
const target = page.getByText('サンプル')
  .or(page.getByTitle('サンプル'));

Locator の特定方法の優先順位まとめ

推奨度の高い順に並べています:

優先度方法使用条件
1(最も推奨)getByRole()要素に明確な ARIA ロールがある
2getByLabel()フォーム要素に関連付けられたラベルがある
3getByPlaceholder()入力欄にプレースホルダーがある
4getByText()要素に明確なテキストコンテンツがある
5getByAltText()画像に alt 属性がある
6getByTitle()要素に title 属性がある
7(フォールバック)getByTestId()上記の方法で特定できない場合
8(最後の選択肢)locator()CSS セレクター / XPath
その他の拡張