seleniumでeachなどで作られた一覧の要素全てに対して操作をする@ruby

seleniumでeachなどで作られた一覧の要素全てに対して操作をする@ruby

 例えば「投稿一覧」のような画面で各投稿を1つずつクリックして開きたい時のパターンを想定。

操作する要素の例

 こんな感じのやつを想定。多分よくあるやつ。
 各post以下のlinkを1つずつ全てクリックしたい。

div class="index"
  div class="post"
    p hoge class="title"
    a class="link"
  div class="post"
    p huga class="title"
    a class="link"
  div class="post"
    p hugahoge class="title"
    a class="link"

取得し直すごとにオブジェクトが変わる場合がある

 Selenium::WebDriver::Elementオブジェクトのfind_elementsは要素を取得し直すごとに各種値が変わってしまう場合がある。

# 子要素に対してクリック操作をしたいループ
@driver.find_elements(:class, "index").each do |element|
  element.find_element(:class, "hoge").click
end

# 1回目の取得
> @driver.find_elements(:class, "index")
[#<Selenium::WebDriver::Element:0x..f693ec4bf80c6eee id="2d75628b-0b8f-4d92-a7db-7e242210b403">,
 #<Selenium::WebDriver::Element:0xbc5fe1f4062f650 id="462b6e29-ef2f-4f1f-bad4-5acb980c74e5">,
 #<Selenium::WebDriver::Element:0x687a1921db97e860 id="193e8b33-669a-4381-8b74-039dcd379dd5">]

# 要素を一つクリックして新しいタブで開く
> element.find_element(:class, "hoge").click

# 2回目の取得
> @driver.find_elements(:class, "index")
=> [#<Selenium::WebDriver::Element:0x..f9a79e5201fcda17a id="08f92ae9-4ecc-4af0-b98b-94eb2bd9b843">,
 #<Selenium::WebDriver::Element:0x..fd075870720e89f2a id="a2d55dd1-1fab-4918-b95e-dfde8b2da705">,
 #<Selenium::WebDriver::Element:0x..fd3599185fcb5576a id="375c0957-1cfd-485f-be29-b8397d9ec2f1">]

# ループ2回目では以下のエラーが出る
Selenium::WebDriver::Error::StaleElementReferenceError: stale element reference: element is not attached to the page document

上記の場合の対処方法の例

 取得するごとにオブジェクト番号が変わってしまっては雑にeachで回してクリックすることはできなくなる。
 なので、以下のように対処した。

current_window = @driver.window_handles.first
# 要素の総数を取得
index_elements_count = @driver.find_elements(:class, "index").count

index_elements_count.times do |time|
  @driver.find_elements(:class, "index")[time].find_element(class: "link").click
  @driver.switch_to.window(current_window)
end

 これなら@driver.find_elements(:class, "index")の値がループごとに変わっても、配列のtime番目から取得するので問題ない。