Skip to content

Commit 931a84e

Browse files
authored
[ZEPPELIN-6409][ZEPPELIN-6400] Fix Selenium integration test flakiness after
## Summary - **`authenticationUser()`**: Replace `sleep(1000)` with explicit wait for the navbar user dropdown to appear (proves login succeeded and AngularJS digest is done), then force-dismiss any lingering Bootstrap modal backdrop via jQuery - **`logoutUser()`**: Use `clickableWait()` instead of raw `findElement()` for robust element interaction; wrap modal close button in try-catch - **`testAngularRunParagraph()`**: Fix race condition where `waitForParagraph("FINISHED")` matched the *previous* run's state. Use `stalenessOf` to detect paragraph output refresh, then `visibilityWait` for the new element, and JavaScript click to bypass ng-click overlay issues ## Context After ZEPPELIN-6400 moved `ZeppelinConfiguration` from `zeppelin-interpreter` to `zeppelin-zengine`, `RemoteInterpreterServer.init()` changed from loading config via `ZeppelinConfiguration.load()` + overlay to pure `Properties`-based initialization. This subtle timing change in interpreter startup exposed pre-existing Selenium test flakiness in the `test-selenium-with-spark-module-for-spark-3-5` CI job. The CI failures were: - `InterpreterModeActionsIT.testPerUserIsolatedAction` — `ElementClickInterceptedException` because login modal was still visible when clicking the navigation dropdown - `ZeppelinIT.testAngularRunParagraph` — `TimeoutException` due to race condition: after re-running an Angular paragraph, the old output element was matched before the new one rendered ## Test plan - [x] CI: `frontend.yml` — `test-selenium-with-spark-module-for-spark-3-5` job passes - [x] CI: `frontend.yml` — All other E2E jobs remain green (Playwright auth failure is unrelated Firefox keyboard shortcut flakiness) Closes #5209 from jongyoul/ZEPPELIN-6409-fix-selenium-tests. Signed-off-by: Jongyoul Lee <jongyoul@gmail.com>
1 parent 4e40c11 commit 931a84e

2 files changed

Lines changed: 48 additions & 12 deletions

File tree

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

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,21 +61,38 @@ protected void authenticationUser(String userName, String password) {
6161
By.xpath("//*[@id='loginModalContent']//button[contains(.,'Login')]"),
6262
MAX_BROWSER_TIMEOUT_SEC).click();
6363

64-
ZeppelinITUtils.sleep(1000, false);
64+
// Wait for the logged-in navbar user dropdown to appear (indicates login completed
65+
// and Angular digest cycle has updated the DOM), then dismiss any leftover modal overlay
66+
visibilityWait(
67+
By.xpath("//div[contains(@class, 'navbar-collapse')]//li//button[contains(@class, 'nav-btn dropdown-toggle ng-scope')]"),
68+
MAX_BROWSER_TIMEOUT_SEC);
69+
try {
70+
((JavascriptExecutor) manager.getWebDriver()).executeScript(
71+
"$('.modal-backdrop').remove(); $('#loginModal').modal('hide');");
72+
} catch (Exception e) {
73+
// ignore if jQuery/Bootstrap not ready
74+
}
75+
ZeppelinITUtils.sleep(500, false);
6576
}
6677

6778
protected void logoutUser(String userName) throws URISyntaxException {
6879
ZeppelinITUtils.sleep(500, false);
69-
manager.getWebDriver().findElement(
70-
By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'" + userName + "')]")).click();
80+
clickableWait(
81+
By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'" + userName + "')]"),
82+
MAX_BROWSER_TIMEOUT_SEC).click();
7183
ZeppelinITUtils.sleep(500, false);
72-
manager.getWebDriver().findElement(
73-
By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'" + userName + "')]//a[@ng-click='navbar.logout()']")).click();
84+
clickableWait(
85+
By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'" + userName + "')]//a[@ng-click='navbar.logout()']"),
86+
MAX_BROWSER_TIMEOUT_SEC).click();
7487
ZeppelinITUtils.sleep(2000, false);
75-
if (manager.getWebDriver().findElement(
76-
By.xpath("//*[@id='loginModal']//div[contains(@class, 'modal-header')]/button")).isDisplayed()) {
77-
manager.getWebDriver().findElement(
78-
By.xpath("//*[@id='loginModal']//div[contains(@class, 'modal-header')]/button")).click();
88+
try {
89+
WebElement closeButton = manager.getWebDriver().findElement(
90+
By.xpath("//*[@id='loginModal']//div[contains(@class, 'modal-header')]/button"));
91+
if (closeButton.isDisplayed()) {
92+
closeButton.click();
93+
}
94+
} catch (NoSuchElementException e) {
95+
// login modal close button not found, which is fine
7996
}
8097
manager.getWebDriver().get(new URI(manager.getWebDriver().getCurrentUrl()).resolve("/classic/#/").toString());
8198
ZeppelinITUtils.sleep(500, false);

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

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,15 @@
3333
import org.junit.jupiter.api.BeforeEach;
3434
import org.junit.jupiter.api.Disabled;
3535
import org.junit.jupiter.api.Test;
36+
import java.time.Duration;
3637
import org.openqa.selenium.By;
38+
import org.openqa.selenium.JavascriptExecutor;
3739
import org.openqa.selenium.Keys;
3840
import org.openqa.selenium.StaleElementReferenceException;
3941
import org.openqa.selenium.TimeoutException;
4042
import org.openqa.selenium.WebElement;
43+
import org.openqa.selenium.support.ui.ExpectedConditions;
44+
import org.openqa.selenium.support.ui.WebDriverWait;
4145
import org.slf4j.Logger;
4246
import org.slf4j.LoggerFactory;
4347

@@ -326,16 +330,31 @@ void testAngularRunParagraph() throws Exception {
326330
"%angular <div id=\\'angularRunParagraph\\' ng-click=\\'z.runParagraph(\""
327331
+ secondParagraphId.trim()
328332
+ "\")\\'>Run second paragraph</div>");
333+
334+
// Capture old output element before re-run to detect when it gets replaced
335+
WebElement oldAngularDiv = manager.getWebDriver().findElement(By.xpath(
336+
getParagraphXPath(1) + "//div[@id=\"angularRunParagraph\"]"));
337+
329338
runParagraph(1);
339+
340+
// Wait for the old output element to become stale (proves the paragraph output
341+
// was actually refreshed, avoiding race where waitForParagraph sees the old FINISHED state)
342+
new WebDriverWait(manager.getWebDriver(), Duration.ofSeconds(MAX_BROWSER_TIMEOUT_SEC))
343+
.until(ExpectedConditions.stalenessOf(oldAngularDiv));
344+
330345
waitForParagraph(1, "FINISHED");
331346

347+
// Wait for new Angular output to render
348+
WebElement newAngularDiv = visibilityWait(By.xpath(
349+
getParagraphXPath(1) + "//div[@id=\"angularRunParagraph\"]"), MAX_BROWSER_TIMEOUT_SEC);
350+
332351
// Set new text value for 2nd paragraph
333352
setTextOfParagraph(2, "%sh echo NEW_VALUE");
334353

335-
// Click on 1 paragraph to trigger z.runParagraph() function
336-
354+
// Click on Angular-rendered div to trigger z.runParagraph() function.
355+
// Use JavaScript click to bypass overlay/clickability issues with ng-click elements.
356+
((JavascriptExecutor) manager.getWebDriver()).executeScript("arguments[0].click();", newAngularDiv);
337357
ZeppelinITUtils.sleep(1000, false);
338-
clickAndWait(By.xpath(getParagraphXPath(1) + "//div[@id=\"angularRunParagraph\"]"));
339358

340359
waitForParagraph(2, "FINISHED");
341360

0 commit comments

Comments
 (0)