Safari 10 の WebDriver ネイティブサポート

こんにちは！ みなさんmacOSしてますか！
アップグレード中にフリーズしたせいでリカバリーするのがたいへんだったよ！！！ クソが！！！！

なんでわざわざそんなクソなアップグレードを急いでやったかというと、Safariの新バージョンが動く最新の環境で確認したいことがあったからです。AppleによるネイティブサポートのWebDriverです！


Safari 10から、WebDriverがネイティブサポートされることになりました。
=============================================================

これまで、 MacOS Xの標準WebブラウザーであるSafariをWebDriverで自動操作するためには、専用の拡張機能プラグインが利用されていました。
新しくリリースされたSafari 10からは、このプラグインを使わなくても、OS側でWebDriverをサポートしてくれることになりました！

https://developer.apple.com/library/content/releasenotes/General/WhatsNewInSafari/Articles/Safari_10_0.html#//apple_ref/doc/uid/TP40014305-CH11-DontLinkElementID_28

Appleの開発者向けWebサイトには [SafariDriverの対応コマンド表](https://developer.apple.com/library/content/documentation/NetworkingInternetWeb/Conceptual/WebDriverEndpointDoc/Commands/Commands.html) こそ掲載されていますが、現状で最もよくまとまっている公式っぽい導入ドキュメントは [WebKitの公式ブログ記事](https://webkit.org/blog/6900/webdriver-support-in-safari-10/) くらいではないでしょうか。この記事に載っている情報も含め、新しいSafariDriverを具体的にどのように利用できるか確認してみましょう。


新しいSafariDriverやってみよう
===============================

新しいSafariをSeleniumで操作するときは、他のブラウザーと同様にSafariDriverサーバーを仲介役として併用することになりました。
Safari 10が導入された環境では、 `/usr/bin/safaridriver` にSafariDriverサーバーの実行ファイルが置かれています。

```bash
$ ls -l /usr/bin/safaridriver

-rwxr-xr-x  1 root  wheel  34384  9 14 09:56 /usr/bin/safaridriver
```

普通は `/usr/bin` にはPATHが通っていると思いますので、特に環境変数をいじらなくてもすぐに使えるはずですね。

初回起動時だけ、セキュリティーに関する確認ダイアログが表示されます。
ここで実行を許可しておけば、次回からはダイアログは表示されません。
待ち受けポート番号を指定する `--port` オプションに、既定値を利用する意味の `0` を指定して初回起動を済ませてみましょう。（なお、既定の待ち受けポートは7055/tcpです）

```bash
$ /usr/bin/safaridriver --port 0

Could not find the required security authorization right, `safaridriver` will attempt to create it.
Please allow `safaridriver` to make the required changes to the security authorization database.
```

![01.png](https://qiita-image-store.s3.amazonaws.com/0/33963/156b990b-d906-8dc0-78c2-2e425e01e29b.png)

さて、これだけではSafariをWebDriverから操作できません。
リモート操作できるAPIがあるということは、セキュリティー上のリスクがあるということです。Safariではセキュリティー上の理由で、WebDriverで動かすことを明示的に許可する設定が必要になります。

環境設定の詳細タブで、チェックボックス「メニューバーに"開発"メニューを表示」にチェックを入れましょう。

![02.png](https://qiita-image-store.s3.amazonaws.com/0/33963/776104d8-b563-bf9c-42a9-8d65900fcba9.png)

Safariのメニューに「開発」が表示されるようになります。
「リモートオートメーションを許可」を選択して、チェックが入っている状態にしましょう。

![03.png](https://qiita-image-store.s3.amazonaws.com/0/33963/1736fb31-5afa-ede6-1469-8f8b78b45138.png)

これで、SafariをWebDriverから動かす準備ができました。
先ほど起動したSafariDriverサーバーが開いている7055/tcpにWebDriver APIへのリクエストを投げれば、Safariを自動操作することができます。

自動操作されているSafariは、アドレスバーが黄色になってわかりやすくなっています。

![04.png](https://qiita-image-store.s3.amazonaws.com/0/33963/950111d7-5f29-050d-d0b7-fc15c942c9a8.png)

また、自動操作されているSafariにフォーカスを合わせると、自動操作されていることを示すダイアログが表示され、不用意にユーザー操作されないようになっています。

![05.png](https://qiita-image-store.s3.amazonaws.com/0/33963/c911bd88-c980-30d0-4191-f5d30b99063b.png)


公式クライアントライブラリーはバージョン3から対応です。
===============================================

Selenium の公式クライアントライブラリーでの対応は、最新バージョンの3からです。2系では使えませんしたぶんバックポートもされないので、無駄な抵抗はやめて3系に移行しJava 7も早急に捨てるのだのだのだ（残響音含む）

正確には、Selenium 3.0.0 beta4から新しいSafariDriverに対応しています。ぜひ試してみましょう！

なお、beta3まではバージョン3系で以前のオープンソース版のプラグインを利用するための `useLegacyDriver` というcapabilitiesが残っていたのですが、beta4では削除されてしまいました。今後、以前のプラグインはもう使えないと考えた方が良いでしょう。


スクリーンショットが一癖あります。
===================

* SafariDriverサーバーの [スクリーンショットを取得するWebDriver API](https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/webdriver-commands/screenshots/#take-screenshot) で返ってくるBASE64データが、ChromeDriverサーバーやGeckoDriverサーバーなどと違って、スラッシュ記号がエスケープされた状態になっています。これはJSON仕様としては正しい（エスケープしてもしなくてもいい）実装で、公式クライアントライブラリーもこれに対応して正しくスクリーンショットのデータを取得できる状態になっています。 [直接WebDriver APIを叩いていたbash野郎](http://qiita.com/hiroshitoda/items/5fa5292ceb1e3e8a9610) は気をつけましょう！！！


ベンチマーク
=====================

[日本Seleniumユーザーコミュニティの提供するサンプルページ](http://example.selenium.jp/reserveApp/)上で基本的なAPIを100回実行して、1回あたりの平均値を取得するだけの簡易ベンチマークをやりました。
https://github.com/hiroshitoda/WebDriverBenchmark

実行環境は手元のMacBook Pro実機で、スペックは次の通りです。

|||
|:--|:--|
|CPU|Intel Core i5 2.5GHz|
|メモリー|16Gbytes|
|OS|macOS Sierra 10.12|
|JDK|1.8.0_101-b13|

結果は次の通りでした。なかなか軽快に動くのがわかりますね。

||ChromeDriver|FirefoxDriver via GeckoDriver|PhantomJSDriver|SafariDriver|
|:--|--:|--:|--:|--:|
|WebDriver.get|289ms|249ms|49ms|48ms|
|WebDriver.findElement(By.Id)|13ms|10ms|10ms|7ms|
|WebDriver.findElement(By.cssSelector)|11ms|9ms|8ms|5ms|
|WebElement.clear|64ms|21ms|21ms|9ms|
|WebElement.sendKeys|70ms|23ms|32ms|66ms|
|WebElement.click|55ms|23ms|39ms|10ms|
|TakeScreenshot.getScreenshotAs|119ms|45ms|50ms|24ms|