Hi, Is it possible to support controls like the ComboBox, or ListView for WPF?
With TestStack.White it is possible to get cell of a ListView, select a ComboBox item by index, how do you do that with WinAppDriver testing a WPF application?
for example:
var listView = myTestStackWindow.Get<ListView>(SearchCriteria.ByAutomationId(automationId));
var rowCount = listView.Rows.Count;
var gridPattern = listView.AutomationElement.GetCurrentPattern(GridPattern.Pattern) as GridPattern;
var cell = gridPattern.GetItem(row, column);
cell.TryGetCurrentPattern(ValuePattern.Pattern, out var valueObject);
var valuePattern = valueObject as ValuePattern;
Yes, it is possible. Everything is a WindowsElement when we're working with WinAppDriver. You just need to go through the items in a sequence, you might use XPath in some cases.
The children of combo-box are present in memory but they're not displayed while the list is not showing on screen. For this, I used a foreach loop to go through the children of a combo and called the click method on the one I wanted to select.
You may just use the SendKeys method with item text to select an item in Combo/ListView.
But you cannot invoke GridPattern or any other Pattern from the native AutomationElement as we could have done with CodedUI (c.f. the sample) ?
Hi @naeemakram, It would be great to see an example, perhaps with a combo box, expanding it and selecting the third item, obviously by no means of sending keys F4, DOWN, DOWN., DOWN because the test is trying to reproduce the mouse interaction.
Here's the code I created for my upcoming course about WinAppDriver. I hope you'll find it to be useful.
```
[TestMethod]
public void ComboTest()
{
var combo = sessionWinForm.FindElementByAccessibilityId("comboBox1");
var open = combo.FindElementByName("Open");
var listItems = combo.FindElementsByTagName("ListItem");
Debug.WriteLine($"Before: Number of list items found: {listItems.Count}");
combo.SendKeys(Keys.Down);
open.Click();
listItems = combo.FindElementsByTagName("ListItem");
Debug.WriteLine($"After: Number of list items found: {listItems.Count}");
// maybe check number of elements in combo
//Assert.AreEqual(6, listItems.Count, "Combo box doesn't contain expected number of elements.");
foreach(var comboKid in listItems)
{
if(comboKid.Text == "NJ")
{
WebDriverWait wdv = new WebDriverWait(sessionWinForm, TimeSpan.FromSeconds(10));
wdv.Until(x => comboKid.Displayed);
comboKid.Click();
}
}
}
```
@naeemakram but this doesn't work with UI (list in that case) virtualization on ?The native pattern woudl allow use to work with virtualization enables.
Does it mean all your UI testing have virtualization off ?
I don't get your question. In the past I've run my automation scripts on various virtual machines, it never has been a problem. I don't think there's a difference between a test running on a physical machine and a VM.
I'm talking about UI virtualization :
https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/optimizing-performance-controls#supporting-bidirectional-virtualization
Whcih means with inspect tool, you only see 4 list item currently rendered in the UI while there is potentially 100 of "view models" to be displayed. the scrollbard reflect that information.
It also means in codedUI we were able to use https://docs.microsoft.com/en-us/windows/win32/winauto/uiauto-implementingscrollitem to ensure that virtualized item can be bring into view.
Interesting, this is new information for me.
Hi, @naeemakram thanks for the example, I suppose it works for a specific scenario in the controls that you are using, which is different when using some other controls let's say Telerik, DevExpress, and so on.
The ideal would be to have an equivalent documentation like this https://www.guru99.com/xpath-selenium.html but focused in WPF.
For example I had to try-error with X-Path in order to open button in the comboBox:
var open = comboBoxElement.FindElementByXPath("//Button[@Name='']");
Which is confusing because the element has a value in the Name property when it is inspected with Snoop.
It works in case of WinForms combo box. Even in that case I had to try a few things to make the scripts work. The following method calls appear to be nonsense but I've found them to be necessary.
combo.SendKeys(Keys.Down);
open.Click();
I will try to spend some time and create a WPF example as well in order to sort these problems.
@naeemakram, in the case of WPF bare in mind that SendKeys doesn't reflect the user interaction with the mouse. I'd prefer avoid generating a different interaction because falsifies the aim of the test.
For the case of
FindElementsByTagName("ListItem")
it doesn't find any list, I wonder how to query the items works in general so that I can see the elements for example in a DevExpress ComboBoxEdit
In WAD UI Recorder, what information is shown when you hover mouse on the ListView in question?
for the hover in one combo box item:
"/Pane[@ClassName=\"#32769\"][@Name=\"Desktop 1\"]/Window[@ClassName=\"Window\"][@Name=\""AMS - [Calculation\"]/Window[@ClassName=\"Popup\"]/Custom/List[@AutomationId=\"PART_Content\"]/ListItem[@ClassName=\"ListBoxItem\"][@Name=\"AMS.ClientApi.Client\"]/Text[@ClassName=\"TextBlock\"][@Name=\"Option 1\"]"
if I try to run this simple search:
private AppiumWebElement FindElementByXPath(string xPath)
=> Timing(() => _session.FindElementByXPath(xPath), xPath);
private AppiumWebElement FindElementByAutomationId(string automationId)
=> Timing(() => _session.FindElementByXPath($"//*[@AutomationId='{automationId}']"), automationId);
private static AppiumWebElement FindElementByAutomationIdFrom(AppiumWebElement parentElement, string automationId)
=> Timing(() => parentElement.FindElementByXPath($"//*[@AutomationId='{automationId}']"), automationId);
private static AppiumWebElement Timing(Func<AppiumWebElement> timingFunc, string automationId)
{
var start = DateTime.Now;
var element = timingFunc();
Console.WriteLine($"Finding '{automationId}' took:{(DateTime.Now - start).TotalSeconds}");
return element;
}
public override async Task SelectCombo(string automationId, int index)
{
await WaitClick();
var comboBoxElement = FindElementByAutomationId(automationId);
var open = FindElementByAutomationIdFrom(comboBoxElement, "PART_Item");
open.Click();
var element = FindElementByXPath($"//Custom/List[@AutomationId='PART_Content']/ListItem[@ClassName='ListBoxItem'][{index}]");
element.Click();
}
The timing result is overwhelmingly slow for the size or complexity of the application:
Finding 'PerformanceAccountAId' took:2.2732279s
Finding 'PART_Item' took:0.1270051s
Finding '//Custom/List[@AutomationId='PART_Content']/ListItem[@ClassName='ListBoxItem'][5]' took:4.7501652s
Hi @naeemakram, would you please let us know if there is any plan to continue with WinAppDriver, especially for support of WPF
@paulovila for us, we stopped using relative path (i.e. using //Custom...) but using absolute path (/Window...)
it is forcing us to describe more our WPF application but the absolute path is the only way we achieve proper performance on our compex WPF app
@fforjan Absolute and even relative paths are hard to maintain. I don't want to end up in a situation where changing a title of a window would end up in unintended tests broken.
The effort in writing ui tests should focus on change-proof robustness. Using paths introduce brittleness in your tests.
I鈥檝e started using FlaUI3. It's fast, and extensible, all working with Automation ids
@paulovila we described our application in a relative manner but we convert them to full path when querying via winappdriver.
The idea is that yes, changing the title of a window will break tests but we should only change it at one place.
@fforjan, based on the activity of the repository, I don't have the feeling that they are going to fix this performance issue. Best you can do is move on and choose a UI test framework more focused in WPF