LoginSignup
9
8

More than 3 years have passed since last update.

CapybaraでjQueryなしでAjaxの完了を待つ

Last updated at Posted at 2020-12-09

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が戻ってきました!

9
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
8