Well now, gather around children…
Once upon a time, the good developers wanted to save their users from losing changes if they left a form before submitting it. And as sometimes happens, Browsers looked kindly on the world, and provided the beforeUnload event, which could be hooked in Javascript so that changes could be automatically saved or a confirm dialog could be shown. The developers and users were overjoyed, and a golden age ensued.
But then enter the evil developers! These developers hooked the beforeUnload event to make the user’s life miserable. They coded event handlers so that people could never leave pages, and were stuck in a hell of eternal dialogs and never-ending pop-up windows.
The Browsers realised that beforeUnload had been a mistake, and something had to be done. The event was still needed by the good developers, but the evil developers had to be contained. So the event was changed to something “special”, and uncontrollable!
Thus code like this was born:
window.addEventListener('beforeunload', function(e) {
e.preventDefault();
e.returnValue = ''; // Triggers the browser's generic warning confirm, cannot be customised
}
});
And it mainly worked. The evil developers were stopped, and the users were freed from the inescapable prisons of popups and alerts.
But the good developers wanted to write automated browser tests to ensure that the beforeUnload event worked, and that users would be saved from losing their changes forever. And this is where the tale becomes even sadder.
In Selenium / Firefox, no confirm dialog is shown from beforeUnload in tests, even though the confirm dialog shows during a usual interactive session.
In Cuprite / Chrome the story is more complicated. A message is shown on the console:
Modal window with text `` has been opened, but you didn't wrap your code into (`accept_prompt` | `dismiss_prompt` | `accept_confirm` | `dismiss_confirm` | `accept_alert`), accepting by default
But none of the ‘accept’ or ‘dismiss’ options work, or catch the dialog. They only work on normal JavaScript confirms and alerts. The only option is:
page.driver.browser.on(:dialog) do |dialog|
# I DO GET CALLED! BUT ASYNCHRONOUSLY.
end
But the dialog object cannot be accepted or dismissed, unlike all other dialog objects. It also has an empty message. Alas, our story ends a testing tragedy. The console warning message is always shown, and no significant test can be written, as the Cancel (dismiss) button cannot be pressed.
And so, to this day, the good developers must test this code manually, like it was the 1990s.
PS – do let me know if this tale is wrong and there is a better way…
PPS – Chrome, Edge and Firefox support beforeUnload in this way. But Safari on iOS ignores it. Safari on Mac largely supports beforeUnload, but after a cancel in the dialog to stay on the page, it doesn’t seem like the page can do a programmatic redirect from JS later (which other browsers allow).
