When working with the IEDriver (C# Selenium Bindings), I'm experiencing some very flaky behavior while waiting for my React components to appear on the screen. All resources I've looked at so far suggest using an explicit wait on the element, before proceeding with the rest of the test.
All permutations of the code below seem to work, but only ~50% of the time. What's more interesting is the HTTP response coming from the HTTP server. Worth noting, the table
I'm waiting for is associated with an axios request, hence having to wait until it's visible.
Attempted Implementations
WebDriverWait waitDriver = new WebDriverWait(driver, TimeSpan.FromSeconds(60));
<-- Explicit Wait using driver.FindElement -->
waitDriver.Until(driver => driver.FindElement(By.XPath("//table")).Displayed);
<-- Explicit Wait using ExpectedConditions -->
waitDriver.Until(ExpectedConditions.ElementIsVisible(By.XPath("//table")));
<-- Explicit Wait using JSExecutor -->
string findTableScript = "return document.getElementsByTagName('table')";
IJavascriptExecutor jsExecutor = (IJavascriptExecutor) driver;
var result = waitDriver.Until(_ => jsExecutor.ExecuteScript(findTableScript));
Response from HTTP Server
<-- When it works as expected, polling DOM until the element appears -->
REQUEST [{dateTime}] POST /session/{sessionId}/element {"using":"xpath", "value":"//table"}
RESPONSE {"sessionId":"{sessionId}", "status": 0, "value":[]}
REQUEST [{dateTime}] POST /session/{sessionId}/element {"using":"xpath", "value":"//table"}
RESPONSE {"sessionId":"{sessionId}", "status": 0, "value":[]}
REQUEST [{dateTime}] POST /session/{sessionId}/element {"using":"xpath", "value":"//table"}
RESPONSE {"sessionId":"{sessionId}", "status": 0, "value":[{"ELEMENT":"{elementId}"}]} <-- result
<--When it doesn't work-->
REQUEST [{dateTime}] POST /session/{sessionId}/element {"using":"xpath", "value":"//table"}
RESPONSE {"sessionId":"{sessionId}", "status": 0, "value":[]}
REQUEST [{dateTime}] POST /session/{sessionId}/element {"using":"xpath", "value":"//table"}
RESPONSE {"sessionId":"{sessionId}", "status": 0, "value":[]}
REQUEST [{dateTime}] POST /session/{sessionId}/element {"using":"xpath", "value":"//table"}
RESPONSE {"sessionId":"{sessionId}", "status": 0, "value":"complete"} <-- what?
REQUEST [{dateTime}] POST /session/{sessionId}/element {"using":"xpath", "value":"//table"}
RESPONSE {"sessionId":"{sessionId}", "status": 0, "value":"complete"}
...continues until timeout
Worthy Notes
- It appears
"complete"
is the result ofdocument.readyState
, which I've read can be misleading when working with dynamic elements. I'm unsure of why this suddenly comes back in the response, as I don't see this in any other logs aside from when this flaky issue occurs. - Not reproducible when running the test in Debug mode (first thing that comes to mind is a race condition or async behavior.
async/await
logic did not resolve the issue) - My test seems to find the
table
element 100% of the time if I doThread.Sleep(60000)
before attempting to search the page for the element. This makes me think polling the DOM too early somehow short circuits thePageSource
, or something related.- I strongly dislike the idea of using Thread.Sleep or an Implicit Wait, as I'm using Browerstack so pausing a single test for some duration adds up when testing against several browsers.
- Increasing the WebWaitDriver's timeout duration to 300 seconds only causes
"complete"
to be returned for 300 seconds, even when I can manually assert, using dev tools during test execution, thetable
is visible on the screen and present in the DOM.- Depending on the search method, typically when an element is not found I receive
"value":[]
or"value":"Unable to find element with xpath == //table"
- Depending on the search method, typically when an element is not found I receive
- During a "failed" run, outputting
driver.PageSource
to a file has a value of"complete"
. In an ideal scenario, this holds an html document. - Once the server starts sending back a response with
"value":"complete"
I am unable to check the DOM for any other elements thereafter. In other words, catching the inevitableWebDriverTimeoutException
and re-querying the DOM for the same (or different) element, results in more"value":"complete"
responses. At this point, the test execution is essentially dead. - If I pass an element that purely doesn't exist, this issue still appears which tells me it's not necessarily an issue with the element itself, rather it's an issue with what it's checking against for the element.
I've done quite a bit of digging on this issue, but all roads lead to using explicit waits to poll the DOM until the element is visible. Unfortunately, I'm unable to find a stable solution aside from a suboptimal hard wait. At this point, I'm less concerned with the solution and more interested in why polling the DOM seems to randomly trigger this behavior. My interim solution is to use NUnit's [Retry()]
until I find something more optimal.
Any suggestions or debugging advice on this would be greatly appreciated.
Update - 9/22/2021 It's been a few months since I last posted this. Unfortunately, I was never able to find a reliable solution, other than using a hard wait through Thread.Sleep(). As I'm starting another round of test automation, I will report back should another round of research turn up any new ideas.