Skip to content

Commit 8e3be8b

Browse files
jongyoulclaude
andcommitted
fix(integration): fix login race conditions in Selenium integration tests
The Selenium test InterpreterModeActionsIT.testPerUserIsolatedAction fails intermittently due to multiple race conditions in the login flow: 1. The login modal may auto-open on page load when authentication is required. Instead of dismissing and re-opening it (which corrupts Bootstrap modal state), detect and reuse the already-open modal. 2. sendKeys may not populate the field if Angular hasn't finished initializing the form. Fall back to JavaScript-based value injection with Angular input event trigger when sendKeys produces empty value. 3. After login, wait for the user dropdown to appear in the navbar (positive signal that login succeeded) instead of relying on modal invisibility which races with Angular's digest cycle. Then clean up any lingering backdrop. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 33214d4 commit 8e3be8b

1 file changed

Lines changed: 57 additions & 6 deletions

File tree

zeppelin-integration/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,68 @@ abstract public class AbstractZeppelinIT {
5151
protected static final long MAX_PARAGRAPH_TIMEOUT_SEC = 120;
5252

5353
protected void authenticationUser(String userName, String password) {
54-
clickableWait(
55-
By.xpath("//div[contains(@class, 'navbar-collapse')]//li//button[contains(.,'Login')]"),
56-
MAX_BROWSER_TIMEOUT_SEC).click();
54+
// Check if login modal is already visible (auto-shown when auth is required)
55+
boolean modalAlreadyOpen = false;
56+
try {
57+
manager.getWebDriver().manage().timeouts().implicitlyWait(Duration.ofSeconds(2));
58+
WebElement modal = manager.getWebDriver().findElement(By.id("loginModal"));
59+
modalAlreadyOpen = modal.isDisplayed();
60+
} catch (NoSuchElementException e) {
61+
// modal not in DOM yet
62+
} finally {
63+
manager.getWebDriver().manage().timeouts()
64+
.implicitlyWait(Duration.ofSeconds(MAX_BROWSER_TIMEOUT_SEC));
65+
}
66+
67+
if (!modalAlreadyOpen) {
68+
clickableWait(
69+
By.xpath("//div[contains(@class, 'navbar-collapse')]//li//button[contains(.,'Login')]"),
70+
MAX_BROWSER_TIMEOUT_SEC).click();
71+
}
72+
73+
// Wait for the username field inside the modal to be interactable
74+
WebElement userNameField = clickableWait(By.xpath("//*[@id='userName']"), MAX_BROWSER_TIMEOUT_SEC);
75+
userNameField.clear();
76+
userNameField.sendKeys(userName);
77+
78+
WebElement passwordField = clickableWait(By.xpath("//*[@id='password']"), MAX_BROWSER_TIMEOUT_SEC);
79+
passwordField.clear();
80+
passwordField.sendKeys(password);
81+
82+
// Verify credentials were entered — use JS as a fallback if sendKeys didn't work
83+
ZeppelinITUtils.sleep(300, false);
84+
String enteredUser = userNameField.getAttribute("value");
85+
if (enteredUser == null || enteredUser.isEmpty()) {
86+
JavascriptExecutor jse = (JavascriptExecutor) manager.getWebDriver();
87+
jse.executeScript(
88+
"var el = document.getElementById('userName');"
89+
+ "el.value = arguments[0];"
90+
+ "angular.element(el).triggerHandler('input');", userName);
91+
jse.executeScript(
92+
"var el = document.getElementById('password');"
93+
+ "el.value = arguments[0];"
94+
+ "angular.element(el).triggerHandler('input');", password);
95+
ZeppelinITUtils.sleep(200, false);
96+
}
5797

58-
visibilityWait(By.xpath("//*[@id='userName']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys(userName);
59-
visibilityWait(By.xpath("//*[@id='password']"), MAX_BROWSER_TIMEOUT_SEC).sendKeys(password);
6098
clickableWait(
6199
By.xpath("//*[@id='loginModalContent']//button[contains(.,'Login')]"),
62100
MAX_BROWSER_TIMEOUT_SEC).click();
63101

64-
ZeppelinITUtils.sleep(1000, false);
102+
// Wait for the logged-in navbar user dropdown to appear (confirms login success)
103+
visibilityWait(
104+
By.xpath("//div[contains(@class, 'navbar-collapse')]//li//button[contains(@class, 'nav-btn dropdown-toggle ng-scope')]"),
105+
MAX_BROWSER_TIMEOUT_SEC);
106+
107+
// Dismiss any lingering modal backdrop via JavaScript
108+
try {
109+
((JavascriptExecutor) manager.getWebDriver()).executeScript(
110+
"document.querySelectorAll('.modal-backdrop').forEach(e => e.remove());"
111+
+ "$('#loginModal').modal('hide');");
112+
} catch (Exception e) {
113+
// ignore if jQuery/Bootstrap not ready
114+
}
115+
ZeppelinITUtils.sleep(500, false);
65116
}
66117

67118
protected void logoutUser(String userName) throws URISyntaxException {

0 commit comments

Comments
 (0)