RailsにjQueryがデフォルトで入っていた頃は、CapybaraでAjaxの終了を待つ時は、wait_for_ajax
というメソッドを作って待っていたものでした。そこでは、jQuery.active
を使って、Ajaxをしている最中か確認したものでした。こんな感じ。
module WaitForAjax
def wait_for_ajax
Timeout.timeout(Capybara.default_max_wait_time) do
loop until finished_all_ajax_requests?
end
end
def finished_all_ajax_requests?
page.evaluate_script('jQuery.active')&.zero?
end
end
RSpec.configure do |config|
config.include WaitForAjax, type: :feature # => ここも昔の名残…
end
しかし、時代は流れ、RailsからもjQuery依存が除かれ、rails-ujsになりました。さきほどのwait_for_ajax
はjQuery依存の実装なので、現代のRailsプログラマは違う方法でAjaxを待たなければなりません。
環境について
筆者の環境
- Rails 6.0.3.4
- Webpacker使用
どうやってAjaxを検知するか?
Ajaxを開始したらカウントアップ、Ajaxが終了したらカウントダウンする仕組みを作ればいいので、このようにしました。
rails-ujsのイベントに合わせてイベントリスナを定義し、そこでカウントアップ・カウントダウンします。
rails-ujsのイベント一覧はこちら。
handleEvent
メソッドは、自分自身をリスナとして登録したときに自動的にコールバックとして呼ばれます。
https://developer.mozilla.org/ja/docs/Web/API/EventTarget/addEventListener
class AjaxRequestCounter {
static start() {
const _this = new AjaxRequestCounter()
window.pendingRequestCount = 0
document.addEventListener('ajax:send', _this)
document.addEventListener('ajax:complete', _this)
}
handleEvent(event) {
switch(event.type) {
case 'ajax:send':
pendingRequestCount++
break
case 'ajax:complete':
pendingRequestCount--
break
}
}
}
export { AjaxRequestCounter }
これを、app/javascript/packs/application.js
から呼び出します。
require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
import { AjaxRequestCounter } from "../src/ajax_request_counter";
AjaxRequestCounter.start()
テストで使えるようにする
あとは、pendingRequestCounter
の値をチェックするようにすればいいので、WaitForAjax
モジュールの中身を修正します。
module WaitForAjax
def wait_for_ajax
Timeout.timeout(Capybara.default_max_wait_time) do
loop until finished_all_ajax_requests?
end
end
def finished_all_ajax_requests?
page.evaluate_script('window.pendingRequestCount').zero?
end
end
RSpec.configure do |config|
config.include WaitForAjax, type: :system # => featurespecなら:featureにしてください。
end
これで、我々の手元に再びwait_for_ajax
が戻ってきました!