Last active
July 3, 2018 12:52
-
-
Save jmgarnier/3b006a46b4d78d527edd1c0ee2dab079 to your computer and use it in GitHub Desktop.
Ruby on Rails with React, Wait for ajax - 💪🏻version
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module WaitForAjax | |
def click_ajax_link(link, timeout: Capybara.default_max_wait_time) | |
with_ajax(timeout: timeout) { click_on link } | |
end | |
def wait_for_ajax | |
wait_for_ajax_status(:inactive) | |
end | |
def wait_for_ajax_status(expected_status, wait_time = Capybara.default_max_wait_time) | |
Timeout.timeout(wait_time) do | |
loop do | |
condition = %[('jQuery' in window && jQuery.active == 1) || ('reactjQuery' in window && reactjQuery.active == 1)] | |
jquery_status = page.evaluate_script(condition) ? :active : :inactive | |
break if expected_status == jquery_status | |
end | |
end | |
end | |
def visit(path) | |
super(path) | |
wait_for_ajax | |
end | |
def wait_for_human(message = nil) | |
`say #{message}` if message | |
page.execute_script('window.wait_for_human = 1') | |
loop do | |
page.execute_script( | |
<<~JS | |
if (!window.hasOwnProperty("wait_for_human")) { | |
window.wait_for_human = 1; | |
} | |
if (!window.hasOwnProperty("humanDone")) { | |
window.humanDone = function() { window.wait_for_human = 0; } | |
console.log('Human, call humanDone() when you are done'); | |
} | |
JS | |
) | |
break if page.evaluate_script('window.hasOwnProperty("wait_for_human") ? window.wait_for_human : 1').zero? | |
sleep 1 | |
end | |
end | |
def with_ajax(timeout: Capybara.default_max_wait_time, &block) | |
wait_for_ajax_status(:inactive, timeout) | |
WithAjax.new(timeout: timeout, page: page).call(&block) | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class WithAjax | |
def initialize(timeout: Capybara.default_max_wait_time, page:) | |
@timeout = timeout | |
@page = page | |
end | |
def call | |
register_ajax_handler | |
yield | |
wait_for_ajax_event | |
unregister_ajax_handler | |
print_warnings | |
end | |
private | |
attr_reader :timeout, :page, :time_spent | |
def register_ajax_handler | |
page.execute_script( | |
<<~JS | |
window.__ajaxWaitingIsOver = false; | |
window.__allPendingAjaxRequestsAreDone = function() { | |
window.__ajaxWaitingIsOver = true; | |
} | |
jQuery(document).on('ajaxStop', window.__allPendingAjaxRequestsAreDone); | |
if (window.reactjQuery !== undefined) { | |
reactjQuery(document).on('ajaxStop', window.__allPendingAjaxRequestsAreDone); | |
} | |
JS | |
) | |
end | |
def wait_for_ajax_event | |
waiting_started_at = Time.zone.now | |
Timeout.timeout(timeout) do | |
loop do | |
break if page.evaluate_script('window.__ajaxWaitingIsOver') | |
sleep 0.05 | |
end | |
end | |
@time_spent = ((Time.zone.now - waiting_started_at).to_f * 1000).to_i | |
end | |
def unregister_ajax_handler | |
page.execute_script( | |
<<~JS | |
jQuery(document).off('ajaxStop', window.__allPendingAjaxRequestsAreDone); | |
if (window.reactjQuery !== undefined) { | |
reactjQuery(document).off('ajaxStop', window.__allPendingAjaxRequestsAreDone); | |
} | |
JS | |
) | |
end | |
def print_warnings | |
return if ENV['CI'] | |
if remaining_time < 100 | |
puts HighLine.color("WITH_AJAX: Waiting for ajax almost failed with only #{remaining_time} msec left", :yellow) | |
puts HighLine.color(current_test_location, :yellow) | |
end | |
if remaining_time > 1000 && timeout != Capybara.default_max_wait_time | |
puts HighLine.color("WITH_AJAX: #{timeout}.seconds seems to be too high (#{(timeout - remaining_time / 1000.0).round(3)} needed)", :yellow) | |
puts HighLine.color(current_test_location, :yellow) | |
end | |
end | |
def remaining_time | |
(timeout * 1000 - time_spent).to_i | |
end | |
def current_test_location | |
spec_location = caller_locations(0, 5).reject { |location| location.path =~ /ajax\.rb$/ }.first | |
"./#{Pathname.new(spec_location.path).relative_path_from(Rails.root)}:#{spec_location.lineno}" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment