Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ protected void authenticationUser(String userName, String password) {
*/
private void dismissLoginModal() {
if (loginModalClosed(MODAL_CLOSE_TIMEOUT_SEC)) {
removeOrphanedModalBackdrops();
return;
}
LOGGER.warn("Login modal still displayed after {}s, taking it down from the page",
Expand All @@ -131,6 +132,44 @@ private void dismissLoginModal() {
}
}

// Bootstrap 3 removes a backdrop through this.$backdrop,
// so reopening the modal mid-transition orphans the earlier backdrop, which blocks every click.
// A hidden modal can keep its "in" class, so only a displayed modal owns a backdrop.
private void removeOrphanedModalBackdrops() {
Object removed = ((JavascriptExecutor) manager.getWebDriver()).executeScript(
"var backdrops = document.querySelectorAll('.modal-backdrop');"
+ "var modals = document.querySelectorAll('.modal.in');"
+ "for (var m = 0; m < modals.length; m++) {"
+ " if (window.getComputedStyle(modals[m]).display !== 'none') {"
+ " return null;"
+ " }"
+ "}"
+ "if (backdrops.length === 0) {"
+ " return null;"
+ "}"
+ "var classes = [];"
+ "for (var i = 0; i < backdrops.length; i++) {"
+ " classes.push(backdrops[i].className);"
+ " backdrops[i].parentNode.removeChild(backdrops[i]);"
+ "}"
+ "return classes.join(', ');");
if (removed != null) {
LOGGER.warn("Removed modal backdrops left behind after the login modal closed: {}", removed);
}
}

private String describeModalState() {
Object state = ((JavascriptExecutor) manager.getWebDriver()).executeScript(
"var modal = document.getElementById('loginModal');"
+ "var backdrops = document.querySelectorAll('.modal-backdrop');"
+ "var classes = [];"
+ "for (var i = 0; i < backdrops.length; i++) { classes.push(backdrops[i].className); }"
+ "var modalState = modal"
+ " ? modal.className + ' display=' + modal.style.display : 'absent';"
+ "return 'loginModal=' + modalState + ', backdrops=[' + classes.join(', ') + ']';");
return String.valueOf(state);
}

/** Returns true once the login modal is hidden or gone, false if it is still displayed. */
private boolean loginModalClosed(final long timeWait) {
try {
Expand Down Expand Up @@ -213,7 +252,8 @@ protected void logoutUser(String userName) throws URISyntaxException {
// login.controller.js re-opens it one second later, so it can appear between the
// wait above and this click. An intercepted click never reached the menu, so the
// dropdown is still closed and opening it again is safe.
LOGGER.warn("Navbar user menu click was intercepted, retrying once", e);
LOGGER.warn("Navbar user menu click was intercepted ({}), retrying once",
describeModalState(), e);
dismissLoginModal();
clickableWait(userMenu, MAX_BROWSER_TIMEOUT_SEC).click();
}
Expand Down
Loading