RSpec でGoogleMap のピンをクリックしたい

# はじめに

RSpecで GoogleMap のピンをクリックするテストを書きたかったがハマってしまったので備忘録です。
記載しているソース、エラーコードは出力されたものを不要なものを省いた状態で載せていますので参考にする場合は注意してください。

# 環境

* macOS 10.15.6
* Ruby 2.5.7
* Rails 5.2.3
* selenium-webdriver 3.142.7
* rspec-rails 4.0.1
* capybara 3.32.2

# 参考URL

<https://qiita.com/jnchito/items/607f956263c38a5fec24>

<https://qiita.com/kon_yu/items/52a0f5f0016564486061>

<https://stackoverflow.com/questions/31479958/capybara-rspec-not-finding-and-clicking-css-element>

<https://code-kitchen.dev/html/map-area/>

# 目標

GoogleMap 内のピンをクリックしてinfowindow が表示されるのかテストしたい

![googlemap.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377207/00f37cf0-8f55-e675-07c2-647a39d82421.png)

# 問題

* capybara でピンがクリックできなかった
* img タグをクリックすると`Selenium::WebDriver::Error::ElementClickInterceptedError:`
* map タグをクリックすると`Selenium::WebDriver::Error::ElementNotInteractableError:`

# 原因

* 検索したい要素に`display:none`, `disabled`, `hidden` になっているとcapybara で検索できない
* 検索したい要素の高さが0px なのでcapybara で認識できない

# 解決策

`visible: false` オプション付きでarea タグをfind するとクリックできる

```HTML:googlemap.html
<div id="map"> //地図全体
  <img src="https://maps.gstatic.com/mapfiles/api-3/images/spotlight-poi2_hdpi.png" usemap="#gmimap0"> //ピン画像
  <map name="gmimap0" id="gmimap0">
    <area> //コイツをクリックすればok
  </map>
</div>
```

```Ruby:googlemap_spec.rb
require 'rails_helper'

RSpec.describe "GoogleMap", type: :system do
  describe "GoogleMap が表示されているページ" do
    context "GoogleMap の動作確認", js: true do
      it "ピンをクリックするとinfowindow が表示されること" do
        pin = find("map#gmimap0 area", visible: false)
        pin.click
        expect(page).to have_css "div.infowindow" # infowindow クラスの有無をテスト
      end
    end
  end
end
```

# ダメなパターン その1

ピンのimg タグを探してしてクリックする

* そもそもimg タグはクリックできない
* `Selenium::WebDriver::Error::ElementClickInterceptedError:`
* area タグはクリック可能というヒントが表示される

```Ruby:googlemap_spec.rb
# ピンのimg タグを探してクリック
  describe "GoogleMap が表示されているページ" do
    context "GoogleMap の動作確認", js: true do
      it "ピンをクリックするとinfowindow が表示されること" do
        pin = find("img[src$='spotlight-poi2_hdpi.png']") #ピンのイメージを検索
        pin.click
        expect(page).to have_css "div.infowindow"
      end
    end
  end
```

```Ruby
# img タグはクリックできないよエラー
# area タグはクリックできるよ
Failures:

  1) GoogleMap が表示されているページ GoogleMap 表示内容 ピンをクリックするとinfowindow が表示されること
     Failure/Error: find("img[src$='spotlight-poi2_hdpi.png']").click

     Selenium::WebDriver::Error::ElementClickInterceptedError:
       element click intercepted: Element <img alt="" src="https://maps.gstatic.com/mapfiles/api-3/images/spotlight-poi2_hdpi.png" draggable="false" style="position: absolute; left: 0px; top: 0px; width: 27px; height: 43px; user-select: none; border: 0px; padding: 0px; margin: 0px; max-width: none;"> is not clickable at point (699, 750). Other element would receive the click: <area log="miw" coords="13.5,0,4,3.75,0,13.5,13.5,43,27,13.5,23,3.75" shape="poly" title="" style="cursor: pointer; touch-action: none;">
         (Session info: headless chrome=85.0.4183.121)

     [Screenshot]: スクリーンショットが保存されているディレクトリへのパス


     # 0   chromedriver                        0x00000001083091b9 chromedriver + 4911545
     .
     .
     .
     # 24  libsystem_pthread.dylib             0x00007fff6c49bb8b thread_start + 15

Finished in 9.05 seconds (files took 0.55005 seconds to load)
1 example, 1 failure

Failed examples:
```

# ダメなパターン その2

map タグを探してクリックする

* map タグが見つからない
* エラーコードにヒントは無し

```Ruby:googlemap_spec.rb
# map タグを探してクリック
  describe "GoogleMap が表示されているページ" do
    context "GoogleMap の動作確認", js: true do
      it "infowindow が表示されること テスト" do
        pin = find("map#gmimap0").click #mapタグを検索
        pin.click
        expect(page).to have_css "div.infowindow"
      end
    end
  end
```

```Ruby
# map タグは見つからないよエラー
Failures

  1) GoogleMap が表示されているページ GoogleMap 表示内容 ピンをクリックするとinfowindow が表示されること
     Failure/Error: pin = find("map#gmimap0").click

     Capybara::ElementNotFound:
       Unable to find visible css "map#gmimap0"

     [Screenshot]: スクリーンショットが保存されているディレクトリへのパス


Finished in 7.84 seconds (files took 0.54001 seconds to load)
1 example, 1 failure

Failed examples:
```

# ダメなパターン その3

map タグを`visible: false` で探してクリックする

* map タグはゼロサイズだからクリックできない
* `Selenium::WebDriver::Error::ElementNotInteractableError:`

```Ruby:googlemap_spec.rb
# map タグにvisible: false を付けてfind
  describe "GoogleMap が表示されているページ" do
    context "GoogleMap 表示内容", js: true do
      it "infowindow が表示されること テスト" do
        pin = find("map#gmimap0", visible: false).click #非表示要素の検索オプションを付加してmap タグを検索
        pin.click
        expect(page).to have_css "div.infowindow"
      end
    end
  end
```

```Ruby
# map タグはゼロサイズだからクリックできないよのエラー
Failures:

  1) GoogleMap が表示されているページ GoogleMap 表示内容 infowindow が表示されること
     Failure/Error: pin = find("map#gmimap0", visible: false).click

     Selenium::WebDriver::Error::ElementNotInteractableError:
       element not interactable: element has zero size
         (Session info: headless chrome=85.0.4183.121)

     [Screenshot]: スクリーンショットが保存されているディレクトリへのパス


     # 0   chromedriver                        0x0000000107a8c1b9 chromedriver + 4911545
     .
     .
     # 21  libsystem_pthread.dylib             0x00007fff7034eb8b thread_start + 15

Finished in 8.19 seconds (files took 0.52799 seconds to load)
1 example, 1 failure
```

# 学び

* capybara で非表示要素を検索したい場合は`visible: false` オプションでfind するとクリックできる
* エラーコードにヒントが隠されているので見逃さない