TonyHallett.WPFExtras.TestTools.WPFPilot 1.0.0

dotnet add package TonyHallett.WPFExtras.TestTools.WPFPilot --version 1.0.0
                    
NuGet\Install-Package TonyHallett.WPFExtras.TestTools.WPFPilot -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="TonyHallett.WPFExtras.TestTools.WPFPilot" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="TonyHallett.WPFExtras.TestTools.WPFPilot" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="TonyHallett.WPFExtras.TestTools.WPFPilot" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add TonyHallett.WPFExtras.TestTools.WPFPilot --version 1.0.0
                    
#r "nuget: TonyHallett.WPFExtras.TestTools.WPFPilot, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package TonyHallett.WPFExtras.TestTools.WPFPilot@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=TonyHallett.WPFExtras.TestTools.WPFPilot&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=TonyHallett.WPFExtras.TestTools.WPFPilot&version=1.0.0
                    
Install as a Cake Tool

Helpers for when ui testing with WPFPilot.

This project depends on WPF Pilot, licensed under the WPF Pilot Split License. See the WPF Pilot license for applicable licensing terms.

AppDriver.Launch helpers

AppDriverWindowLauncher launches the exe with --window=FullyQualifiedNameOfWindow.

SlnConventionAppDriverWindowLauncher

public static AppDriver LaunchWindow<TWindow>(Assembly testAssembly)

extends above so the exe path is not necessary.

e.g given testAssembly

"\TonyHallett.WPFExtras\TonyHallett.WPFExtras.UITests\bin\Debug\net10.0-windows\TonyHallett.WPFExtras.UITests.dll"

and TWindow in assembly TonyHallett.WPFExtras.TestHost

will replace the directory, TonyHallett.WPFExtras.UITests, in the path above with TonyHallett.WPFExtras.TestHost and have the exe file name as TonyHallett.WPFExtras.TestHost.exe

For NUnit - TonyHallett.WPFExtras.TestTools.WPFPilot.NUnit this is incorporate into a base test class that launches the TWindow app.

Strongly typed AppDriver.GetElement

Not using the indexer in the GetElement matcher.

public static Element GetElementByName(this AppDriver appDriver, string name, int timeoutMs = 30000)

Not specifying TypeName string in the GetElement matcher.

public static Element GetElementByType<TWpf>(this AppDriver appDriver, int timeoutMs = 30000) where TWpf : Visual public static IReadOnlyList<Element> GetElementsByType<TWpf>(this AppDriver appDriver, int timeoutMs = 30000) where TWpf : Visual

There is also a custom element versions of these.

Custom elements

WPFPilot has the concept of custom elements. These are derived Element whose purpose is to provide helpers for a specific control type.

They are obtained from the AppDriver GetElement<TElement>.

The only downside is that custom elements have to have a specific constructor instantiated by WPFPilot and do not receive the AppDriver for future usage.

As such the strongly typed "Get" AppDriver extension methods

public static TElement GetElementByName<TElement>(this AppDriver appDriver, string name, int timeoutMs = 30000) where TElement : Element
public static TElement GetElementByType<TElement, TWpf>(this AppDriver appDriver, int timeoutMs = 30000)
    where TElement : Element
    where TWpf : Visual

public static IReadOnlyList<TElement> GetElementsByType<TElement, TWpf>(this AppDriver appDriver, int timeoutMs = 30000)
    where TElement : Element
    where TWpf : Visual

that will set the AppDriver if the Element derivation is ICustomElementWithAppDriver


    public interface ICustomElementWithAppDriver
    {
        void SetAppDriver(AppDriver appDriver);
    }

A base implementation of this interface is provided Note the ICustomElement.

    public abstract class CustomElement(Element element) :
        Element<CustomElement>(element), ICustomElementWithAppDriver, ICustomElement
    {
        private AppDriver? appDriver;
        public Element Element { get; private set; } = element;

        public void SetAppDriver(AppDriver appDriver) => this.appDriver = appDriver;

        protected AppDriver AppDriver => appDriver ?? throw new InvalidOperationException("AppDriver has not been set. Ensure that SetAppDriver is called before using this element.");
    }

    public interface ICustomElement
    {
        Element Element { get; }
    }

There are a couple of marker interfaces that extend ICustomElement ( that are reliant upon the Element property ) with extension methods applied.

ISelectableElement has IsSelected()

IContentControlElement has Content() and StringContent()

these just avoid the magic strings in the indexer.

For containers there is

    public interface IHaveSelectableItemsElement<TItemElement> where TItemElement : ISelectableElement
    {
        IReadOnlyList<TItemElement> GetItemElements();
    }

with extension methods to select an ISelectableElement from GetItemElements()

    public static IHaveSelectableItemsElement<TItem> SelectItem<TItem>(this IHaveSelectableItemsElement<TItem> containerElement, int index)
        where TItem : ISelectableElement

    public static IHaveSelectableItemsElement<TItem> SelectLast<TItem>(this IHaveSelectableItemsElement<TItem> containerElement)
            where TItem : ISelectableElement

    public static IHaveSelectableItemsElement<TItem> SelectFirst<TItem>(this IHaveSelectableItemsElement<TItem> containerElement)
            where TItem : ISelectableElement

implementations of IHaveSelectableItemsElement / ISelectableElement are

ListBoxElement and ListBoxItemElement

DataGridElement and DataGridRowElement

ListBoxElement and DataGridElement also implement IHaveSelectedItemsElement nad received via extension method TSelectedItem[] GetSelectedItems<TSelectedItem>() - this uses reflection on "SelectedItems" property of type IEnumerable.

ListBoxItemElement is a IContentControlElement whereas DataGridRowElement has the generic Item method.

    public class DataGridRowElement(Element element) : CustomElement(element), ISelectableElement
    {
        public TItem Item<TItem>() => Invoke<DataGridRow, TItem>(dgr => (TItem)dgr.Item);
    }

WPFPilot serialization applies

Invoke issues an InvokeCommand on the target element and is one of the most flexible and useful methods in WPF Pilot. Invoke expects a LambdaExpression, which will typically look like a plain old function call, as seen in the example. There are many flavors of Invoke depending on if we care about the result, if we need to specify a custom element, or if we need to call an async method. The LambdaExpression is serialized into a JSON representation and sent over the NamedPipe. The app then compiles the LambdaExpression and invokes it on the element. The result is then serialized and returned over the NamedPipe back to the client. Most standard classes will be serializable by Newtonsoft.Json, but not all are, in which case null is returned.

Additional AppDriver extensions

There are a couple of utility methods

public static Element GetElementByNameAndClick(this AppDriver appDriver, string name, int timeoutMs = 30000)

public static string? GetTextByName(this AppDriver appDriver, string name, int timeoutMs = 30000)
    => appDriver.GetElementByName(name, timeoutMs)["Text"];

public static void AssertTextByName(
    this AppDriver appDriver, 
    string name, 
    string expectedText, 
    int findTimeoutMs = 30000, 
    int assertTimeoutMs = 10000)

Element InvokeDataContext extensions

This makes the element.Invoke code succinct

Instead of

element.Invoke<MyControl,string>(c => ((MyDataContext)c.DataContext).MyString);

Use element.InvokeDataContext<MyDataContext,string>(dc => dc.MyString);

When not returning a value

element.InvokeDataContext<MyDataContext>(dc => dc.DoSomething());

Product Compatible and additional computed target framework versions.
.NET net10.0-windows7.0 is compatible. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on TonyHallett.WPFExtras.TestTools.WPFPilot:

Package Downloads
TonyHallett.WPFExtras.TestTools.WPFPilot.NUnit

Base test classes for WPFPilot testing in NUnit

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 108 9/7/2026