From 09137a18e5ffc1fb3c01449822e2b284b5d79a04 Mon Sep 17 00:00:00 2001 From: rconner46 Date: Thu, 6 Mar 2025 11:46:02 -0600 Subject: [PATCH] Fix StaleElementReferenceException in locator chain --- .../auto/helpers/control/BrowserControl.java | 2 +- .../applause/auto/helpers/sync/UiConditions.java | 4 ++-- .../auto/pageobjectmodel/base/BaseComponent.java | 10 ++++++++++ .../auto/pageobjectmodel/base/BaseElement.java | 5 +++++ .../auto/pageobjectmodel/base/UIElement.java | 14 +++++++++++++- .../pageobjectmodel/factory/LazyWebElement.java | 5 +++++ .../pageobjectmodel/factory/LocatorChain.java | 16 ++-------------- 7 files changed, 38 insertions(+), 18 deletions(-) diff --git a/auto-sdk-java-helpers/src/main/java/com/applause/auto/helpers/control/BrowserControl.java b/auto-sdk-java-helpers/src/main/java/com/applause/auto/helpers/control/BrowserControl.java index 7552af4..3cbee98 100644 --- a/auto-sdk-java-helpers/src/main/java/com/applause/auto/helpers/control/BrowserControl.java +++ b/auto-sdk-java-helpers/src/main/java/com/applause/auto/helpers/control/BrowserControl.java @@ -172,7 +172,7 @@ public void clickAndHold(final UIElement element, final long millis) { * @param element the target element to perform the action on. */ public void hoverOverElement(final UIElement element) { - Dimension size = element.getUnderlyingWebElement().getSize(); + Dimension size = element.getLazyWebElement().getSize(); mouseMove(element, size.getWidth() / 2, size.getHeight() / 2); } diff --git a/auto-sdk-java-helpers/src/main/java/com/applause/auto/helpers/sync/UiConditions.java b/auto-sdk-java-helpers/src/main/java/com/applause/auto/helpers/sync/UiConditions.java index 2e0f6aa..6e3bc7b 100644 --- a/auto-sdk-java-helpers/src/main/java/com/applause/auto/helpers/sync/UiConditions.java +++ b/auto-sdk-java-helpers/src/main/java/com/applause/auto/helpers/sync/UiConditions.java @@ -197,7 +197,7 @@ public static Function textEquals(final @NonNu withNoWait( uiElement, element -> { - String textValue = element.getUnderlyingWebElement().getText(); + String textValue = element.getLazyWebElement().getText(); return textValue != null && textValue.equals(text); }); } @@ -215,7 +215,7 @@ public static Function textContains( withNoWait( uiElement, element -> { - String textValue = element.getUnderlyingWebElement().getText(); + String textValue = element.getLazyWebElement().getText(); return textValue != null && textValue.contains(substring); }); } diff --git a/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/BaseComponent.java b/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/BaseComponent.java index 3207cc6..6e005cb 100644 --- a/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/BaseComponent.java +++ b/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/BaseComponent.java @@ -206,6 +206,16 @@ public LazyWebElement getParent() { return this.underlying.getParent(); } + @Override + public LazyWebElement getLazyWebElement() { + if (this.underlying == null) { + throw new UnsupportedOperationException( + "Cannot get underlying element for component [%s] with no underlying element" + .formatted(this.getClass().getSimpleName())); + } + return this.underlying; + } + @Override public WebElement getUnderlyingWebElement() { if (this.underlying == null) { diff --git a/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/BaseElement.java b/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/BaseElement.java index b969e90..3b3dedb 100644 --- a/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/BaseElement.java +++ b/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/BaseElement.java @@ -145,6 +145,11 @@ public Locator getLocator() { return this.underlying.getLocator(); } + @Override + public LazyWebElement getLazyWebElement() { + return this.underlying; + } + @Override public WebElement getUnderlyingWebElement() { return this.underlying.getUnderlyingWebElement(); diff --git a/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/UIElement.java b/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/UIElement.java index 7592d4a..1387352 100644 --- a/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/UIElement.java +++ b/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/base/UIElement.java @@ -19,6 +19,7 @@ import com.applause.auto.data.enums.SwipeDirection; import com.applause.auto.pageobjectmodel.elements.ContainerElement; +import com.applause.auto.pageobjectmodel.factory.LazyWebElement; import com.applause.auto.pageobjectmodel.factory.Locator; import java.util.List; import org.openqa.selenium.By; @@ -32,6 +33,7 @@ */ @SuppressWarnings("checkstyle:AbbreviationAsWordInName") public interface UIElement extends Locatable { + /** * Gets a child UIElement of this UIElement. This can be either a subclass of BaseElement or * BaseComponent. @@ -208,7 +210,17 @@ default void swipeToElement(final SwipeDirection direction) { String getAttribute(String attribute); /** - * Gets the underlying web element + * Gets the underlying LazyWebElement + * + * @return The underlying LazyWebElement + */ + LazyWebElement getLazyWebElement(); + + /** + * Gets the underlying web element. This is the actual selenium WebElement object that is being + * wrapped by the Applause Library. This is useful for when you need to interact with the + * WebElement directly. In most cases, you should consider using the getLazyWebElement() method + * instead, as the LazyWebElement can handle lazy loading and StaleElementRefereceExceptions. * * @return The underlying WebElement */ diff --git a/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/factory/LazyWebElement.java b/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/factory/LazyWebElement.java index e6326aa..a8fcbb6 100644 --- a/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/factory/LazyWebElement.java +++ b/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/factory/LazyWebElement.java @@ -212,6 +212,11 @@ public void setWait(final Duration theTimeout, final Duration thePollingInterval this.pollingInterval = thePollingInterval; } + @Override + public LazyWebElement getLazyWebElement() { + return this; + } + @Override public WebElement getUnderlyingWebElement() { return runLazily(() -> underlying); diff --git a/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/factory/LocatorChain.java b/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/factory/LocatorChain.java index 7b65d1b..30eaf9b 100644 --- a/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/factory/LocatorChain.java +++ b/auto-sdk-java-page-object/src/main/java/com/applause/auto/pageobjectmodel/factory/LocatorChain.java @@ -118,13 +118,7 @@ public WebElement findElementInChain() { // If we have a parent var parent = this.chain.isEmpty() ? null : this.chain.getLast(); if (parent != null) { - if (!parent.isInitialized()) { - parent.initialize(); - } - var searchContext = - parent.getLocator().isShadowRoot() - ? parent.getShadowRoot() - : parent.getUnderlyingWebElement(); + var searchContext = parent.getLocator().isShadowRoot() ? parent.getShadowRoot() : parent; return this.findElementInContext( searchContext, this.underlying.getLocator(), this.underlying.getFormatArgs()); } @@ -143,13 +137,7 @@ public List findElementsInChain() { // If we have a parent var parent = this.chain.isEmpty() ? null : this.chain.getLast(); if (parent != null) { - if (!parent.isInitialized()) { - parent.initialize(); - } - var searchContext = - parent.getLocator().isShadowRoot() - ? parent.getShadowRoot() - : parent.getUnderlyingWebElement(); + var searchContext = parent.getLocator().isShadowRoot() ? parent.getShadowRoot() : parent; return this.findElementsInContext( searchContext, this.underlying.getLocator(), this.underlying.getFormatArgs()); }