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 6d2746eeab9..1c5e8be00ce 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 @@ protected void authenticationUser(String userName, String password) { 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 003f7cc5376..745cfd284bf 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.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 @@ void testAngularRunParagraph() throws Exception { "%angular
Run second paragraph
"); + + // 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");