Last active
December 23, 2015 22:59
-
-
Save tommyh/6706987 to your computer and use it in GitHub Desktop.
This is a workaround for intermittent timing issues with the capybara/poltergeist/phantomjs stack. Approach: have each iframe phone home to the parent page on dom ready. In capybara, you can use this knowledge that the iframe has fully loaded before you attempt to use "within_frame" for the iframe.
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
# app/views/layouts/application.haml | |
= render "shared/test_iframe_loaded" | |
# app/views/shared/_test_iframe_loaded.haml | |
- if Rails.env.test? | |
:javascript | |
(function(){ | |
if(window.top !== window.self){ | |
$(document).ready(function(){ | |
window.top.postMessage("testing:iframeLoaded:" + window.location.href, "*"); | |
}); | |
} | |
})(); | |
# features/support/iframe_helper.rb | |
module IframeHelper | |
def wait_until_child_window_loaded | |
# check how many child windows have been loaded before their step has been executed | |
child_windows_loaded_before = child_windows_loaded | |
# execute their step | |
yield if block_given? | |
# wait until the current number of child windows which have been loaded is greater than the child windows which have been loaded before | |
wait_until do | |
child_windows_loaded.length > child_windows_loaded_before.length | |
end | |
end | |
def child_windows_loaded | |
loaded = page.evaluate_script("window.childWindowsLoaded") | |
if loaded.nil? | |
# inject the listener on the parent page which will listen for child pages loading. | |
# note: we only care about messages which start with "test:iframeLoaded:" so we can ignore | |
# postMessage calls being made by the application | |
js = 'window.childWindowsLoaded = [];' \ | |
'window.addEventListener("message", function(event){ ' \ | |
' if(event.data.substring(0,21) === "testing:iframeLoaded:"){ ' \ | |
' window.childWindowsLoaded.push(event.data.substring(21));' \ | |
' }' \ | |
'}, false);' | |
page.execute_script(js) | |
return [] | |
end | |
loaded | |
end | |
# Capybara 2.0 has removed wait_until | |
# this code was taken from here: https://gist.github.com/jnicklas/d8da686061f0a59ffdf7 | |
# Don't include this if you are on capybara v1 | |
def wait_until(wait_time=Capybara.default_wait_time) | |
require "timeout" | |
Timeout.timeout(wait_time) do | |
sleep(0.1) until value = yield | |
value | |
end | |
end | |
end | |
World(IframeHelper) | |
# your cucumber step | |
wait_until_iframe_load do | |
find("#link-which-launches-an-iframe-modal").click | |
end | |
# Why do I need to wrap my step in wait_unitl_iframe_load? Because we need to count how many iframes have | |
# fully loaded before running your step. Then we run your step, and then we poll how many iframes | |
# have fully loaded. | |
# Limitations: If you have multiple iframes loading at the same time, this stradegy will need to be improved. | |
# My suggestion: have "wait_until_iframe_load" take a parameter, regex/substring of a url, which can be used | |
# to limit the "iframes_loaded_*" count |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment