I have experience of developing a large Automated Test solution using C#, WinAppDriver (aka WAD) and Selenium. This tests a large WPF windows application.
So far, so good.
Now as you can imagine the System Under Test has reached version 2 which is similar but now the basic WPF / XAML uses ReactiveUserControl or React UI.
Using my test solution that has worked before, I have created a very simple test project for the version 2 / React application and we get so far, the application is launched and the "driver" session is active but using the many "FindElementsBy" selenium methods I can't find anything.
I have sense checked this by making my Test launch Notepad++ instead of my React UI system as the System Under Test and my tests work perfectly. I have also tested Notepad, Windows Photo Viewer, Windows Explorer and of course the WPF app that my company develops in house. These all work but the React application elements seem invisible to auto test.
For info, here is some of the xaml from the React UI app:
<rxui:ReactiveUserControl x:Class="MyReactUIApp.Views.MyView"
x:TypeArguments="vms:MyViewModel"
xmlns:rxui="http://reactiveui.net"
xmlns:vms="clr-namespace:MyReactUIApp.ViewModels"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="400" d:DesignWidth="640" Background="{StaticResource White}">
Now I am not an expert in xaml but I think the clue here is ReactiveUserControl. I am not 100% sure that it works well with my 'driver' variable in the code:
protected static WindowsDriver<RemoteWebElement> driver;
Anyway here is my code. You can see I have a test named 'MyShortTest()' and before this test runs, there is a [TestInitialize] which calls the method Setup(context). You can see that the Setup method launches the application and then finds the MainWindowHandle of that process and then assigns the process with that Main Window Handle to the driver variable.
This all works and there are no errors.
Within MyShortTest though, the FindElementsByAccessibilityId methods don't find anything. They should find the element with the ID "Email" because it is there. It should also find the element named "TitleBar" which is does for Notepad++ etc but not for my React system under test.
Test Code and class:
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace notepad_AutoTest
{
[TestClass]
public class BasicSanityCheckTests : driver_class
{
[TestMethod]
public void MyShortTest()
{
//Check if worked
bool foundSomething = false;
try
{
//Notepad++ has an element with the ID "TitleBar", find it...
//WHY IS THE titleBarCount = 0 ???????????????
int titleBarCount_AS1 = driver.FindElementsByAccessibilityId("TitleBar").Count; //Notepad OK //Notepad++ OK //Windows Photo Viewer OK
if (titleBarCount_AS1 > 0)
{
foundSomething = true;
}
}
catch { }
try
{
//MyReactUIApp has an element with the ID "Email", find it...
//WHY IS THE emailCount_AS1 = 0 ???????????????
int emailCount_AS1 = driver.FindElementsByAccessibilityId("Email").Count;
if (emailCount_AS1 > 0)
{
//Why is this not found???????
foundSomething = true;
}
}
catch { }
//Why is foundSomething still false????????
Assert.IsTrue(foundSomething, "We did not find anything in the application under test.");
//Close
driver.CloseApp();
}
#region Class Initialize and Cleanup
[ClassInitialize]
public static void ClassInitialize(TestContext context)
{
if (!CommonMethods.IsProcessOpen(GUI_Map.AppUnderTest_ProcessName, false))
{
//Launch etc
Setup(context);
}
}
[ClassCleanup]
public static void ClassCleanup()
{
TearDown();
}
#endregion Class Initialize and Cleanup
}
}
Driver / session code and class:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OpenQA.Selenium.Appium;
using OpenQA.Selenium.Appium.Windows;
using OpenQA.Selenium.Remote;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
namespace notepad_AutoTest
{
public class driver_class
{
//WinAppDriver url
private const string WindowsApplicationDriverUrl = "http://127.0.0.1:4723";
//driver
protected static WindowsDriver<RemoteWebElement> driver;
//protected static OpenQA.Selenium.Appium.Service.AppiumLocalService service;
//Launch notepad++ - variables
//public static string filePath = "C:\\Program Files (x86)\\Notepad++\\";
//public static string fileName = "notepad++.exe";
//public static string processName = "Notepad++";
//Launch MyReactUIApp - variables
public static string filePath = "C:\\Projects\\MyReactiveUI_App\\";
public static string fileName = "myReactUIApp.exe";
public static string processName = "MyReactUIApp";
public static string filepathWinAppD = "C:\\Program Files (x86)\\Windows Application Driver\\WinAppDriver.exe";
public static void Setup(TestContext context)
{
// Launch Windows Application Driver if it is not already launched
if (driver == null)
{
TearDown();
//Check if Windows Application Driver is running and launch it if needed.
if (!CommonMethods.IsProcessOpen("WinAppDriver", false))
{
//Launch Windows Application Driver
Process winAppD = new Process();
winAppD.StartInfo.FileName = filepathWinAppD;
winAppD.Start();
while (!CommonMethods.IsProcessOpen("WinAppDriver", true))
{
Thread.Sleep(500);
}
}
// Launch the application and session
//Options - filepath to launch
AppiumOptions options = new AppiumOptions();
options.AddAdditionalCapability("app", filePath);
//Check the exe file exists
Assert.IsTrue(File.Exists(filePath + fileName),
"Application does not exist in the location: " + filePath.ToString(), filePath);
try
{
//LaunchWPF app and wpf session
if (!CommonMethods.IsProcessOpen(processName, false))
{
//Launch application under test
ProcessStartInfo psi = new ProcessStartInfo();
psi.WorkingDirectory = filePath;
psi.FileName = fileName;
Process.Start(psi);
DateTime startLaunch = DateTime.Now;
int i = 0;
while (!CommonMethods.IsProcessOpen(processName, true) && !CommonMethods.TimedOut(startLaunch, 30))
{
Thread.Sleep(500 + i);
i += 100;
}
}
string TopLevelWindowHandleHex = null;
IntPtr TopLevelWindowHandle = new IntPtr();
DateTime startFindWindow = DateTime.Now;
startFindWindow = DateTime.Now;
int processFindCount = 0;
while (TopLevelWindowHandleHex == null)
{
if (CommonMethods.TimedOut(startFindWindow, 30))
{
Assert.Fail("Failed to find Process for application under test " + processName + " attempts: " + processFindCount);
}
foreach (Process SUT_Process in Process.GetProcesses())
{
if (SUT_Process.ProcessName.ToUpper().StartsWith(processName.ToUpper()))
{
TopLevelWindowHandle = SUT_Process.MainWindowHandle;
TopLevelWindowHandleHex = SUT_Process.MainWindowHandle.ToString("x");
}
}
}
AppiumOptions appOptions = new AppiumOptions();
appOptions.AddAdditionalCapability("appTopLevelWindow", TopLevelWindowHandleHex);
appOptions.AddAdditionalCapability("ms:experimental-webdriver", true);
appOptions.AddAdditionalCapability("ms:waitForAppLaunch", "25");
driver = new WindowsDriver<RemoteWebElement>(new Uri("http://127.0.0.1:4723"), appOptions);
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(60);
}
catch (Exception ex)
{
Assert.Fail("Failed to launch " + processName);
}
finally
{
//Assert it's running
Assert.IsNotNull(driver);
Assert.IsNotNull(driver.SessionId);
}
}
}
public static void TearDown()
{
// Close the application and delete the session
if (driver != null)
{
driver.Quit();
driver = null;
}
}
}
}