Solving window.onbeforeunload nasty prompts

One of our tests that we run continuously is an iteration over all the pages in our control room app, checking to ensure the page loads without any error keywords. On a particular set of pages, there are Javascript window.onbeforeunload event listeners that generate the nasty prompts that want the user to verify they do indeed want to leave the page (presumably without saving their work).While looking into the application code, I notice that window.onbeforeunload gets set after every autoSave (application) call, which of course happens at random/timed intervals as well as when certain events are fired.

My first attempts at addressing this issue looked something like this:

@driver.execute_script("window.onbeforeunload = undefined;")
@driver.find_element(:css => "a.unload_prompt").click
## Wait for next page ... hope that a race condition doesn't occur

This worked most of the time. But one out of ten or fifteen times, the race condition would occur where the autoSave js call would occur just after I cleared the window.onbeforeunload and just before the element actually got clicked. This re-set the unload prompt, and caused the extremely frustrating

Selenium::WebDriver::Error::UnhandledAlertError: Modal dialog present

error to appear, failing my control room tests from that point on. 😦

If you are one of the many users struggling with this, never fear. There is hope!

It all centers around the brilliance of what click returns. If we are just clicking on a regular link that either takes us to a page or executes some javascript, we get this response:

pry> @driver.find_element(:css => ".homepage a").click
=> "ok"

If we click a link that shows this window.onbeforeunload prompt:

pry> @driver.find_element(:css => "a.unload_prompt").click
=> "Are you sure you would like to leave this page?"

Aha! Webdriver is nice enough to detect that this unload is going to throw a nasty prompt and sends it back through the JSON Wire to be returned to the Ruby call to click. So how does this solve our issues? We can just get and accept the alert to continue on in the page!

pry> @driver.switch_to.alert.text
=> "Are you sure you would like to leave this page?"
pry> @driver.switch_to.alert.accept
=> ""

Thus, we can update our code to conditionally take care of the alert if the click returns anything other than an ok response:

click_resp = @driver.find_element(:css => "a.unload_prompt").click
@driver.switch_to.alert.accept unless click_resp.eql?("ok")
## Continue on to the next page, etc...

And that is how you solve this nasty problem!