I have such a trivial issue but I am having a hard time getting my code to properly wait for an object before moving on.
I have the following config set for my driver
session.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(60);
I was expecting this to mean that it would wait a minimum of 60 seconds before throwing errors related to element identification such as
Message: System.InvalidOperationException : An element could not be located on the page using the given search parameters.
However this is not the case. I get the error around 2 seconds in when trying to call the following command.
WindowsElement btn = session.FindElementByXPath("//Button[@Name='NEXT']");
btn.Click();
The error gets thrown on the line where I am just defining the buttons properties and not on the actual Click() method. Am I not passing the elements properties correctly? Why would the instantiation of the button also do the searching of it?
There it a open issue on the winappdriver github. Take a look at this comment about it. It seems to be a Appium issue. I have no idea about the status of this issue.
Basically, this means you will have to resort to a workaround. Using Thread.Sleep(/*milliseconds*/)
is a bad idea.
I implemented a while
loop in a function to get a control by Automation ID like this:
/// <summary>
/// Gets a UI element based on a AutomationId.
/// </summary>
/// <param name="automationId">The AutomationId is a unique value that can be found with UI inspector tools.</param>
/// <param name="controlName">The name of the UI element.</param>
/// <param name="timeOut">TimeOut in milliseconds</param>
/// <returns></returns>
protected WindowsElement GetElement(string automationId, string controlName, int timeOut = 10000)
{
bool iterate = true;
WindowsElement control = null;
_elementTimeOut = TimeSpan.FromMilliseconds(timeOut);
timer.Start();
while (timer.Elapsed <= _elementTimeOut && iterate == true)
{
try
{
control = Driver.FindElementByAccessibilityId(automationId);
iterate = false;
}
catch (WebDriverException ex)
{
LogSearchError(ex, automationId, controlName);
}
}
timer.Stop();
Assert.IsFalse(timer.Elapsed > _elementTimeOut, "Timeout Elapsed, element not found.");
timer.Reset();
return control;
}
Using a loop has some advantages versus Thread.Sleep()
, it's more flexible and you have much more options than simply blocking the code execution.
A few of the advantages:
Thread.Sleep(5000)
will assume it's OK to continue, while the loop knows it's OK to continue the test.Alternately, this code will work just as well:
protected WindowsElement GetElement(string automationId, string propertyName, int timeOut = 10000)
{
WindowsElement element = null;
var wait = new DefaultWait<WindowsDriver<WindowsElement>>(Driver)
{
Timeout = TimeSpan.FromMilliseconds(timeOut),
Message = $"Element with automationId \"{automationId}\" not found."
};
wait.IgnoreExceptionTypes(typeof(WebDriverException));
try
{
wait.Until(Driver =>
{
element = Driver.FindElementByAccessibilityId(automationId);
return element != null;
});
}
catch(WebDriverTimeoutException ex)
{
LogSearchError(ex, automationId, propertyName);
Assert.Fail(ex.Message);
}
return element;
}
Above code will only throw a WebDriverTimeoutException
instead of continuously throwing NoSuchElementException
. It does not use a while loop, but I suspect wait.Until(...)
is doing something similar, since WinAppDriver polls the gui every 500ms (see the PollingInterval
property on the DefaultWait
object.