Open vberesnev opened 4 years ago
Does you application have a splash screen? Check the window name after the application is opened. In the screen shot I see that you starting the application from app data roaming. folder, better option will be to start the application from cortana.
[ClassInitialize]
public static void Setup(TestContext context)
{
// Create a session for Desktop
DesiredCapabilities desktopCapabilities = new DesiredCapabilities();
desktopCapabilities.SetCapability("app", "Root");
desktopSession = new WindowsDriver
// Launch Cortana Window using Windows Key + S keyboard shortcut to allow session creation to find it
desktopSession.Keyboard.SendKeys(OpenQA.Selenium.Keys.Meta + "s" + OpenQA.Selenium.Keys.Meta);
Thread.Sleep(TimeSpan.FromSeconds(1));
WindowsElement CortanaWindow = desktopSession.FindElementByName("Cortana");
string CortanaTopLevelWindowHandle = CortanaTopLevelWindowHandle = (int.Parse(CortanaWindow.GetAttribute("NativeWindowHandle"))).ToString("x");
// Create session for already running Cortana
DesiredCapabilities appCapabilities = new DesiredCapabilities();
appCapabilities.SetCapability("appTopLevelWindow", CortanaTopLevelWindowHandle);
cortanaSession = new WindowsDriver<WindowsElement>(new Uri(WindowsApplicationDriverUrl), appCapabilities);
Assert.IsNotNull(cortanaSession);
// Set implicit timeout to 5 seconds to make element search to retry every 500 ms for at most ten times
cortanaSession.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(5);
}
And to start the application I use the below code to find add and remove programs, you can use your application name:
// Type "add" in Cortana search box searchBox.SendKeys("add"); Thread.Sleep(TimeSpan.FromSeconds(1)); var bingPane = cortanaSession.FindElementByName("Bing"); Assert.IsNotNull(bingPane);
// Verify that a shortcut to "System settings Add or remove programs" is shown as a search result
var bingResult = bingPane.FindElementByXPath("//ListItem[starts-with(@Name, \"Add or remove\")]");
Assert.IsNotNull(bingResult);
Assert.IsTrue(bingResult.Text.Contains("System settings"));
You can find more info at the below link: https://github.com/microsoft/WinAppDriver/wiki/Frequently-Asked-Questions
Hi @vberesnev , I had an experience automating ClickOnce application using the WinAppDriver. The main point of the solution is that you need to start your "C:\Path\To\My\ApplicationName.appref-ms" as a process, and then attach to the opened application using the WinAppDriver root session (also known as "desktop session").
It was quite tricky for me, also I needed to handle application updates, so we ended up with creating a wrapper over the WinAppDriver - this one: https://github.com/aquality-automation/aquality-winappdriver-dotnet. It has in-build utilities to work with the root session, handling the process start/stop, starting WinAppDriver programmatically. You can specify all the capabilities you need in the settings.json file instead of hardcoding them. It has a fancy logging for all build-in methods. It also implements PageObject pattern, have built-in classes for common UI elements, such as Forms, WIndows, TextBoxes, Buttons and so on.
You can add this library as a Nuget package Aquality.WinAppDriver
Please take a look at example test here: https://github.com/aquality-automation/aquality-winappdriver-dotnet/blob/master/Aquality.WinAppDriver/tests/Aquality.WinAppDriver.Tests/Applications/WindowHandleApplicationFactoryTests.cs . What you need will look very similar and quite simple:
using Aquality.WinAppDriver.Applications;
using Aquality.WinAppDriver.Extensions;
using Aquality.WinAppDriver.Forms;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using System;
namespace YourNamespace
{
public class YourClass
{
private const string ApplicationPath = @"C:\Path\To\Your\ApplicationName.appref-ms";
// we prefer to store such path in settings.json, but you could have it in code if you mind
static void Main(string[] args)
{
AqualityServices.ProcessManager.Start(ApplicationPath);
AqualityServices.SetWindowHandleApplicationFactory(rootSession => new ApplicationWindow(() => rootSession).GetNativeWindowHandle());
// continue working with your application using respective Window/Form classes
}
private class ApplicationWindow : Window
{
public ApplicationWindow(Func<WindowsDriver<WindowsElement>> customSessionSupplier)
: base(MobileBy.AccessibilityId("YourAppWindowLocator"), "Your Application", customSessionSupplier)
{
}
}
}
}
Hi @vberesnev , I had an experience automating ClickOnce application using the WinAppDriver. The main point of the solution is that you need to start your "C:\Path\To\My\ApplicationName.appref-ms" as a process, and then attach to the opened application using the WinAppDriver root session (also known as "desktop session").
It was quite tricky for me, also I needed to handle application updates, so we ended up with creating a wrapper over the WinAppDriver - this one: https://github.com/aquality-automation/aquality-winappdriver-dotnet. It has in-build utilities to work with the root session, handling the process start/stop, starting WinAppDriver programmatically. You can specify all the capabilities you need in the settings.json file instead of hardcoding them. It has a fancy logging for all build-in methods. It also implements PageObject pattern, have built-in classes for common UI elements, such as Forms, WIndows, TextBoxes, Buttons and so on.
You can add this library as a Nuget package Aquality.WinAppDriver
Please take a look at example test here: https://github.com/aquality-automation/aquality-winappdriver-dotnet/blob/master/Aquality.WinAppDriver/tests/Aquality.WinAppDriver.Tests/Applications/WindowHandleApplicationFactoryTests.cs . What you need will look very similar and quite simple:
using Aquality.WinAppDriver.Applications; using Aquality.WinAppDriver.Extensions; using Aquality.WinAppDriver.Forms; using OpenQA.Selenium.Appium; using OpenQA.Selenium.Appium.Windows; using System; namespace YourNamespace { public class YourClass { private const string ApplicationPath = @"C:\Path\To\Your\ApplicationName.appref-ms"; // we prefer to store such path in settings.json, but you could have it in code if you mind static void Main(string[] args) { AqualityServices.ProcessManager.Start(ApplicationPath); AqualityServices.SetWindowHandleApplicationFactory(rootSession => new ApplicationWindow(() => rootSession).GetNativeWindowHandle()); // continue working with your application using respective Window/Form classes } private class ApplicationWindow : Window { public ApplicationWindow(Func<WindowsDriver<WindowsElement>> customSessionSupplier) : base(MobileBy.AccessibilityId("YourAppWindowLocator"), "Your Application", customSessionSupplier) { } } } }
Hello! Thank you for your answer! I am using the way then I get Desktop WAD Session, and then I get my opened application. I see you have good knowledge of WinAppDriver. Can you help me with my second issue? https://github.com/microsoft/WinAppDriver/issues/1182 I need to "refresh" UA tree, but I don't know how I can do it
@vberesnev From what I know, WAD "refreshes" elements tree by default each time you're searching for the element. Do you experience some "caching" that makes you unable to find elements in the changed UI? If so, then you probably searching relatively from some element, and you need to update the searchContext source. For example,
var parentElement = driverSession.FindElement(By.Name("parentName")); var childElement = parentElement .FindElement(By.Name("childName")); // <...> some actions, as result you have UI changed var newChildElement = parentElement .FindElement(By.Name("newChildName")); //this could fail as you cached parentElement before your changing UI actions //you need to refresh the search context of the parent, by finding it again: parentElement = driverSession.FindElement(By.Name("parentName")); //now you should be able to find your new child: var newChildElement = parentElement .FindElement(By.Name("newChildName"));
If I'm getting this wrong and it's not what happening in your case, please describe in more details: what problems are you facing with?
@mialeska Thank you, I will try your method and write feedback to you!
@mialeska I tried it:
//App is opened on the first page
var parentElement = driver.FindElement(By.Name(_mainWindowTitle)); //Get application window as parent element
parentElement.ClearCache(); // with this method and without it - no result
parentElement.DisableCache(); // with this method and without it - no result
var menu = parentElement.FindElementByAccessibilityId("mergedMenuControl"); //get menu element
var menuItems = menu.FindElements(By.ClassName("Button")); //get buttons array
menuItems[1].Click(); // click second page, second page open
Thread.Sleep(5000); //sleep for load second page
parentElement = driver.FindElement(By.Name(_mainWindowTitle)); //reload parent
var chart = parentElement.FindElements(By.ClassName("ChartGroupContainerView")).FirstOrDefault(); //second try to reloading parent on the second page (returns NULL)
if (chart == null)
{
parentElement = driver.FindElement(By.Name(_mainWindowTitle)); //second try to reloading parent
chart = parentElement.FindElements(By.ClassName("ChartGroupContainerView")).FirstOrDefault(); //second try to find element
}
chart.Click(); //Exception because of chart is NULL
I tried to reload WinAppDriver process and get new session (I get new session, but I still could not find element on the second page).
I have not any ideas...
@vberesnev I'm afraid that your problem is that your second page is not attached to your main window. AFAIK the best option here would be to search for the parent windowElement of your second page from the root (desktop) WAD session, and then to find your elements relatively from that windowElement. As I said before, from my perspective the most convenient way to handle this is implemented within Aquality.WinAppDriver package, you should give it a try :)
@mialeska I used Inspect.exe for find UI elements name, classe and id. So, then I open my app on the first page, I see elements for first page (and main window is parent). Then I click menu item, second page opened, but in Inspect elements tree does not reload. I press "refresh" button, but it does not help. And only after my click to some element on the second page, Ispect refreshes elements tree.
@mialeska It seems I find the resolve of this problem! But it's too ugly, lol :) Using Inspect.exe I noticed that refresh of UI elements starts after mouse over one of element second page. But I haven't access to this element by name or id before refreshing. So my actions: I open second page, move mouse coursor to the element by pixcel coordinate (my app is fulscreen), after that WAD refreshs tree and I have access to the UI elements of the second page. Thank you so much, you gave me way to resolving my issue! :) Have a nice day!
@vberesnev how exactly did you move mouse cursor? I tried "moveToElement" but it didn't help.
@splekhan Hello! moveToElement can't help you because WAD doesn't see needed element before reloading of UI elements tree. So, I'm using this crutch:
var parentElement = driver.FindElement(By.Name(_mainWindowTitle)); //Get application window as parent element
var menu = parentElement.FindElementByAccessibilityId("MenuControl"); //get menu element
var menuItems = menu.FindElements(By.ClassName("Button")); //get buttons array
menuItems[1].Click(); // click second page, second page open
Thread.Sleep(2000); // sleep for load second page
driver.Mouse.MouseMove(parentElement.Coordinates, 575, 75); // move mouse to element with coordinates 575,75 for activate mousehover event and reloading UI elements tree
Thread.Sleep(5000);
I have reloading of UI tree after mouse over to button on second page and tooltip showing.
It is terrible solution to the problem, but it's work for me
@splekhan Hello! moveToElement can't help you because WAD doesn't see needed element before reloading of UI elements tree. So, I'm using this crutch:
var parentElement = driver.FindElement(By.Name(_mainWindowTitle)); //Get application window as parent element var menu = parentElement.FindElementByAccessibilityId("MenuControl"); //get menu element var menuItems = menu.FindElements(By.ClassName("Button")); //get buttons array menuItems[1].Click(); // click second page, second page open Thread.Sleep(2000); // sleep for load second page driver.Mouse.MouseMove(parentElement.Coordinates, 575, 75); // move mouse to element with coordinates 575,75 for activate mousehover event and reloading UI elements tree Thread.Sleep(5000);
I have reloading of UI tree after mouse over to button on second page and tooltip showing.
It is terrible solution to the problem, but it's work for me
Thank you! I'll try this and let you know how it went. I think that the issue we've faced with is kinda popular. WAD should have the caution on its "box" to warn consumers about the inability to refresh the elements.
My app allows to perform "Refresh" by clicking the button in Inspect.exe and it helps. Maybe it's a good idea to invoke this API method using driver.
@vberesnev thanks again, but it seems like it's not my solution. I noticed that pane tree doesn't refresh when I hover mouse even manually. Only Inspect.exe "Refresh" button does that.
@splekhan As I know WAD should reload tree automatically. So, how I found this solution: I open my app, open Isnpect.exe, open second page (Insect did't reload tree), and I was looking for any case, then Inspect.exe reload the tree automatically, without button "refresh". So, I found, if I cover by mouse some button on second page (without click!!!) and invoke tool tip and then I move mouse to some free space -> Inspect.exe reloads UI tree automatically. So I started to use this case in my app. Please, if you will found more lucky case, tell me about it :)
@vberesnev the only thing that worked is pretty radical ))) I created this method and it's seem like now I have to call it every time I use pagination :( You still think your solution is ugly? Watch mine!
protected void reloadElements() {
URL url = null;
try {
url = new URL(DRIVER_HUB_URL);
} catch (MalformedURLException e) {
e.printStackTrace();
}
WebElement existingWindow = getWebDriver().findElement(By.name("Window title name"));
RemoteWebDriver existingWindowSession = null;
int handle = Integer.parseInt(existingWindow.getAttribute("NativeWindowHandle"));
String currentHandle = Integer.toHexString(handle);
DesiredCapabilities caps = new DesiredCapabilities();
caps.setCapability("appTopLevelWindow", currentHandle);
existingWindowSession = new RemoteWebDriver(url, caps);
setWebDriver(existingWindowSession);
}
@splekhan My solution depends on app window, so if app will not be open full screen or place of elelment will be changed - my solution will go to trash :)
I tried reopen win app driver and reattach my application to it, but it didn't work for me
Hello. I have some problems with starting click-once application by AppiumOptions class. My code is too simple:
and I catch Exception "OpenQA.Selenium.WebDriverException: 'Failed to locate opened application window with appId: C:\Path\To\My\ApplicationName.appref-ms, and processId: 7356'" on the last line, but my click once app;ication starts without any problems.
I am using ms:waitForAppLaunch with 30 seconds of timeout (it's enought) but it can't help me.