Examples

Each example below is a self-contained test method. Add using Flawright; and a reference to Flawright to compile them.

Calculator: arithmetic sequence

Clicks digits and operators in sequence, then asserts the display shows the correct result. Windows Calculator uses AutomationIds for its buttons.

[Fact]
public async Task Calculator_AddsTwoNumbers()
{
    await using var fw = await Flawright.LaunchAsync(new LaunchOptions
    {
        ApplicationPath = "calc.exe"
    });
    var page = await fw.Browser.NewPageAsync();

    // Windows Calculator AutomationIds: digits are their face values ("1", "2", ...),
    // operators are "plus", "minus", "multiply", "divide", "equals"
    await page.ClickAsync("#1");
    await page.ClickAsync("#plus");
    await page.ClickAsync("#2");
    await page.ClickAsync("#equals");

    // The result display has AutomationId "CalculatorResults"
    await page.Locator("#CalculatorResults").Expect().ToHaveTextAsync("Display is 3");
}

[Fact]
public async Task Calculator_HasButtons()
{
    await using var fw = await Flawright.LaunchAsync(new LaunchOptions
    {
        ApplicationPath = "calc.exe"
    });
    var page = await fw.Browser.NewPageAsync();

    var buttonCount = await page.Locator("controltype:Button").CountAsync();
    Assert.True(buttonCount > 0, "Calculator should have buttons");

    // Spot-check a specific button
    await page.Locator("controltype:Button").Expect().ToBeVisibleAsync();
}

Note: Windows Calculator's AutomationIds vary by Windows version and Calculator version. Use Accessibility Insights to verify the exact IDs on your machine before running these tests.

Notepad: type, screenshot, verify

Launches Notepad, types a multi-line document, takes a screenshot, and verifies the editor is populated.

Windows 10 vs Windows 11 selectors

Windows 11 ships a packaged WinUI3 Notepad. Its text editor has AutomationId = "RichEditBox". Classic Windows 10 Notepad's textarea has Win32 ClassName Edit, so use class:Edit. Its UIA ControlType is Document (not Edit) — controltype:Edit will not match it. Inspect with Accessibility Insights to confirm.

[Fact]
public async Task Notepad_TypeAndVerify()
{
    // Configure DismissDialogCloseBehavior so CloseAsync handles the
    // "save changes?" dialog that Notepad shows when exiting with unsaved content.
    await using var fw = await Flawright.LaunchAsync(
        new LaunchOptions { ApplicationPath = "notepad.exe" },
        new FlawrightOptions
        {
            CloseBehavior = new DismissDialogCloseBehavior() // handles Win10 + Win11 Notepad
        });
    var page = await fw.Browser.NewPageAsync();

    const string content = "Line 1\nLine 2\nLine 3";

    // Win11 Notepad (WinUI3): use "#RichEditBox"
    // Classic Win10 Notepad:  use "class:Edit"  (ClassName = Edit; UIA ControlType = Document)
    await page.FillAsync("#RichEditBox", content);

    // Verify the text landed
    var text = await page.Locator("#RichEditBox").InnerTextAsync();
    Assert.Equal(content, text);

    // Capture a screenshot for the test report
    byte[] png = await page.ScreenshotAsync(@"C:\temp\notepad-test.png");
    Assert.True(png.Length > 0);

    // Runs the configured DismissDialogCloseBehavior — dismisses the dialog, then exits.
    await fw.Browser.CloseAsync();
}

[Fact]
public async Task Notepad_MenuBarIsPresent()
{
    await using var fw = await Flawright.LaunchAsync(
        new LaunchOptions { ApplicationPath = "notepad.exe" },
        new FlawrightOptions
        {
            CloseBehavior = new DismissDialogCloseBehavior()
        });
    var page = await fw.Browser.NewPageAsync();

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

    // Count menu items: File, Edit, View, ... (varies by Windows version)
    var menuItemCount = await page.Locator("controltype:MenuItem").CountAsync();
    Assert.True(menuItemCount >= 3);
}

File Explorer: navigate and count items

Launches File Explorer pointing at a specific path and counts visible items. Uses the Arguments field on LaunchOptions.

[Fact]
public async Task FileExplorer_CountsItemsInWindowsFolder()
{
    await using var fw = await Flawright.LaunchAsync(new LaunchOptions
    {
        ApplicationPath = "explorer.exe",
        Arguments = new[] { @"C:\Windows\System32" }
    });
    var page = await fw.Browser.NewPageAsync();

    // File Explorer populates asynchronously; in production tests you'd
    // poll until count stabilizes. Here we just verify at least one item appears.
    var items = await page.Locator("controltype:ListItem").CountAsync();
    Assert.True(items > 0, "Expected items in System32 folder");
}

Attaching to an already-running process

If the application is already running and you don't want Flawright to launch it, use AttachAsync.

[Fact]
public async Task AttachToRunningNotepad()
{
    // Find the PID of an already-running Notepad
    var pid = System.Diagnostics.Process
        .GetProcessesByName("notepad")
        .FirstOrDefault()?.Id
        ?? throw new InvalidOperationException("Notepad is not running");

    await using var fw = await Flawright.AttachAsync(new AttachOptions
    {
        ProcessId = pid
    });
    var page = await fw.Browser.NewPageAsync();

    // Win11 Notepad: "#RichEditBox"; classic Win10: "class:Edit"
    await page.Locator("#RichEditBox").Expect().ToBeVisibleAsync();
}

Multi-window: wait for a dialog

After clicking a menu item that opens a dialog, wait for the dialog window by title substring.

[Fact]
public async Task Notepad_SaveAsDialog_Opens()
{
    await using var fw = await Flawright.LaunchAsync(
        new LaunchOptions { ApplicationPath = "notepad.exe" },
        new FlawrightOptions
        {
            CloseBehavior = new DismissDialogCloseBehavior()
        });
    var page = await fw.Browser.NewPageAsync();

    // Open the Save As dialog via keyboard shortcut
    // Win11 Notepad: "#RichEditBox"; classic Win10: "class:Edit"
    await page.PressAsync("#RichEditBox", "Ctrl+Shift+S");

    // Wait for the Save As dialog to appear
    var dialog = await fw.Browser.WaitForPageAsync("Save As", timeout: TimeSpan.FromSeconds(10));
    Assert.NotNull(dialog);
}

Keyboard input

Use TypeAsync for realistic key-by-key input (useful for controls with key handlers) and PressAsync for named keys and chords.

[Fact]
public async Task Notepad_KeyboardInput()
{
    await using var fw = await Flawright.LaunchAsync(
        new LaunchOptions { ApplicationPath = "notepad.exe" },
        new FlawrightOptions
        {
            CloseBehavior = new DismissDialogCloseBehavior()
        });
    var page = await fw.Browser.NewPageAsync();

    // Win11 Notepad (WinUI3): "#RichEditBox"; classic Win10: "class:Edit"
    // Type character-by-character (key events fire for each character)
    await page.TypeAsync("#RichEditBox", "Hello");

    // Press Enter
    await page.PressAsync("#RichEditBox", "Enter");

    // Type more
    await page.TypeAsync("#RichEditBox", "World");

    // Select all and copy with a chord
    await page.PressAsync("#RichEditBox", "Ctrl+A");

    // Runs the configured DismissDialogCloseBehavior — dismisses the dialog, then exits.
    await fw.Browser.CloseAsync();
}

Custom FlawrightOptions

Configure timeouts and screenshot output at the session level.

await using var fw = await Flawright.LaunchAsync(
    new LaunchOptions { ApplicationPath = "myapp.exe" },
    new FlawrightOptions
    {
        DefaultTimeout       = TimeSpan.FromSeconds(10),
        DefaultRetryInterval = TimeSpan.FromMilliseconds(50),
        ScreenshotDirectory  = @"C:\TestOutput\Screenshots"
    });

var page = await fw.Browser.NewPageAsync();

// ScreenshotAsync with no path uses ScreenshotDirectory + auto-generated filename
byte[] png = await page.ScreenshotAsync();

Installer wizard: step through pages

A pattern for multi-step wizards. Each Next click advances the wizard; assertions verify you reached the expected step before proceeding.

[Fact]
public async Task InstallerWizard_StepsThroughToCompletion()
{
    await using var fw = await Flawright.LaunchAsync(new LaunchOptions
    {
        ApplicationPath = @"C:\Installers\MyApp.exe"
    });
    var page = await fw.Browser.NewPageAsync();

    // Step 1: Welcome page
    await page.Locator("name:Welcome to the MyApp Setup Wizard").Expect().ToBeVisibleAsync();
    await page.ClickAsync("name:Next");

    // Step 2: License agreement
    await page.Locator("name:License Agreement").Expect().ToBeVisibleAsync();
    await page.ClickAsync("name:I Agree");
    await page.ClickAsync("name:Next");

    // Step 3: Installation folder (accept default)
    await page.Locator("name:Choose Install Location").Expect().ToBeVisibleAsync();
    await page.ClickAsync("name:Install");

    // Step 4: Completion
    await page.Locator("name:Completing the MyApp Setup Wizard").Expect().ToBeVisibleAsync();
    await page.ClickAsync("name:Finish");
}

Note: Installer wizard control names are highly application-specific. Run Accessibility Insights against your installer to find exact Name values for each control.