You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@zeppelin.apache.org by jo...@apache.org on 2026/04/11 11:35:27 UTC
(zeppelin) branch master updated: [ZEPPELIN-6409][ZEPPELIN-6400] Fix Selenium integration test flakiness after
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 <jo...@gmail.com>
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 <jo...@gmail.com>
---
.../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");