[T-DASH] Shadow DOM を操作する

# Outline

Salesforceなどのように、Shadow DOMを用いたサイトを自動化するのは厄介だ。

https://qiita.com/alangdm/items/cec32f21151a9da3c3f2

T-DASHでは、Shadow DOMを使った要素を識別することができない(※ 2024/09/01 現在)

そこをなんとかする。

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


# 技術的情報

Document:querySelector()とShadowRootを用いて要素を特定する

https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector

https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot

## 具体例

まず、簡単なShadow DOMのボタンを定義しているサイトを用いる。

https://practice.expandtesting.com/shadowdom

このサイトの「This button is inside a Shadow DOM」のボタンを要素特定してみる。
Developer toolで XPathを取得すると、以下の値になる
```
//*[@id="my-btn"]
```
しかし、これは「Here's a basic button example.」の上のボタンだけヒットして、とれません


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/fdb65639-7422-93cc-cd97-8c901e051f25.png)

では、以下のようにボタンのtextを指定してみるとどうなるか？

```
//button[text()="This button is inside a Shadow DOM."]
```

この場合、ヒットされません。
つまり、単純にXPathでは、Shadow DOMの中身を特定できないということです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/ccd5f7d4-8b5c-884f-77bd-7f6d5988524b.png)

では、querySelector や ShadowRootを使ってどうやって取得するか？

<B>querySelector</B>はXPathと類似で要素を効率よく検索するMethodである。
CSS selectorを指定して、それが一番最初にあるものを返します。

<B>ShadowRoot</B>はDOMツリーとは別にレンダリングされるDOMサブツリーのルートノードを指定します。


では、selectorはどうやって取得するか？

２パターンあります

- 対象のHTMLで”selectorをコピー”する
- selectorの階層の情報を参照する（構成は "タグ" "selector" である）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/15755bbe-33fd-90b3-bff5-b4aba9bec144.png)

では、具体的に「This button is inside a Shadow DOM」のボタンを querySelector で特定してみる。

① Shadow DOMの親階層を指定する
```
document.querySelector('#shadow-host')
```
② Shadow DOMのroot nodeであることを指定します
```
document.querySelector('#shadow-host').shadowRoot
```
③ buttonを特定します
```
document.querySelector('#shadow-host').shadowRoot.querySelector('#my-btn')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/cb8d6483-ba22-ff8d-6cda-f97504c28c5e.png)

では、実際にこの記載で正しく特定できているかを、developer toolのコンソールを使って確認することができます。
コンソールで先ほどのqueryを記載して、リターンを押すと、対象のボタンが取れることが確認できると思います。

```
document.querySelector('#shadow-host').shadowRoot.querySelector('#my-btn')
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/32e73a9f-40cc-d874-30ae-25a7401efd8c.png)

こちら、robot frameworkで実際に処理したいときどうするか

```
*** Settings ***
Library  SeleniumLibrary


*** Test Cases ***
Open Browser and Access practice demo
	Open Browser  https://practice.expandtesting.com/shadowdom  chrome
	Click Element  dom:document.querySelector('#shadow-host').shadowRoot.querySelector('#my-btn')

```

```
Click Element  locator
```
こちらが、「locatorで指定された要素をクリックする」命令である。

locatorとして、xpath=${xpath} の形式で、xpathを指定するのがよくつかわれるパターン。
今回、locatorとして、domで指定する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/686613d9-4417-a915-d3f9-b85f15ad7718.png)

https://robotframework.org/SeleniumLibrary/SeleniumLibrary.html

# T-DASHの手順

## カスタム動作

以下設定内容を selenium.yamlのファイル名で保存する

```
ACT-CAT-CUSTOM-a7b47e3f-ab46-4e69-8133-bcc13b702cd0:
  action_category_name: Selenium
  icon: ''
  color: '#3b16c1'
  custom_data:
    file_name: Selenium
    pip_list: []
    library_list: []
  actions:
    ACT-CUSTOM-c92d773f-f636-4ff9-af5f-d9f329f507df:
      action_name: マウスで指定したlocatorをクリックする
      action_type: operation
      action_format: マウスでlocator「設定値1」をクリックする
      action_note: ''
      action_args:
      - value1
      action_def:
      - - Click Element
        - ${value1}
    ACT-CUSTOM-b33c3edc-c60d-41da-bffd-487c5745629a:
      action_name: Locatorをマウスオーバーする
      action_type: operation
      action_format: Locator「設定値1」をマウスオーバーする
      action_note: ''
      action_args:
      - value1
      action_def:
      - - Mouse Over
        - ${value1}
```
次に、作成された selenium.yamlをT-DASHの動作定義 -> カスタム動作 -> カスタム動作をインポート にてインポートする

２つのカスタム動作は、Selenium keywordsの操作をそのまま利用している。
ただ、locatorを引数で指定できるようにしている。
この引数に、先ほどのdomの情報を入力することで、domで定義したlocationをクリック、マウスオーバーできるようになる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/5588b72c-a510-5f97-4d77-dda0fe18616b.png)

## スクリプト

ブラウザを開き、対象のボタンをマウスオーバーし、クリックするだけである。
※尚、「秒待機」をいれているのは、このサイトは広告がたまにでるため、その広告を閉じるために設けている。
また、この練習サイトは、ボタンをクリックしても遷移はしません（動作が期待通りかわからない）。ただ、マウスオーバーすると、ボタンの色が変わるため、「マウスオーバー」処理を入れている。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/53a2378f-dcd0-6626-4bba-7cbf8b39d619.png)

## テスト結果

スクリーンショットを確認すると、「This button is inside a Shadow DOM」のボタンの色が変わっており、そのボタンを特定していることが確認できる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267085/114b3a59-9b02-e79e-7d0e-4ba11821cb05.png)