Skip to content

Instantly share code, notes, and snippets.

@tommyh
Last active December 23, 2015 22:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tommyh/6706987 to your computer and use it in GitHub Desktop.
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.
# 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