This is an automated email from the ASF dual-hosted git repository.
jongyoul pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/zeppelin.git
The following commit(s) were added to refs/heads/master by this push:
new 931a84ea3f [ZEPPELIN-6409][ZEPPELIN-6400] Fix Selenium integration
test flakiness after
931a84ea3f is described below
commit 931a84ea3fa3a13d4d6ed491d2f7036071451f6f
Author: Jongyoul Lee <[email protected]>
AuthorDate: Sat Apr 11 20:35:21 2026 +0900
[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 <[email protected]>
---
.../org/apache/zeppelin/AbstractZeppelinIT.java | 35 ++++++++++++++++------
.../apache/zeppelin/integration/ZeppelinIT.java | 25 ++++++++++++++--
2 files changed, 48 insertions(+), 12 deletions(-)
diff --git
a/zeppelin-integration/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
b/zeppelin-integration/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
index 6d2746eeab..1c5e8be00c 100644
---
a/zeppelin-integration/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
+++
b/zeppelin-integration/src/test/java/org/apache/zeppelin/AbstractZeppelinIT.java
@@ -61,21 +61,38 @@ abstract public class AbstractZeppelinIT {
By.xpath("//*[@id='loginModalContent']//button[contains(.,'Login')]"),
MAX_BROWSER_TIMEOUT_SEC).click();
- ZeppelinITUtils.sleep(1000, false);
+ // Wait for the logged-in navbar user dropdown to appear (indicates login
completed
+ // and Angular digest cycle has updated the DOM), then dismiss any
leftover modal overlay
+ visibilityWait(
+ By.xpath("//div[contains(@class,
'navbar-collapse')]//li//button[contains(@class, 'nav-btn dropdown-toggle
ng-scope')]"),
+ MAX_BROWSER_TIMEOUT_SEC);
+ try {
+ ((JavascriptExecutor) manager.getWebDriver()).executeScript(
+ "$('.modal-backdrop').remove(); $('#loginModal').modal('hide');");
+ } catch (Exception e) {
+ // ignore if jQuery/Bootstrap not ready
+ }
+ ZeppelinITUtils.sleep(500, false);
}
protected void logoutUser(String userName) throws URISyntaxException {
ZeppelinITUtils.sleep(500, false);
- manager.getWebDriver().findElement(
- By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'"
+ userName + "')]")).click();
+ clickableWait(
+ By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'"
+ userName + "')]"),
+ MAX_BROWSER_TIMEOUT_SEC).click();
ZeppelinITUtils.sleep(500, false);
- manager.getWebDriver().findElement(
- By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'"
+ userName + "')]//a[@ng-click='navbar.logout()']")).click();
+ clickableWait(
+ By.xpath("//div[contains(@class, 'navbar-collapse')]//li[contains(.,'"
+ userName + "')]//a[@ng-click='navbar.logout()']"),
+ MAX_BROWSER_TIMEOUT_SEC).click();
ZeppelinITUtils.sleep(2000, false);
- if (manager.getWebDriver().findElement(
- By.xpath("//*[@id='loginModal']//div[contains(@class,
'modal-header')]/button")).isDisplayed()) {
- manager.getWebDriver().findElement(
- By.xpath("//*[@id='loginModal']//div[contains(@class,
'modal-header')]/button")).click();
+ try {
+ WebElement closeButton = manager.getWebDriver().findElement(
+ By.xpath("//*[@id='loginModal']//div[contains(@class,
'modal-header')]/button"));
+ if (closeButton.isDisplayed()) {
+ closeButton.click();
+ }
+ } catch (NoSuchElementException e) {
+ // login modal close button not found, which is fine
}
manager.getWebDriver().get(new
URI(manager.getWebDriver().getCurrentUrl()).resolve("/classic/#/").toString());
ZeppelinITUtils.sleep(500, false);
diff --git
a/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
b/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
index 003f7cc537..745cfd284b 100644
---
a/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
+++
b/zeppelin-integration/src/test/java/org/apache/zeppelin/integration/ZeppelinIT.java
@@ -33,11 +33,15 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
+import java.time.Duration;
import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -326,16 +330,31 @@ class ZeppelinIT extends AbstractZeppelinIT {
"%angular <div id=\\'angularRunParagraph\\'
ng-click=\\'z.runParagraph(\""
+ secondParagraphId.trim()
+ "\")\\'>Run second paragraph</div>");
+
+ // Capture old output element before re-run to detect when it gets
replaced
+ WebElement oldAngularDiv = manager.getWebDriver().findElement(By.xpath(
+ getParagraphXPath(1) + "//div[@id=\"angularRunParagraph\"]"));
+
runParagraph(1);
+
+ // Wait for the old output element to become stale (proves the paragraph
output
+ // was actually refreshed, avoiding race where waitForParagraph sees the
old FINISHED state)
+ new WebDriverWait(manager.getWebDriver(),
Duration.ofSeconds(MAX_BROWSER_TIMEOUT_SEC))
+ .until(ExpectedConditions.stalenessOf(oldAngularDiv));
+
waitForParagraph(1, "FINISHED");
+ // Wait for new Angular output to render
+ WebElement newAngularDiv = visibilityWait(By.xpath(
+ getParagraphXPath(1) + "//div[@id=\"angularRunParagraph\"]"),
MAX_BROWSER_TIMEOUT_SEC);
+
// Set new text value for 2nd paragraph
setTextOfParagraph(2, "%sh echo NEW_VALUE");
- // Click on 1 paragraph to trigger z.runParagraph() function
-
+ // Click on Angular-rendered div to trigger z.runParagraph() function.
+ // Use JavaScript click to bypass overlay/clickability issues with
ng-click elements.
+ ((JavascriptExecutor)
manager.getWebDriver()).executeScript("arguments[0].click();", newAngularDiv);
ZeppelinITUtils.sleep(1000, false);
- clickAndWait(By.xpath(getParagraphXPath(1) +
"//div[@id=\"angularRunParagraph\"]"));
waitForParagraph(2, "FINISHED");