PlaywrightとT-DASHではじめる、次世代の自動テスト

# はじめに

テスト自動化は、私の組織にとって長年の目標でしたが、専門スキルを持つ人材が限られていたため、実現は困難でした。自動テストの構築自体は可能でしたが、実行段階での大規模保守が必要となり、これが大きなハードルになっていました。

定期的な自動テストの実行と、失敗したテストの保守を試みましたが、再び人材不足が障害となりました。この問題を解決するには、適切なスキルを持つ人材の増加が必要だと感じていました。

この課題に対応するため、有料の自動化ツールを導入しました。しかし、高価な価格設定のため、ツールライセンスを持つ人材を増やすことができず、これが新たな障壁となりました。

## なぜ、PlaywrightとT-DASHか

T-DASHツールは低価格であり、使いやすさに魅力とこれまでの課題解決の可能性を感じています。

社内導入を進めるうえで、重要な条件としてPlaywrightを使用することです。理由としては、過去の経験から、私はWebDriverの使用において不安定さを感じているためです。

この記事は、T-DASH＋Playwrightでの自動テストに成功したので紹介します。
私の理解で書いているので、誤りがあるかもしれません。誤りがあれば教えてください。

# Playwrightとは

Playwrightは、ウェブアプリケーションのテストを自動化するためのオープンソースのツールです。Chrome、Firefox、Safariなど、複数のブラウザに対応しており、様々なプラットフォームでの動作も可能です。Playwrightは、テストの記述や実行を簡単かつ高速にするために設計されています。その直感的なAPIは、テストスクリプトの作成を容易にし、自動テストの開発を効率化します。

# 実現方法について

実現した環境について

* Windows 10
* Python 3.9.12
* Node.js インストールなし

T-DASHはWeb Driverを使ったテスト自動化ツールです。

![3-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/bdff8611-8b4f-0596-93a7-a5cd7623e266.png)

Playwrightを使うには、T-DASHにある「カスタム動作」で作る必要があります。

![3-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/5289acf5-2c17-8427-f2d3-ed5cac9d5d8a.png)

「カスタム動作」をつくるにあたって、参考にしたのは公式ページのチュートリアルです。このページ通りに進めたら、「カスタム動作」の作り方と使い方がわかります。

https://service.valtes.co.jp/t-dash/function/tutorial/custombehavior_vol_003



# Playwrightのインストール

Windowsのパソコンに、Playwrightをインストールします。

利用条件としては、Python 3.7以降が必要です。

(1) Pythonのバージョンを確認します。

```
> python --version
Python 3.9.12
```

(2) pipとsetuptoolsを更新します。

```
python -m pip install --upgrade pip setuptools
```

(3) Playwrightをインストールします。

```
python install playwright
```

(4) Playwrightにブラウザをインストールします。

```
python -m playwright install
```

Windowsの場合はこれでインストールは完了です。

(5) 起動するかどうか確認します。

```
python -m playwright open www.google.com
```

Googleのページが表示されたら、インストールは成功です。

:::note warn
もしページが表示されない場合は、パソコンにインストールされているソフトが悪さをしている可能性があります。会社で利用されているパソコンの場合は、情報システム部門と一緒に解決されると良いかもしれません。
:::


# Playwrightを使う自作ライブラリの作成

`playwrightlib.py` を作成します。
作成する関数は、簡単な操作をするための、いくつかの動作、表示文字のチェック、スクリーンショットを撮るです。

作成したコードは以下の通りです。

```python:playwrightlib.py
from playwright.sync_api import Playwright, sync_playwright, expect

browser = None
context = None
page = None
playwright = None
headless = False

# ヘッドレスの設定
def playwright_set_headless(p_headless=False):
    global headless
    headless = p_headless


# ブラウザを起動する
def playwright_open_browser(p_browser="chrome"):
    global page
    global playwright
    playwright = sync_playwright().start()
    if "chrom" in p_browser:
        context = playwright.chromium.launch(headless=headless).new_context()
    elif  "firefox" in p_browser:
        context = playwright.firefox.launch(headless=headless).new_context()
    elif  "webkit" in p_browser:
        context = playwright.webkit.launch(headless=headless).new_context()
    else:
        device = playwright.devices[p_browser]
        context = playwright.webkit.launch(headless=headless).new_context(**device)
    page = context.new_page()
    
# ページを開く
def playwright_open_page(p_url):
    page.goto(p_url)

# ブラウザを閉じる
def playwright_close():
    playwright.stop()

# テキストを入力する
def playwright_input_text(p_locator, p_value):
    page.locator(p_locator).fill(p_value)

# クリックする
def playwright_click(p_locator):
    page.locator(p_locator).click()

# 表示文字が指定した文字かチェックする
def playwright_element_should_be(p_locator, p_value):
    expect(page.locator(p_locator)).to_have_text(p_value)

# スクリーンショットをとる
def playwright_page_screenshot(p_path):
    page.screenshot(path=p_path, full_page=True)
```

:::note warn
RobotFrameworkには、BrowserLibraryというPlaywrightの操作用のライブラリがありますが、Node.jsのインストールが必須であるため、今回はPythonコードで必要な動作を自作しました。Node.jsを使える環境である場合は、BrowserLibraryを使用したほうが簡単です。

https://robotframework-browser.org/
https://marketsquare.github.io/robotframework-browser/Browser.html
:::


# カスタム動作の作成

![5-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/d5120b06-63a3-00db-d90e-af9434ca601c.png)

| フィールド | 入力する値 |
| ----- | ---- |
| キー | playwright |
| 動作カテゴリ | PlayWright |
| 色 | <<任意>> |

上記の値を入力し、「作成」ボタンを押下します。

![5-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/1b82c86a-cfc3-9c0b-ee2b-2ad00da791f4.png)

「＋カスタム動作を追加」ボタンを押下します。

![5-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/5bfdf5af-bdf8-d106-7903-763b28073c3c.png)


「＋ライブラリを追加する」ボタン、自作ライブラリの「フォルダを開く」リンクを順番に押下します。

![5-4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/59e06189-6611-ea2a-26ab-aa966184c5dc.png)


エクスプローラが表示されますので、「4.Playwrightを使う自作ライブラリの作成」で作成した`playwrightlib.py`をここにコピーし、閉じます。

![5-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/3341bda3-34c8-55ad-e4c9-313c20d4191d.png)

T-DASHに戻って、自作ライブラリの「再読み込み」を押下すると、`playwright.py`が表示されるので、その右にある「＋」を押下します。Libraryに「`Library playwright.py`」が追加されます。

ライブラリ追加の右上にある「×（閉じる）」を押下します。

## （１）ヘッドレスの設定をする動作

| フィールド | 入力する値 |
| ----- | ---- |
| 動作名 | Set Headless |
| 利用変数 | - [X] 画面定義を使用<br>- [ ] 通常の変数として使用 |
| 利用変数 | - [ ] 「画面名」<br>- [ ] 「要素名」<br>- [X] 「設定値」 |
| フォーマット | ヘッドレス設定に「設定値」を設定する |
| 動作種類 | - [X] 操作<br>- [ ] 検証 |
| 説明 | Trueを設定するとブラウザをヘッドレスで起動します。 |

スプレッドシートに以下のように入力します。

![5-6-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/97d6adb9-43d9-a48c-e1cc-f70b883a04d6.png)

| |A | B                       | C                  |
|---|--- |---|---|
|4|  | playwright set headless | ${value} |

![5-6-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/a3798b2f-e5f0-dfbc-5385-817a5ed2c3d6.png)

「保存する」をクリックします。

:::note alert
なぜか、スプレッドシートのヘッダに「E」が2つあります。
きっとバグですね。
:::

## （２）ブラウザを開く動作

| フィールド | 入力する値 |
| ----- | ---- |
| 動作名 | Open Browser |
| 利用変数 | - [X] 画面定義を使用<br>- [ ] 通常の変数として使用 |
| 利用変数 | - [ ] 「画面名」<br>- [ ] 「要素名」<br>- [X] 「設定値」 |
| フォーマット | ブラウザ「設定値」を起動する |
| 動作種類 | - [X] 操作<br>- [ ] 検証 |
| 説明 | chrome,firefox,webkit,<<playwright device>>を入力します。 |

スプレッドシートに以下のように入力します。

![5-7-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/2d964398-f503-f0b1-48d8-7f5b236474d7.png)

| |A | B                       | C                  |
|---|--- |---|---|
|4|  | playwright open browser | ${value} |

![5-7-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/6720c7bd-d971-2e2b-6bb3-cee9474ddefe.png)

「保存する」をクリックします。


## （３）ブラウザを閉じる動作

| フィールド | 入力する値 |
| ----- | ---- |
| 動作名 | Close |
| 利用変数 | - [X] 画面定義を使用<br>- [ ] 通常の変数として使用 |
| 利用変数 | - [ ] 「画面名」<br>- [ ] 「要素名」<br>- [ ] 「設定値」 |
| フォーマット | テストを終了する |
| 動作種類 | - [X] 操作<br>- [ ] 検証 |
| 説明 | Playwrightを使ったテストの最後に必ず実行します。 |

スプレッドシートに以下のように入力します。

![5-8-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/6cc2b10a-c05e-091a-fad0-f8259a0c004a.png)

| |A | B                       |
|---|--- |---|
|4|  | playwright close |

![5-8-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/1a05fc73-bfb1-9148-f828-97e5098bb983.png)

「保存する」をクリックします。

## （４）文字を入力する動作

| フィールド | 入力する値 |
| ----- | ---- |
| 動作名 | Input Text |
| 利用変数 | - [X] 画面定義を使用<br>- [ ] 通常の変数として使用 |
| 利用変数 | - [X] 「画面名」<br>- [X] 「要素名」<br>- [X] 「設定値」 |
| フォーマット | 「画面名」の「要素名」に「設定値」を入力する |
| 動作種類 | - [X] 操作<br>- [ ] 検証 |
| 説明 | <<なし>> |

スプレッドシートに以下のように入力します。

![5-9-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/a88e89dd-d358-847e-47a5-c2d17a4f56d1.png)


| |A | B                       | C                  |D                  |
|---|--- |---|---|---|
|4|  | playwright input text | ${location} | ${value} |

![5-9-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/0cb59ae3-74e8-09d0-a5bf-74ab9d97b3c6.png)


「保存する」をクリックします。

## （５）クリックする動作

| フィールド | 入力する値 |
| ----- | ---- |
| 動作名 | Click Element |
| 利用変数 | - [X] 画面定義を使用<br>- [ ] 通常の変数として使用 |
| 利用変数 | - [X] 「画面名」<br>- [X] 「要素名」<br>- [ ] 「設定値」 |
| フォーマット | 「画面名」の「要素名」をクリックする |
| 動作種類 | - [X] 操作<br>- [ ] 検証 |
| 説明 | <<なし>> |

スプレッドシートに以下のように入力します。

![5-A-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/e90afb06-753b-c4ca-b98e-d99187d90b28.png)



| |A | B                       | C                  |
|---|--- |---|---|
|4|  | playwright click | ${location} |

![5-A-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/96491a87-a17e-9063-6b6a-2e2a008338c0.png)

「保存する」をクリックします。


## （６）URLにアクセスする動作

| フィールド | 入力する値 |
| ----- | ---- |
| 動作名 | Open Page |
| 利用変数 | - [X] 画面定義を使用<br>- [ ] 通常の変数として使用 |
| 利用変数 | - [ ] 「画面名」<br>- [ ] 「要素名」<br>- [X] 「設定値」 |
| フォーマット | URL「設定値」にアクセスする |
| 動作種類 | - [X] 操作<br>- [ ] 検証 |
| 説明 | <<なし>> |

スプレッドシートに以下のように入力します。

![5-B-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/b2989fb0-75ea-bb9e-b9a0-2b143fbecb85.png)

| |A | B                       | C  | D  |
|---|--- |---|---|---|
|4|  | playwright open page | ${value} |  |

![5-B-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/64d063d8-5c8c-82a4-c02d-944b350ce956.png)


## （７）表示文字をチェックする動作

| フィールド | 入力する値 |
| ----- | ---- |
| 動作名 | Element Should Be |
| 利用変数 | - [X] 画面定義を使用<br>- [ ] 通常の変数として使用 |
| 利用変数 | - [X] 「画面名」<br>- [X] 「要素名」<br>- [X] 「設定値」 |
| フォーマット | 「画面名」の「要素名」は文字列「設定値」を表示すること |
| 動作種類 | - [ ] 操作<br>- [X] 検証 |
| 説明 | 完全一致で表示文字をチェックします。 |

スプレッドシートに以下のように入力します。

![5-C-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/ccd3212a-a9c9-8df1-a669-4818f81f9510.png)


| |A | B                       | C  | D  |
|---|--- |---|---|---|
|4|  | playwright element should be | ${location} | ${value} |

![5-C-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/55cccbce-78ca-6954-eb6e-0a9690ea7455.png)


「保存する」をクリックします。

## （８）スクリーンショットを撮る動作

| フィールド | 入力する値 |
| ----- | ---- |
| 動作名 | Full Screenshot |
| 利用変数 | - [X] 画面定義を使用<br>- [ ] 通常の変数として使用 |
| 利用変数 | - [ ] 「画面名」<br>- [ ] 「要素名」<br>- [X] 「設定値」 |
| フォーマット | フルページのスクリーンショットを「設定値」に保存する |
| 動作種類 | - [X] 操作<br>- [ ] 検証 |
| 説明 |  |

スプレッドシートに以下のように入力します。

![5-D-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/c0899a94-906a-aab6-fd10-cf5fd34065a0.png)

| |A | B                       | C  | D  |
|---|--- |---|---|---|
|4|  | playwright page screenshot | `${OUTPUT DIR}${/}${value}` |  |
|5|  | Log | `<img src="${OUTPUT DIR}${/}${value}" />` | html=yes |

![5-D-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/57c10c12-447e-8281-7add-76763754c3e0.png)


「保存する」をクリックします。


# 作成した動作のテストの作成

テスト自動化の学習用の練習サイトを使います。

## （１）テストの内容

https://hotel.testplanisphere.dev/ja/reserve.html?plan-id=4

今年のクリスマスイブに１人で１泊して、クリスマスの朝食はひとりでやけ食いの予約をします。

![6-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/1d233fbc-f90b-cb14-7270-3ba8c39cc860.png)



| フィールド | 入力する値 |
| --- | --- |
| 宿泊日 | 2023/12/24 |
| 宿泊数 | 1 |
| 人数 | 1 |
| 追加プラン | -[X] 朝食バイキング<br> -[ ] 昼からチェックインプラン<br> -[ ] お得な観光プラン |
| 氏名 | アドベントカレンダー2023 |
| 確認のご連絡 | <<セレクトボックスの動作を作っていないので、今回はパスです>> |

これで、7,875円になる確認を行います。

## （２）T-DASHの画面定義

T-DASHでは「画面定義」で「要素名」と「XPath」を設定します。以下のように設定しました。

![6-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/df67079d-fa0c-3299-d1a0-01033b9c3796.png)


画面名：　宿泊予約

| 要素名 | 値 | 要素画像パス |
| --- | --- | --- |
| 宿泊日 | //input[@id="date"] |  |
| 宿泊数 | //input[@id="term"] |  |
| 人数 | //input[@id="head-count"] |  |
| 朝食バイキング | //input[@id="breakfast"] |  |
| 氏名 | //input[@id="username"] |  |
| 合計金額 | //output[@id="total-bill"] |  |


## （３）T-DASHのテストケース

![6-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/b54224b8-35a5-8726-dcd3-43f1d4a622b3.png)


テストスイート：　ホテル予約サイトのテスト
テストケース：　予約金額の確認

| T-DASHキーワード手順 | 動作 | 画面名（設定値1） | 要素名（設定値2） | 設定値（設定値3） |
| --- | --- | --- | --- | --- |
| ヘッドレス設定に「False」を設定する | Set Headless |  |  | False |
| ブラウザ「chrome」を起動する | Open Browser |  |  | chrome |
| URL「https://hotel.testplanisphere.dev/ja/reserve.html?plan-id=4」にアクセスする | Open Page |  |  | https://hotel.testplanisphere.dev/ja/reserve.html?plan-id=4 |
| 「宿泊予約」の「宿泊日」に「2023/12/24」を入力する | Input Text | 宿泊予約 | 宿泊日 | 2023/12/24 |
| 「宿泊予約」の「合計金額」をクリックする | Click Element | 宿泊予約 | 合計金額 |  |
| 「宿泊予約」の「宿泊数」に「1」を入力する | Input Text | 宿泊予約 | 宿泊数 | 1 |
| 「宿泊予約」の「人数」に「1」を入力する | Input Text | 宿泊予約 | 人数 | 1 |
| 「宿泊予約」の「朝食バイキング」をクリックする | Click Element | 宿泊予約 | 朝食バイキング |  |
| 「宿泊予約」の「氏名」に「アドベントカレンダー2023」を入力する | Input Text | 宿泊予約 | 氏名 | アドベントカレンダー2023 |
| フルページのスクリーンショットを「advent.png」に保存する | Full Screenshot |  |  | advent.png |
| 「宿泊予約」の「合計金額」は文字列「7,875円」を表示すること | Element Should Be | 宿泊予約 | 合計金額 | 7,875円 |
| テストを終了する | Close |  |  |  |


# テストの実行

![7-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/c5db8af1-508c-310e-a228-a10f7ed09449.png)

スクリーンショットを撮った画像はここでは表示されません。
どこにあるか探したら見つけました。

![7-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/0978d95b-9b45-7c09-1b0f-e8b5cdcedb92.png)

レポートの右上にある「Log」を押下します。

![7-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/fff0a743-49f7-99ae-c4f8-b3c2b3d2ac0a.png)

スクリーンショットはちゃんと取れていました。

:::note alert
LOGのレポートではなく、T-DASHのレポートに表示する方法はわかりませんでした。
RobotFrameworkのログファイルを解析して出力していると思うのですが、思ったものを出せないようです。
標準の動作に、BuiltInのLogと同じ動作をするものを準備してもらいたいです。
:::

###  WebDriverとの比較

サイトの特性にも影響しますが、Playwrightの使用により、自動テストの実行時間を約33%から50%削減することが可能になりました。

![8-0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/8fe5e52e-31af-419c-d53e-11507597ec26.png)


# 総評

最近流行っているPlaywrightを使ってT-DASHで自動化してみました。
この記事を通じて感じたことは、「カスタム動作」の追加機能については、自動化の可能性を拡げる機能だと思いました。T-DASHは使いやすく低価格ということで、価格に見合った範囲しか自動化できないのかと最初は思っていましたが、今回紹介しなかった動作をひとまとめに共有化する「動作セット」や「データドリブン」のあたりまえ機能は抑えていますし、記事の通り「カスタム動作」でどれだけでも拡張できる機能を有していて高機能自動化ツールとしても使えるものだと感じました。



## （１）良いと思った点

* 可読性が高い自動テスト
  * 自動テストの内容が日本語で記述されており、わかりやすい。
* 使いやすさ
  * カスタム動作を除いたツールの習熟コストは非常に低いと感じたので、テスト自動化をする人を簡単に増やしやすい。
  * カスタム動作は配布でき、ツールの拡張性は非常に高い。
* 低価格
  * これもテスト自動化をする人を簡単に増やしやすい要素。
  * テスト自動化のコードにおいて、統一された基準があっても好き勝手に書く人を是正するのは難しいが、このツールを使えばその問題が解決できる。
* マニュアルが豊富
  * 公式ページのマニュアルをみれば、使い方がわからないということはない。ただ、ノンプログラマーには、全体的な流れがわかるマニュアルがなさそうなので、迷うかもしれない。


## （２）改善の余地ありと思った点

他のかたも書かれていますが、そのとおり！と思ったので書きます。
効率化と安定した自動テストには不可欠ではないかと思います。

* SetupやTeardownがほしい
  * 今回作った「カスタム動作」の「Open Browser」や「Close」はSetupやTeardownで実行したい。
  * ![8-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/ab1e2779-d334-12f5-9420-f3b0913c6930.png)

  途中のチェックでNGになったときにCloseしないため、コンソールにエラーが残っています。作成した動作の中でクローズを入れてもいいんですが、ツール上で明示的に呼びたいです。
* カスタム動作の動作チェックがとても面倒
  * カスタム動作⇔テストケースを頻繁に行き来するのでかなり面倒でした。
  * これからもカスタム動作をつくりたいので、ここは改善してほしいところです。

その他

* カスタム動作のライブラリは、カテゴリに紐づいてそうなのに動作の中で設定するのに違和感がある
* 色が薄く、コントラスト、明るさを落としているモニターでは、カスタム動作のスプレッドシートの薄い青の部分が判別できない
  * ![8-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3641355/0b01c30b-fe57-f856-62fb-85598b5237f9.png)
  実際の見え方をスマホで撮ってみました。３行目、４行目の区別が殆どなく、しばらく３行目に入力できない理由がわからず悩んでました。
* 画像をレポートに出力する動作やAPIがほしい
    * RobotFrameworkのレポートには出力されているので、それをT-DASHのレポートでも表示したいと思いました。（表示する方法があれば知りたいです）
  



# おわりに

わずか3,960円でこれほどのことができるとは驚きです！このツールを使用して、自動化ツールの可能性を実感しました。多くの場合、自動化したい動作を実現できないためにテスト自動化ツールの導入を諦めがちですが、T-DASHにおいてはそうではないと感じました。ツールは日々進化しており、その進化に期待しつつ、今後も自動化のさらなる可能性を探求していきたいと思います。

# 参考サイト

## （１）公式サイト

このマニュアルをみるとほぼ使えるようになりました。

https://service.valtes.co.jp/t-dash/

* [手動で画面定義を作成しよう](https://service.valtes.co.jp/t-dash/function/tutorial/screendefinition_vol_003)
* [画面定義作成後、テストケースを完成させよう](https://service.valtes.co.jp/t-dash/function/tutorial/testscenarios_test_cases_vol_002)
* [カスタム動作を作成しよう / Pythonで作成](https://service.valtes.co.jp/t-dash/function/tutorial/custombehavior_vol_003)

## （２）アドベントカレンダー

カスタム動作の作成で非常に参考になりました。

https://qiita.com/troilus1112/items/60bf74a6096f530b742d

https://qiita.com/troilus1112/items/a273eb31abd8f4246882

基本的な使い方の学習の参考になりました。（先に見ておけば良かったと思った記事でした）

https://qiita.com/94dango/items/2fafccaea96b7e1d33e2

## （３）Playwright for Python

カスタム動作の中身をつくるのに参照しました。

https://playwright.dev/python/

## （４）Playwright

今回はNode.jsなしということであまり見なかった。

https://robotframework-browser.org/

https://marketsquare.github.io/robotframework-browser/Browser.html