Edited at

Capybara Poltergeist で Bootstrap3 のモーダルを使ったテストが失敗する

More than 1 year has passed since last update.

こんな感じの rspec が成功したり失敗したりしていました。

click_link '田中 花子' # モーダルを呼び出す

click_link 'キャンセル' # モーダル内のリンクをクリックするはず(ができないことがある)
expect(page).to have_css '.is-canceled' # クリックできてなければ失敗する


問題の原因

Bootstrap3 のモーダルがスクロールしている最中にモーダル内のボタンをクリックすると、クリックしたときにはその位置にすでにボタンがないことがあるようです。

poltergeistのソースを見ると、このへんで問題が起きていそうでした。

  mouseEvent: (name) ->

if area_image = @_getAreaImage()
area_image.scrollIntoView()
else
@scrollIntoView()
# クリックしたいDOMノードの位置を取得
pos = this.mouseEventPosition()
# その位置でノードがクリックできるかチェック(ここでも失敗する可能性はありそうだけど、今回はここではない)
test = this.mouseEventTest(pos.x, pos.y)
if test.status == 'success'
if name == 'rightclick'
@page.mouseEvent('click', pos.x, pos.y, 'right')
this.trigger('contextmenu')
else
# 実際のクリックは座標だけで指定(クリックするノードを指定しているわけではないので、すでにそこにノードがなくてもエラーにならずにスルーされる)
@page.mouseEvent(name, pos.x, pos.y)
pos
else
throw new Poltergeist.MouseEventFailed(name, test.selector, pos)


修正方法 その1

モーダルが出現するまでの時間 sleep します。

click_link '田中 花子' # モーダルを呼び出す

sleep 1 # モーダルがスクロール完了するのを待つ(bootstrap のモーダルのスクロールは 0.3s)
click_link 'キャンセル' # モーダル内のリンクをクリック
expect(page).to have_css '.is-canceled'


修正方法 その2

sleep なんて不確実な方法は嫌という場合、こんな感じのヘルパーを用意します。

  def wait_for_bs_modal

script = <<~EOS
window._capybaraModalWait = 1;
jQuery(document).one("shown.bs.modal", function(){window._capybaraModalWait = 0;});
EOS
page.execute_script(script)
yield
Timeout.timeout(Capybara.default_max_wait_time) do
loop until page.evaluate_script('window._capybaraModalWait').zero?
end
end

で、こう書きます。

# モーダルが出現するまで待つ

wait_for_bs_modal do
click_link '田中 花子' # モーダルを呼び出す
end
click_link 'キャンセル' # モーダル内のリンクをクリック
expect(page).to have_css '.is-canceled'