初心者からずっと使い続けたいPlaywrightスクリプトチートシート

# はじめに
この記事は自分の記事にAIが加筆したものです。
情報が不正確と思われる場合は必ず一時情報にあたってください。

# インストール, 概要
https://qiita.com/Y_MAGARI000/items/665985da6fffbf2d45c9

# チュートリアル
https://www.lambdatest.com/learning-hub/automate-date-pickers-with-playwright

# ページオブジェクトモデル
https://playwright.dev/docs/pom

## How To Write POM 10x faster
https://www.youtube.com/watch?v=8WTD26DA690

# ベストプラクティス
https://playwright.dev/docs/best-practices


# スクリプト

### ページ遷移
| 方法 | 説明 |
|---|---|
| await page.goto(URL) |URLに遷移|
|await page.waitForURL(URL) | 指定したURLに移動するのを待つ|

```javascript
await page.goto('https://qiita.com/')
```

### 要素を検索

ロケータってなに？という方はまずはこちらをご覧ください。

https://qiita.com/yurihyp/items/ddc8b0224e17a869ccf1


| 方法 | 説明 |
|---|---|
| await page.locator(locator) |ロケータに該当する要素を検索|
| await page.locator(locator).all() |ロケータに該当する全ての要素を検索|
| await page.$('a') |aタグを持つの１番目を検索|
| await page.$$('a') |aタグを持つ全ての要素を検索|
|await page.getByRole(role, {name: text})|要素のrole属性で要素を取得|
| await page.getByText(text)|文言でノードを検索|
| await page.getByTitle(text)|title属性の値で要素を検索|
| await page.getByAltText(text)|alt属性で要素を取得|
|await page.getByLabel(text)|要素のLabel属性で要素を取得|
|await page.getByPlaceholder(text)|要素のplaceholder属性で要素を取得|
|await page.getByTestId(testId)|要素のdata-testid属性で要素を取得|

```javascript
//typeがsubmitのボタンの一番目を検索
await page.locator('button[type="submit"]')
```
```javascript
//一致する要素を全件検索して一つずつクリック
for (const li of await page.getByRole('listitem').all())
  await li.click();
```

#### 非推奨パターン
ElementHandle の使用は推奨されず、代わりにLocatorオブジェクトを使うことが望ましいです。

```javascript
const elment = await page.$('a')
await element.click()
```
詳細は公式をご覧ください
https://playwright.dev/docs/api/class-elementhandle




### 要素のフィルタリング

| 方法 | 説明 |
|---|---|
|await page.locator(locator).filter({hasText: text})| 要素をテキストでフィルター|

```javascript
//role属性がbutton&&文言が「投稿する」のbutton要素をkリック
await page
    .getByRole('button')
    .filter({ hasText: '投稿する' })
    .click();
```

https://playwright.dev/docs/locators#filtering-locators



### クリックとホバー
| 方法 | 説明 |
|---|---|
| await page.click(locator) |要素をクリック|
| await page.hover(locator)|要素の上にカーソルを移動|


```javascript
await page.click('button[type="submit"]').click()
```

### フォーカス
| 方法 | 説明 |
|---|---|
|await page.locator(locator).focus()|特定の要素にフォーカスを当てる|
|await page.locator(locator).blur()|特定の要素にフォーカスをはずす|

```javascript
const input = await page.locator("input");
await input.blur();
```


### 文字入力とクリア
| 方法 | 説明 |
|---|---|
|await page.type(locator, text)|指定された要素にテキストを入力し|
|await page.locator(locator).fill(text)|指定された要素にテキストを入力|
|await page.clear(locator)|指定された要素のテキストをクリア|


```javascript
await page.locator('input[name="name"]').fill('Michael Jackson')
```

### セレクトボックス
| 方法 | 説明 |
|---|---|
|await page.selectOption(locator, option)|指定された要素からオプションを選択|

### チェックボックス

| 方法 | 説明 |
|---|---|
|await page.check(locator)|指定された要素のチェックボックスをオンにする|
|await page.uncheck(locator)|指定された要素のチェックボックスをオフにする|

```html
<h3>Sign up</h3>
<label>
  <input type="checkbox" /> Subscribe
</label>
<br/>
<button>Submit</button>
```

```javascript
await page.getByRole('checkbox', { name: 'Subscribe' }).check();
```

### ドロップ＆ドラッグ

| 方法 | 説明 |
|---|---|
|await page.dragAndDrop(sourceLocator, targetLocator)| 指定された要素を別の要素にドラッグアンドドロップ|

### ファイルアップロード, ダウンロード

#### ファイルアップロード
| 方法 | 説明 |
|---|---|
|await page.setInputFiles(locator, files)	|指定された要素にファイルをアップロード|

```typescript
// ファイルのパスを指定してアップロード
const fileInput = await page.locator('input[type="file"]');
await fileInput.setInputFiles('/path/to/file.txt');
```
https://playwright.dev/docs/api/class-filechooser#set-input-files

#### ファイルダウンロード
Playwrightには、ファイルダウンロードを自動的に処理する機能はないが、ダウンロードの動作をシミュレートすることは可能。

例)
1.ダウンロードリンクをクリックする。
2.ダウンロードが完了するまで待機する。
3.ダウンロードされたファイルを指定の場所に移動する。
4.Playwrightでは、ダウンロードされたファイルを移動するために、OSのファイル操作を利用する必要あり。

```typescript
const downloadLink = await page.locator('a.download-link');
await downloadLink.click();

// ダウンロード完了まで待機する（例: ファイルのダウンロードに5秒かかる場合）
await page.waitForTimeout(5000);

// ダウンロードされたファイルを移動する（例: ファイルを指定のディレクトリに移動）
const sourcePath = '/path/to/downloaded/file';
const destinationPath = '/path/to/destination/file';
// ファイルを移動するOSのコマンドに応じた方法を利用して移動させる
const fs = require('fs');
fs.renameSync(sourcePath, destinationPath);
```
上記の例では、ダウンロードリンクをクリックした後、5秒間待機してからダウンロードされたファイルを指定のディレクトリに移動。ファイルの移動には、fs.renameSyncを使用している。


### ページ遷移, タブ切り替え

以下は、タブを切り替えるための一般的な手順。
タブを開くために新しいページインスタンスを作成。

```typescript
const newPage = await browser.newPage();
//新しいページで指定したURLに移動

await newPage.goto('https://example.com');
//現在のページのタブを取得

const currentPage = await context.currentPage();
//タブを切り替え

await context.pages()[0].bringToFront(); 
// 0はタブのインデックスを表す
//これにより、新しいタブを開き、既存のタブと切り替えることが可能。タブを切り替える際には、bringToFront()メソッドを使用する。

//注意: contextは、PlaywrightのbrowserContextオブジェクトを表す。context.pages()は、現在開いているすべてのページのリストを返すメソッド。
```
以下は、タブ切り替えの例

```typescript
const page1 = await context.newPage();
await page1.goto('https://example.com');

const page2 = await context.newPage();
await page2.goto('https://google.com');

// page1とpage2のタブを切り替える
await context.pages()[0].bringToFront(); // page1に切り替え
await context.pages()[1].bringToFront(); // page2に切り替え
```



### タッチスクリーン

Playwrightを使用してタッチイベントをシミュレート可能。
タッチスクリーンの操作に関連する主なメソッドとその使用方法。

```typescript
page.touchscreen.tap(x, y): 
//指定した座標(x, y)で画面をタップ

await page.touchscreen.tap(100, 200);
//page.touchscreen.move(x, y): 指定した座標(x, y)にタッチポイントを移動

await page.touchscreen.move(200, 300);
page.touchscreen.down(): 
//タッチポイントを画面に押し下げる

await page.touchscreen.down();
page.touchscreen.up(): タッチポイントを画面から離す

await page.touchscreen.up();
これらのメソッドを組み合わせることで、タッチスクリーンの操作をシミュレート可能。
```
以下は、タッチスクリーンの操作例。

```typescript
// 画面上の座標(100, 200)でタップ
await page.touchscreen.tap(100, 200);

// タッチポイントを座標(200, 300)に移動
await page.touchscreen.move(200, 300);

// タッチポイントを画面に押し下げる
await page.touchscreen.down();

// タッチポイントを画面から離す
await page.touchscreen.up();
```
これらのメソッドを適切に組み合わせることで、タッチスクリーン上での操作を再現することが可能。

### Video

Playwrightには、ページのビデオ録画機能が組み込まれており、以下の方法で使用することが可能。

#### Video録画の有効化と無効化

```typescript
//Video録画を有効化するには、以下のようにオプションを設定する
const browser = await playwright.chromium.launch();
const context = await browser.newContext({
  recordVideo: {
    dir: '/path/to/video/folder',
    size: { width: 800, height: 600 }
  }
});
const page = await context.newPage();
//上記の例では、recordVideoオプションを使用してビデオ録画を有効化しています。dirオプションは、Videoファイルの保存先ディレクトリを指定し、sizeオプションはビデオの解像度を指定している。

//Video録画を無効化する場合は、recordVideoオプションをfalseに設定することで、ビデオ録画を無効化

const context = await browser.newContext({
  recordVideo: false
});
```

#### Video録画の開始と停止
Video録画を開始するには、contextオブジェクトのstartVideoRecordingメソッドを使用する。

```typescript
await context.startVideoRecording();
//Videoを停止するには、contextオブジェクトのstopVideoRecordingメソッドを使用する。

const video = await context.stopVideoRecording();
stopVideoRecordingメソッドは、録画されたVideoファイルへのパスを返す。
```

#### Videoの再生
録画されたVideoを再生するには、Playwrightの機能ではなく、外部のビデオプレーヤーを使用する必要があります。

### イベント送信
| 方法 | 説明 |
|---|---|
await locator.dispatchEvent(event) | 要素にイベントを送信する

```javascript
const input = await page.locator("input");
await input.dispatchEvent('click');
```
https://playwright.dev/docs/api/class-locator


### ブラウザ操作
| 方法 | 説明 |
|---|---|
| await page.goBack()|ブラウザバック|
| await page.goForward()|ブラウザバックの次へに相当|
| await page.reload()|ブラウザの再読み込みに相当|
| await page.close() | ブラウザを閉じる(xボタンに相当)|

### キーボード操作
Playwrightでは、キーボードの操作もシミュレートすることが可能。

| 方法 | 説明 |
|---|---|
|await page.keyboard.press(key)	|指定したキーを押下する|
|await page.keyboard.release(key)	|指定したキーを離す|
|await page.keyboard.type(text)	|指定したテキストを入力する|

特定のキーを押下する際には、キーコードや修飾キーを指定することも可能。

```typescript
await page.keyboard.press('ArrowDown'); // 下向き矢印キーを押下
await page.keyboard.press('Shift+KeyA'); // ShiftキーとAキーを同時に押下
```

https://playwright.dev/docs/api/class-keyboard

### スクリーンショットとビジュアルリグレッションテスト

```typescript
await page.screenshot({ path: 'screenshot.png' });
//この例では、screenshot.pngというファイル名でスクリーンショットを保存。
```
page.screenshot()メソッドには、オプションを指定することも可能

```typescript
await page.screenshot({
  path: 'screenshot.png',
  fullPage: true,
  clip: { x: 0, y: 0, width: 800, height: 600 }
});
```
上記の例では、以下のオプションを指定

**path**
スクリーンショットの保存先ファイルパスを指定。

**fullPage**
ページ全体のスクリーンショットを取得するかどうかを指定。デフォルトはfalse。

**clip**
スクリーンショットの範囲を指定。x、y、width、heightのプロパティを指定。

https://youtu.be/Y1eTK-j66PU

## アサーション


| アサーション | 説明 |
|---|---|
|expect(locator).toBeChecked()|	Checkboxがcheckされていること |
|expect(locator).toBeEnabled()|	要素が有効化されていること |
|expect(locator).toBeVisible()|	要素が可視であること |
|expect(locator).toContainText()|	要素が文言を含むこと |
|expect(locator).toHaveAttribute()|	要素が属性を含むこと |
|expect(locator).toHaveCount()| 要素のリストのlengthが一致していること |
|expect(locator).toHaveText()|	要素の文言が一致すること|
|expect(locator).toHaveValue()|	Input要素が特定の値を持っていること |
|expect(page).toHaveTitle()|	ページが特定のタイトルを持っていること|
|expect(page).toHaveURL()|	ページが特定のURLを持っていること|
|expect(page).toHaveScreenshot()|	ページに特定のスクリーンショットがあること |

https://playwright.dev/docs/api/class-genericassertions

https://playwright.dev/docs/api/class-pageassertions

https://playwright.dev/docs/api/class-snapshotassertions

### ソフトアサーション
通常のアサーションは失敗すると即座にテストが失敗状態になり、終了する。
softアサーションは失敗しても即座にテストを終了せず、最後まで実行してから結果を出力する。
```typescript
// Make a few checks that will not stop the test when failed...
await expect.soft(page.getByTestId('status')).toHaveText('Success');

// ... and continue the test to check more things.
await page.getByRole('link', { name: 'next page' }).click();
```

https://playwright.dev/docs/best-practices#productivity-tips



## タイムアウト
timeoutErrorとは、Playwrightが特定の操作を実行する際に、指定された時間内に完了しなかった場合に発生させるエラー

```typescript
// ページ上のFooというテキストを持つ要素をクリックする
// タイムアウトは100ミリ秒に設定する
await page.locator("text=Foo").click({ timeout: 100 });
```

このコードは、ページ上にFooというテキストを持つ要素が存在し、かつ100ミリ秒以内にクリックできる場合は正常に動作する。しかし、もしFooというテキストを持つ要素が存在しない場合や、100ミリ秒以内にクリックできない場合は、timeoutErrorが発生する。このように、timeoutErrorはPlaywrightが期待する動作がタイムアウトしたことを示すエラー。

PlaywrightのtimeoutErrorの設定方法は、主に以下の二つがある。

1.グローバルなタイムアウトの設定
2.個別の操作ごとのタイムアウトの設定

### グローバルなタイムアウトの設定


グローバルなタイムアウトの設定とは、Playwrightが実行するすべての操作に対して共通のタイムアウトを設定する方法です。グローバルなタイムアウトの設定は、playwright.config.jsという設定ファイルで行うことができます3。例えば、以下のように設定ファイルを作成したとします。

```typescript
// playwright.config.js
module.exports = {
  // グローバルなタイムアウトを5000ミリ秒に設定
  timeout: 5000,
};
```

この設定ファイルを使ってテストを実行すると、Playwrightが実行するすべての操作は5000ミリ秒以内に完了しなければならない。もし5000ミリ秒以上かかる操作があれば、timeoutErrorが発生する。このように、グローバルなタイムアウトの設定は、Playwrightが実行するすべての操作に対して一律に適用される設定。

### 個別のタイムアウト設定

個別の操作ごとのタイムアウトの設定とは、Playwrightが実行する特定の操作に対して個別にタイムアウトを設定する方法。個別の操作ごとのタイムアウトの設定は、各操作のオプションで行うことができる。例えば、以下のようなコードを実行したとする。

```typescript
// ページ上のFooというテキストを持つ要素をクリックする
// タイムアウトは100ミリ秒に設定する
await page.locator("text=Foo").click({ timeout: 100 });
```

このコードは、ページ上にFooというテキストを持つ要素が存在し、かつ100ミリ秒以内にクリックできる場合は正常に動作する。
しかし、もしFooというテキストを持つ要素が存在しない場合や、100ミリ秒以内にクリックできない場合は、timeoutErrorが発生する。
このように、個別の操作ごとのタイムアウトの設定は、特定の操作に対して個別に適用される設定。

個別の操作ごとのタイムアウトの設定は、グローバルなタイムアウトの設定よりも優先される。

## ロガー

playwrightのロガーは、playwrightのAPIを使ってブラウザやページの動作を記録するためのツール。できることは以下の通り。

- ログレベルを指定して、ログの重要度や種類を分けることができる。例えば、verbose, info, warning, errorなど。
- ログの出力先やフォーマットを設定することができる。例えば、コンソール、ファイル、JSONなど。
- ログには、ブラウザやページの識別子やタイムスタンプなどのメタデータが付与される。

## グローバルなロガー

playwrightのロガーを使うには、以下の手順が必要。

1. playwrightをインストールする。
2. playwright.config.jsという設定ファイルを作成する。
3. 設定ファイルにloggerオプションを追加して、ログレベルや出力先などを指定する。
4. playwright testコマンドでテストを実行する。

例えば、以下のような設定ファイルを作成したとする。

```typescript: playwright.config.js
module.exports = {
  // ログレベルをverboseに設定
  logger: {
    level: 'verbose',
    // ログをファイルに出力する関数を定義
    sink: (name, severity, message, args) => {
      const fs = require('fs');
      const util = require('util');
      // ファイル名はブラウザとページの識別子から生成
      const fileName = `${name.replace(/[:]/g, '-')}.log`;
      // メッセージにメタデータと引数を追加
      const logMessage = `${message} ${util.format(...args)}\n`;
      // ファイルに追記モードで書き込み
      fs.appendFileSync(fileName, logMessage);
    },
  },
};
```

この設定ファイルを使ってテストを実行すると、以下のようなログファイルが生成される。

```log
// chromium:page.log
[pid=1234][err] Failed to load resource: net::ERR_CONNECTION_REFUSED http://localhost:3000/
[pid=1234][err] Uncaught (in promise) Error: Request failed with status code 500
[pid=1234][info] navigated to "http://example.com/"
[pid=1234][info] clicked "text=Login"
[pid=1234][info] typed "input[name=username]", "testuser"
[pid=1234][info] typed "input[name=password]", "testpass"
[pid=1234][info] pressed "Enter"
[pid=1234][info] screenshot "test-login.png"
```

実際に出力されるログの中身は、以下のような情報が含まれる。

- [err] ログレベル。verbose, info, warning, errorなどがあり、設定したレベル以上のものが出力される。
- Failed to load resource: net::ERR_CONNECTION_REFUSED http://localhost:3000/: ログメッセージ。ブラウザやページの動作に関する情報が記録される。
- http://localhost:3000/: ログ引数。ログメッセージに補足的な情報を与えるために使われる。

### 個別ファイルにロガーを設定する

```typescript
// test2.spec.ts
import { test, expect } from '@playwright/test';

// このファイル内でのみ有効なロガーのオプションを指定
test.describe.configure({
  logger: {
    level: 'info',
  },
});

test('test2', async ({ page }) => {
  await page.goto('https://example.com/');
  // テストの内容
});

```
https://playwright.dev/docs/api/class-logger

## pause
テスト実行中を途中で止めてスクリーンショットを撮ったり、要素をinspectすることが可能。

```typescript
await page.pause()
```
https://playwright.dev/docs/api/class-page#page-pause

https://note.com/shift_tech/n/nb86761c14c9e

## Test
### test.beforeEach

各テストの直前に実行される
テストケースを実行する前に必要な初期化処理を行う
ラウザを開いたり、ページに移動したり、テストデータの準備をしたりするのに便利


```typescript
import { test, expect } from '@playwright/test';

test('test1', async ({ page }) => {
  // test.beforeEach() でブラウザを開く
  await page.goto('https://example.com/');

  // テストの内容
});

test.beforeEach(() => {
  // test.beforeEach() でテストデータを準備する
  const data = [1, 2, 3];
});
```

### test.beforeAll

ストスイート全体で実行される。
テストスイートを実行する前に必要な初期化処理を行うことができる。
たとえば、ブラウザを複数開いたり、テストデータベースを作成したりするのに便利

```typescript
import { test, expect } from '@playwright/test';

test.beforeAll(() => {
  // test.beforeAll() でブラウザを複数開く
  const browsers = ['chromium', 'firefox'];
  for (const browser of browsers) {
    await browser.launch();
  }
});

test('test1', async ({ page }) => {
  // テストの内容
});

test('test2', async ({ page }) => {
  // テストの内容
});
```



### test.describe(title, callback)
テストケースのグループを定義する。
テストケースのグループのタイトルと、テストケースを実行する関数を指定する。
```typescript
import { test, expect } from '@playwright/test';

test.describe('test1', async () => {
  // test.describe() でテストケースのグループを定義する
  test('test1-1', async ({ page }) => {
    // テストの内容
  });

  test('test1-2', async ({ page }) => {
    // テストの内容
  });
});

test.describe('test2', async () => {
  // test.describe() でテストケースのグループを定義する
  test('test2-1', async ({ page }) => {
    // テストの内容
  });

  test('test2-2', async ({ page }) => {
    // テストの内容
  });
});
```


### test.describe.fixme

テストを実行する前に修正する必要がある問題があることをテストの説明に追加する

### test.describe.skip

対象のテストグループを実行しないようにするメソッド

### test.skip
テストを実行しないように指定するメソッドです。

### test.only

このテストのみを実行する必要がある場合に、他のすべてのテストをスキップしてこのテストのみを実行するように指定する

### test.setTimeout

テストが指定された時間（ミリ秒）内に完了しない場合、テストを失敗させる

## browserとbrowserContext

browserはplaywrightでブラウザを操作するためのオブジェクト。
browser.newContext()やbrowser.newPage()などのメソッドを使って、ブラウザのコンテキストやページを作成したり、閉じたりできる。

browserContextはブラウザのセッションを表すオブジェクす。
browserContextは複数のページやポップアップを含むことができる。
また、cookieやストレージなどのデータを管理する。
browserContextを使って、インコグニートモードやカスタム設定のブラウザセッションを作成することができる。

例えば、playwrightでChromeブラウザを起動したいとする。その場合、まずchromiumオブジェクトを使ってbrowserオブジェクトを作成する。

```typescript
const { chromium } = require('playwright');
//このbrowserオブジェクトは、Chromeブラウザのインスタンスを表す。
const browser = await chromium.launch();
```

```typescript
//browserオブジェクトには、新しいコンテキストやページを作成するメソッドがある。

const context = await browser.newContext(); // 新しいコンテキストを作成
const page = await browser.newPage(); // 新しいページを作成

//このcontextオブジェクトは、ブラウザのセッションを表す。contextオブジェクトには、ページやポップアップを管理するメソッドがある。

const page = await context.newPage(); // コンテキスト内に新しいページを作成
await page.goto('https://example.com'); // ページに移動
const popup = await page.waitForEvent('popup'); // ページからポップアップが開かれるのを待つ
await popup.close(); // ポップアップを閉じる
```
contextオブジェクトは、インコグニートモードやカスタム設定のセッションを作成することができる。

```typescript
const context = await browser.newContext({ // カスタム設定のコンテキストを作成
  viewport: { width: 800, height: 600 }, // ビューポートのサイズを指定
  geolocation: { latitude: 59.95, longitude: 30.31667 }, // ジオロケーションを指定
  permissions: ['geolocation'] // ジオロケーションの権限を許可
});
```





## コマンドライン

| 実行オプション | 説明 |
|---|---|
|-headed|ヘッドブラウザでテストを実行。デバッグに便利|
|--browser|特定のブラウザでテストを実行。使用可能なオプションは、同時に3つのブラウザでテストを実行する場合は「chromium」、「firefox」、「webkit」、または「all」|
|--debug|Playwright Inspectorを使用してテストを実行|
|-c <dir>または--config <dir>|設定ファイルを指定。指定しない場合は、カレントディレクトリのplaywright.config.tsまたはplaywright.config.jsがデフォルトになる|
|--forbid-only|test.onlyを許可しないかどうか。CIで便利|
|-g <grep>または--grep <grep>|この正規表現に一致するテストのみ実行する。たとえば、-g "add to cart"を渡すと、「should add to cart」が実行される|
|--grep-invert <grep>|この正規表現に一致しないテストのみ実行。--grepの逆。|
|--global-timeout <number>|テスト全体の合計タイムアウト（ミリ秒）。デフォルトではグローバルタイムアウトはない。|
|--list　|すべてのテストを表示するが、実行はしない|
|--max-failures <N>または-x|　最初のN回のテストの失敗後に停止。-xを指定すると、最初の失敗後に停止|
|--repeat-each <N>|各テストをN回実行。デフォルトは1回|

https://playwright.dev/docs/test-cli