diff --git a/java/src/org/openqa/selenium/support/ui/BUILD.bazel b/java/src/org/openqa/selenium/support/ui/BUILD.bazel index 3561037e59124..83dec8728a0fc 100644 --- a/java/src/org/openqa/selenium/support/ui/BUILD.bazel +++ b/java/src/org/openqa/selenium/support/ui/BUILD.bazel @@ -58,5 +58,6 @@ java_library( "//java/src/org/openqa/selenium:core", "//java/src/org/openqa/selenium/remote", artifact("com.google.guava:guava"), + artifact("org.jspecify:jspecify"), ], ) diff --git a/java/src/org/openqa/selenium/support/ui/ExpectedCondition.java b/java/src/org/openqa/selenium/support/ui/ExpectedCondition.java index aa2c992c01acf..64924f661b888 100644 --- a/java/src/org/openqa/selenium/support/ui/ExpectedCondition.java +++ b/java/src/org/openqa/selenium/support/ui/ExpectedCondition.java @@ -18,6 +18,8 @@ package org.openqa.selenium.support.ui; import com.google.common.base.Function; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.WebDriver; /** @@ -34,5 +36,6 @@ // NB: this originally extended Guava's Function interface since Java didn't have one. To avoid code // such as "com.google.common.base.Function condition = ExpectedConditions.elementFound(By);" // breaking at compile time, we continue to extend Guava's Function interface. -public interface ExpectedCondition +@NullMarked +public interface ExpectedCondition extends Function, java.util.function.Function {} diff --git a/java/src/org/openqa/selenium/support/ui/ExpectedConditions.java b/java/src/org/openqa/selenium/support/ui/ExpectedConditions.java index 2eb77db749ed8..4017888567ba6 100644 --- a/java/src/org/openqa/selenium/support/ui/ExpectedConditions.java +++ b/java/src/org/openqa/selenium/support/ui/ExpectedConditions.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Optional; import java.util.regex.Pattern; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.openqa.selenium.Alert; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; @@ -34,6 +36,7 @@ import org.openqa.selenium.WebElement; /** Canned {@link ExpectedCondition}s which are generally useful within webdriver tests. */ +@NullMarked public class ExpectedConditions { private ExpectedConditions() { @@ -48,7 +51,7 @@ private ExpectedConditions() { */ public static ExpectedCondition titleIs(final String title) { return new ExpectedCondition() { - private String currentTitle = ""; + private @Nullable String currentTitle = ""; @Override public Boolean apply(WebDriver driver) { @@ -71,7 +74,7 @@ public String toString() { */ public static ExpectedCondition titleContains(final String title) { return new ExpectedCondition() { - private String currentTitle = ""; + private @Nullable String currentTitle = ""; @Override public Boolean apply(WebDriver driver) { @@ -94,7 +97,7 @@ public String toString() { */ public static ExpectedCondition urlToBe(final String url) { return new ExpectedCondition() { - private String currentUrl = ""; + private @Nullable String currentUrl = ""; @Override public Boolean apply(WebDriver driver) { @@ -117,7 +120,7 @@ public String toString() { */ public static ExpectedCondition urlContains(final String fraction) { return new ExpectedCondition() { - private String currentUrl = ""; + private @Nullable String currentUrl = ""; @Override public Boolean apply(WebDriver driver) { @@ -141,12 +144,12 @@ public String toString() { public static ExpectedCondition urlMatches(final String regex) { return new ExpectedCondition() { private final Pattern pattern = Pattern.compile(regex); - private String currentUrl; + private @Nullable String currentUrl; @Override public Boolean apply(WebDriver driver) { currentUrl = driver.getCurrentUrl(); - return pattern.matcher(currentUrl).find(); + return currentUrl != null && pattern.matcher(currentUrl).find(); } @Override @@ -186,10 +189,11 @@ public String toString() { * @param locator used to find the element * @return the WebElement once it is located and visible */ - public static ExpectedCondition visibilityOfElementLocated(final By locator) { - return new ExpectedCondition() { + public static ExpectedCondition<@Nullable WebElement> visibilityOfElementLocated( + final By locator) { + return new ExpectedCondition<@Nullable WebElement>() { @Override - public WebElement apply(WebDriver driver) { + public @Nullable WebElement apply(WebDriver driver) { try { return elementIfVisible(driver.findElement(locator)); } catch (StaleElementReferenceException | NoSuchElementException e) { @@ -213,11 +217,11 @@ public String toString() { * @param locator used to find the element * @return the list of WebElements once they are located */ - public static ExpectedCondition> visibilityOfAllElementsLocatedBy( + public static ExpectedCondition<@Nullable List> visibilityOfAllElementsLocatedBy( final By locator) { - return new ExpectedCondition>() { + return new ExpectedCondition<@Nullable List>() { @Override - public List apply(WebDriver driver) { + public @Nullable List apply(WebDriver driver) { List elements = driver.findElements(locator); for (WebElement element : elements) { if (!element.isDisplayed()) { @@ -242,7 +246,7 @@ public String toString() { * @param elements list of WebElements * @return the list of WebElements once they are located */ - public static ExpectedCondition> visibilityOfAllElements( + public static ExpectedCondition<@Nullable List> visibilityOfAllElements( final WebElement... elements) { return visibilityOfAllElements(Arrays.asList(elements)); } @@ -255,11 +259,11 @@ public static ExpectedCondition> visibilityOfAllElements( * @param elements list of WebElements * @return the list of WebElements once they are located */ - public static ExpectedCondition> visibilityOfAllElements( + public static ExpectedCondition<@Nullable List> visibilityOfAllElements( final List elements) { - return new ExpectedCondition>() { + return new ExpectedCondition<@Nullable List>() { @Override - public List apply(WebDriver driver) { + public @Nullable List apply(WebDriver driver) { for (WebElement element : elements) { if (!element.isDisplayed()) { return null; @@ -283,10 +287,10 @@ public String toString() { * @param element the WebElement * @return the (same) WebElement once it is visible */ - public static ExpectedCondition visibilityOf(final WebElement element) { - return new ExpectedCondition() { + public static ExpectedCondition<@Nullable WebElement> visibilityOf(final WebElement element) { + return new ExpectedCondition<@Nullable WebElement>() { @Override - public WebElement apply(WebDriver driver) { + public @Nullable WebElement apply(WebDriver driver) { return elementIfVisible(element); } @@ -300,7 +304,7 @@ public String toString() { /** * @return the given element if it is visible and has non-zero size, otherwise null. */ - private static WebElement elementIfVisible(WebElement element) { + private static @Nullable WebElement elementIfVisible(WebElement element) { return element.isDisplayed() ? element : null; } @@ -310,11 +314,11 @@ private static WebElement elementIfVisible(WebElement element) { * @param locator used to find the element * @return the list of WebElements once they are located */ - public static ExpectedCondition> presenceOfAllElementsLocatedBy( + public static ExpectedCondition<@Nullable List> presenceOfAllElementsLocatedBy( final By locator) { - return new ExpectedCondition>() { + return new ExpectedCondition<@Nullable List>() { @Override - public List apply(WebDriver driver) { + public @Nullable List apply(WebDriver driver) { List elements = driver.findElements(locator); return !elements.isEmpty() ? elements : null; } @@ -456,11 +460,11 @@ public String toString() { * @param frameLocator used to find the frame (id or name) * @return WebDriver instance after frame has been switched */ - public static ExpectedCondition frameToBeAvailableAndSwitchToIt( + public static ExpectedCondition<@Nullable WebDriver> frameToBeAvailableAndSwitchToIt( final String frameLocator) { - return new ExpectedCondition() { + return new ExpectedCondition<@Nullable WebDriver>() { @Override - public WebDriver apply(WebDriver driver) { + public @Nullable WebDriver apply(WebDriver driver) { try { return driver.switchTo().frame(frameLocator); } catch (NoSuchFrameException e) { @@ -483,10 +487,11 @@ public String toString() { * @param locator used to find the frame * @return WebDriver instance after frame has been switched */ - public static ExpectedCondition frameToBeAvailableAndSwitchToIt(final By locator) { - return new ExpectedCondition() { + public static ExpectedCondition<@Nullable WebDriver> frameToBeAvailableAndSwitchToIt( + final By locator) { + return new ExpectedCondition<@Nullable WebDriver>() { @Override - public WebDriver apply(WebDriver driver) { + public @Nullable WebDriver apply(WebDriver driver) { try { return driver.switchTo().frame(driver.findElement(locator)); } catch (NoSuchFrameException e) { @@ -509,11 +514,11 @@ public String toString() { * @param frameLocator used to find the frame (index) * @return WebDriver instance after frame has been switched */ - public static ExpectedCondition frameToBeAvailableAndSwitchToIt( + public static ExpectedCondition<@Nullable WebDriver> frameToBeAvailableAndSwitchToIt( final int frameLocator) { - return new ExpectedCondition() { + return new ExpectedCondition<@Nullable WebDriver>() { @Override - public WebDriver apply(WebDriver driver) { + public @Nullable WebDriver apply(WebDriver driver) { try { return driver.switchTo().frame(frameLocator); } catch (NoSuchFrameException e) { @@ -536,11 +541,11 @@ public String toString() { * @param frameLocator used to find the frame (webelement) * @return WebDriver instance after frame has been switched */ - public static ExpectedCondition frameToBeAvailableAndSwitchToIt( + public static ExpectedCondition<@Nullable WebDriver> frameToBeAvailableAndSwitchToIt( final WebElement frameLocator) { - return new ExpectedCondition() { + return new ExpectedCondition<@Nullable WebDriver>() { @Override - public WebDriver apply(WebDriver driver) { + public @Nullable WebDriver apply(WebDriver driver) { try { return driver.switchTo().frame(frameLocator); } catch (NoSuchFrameException e) { @@ -620,10 +625,10 @@ public String toString() { * @param locator used to find the element * @return the WebElement once it is located and clickable (visible and enabled) */ - public static ExpectedCondition elementToBeClickable(final By locator) { - return new ExpectedCondition() { + public static ExpectedCondition<@Nullable WebElement> elementToBeClickable(final By locator) { + return new ExpectedCondition<@Nullable WebElement>() { @Override - public WebElement apply(WebDriver driver) { + public @Nullable WebElement apply(WebDriver driver) { WebElement element = visibilityOfElementLocated(locator).apply(driver); try { if (element != null && element.isEnabled()) { @@ -648,11 +653,12 @@ public String toString() { * @param element the WebElement * @return the (same) WebElement once it is clickable (visible and enabled) */ - public static ExpectedCondition elementToBeClickable(final WebElement element) { - return new ExpectedCondition() { + public static ExpectedCondition<@Nullable WebElement> elementToBeClickable( + final WebElement element) { + return new ExpectedCondition<@Nullable WebElement>() { @Override - public WebElement apply(WebDriver driver) { + public @Nullable WebElement apply(WebDriver driver) { WebElement visibleElement = visibilityOf(element).apply(driver); try { if (visibleElement != null && visibleElement.isEnabled()) { @@ -709,10 +715,10 @@ public String toString() { * @param return type of the condition provided * @return the result of the provided condition */ - public static ExpectedCondition refreshed(final ExpectedCondition condition) { - return new ExpectedCondition() { + public static ExpectedCondition<@Nullable T> refreshed(final ExpectedCondition condition) { + return new ExpectedCondition<@Nullable T>() { @Override - public T apply(WebDriver driver) { + public @Nullable T apply(WebDriver driver) { try { return condition.apply(driver); } catch (StaleElementReferenceException e) { @@ -784,10 +790,10 @@ public String toString() { }; } - public static ExpectedCondition alertIsPresent() { - return new ExpectedCondition() { + public static ExpectedCondition<@Nullable Alert> alertIsPresent() { + return new ExpectedCondition<@Nullable Alert>() { @Override - public Alert apply(WebDriver driver) { + public @Nullable Alert apply(WebDriver driver) { try { return driver.switchTo().alert(); } catch (NoAlertPresentException e) { @@ -855,7 +861,7 @@ public String toString() { public static ExpectedCondition attributeToBe( final By locator, final String attribute, final String value) { return new ExpectedCondition() { - private String currentValue = null; + private @Nullable String currentValue = null; @Override public Boolean apply(WebDriver driver) { @@ -885,7 +891,7 @@ public String toString() { */ public static ExpectedCondition textToBe(final By locator, final String value) { return new ExpectedCondition() { - private String currentValue = null; + private @Nullable String currentValue = null; @Override public Boolean apply(WebDriver driver) { @@ -915,7 +921,7 @@ public String toString() { */ public static ExpectedCondition textMatches(final By locator, final Pattern pattern) { return new ExpectedCondition() { - private String currentValue = null; + private @Nullable String currentValue = null; @Override public Boolean apply(WebDriver driver) { @@ -944,13 +950,13 @@ public String toString() { * @param number used to define minimum number of elements * @return Boolean true when size of elements list is more than defined */ - public static ExpectedCondition> numberOfElementsToBeMoreThan( + public static ExpectedCondition<@Nullable List> numberOfElementsToBeMoreThan( final By locator, final Integer number) { - return new ExpectedCondition>() { + return new ExpectedCondition<@Nullable List>() { private Integer currentNumber = 0; @Override - public List apply(WebDriver webDriver) { + public @Nullable List apply(WebDriver webDriver) { List elements = webDriver.findElements(locator); currentNumber = elements.size(); return currentNumber > number ? elements : null; @@ -973,13 +979,13 @@ public String toString() { * @param number used to define maximum number of elements * @return Boolean true when size of elements list is less than defined */ - public static ExpectedCondition> numberOfElementsToBeLessThan( + public static ExpectedCondition<@Nullable List> numberOfElementsToBeLessThan( final By locator, final Integer number) { - return new ExpectedCondition>() { + return new ExpectedCondition<@Nullable List>() { private Integer currentNumber = 0; @Override - public List apply(WebDriver webDriver) { + public @Nullable List apply(WebDriver webDriver) { List elements = webDriver.findElements(locator); currentNumber = elements.size(); return currentNumber < number ? elements : null; @@ -1001,13 +1007,13 @@ public String toString() { * @param number used to define number of elements * @return Boolean true when size of elements list is equal to defined */ - public static ExpectedCondition> numberOfElementsToBe( + public static ExpectedCondition<@Nullable List> numberOfElementsToBe( final By locator, final Integer number) { - return new ExpectedCondition>() { + return new ExpectedCondition<@Nullable List>() { private Integer currentNumber = 0; @Override - public List apply(WebDriver webDriver) { + public @Nullable List apply(WebDriver webDriver) { List elements = webDriver.findElements(locator); currentNumber = elements.size(); return currentNumber.equals(number) ? elements : null; @@ -1033,7 +1039,7 @@ public String toString() { public static ExpectedCondition domPropertyToBe( final WebElement element, final String property, final String value) { return new ExpectedCondition() { - private String currentValue = null; + private @Nullable String currentValue = null; @Override public Boolean apply(WebDriver driver) { @@ -1060,7 +1066,7 @@ public String toString() { public static ExpectedCondition domAttributeToBe( final WebElement element, final String attribute, final String value) { return new ExpectedCondition() { - private String currentValue = null; + private @Nullable String currentValue = null; @Override public Boolean apply(WebDriver driver) { @@ -1087,7 +1093,7 @@ public String toString() { public static ExpectedCondition attributeToBe( final WebElement element, final String attribute, final String value) { return new ExpectedCondition() { - private String currentValue = null; + private @Nullable String currentValue = null; @Override public Boolean apply(WebDriver driver) { @@ -1191,12 +1197,12 @@ private static Optional getAttributeOrCssValue(WebElement element, Strin * @param childLocator used to find the ultimate child element. * @return visible nested element */ - public static ExpectedCondition> visibilityOfNestedElementsLocatedBy( + public static ExpectedCondition<@Nullable List> visibilityOfNestedElementsLocatedBy( final By parent, final By childLocator) { - return new ExpectedCondition>() { + return new ExpectedCondition<@Nullable List>() { @Override - public List apply(WebDriver driver) { + public @Nullable List apply(WebDriver driver) { WebElement current = driver.findElement(parent); List allChildren = current.findElements(childLocator); @@ -1222,12 +1228,12 @@ public String toString() { * @param childLocator used to find child element. For example td By.xpath("./tr/td") * @return visible sub-element */ - public static ExpectedCondition> visibilityOfNestedElementsLocatedBy( + public static ExpectedCondition<@Nullable List> visibilityOfNestedElementsLocatedBy( final WebElement element, final By childLocator) { - return new ExpectedCondition>() { + return new ExpectedCondition<@Nullable List>() { @Override - public List apply(WebDriver webDriver) { + public @Nullable List apply(WebDriver webDriver) { List allChildren = element.findElements(childLocator); // The original code only checked the visibility of the first element. if (!allChildren.isEmpty() && allChildren.get(0).isDisplayed()) { @@ -1298,12 +1304,12 @@ public String toString() { * @param childLocator used to find child element. For example td By.xpath("./tr/td") * @return sub-element */ - public static ExpectedCondition> presenceOfNestedElementsLocatedBy( + public static ExpectedCondition<@Nullable List> presenceOfNestedElementsLocatedBy( final By parent, final By childLocator) { - return new ExpectedCondition>() { + return new ExpectedCondition<@Nullable List>() { @Override - public List apply(WebDriver driver) { + public @Nullable List apply(WebDriver driver) { List allChildren = driver.findElement(parent).findElements(childLocator); return allChildren.isEmpty() ? null : allChildren; @@ -1492,10 +1498,10 @@ public String toString() { * @param javaScript as executable js line * @return object once javaScript executes without errors */ - public static ExpectedCondition jsReturnsValue(final String javaScript) { - return new ExpectedCondition() { + public static ExpectedCondition<@Nullable Object> jsReturnsValue(final String javaScript) { + return new ExpectedCondition<@Nullable Object>() { @Override - public Object apply(WebDriver driver) { + public @Nullable Object apply(WebDriver driver) { try { Object value = ((JavascriptExecutor) driver).executeScript(javaScript);