Search code examples
c#integration-testingui-automationui-testingflaui

UI Automation - InvokePattern.Invoke() blocking execution until user interaction in integration tests


Edit: I went with the route of using FlaUI. I have updated the tag on this question to include it and also posted my solution as the answer.

I'm writing some integration tests for an app that monitors certain windows forms apps to see if those apps are running or crashed.

One of the requirements is to identify if it's showing error window or information window and act accordingly.

For eg:

To mock the case where an information window is showing, I created a test app (as shown above) with a button that would show some information window when clicked.

This is how my test looks like:

[Fact]
public async Task Can_Identify_An_Information_Window()
{
    //ARRANGE
    // My service that will start the app:
    await _myUIAutomationService.StartAppAsync("PathToMyTestApp");

    //Find the app window
    var appProcess = Process.GetProcessesByName("MyTestApp'sProcessName")[0];
    var appWindow = AutomationElement.FromHandle(appProcess.MainWindowHandle);

    //Find the buttons on the app window:
    var buttons = appWindow.FindAll(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button));
    //Grab the button that will show the information window and click it to show the information window:
    foreach (AutomationElement button in buttons)
    {
        if (button.Current.Name == "Open Info Window")
        {
            // click it to open the information window:
            var invoke = (InvokePattern)button.GetCurrentPattern(InvokePattern.Pattern);
            invoke.Invoke();
        }
    }

    //ACT
    // My service that will get the status of the app including the windows it's showing:
    var appstatus = await _myUIAutomationService.GetAppStatusAsync("MyTestApp'sName");
    var informationWindow = appstatus.InformationWindows[0];

    //ASSERT
    Assert.NotNull(informationWindow);
}

My issue with this test is that I cannot get past the invoke.Invoke(); line until I manually click "OK" on the information window that's showing.

I even tried the route of attaching the event handler to the button to handle this, but that didn't work out as I could never get into that OnInvoked handler method:

Automation.AddAutomationEventHandler(InvokePattern.InvokedEvent,
                                        button,
                                        TreeScope.Element,
                                        OnInvoked);

// Handler for information window showing button invoke event:
private void OnInvoked(object Sender, AutomationEventArgs E)
{
    // The control never reaches here.
}

I just need this test to be automatic without manual intervention.

Any help/ direction would be greatly appreciated.

Thanks!


Solution

  • I tried the way @stuartd suggested in the comment, but unfortunately that didn't work out as I ran into the same issue of not going anywhere when the .Invoke() method is (called until manual intervention is made).

    So I looked into this awesome nuget package called FlaUI by Roman Roemer and things went pretty smooth afterwards.

    enter image description here

    This is how my test looks now:

    using FlaUI.UIA3;
    using FlaUI.Core.AutomationElements;
    
    [Fact]
    public async Task Can_Identify_An_Information_Window_Async()
    {
        //ARRANGE
        // My service that will start the app:
        await _myUIAutomationService.StartAppAsync("PathToMyTestApp");
    
        //Create FLA UI core application (i.e. myApp) from the Process object (i.e. appProcess).
        var appProcess = Process.GetProcessesByName("MyTestApp'sProcessName")[0];
        var myApp = new FlaUI.Core.Application(appProcess);
    
        using (var automation = new UIA3Automation())
        {
            var window = myApp.GetMainWindow(automation);
            window.WaitUntilClickable(TimeSpan.FromSeconds(2)); //Wait with a timeout duration of 2 seconds.
    
            //Find the button
            var myBtn = window.FindFirstDescendant(cf => cf.ByText("Open Info Window"));
            myBtn.Click(true); // This will show the information window. The true flag show the mouse pointer actually finding the button and clicking it. It's very COOL!
    
            //ACT
            //Get the status
            var appstatus = await _myUIAutomationService.GetAppStatusAsync("MyTestApp'sName");
            var informationWindow = appstatus.InformationWindows[0];
    
            //ASSERT
            Assert.NotNull(informationWindow);
        }
    }