PhantomJSからHeadless Chromeに移行したときにハマったポイント

# 祝！Chrome Headless移行完了

私が担当しているプロジェクトのFeatureSpecで、Headless Chromeに移行が完了しました。なかなか大変だったので、ハマったポイントを覚えている限り書いておこうと思います。

1. Capybara.register_driver周辺
    1. 画像をダウンロードしない設定など
2. テーブルに行がなくなるとテーブルごとvisibleがfalse扱いになる
3. DatePickerを使用している箇所にfill_inでデータを入れると、DatePickerが表示されっぱなしになり、被っているボタンが押せなくなる
4. driver依存の操作ができなくなる
    1. trigger('click')など
    2. driver.network_trafficなど
5. alert, confirm, promptなどのダイアログ系が自動でOKを押されなくなる

## register_driverでHeadless Chromeを登録する

Seleniumのドライバーとして、chromeを登録し、オプションでHeadlessにしていきます。

chrome_optionsで色々と指定します。

- headless・・・Headlessにする
- disable-gpu・・・GPUを使わない（現状必須）
- window-size=1680,1050・・・ウィンドウサイズ指定
- blink-settings=imagesEnabled=false・・・画像をダウンロードしない
- lang=ja・・・日本語指定

また、bridgeを使ってファイルのダウンロードを許可し、ダウンロードパスを指定しています。
`WaitForDownload::PATH`については、別記事の[RSpecでファイルがダウンロードされるまで待つ](https://qiita.com/patorash/items/8c4d011bee4d0d5c99f6)を参照してください。

```ruby
Capybara.register_driver :selenium do |app|
  driver = Capybara::Selenium::Driver.new(app, browser: :chrome,
                                          desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome(
                                              login_prefs: { browser: 'ALL' },
                                              chrome_options: {
                                                  args: %w(headless disable-gpu window-size=1680,1050 blink-settings=imagesEnabled=false lang=ja),
                                              },
                                          )
  )
  bridge = driver.browser.send(:bridge)
  path = "session/#{bridge.session_id}/chromium/send_command"
  bridge.http.call(:post, path, cmd: 'Page.setDownloadBehavior',
                   params: {
                       behavior: 'allow',
                       downloadPath: WaitForDownload::PATH
                   })
  driver
end

Capybara.javascript_driver = :selenium
```

## visible: falseになるものの検証をどうにかする

```ruby
#　テーブルのデータを削除する
within "table tbody" do
  expect(page).to have_content "1行目"
  click_button "削除"
end

# ここで、テーブルが非表示ですと怒られて落ちる
within("table tbody") do
  expect(page).to have_no_content "1行目"
end
```

こういうのは、以下のようにしました。

```ruby
expect(find('table', visible: false)).not_to be_visible
```

`visible: false`のものは見えないことというテスト内容になるので当たり前すぎておかしい気もしますが、行がないことを検証できるからとりあえずこうしてます。

いい書き方があったらアドバイスください。

## DatePicker対策

DatePickerが表示されっぱなし問題に関しては、新たに`fill_in_date`メソッドを定義して対応しました。

```ruby
def fill_in_date(target, options={})
  fill_in target, options
  hide_datepicker
end

def hide_datepicker
  page.execute_script("$('.datepicker').hide()")
end
```

## Driver依存のものの対応

find('.foo').trigger('click')は使えないので、find('.foo').clickに全て変更しました。

```ruby
# before
find('.foo').trigger('click')

# after
find('.foo').click
```

network_trafficは、PDFファイルなどへリダイレクトしたときに、URLが取れないため、network_trafficからURLを取得してちゃんとアクセスしたことを検証していたのですが、それができなくなったので、ファイルがダウンロードされているかどうかという観点に変更して対応しました（ダウンロードされている=そのURLにアクセスした）

## ダイアログ系の対応

これは、[RSpec + Capybaraでアラート/確認ダイアログを操作する場合は page.driver.browser.switch_to.alert.accept じゃなくて page.accept_confirm を使おう](https://qiita.com/regonn/items/9558a016c0992d7897e8)を参考に対応しました。

```ruby
# before
click_link '削除'

# after
accept_confirm { click_link '削除' }
```

ざっくり置換していきましたが、大概大丈夫でした。