I have a list defined as a propterty on a selenium page, e.g:
public IEnumerable<MyItem> MyList =>
webDriverInstance.FindListElementsWithWait<MyItem>(By.ClassName("my-item"));
Where FindListElementsWithWait is an extension method:
public static IEnumerable<T> FindListElementsWithWait<T>(this IWebDriver driver, By locator, int timeOutMilliseconds = 10000)
{
var elements = new List<T>();
var wait = new WebDriverWait(driver, TimeSpan.FromMilliseconds(timeOutMilliseconds));
wait.Until(d =>
{
try
{
elements = d.FindElements(locator).Select(x => (T)Activator.CreateInstance(typeof(T), x)).ToList();
return true;
}
catch (TargetInvocationException)
{
return false;
}
}
);
return elements;
}
This is making use of WebDriverWait rather than a Thread.Sleep. As this is a list, there is no unique locator for each MyItem. This approach works but there are a few quirks.
- It is relying on the constructor of MyItem (T in the extension method example) to throw an exception (StaleElementReferenceException)
- this means putting property initialisation in the constructor for MyItem
for example:
public class MyItem
{
public string Name { get; private set; }
public MyItem(IWebElement webElement)
{
Name = webElement.FindElement(By.TagName("H4")).Text;
}
}
This works, but the issue I have is if I had additional properties on MyItem which aren't initially on the element. For example, if I change some state and now a cancel button appears. If I have the cancel button initialisation in the constructor then it would throw a NoSuchElementException and fail on page load. I can get around this by providing a getter body for the new Cancel button property. But this is producing/promoting fragile tests.
Has anyone found a clean way to update/refresh a stale WebElement within a list where there is no unique locator for the stale WebElement?