Getting Started

Prerequisites

Operating system

Windows 10 version 1903 or later. UI Automation (UIA3) is a Windows-only technology. Running in a Linux or macOS container will not work; the test runner must execute on a machine with a live desktop session.

Runtime

.NET 10.0 SDK or later. Download from dotnet.microsoft.com.

FlaUI requirements

Flawright wraps FlaUI, which in turn wraps Microsoft's UI Automation (UIA3) APIs. No additional installation is required — FlaUI ships as NuGet packages and is pulled in transitively. However, the target application must:

  • Expose its controls via UIA. Most Win32, WinForms, WPF, and WinUI3 applications do. Legacy applications using custom-drawn UI may not.
  • Not run at a higher integrity level than the test process. If the application requires elevation (UAC prompt), the test runner must also run as administrator.

Inspect tool

Before writing selectors, use an inspection tool to browse the UIA tree of your application:

  • Accessibility Insights for Windowsaccessibilityinsights.io (recommended)
  • inspect.exe — ships with the Windows SDK at C:\Program Files (x86)\Windows Kits\10\bin\<version>\x64\inspect.exe

These tools show AutomationId, Name, ControlType, and other properties for each element, which map directly to Flawright selectors.

Installing

Add the package to your test project:

dotnet add package Flawright

Or via the Directory.Packages.props / .csproj pattern:

<PackageReference Include="Flawright" />

Flawright targets net10.0-windows. Your test project must target a Windows TFM (e.g., net10.0-windows).

First Test: Notepad

The following is a complete, runnable xUnit test that launches Notepad, types text, and asserts visibility.

Project file

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net10.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Flawright" Version="*" />
    <PackageReference Include="xunit" Version="2.*" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.*" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
  </ItemGroup>
</Project>

Test file

using Flawright;
using Xunit;

public class NotepadTests : IAsyncLifetime
{
    private Flawright? _fw;

    public async Task InitializeAsync()
    {
        // Configure DismissDialogCloseBehavior so CloseAsync handles the
        // "save changes?" dialog that Notepad shows when exiting with unsaved content.
        _fw = await Flawright.LaunchAsync(
            new LaunchOptions { ApplicationPath = "notepad.exe" },
            new FlawrightOptions
            {
                CloseBehavior = new DismissDialogCloseBehavior() // handles Win10 + Win11 Notepad
            });
    }

    [Fact]
    public async Task TypeText_AppearsInEditor()
    {
        var page = await _fw!.Browser.NewPageAsync();

        // Win11 Notepad (WinUI3) uses AutomationId "RichEditBox" for the editor.
        // On classic Win10 Notepad use "class:Edit" instead (the Win32 ClassName is
        // "Edit", but its UIA ControlType is Document — so class:Edit, not controltype:Edit).
        await page.FillAsync("#RichEditBox", "Hello from Flawright!");

        var text = await page.Locator("#RichEditBox").InnerTextAsync();
        Assert.Equal("Hello from Flawright!", text);
    }

    [Fact]
    public async Task MenuBar_IsVisible()
    {
        var page = await _fw!.Browser.NewPageAsync();

        await page.Locator("controltype:MenuBar").Expect().ToBeVisibleAsync();
    }

    [Fact]
    public async Task Screenshot_ReturnsPngBytes()
    {
        var page = await _fw!.Browser.NewPageAsync();

        var png = await page.ScreenshotAsync();

        Assert.NotNull(png);
        Assert.True(png.Length > 0);
    }

    public async Task DisposeAsync()
    {
        if (_fw != null)
        {
            // Runs the configured DismissDialogCloseBehavior — dismisses the
            // "save changes?" dialog if it appears, then waits for exit.
            await _fw.Browser.CloseAsync();
            await _fw.DisposeAsync();
        }
    }
}

Windows 10 vs Windows 11 selector differences

Version Editor selector Notes
Windows 11 Notepad (WinUI3) #RichEditBox AutomationId-based; reliable across Win11 builds
Classic Windows 10 Notepad (Win32) class:Edit ClassName-based; use class: not controltype: — the UIA ControlType for this multi-line edit is Document, not Edit

When in doubt, use Accessibility Insights for Windows or inspect.exe to browse the live UIA tree and find the right selector.

Run the tests

dotnet test

The test runner launches Notepad, drives it, and closes it. Tests run sequentially by default in xUnit. For parallel execution with UI automation, pin each test class to a single thread using [Collection] or a custom ITestCollectionOrderer.

What just happened

  1. Flawright.LaunchAsync(options) is a single static call that starts notepad.exe. On Windows 11, Flawright detects that notepad.exe is a packaged WinUI3 app and automatically calls Application.LaunchStoreApp("Microsoft.WindowsNotepad_8wekyb3d8bbwe!App") so FlaUI binds to the real application instead of the short-lived alias stub.
  2. Browser.NewPageAsync() calls GetMainWindow on the FlaUI Application and wraps the resulting Window in a FlawrightPage.
  3. FillAsync("#RichEditBox", ...) finds the element with AutomationId = "RichEditBox" (the Win11 WinUI3 editor), auto-waiting up to 5 seconds, and sets its value via ValuePattern.SetValue.
  4. Expect().ToBeVisibleAsync() resolves the locator and polls until the element is not offscreen, or throws FlawrightTimeoutException after the timeout.
  5. IAsyncLifetime.DisposeAsync() calls Flawright.DisposeAsync(), which closes the Notepad process and releases UIA resources.

Where to go next