Skip to content

Commit

Permalink
UITest/InfoMan: first draft of ShowContactsTest; extend App to suppor…
Browse files Browse the repository at this point in the history
…t Automation Ids for dynamic menus
  • Loading branch information
jbe2277 committed Aug 18, 2024
1 parent ca41550 commit 2b5dd05
Show file tree
Hide file tree
Showing 25 changed files with 145 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Xunit.Abstractions;
using Xunit;
using UITest.SystemViews;
using FlaUI.Core.Input;

namespace UITest.BookLibrary.Tests;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Xunit.Abstractions;
using Xunit;

namespace UITest.InformationManager.Tests;

public class AddressBookTest(ITestOutputHelper log) : UITest(log)
{
[Fact]
public void ShowContactsTest() => Run(() =>
{
Launch();
var window = GetShellWindow();
window.RootTreeItem.ContactsNode.Click();

var contactListView = window.ContactLayoutView.ContactListView;
Assert.Equal(5, contactListView.ContactList.Items.Length);

window.ExitCommand.Click();
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public void AboutTest() => Run(() =>
{
Launch();
var window = GetShellWindow();
window.AboutButton.Click();
window.AboutCommand.Click();

var messageBox = window.FirstModalWindow().As<MessageBox>();
Assert.Equal("Waf Information Manager", messageBox.Title);
Expand All @@ -22,6 +22,6 @@ public void AboutTest() => Run(() =>
Capture.Screen().ToFile(GetScreenshotFile("About"));
messageBox.Buttons[0].Click();

window.ExitButton.Click();
window.ExitCommand.Click();
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using FlaUI.Core.AutomationElements;
using FlaUI.Core;

namespace UITest.InformationManager.Views;

public class ContactLayoutView(FrameworkAutomationElementBase element) : AutomationElement(element)
{
public ContactListView ContactListView => this.Find("ContactListView").As<ContactListView>();

public ContactView ContactView => this.Find("ContactView").As<ContactView>();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using FlaUI.Core.AutomationElements;
using FlaUI.Core;

namespace UITest.InformationManager.Views;

public class ContactListView(FrameworkAutomationElementBase element) : AutomationElement(element)
{
public ListBox ContactList => this.Find("ContactList").AsListBox();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using FlaUI.Core.AutomationElements;
using FlaUI.Core;

namespace UITest.InformationManager.Views;

public class ContactView(FrameworkAutomationElementBase element) : AutomationElement(element)
{
}
24 changes: 22 additions & 2 deletions src/Samples.UITest/InformationManager.Test/Views/ShellWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,27 @@ namespace UITest.InformationManager.Views;

public class ShellWindow(FrameworkAutomationElementBase element) : Window(element)
{
public Button AboutButton => this.Find("AboutButton").AsButton();
public Button NewEmailCommand => this.Find("NewEmailCommand").AsButton();

public Button ExitButton => this.Find("ExitButton").AsButton();
public Button DeleteEmailCommand => this.Find("DeleteEmailCommand").AsButton();

public Button EmailAccountsCommand => this.Find("EmailAccountsCommand").AsButton();

public Button NewContactCommand => this.Find("NewContactCommand").AsButton();

public Button DeleteCommand => this.Find("DeleteCommand").AsButton();

public Button AboutCommand => this.Find("AboutCommand").AsButton();

public Button ExitCommand => this.Find("ExitCommand").AsButton();


public NavigationRootTreeItem RootTreeItem => this.Find("RootTreeItem").As<NavigationRootTreeItem>();

public ContactLayoutView ContactLayoutView => this.Find("ContactLayoutView").As<ContactLayoutView>();
}

public class NavigationRootTreeItem(FrameworkAutomationElementBase element) : TreeItem(element)
{
public TreeItem ContactsNode => this.Find("ContactsNode").AsTreeItem();
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void Initialize()
}
else Root = (AddressBookRoot)serializer.Value.ReadObject(stream)!;
}
navigationService.AddNavigationNode("Contacts", ShowAddressBook, CloseAddressBook, 2, 1);
navigationService.AddNavigationNode("ContactsNode", "Contacts", ShowAddressBook, CloseAddressBook, 2, 1);
}

public void Run() { }
Expand Down Expand Up @@ -78,8 +78,8 @@ private void ShowAddressBook()
activeContactController.Initialize();
activeContactController.Run();

var uiNewContactCommand = new ToolBarCommand(activeContactController.NewContactCommand, "_New contact", "Creates a new contact.");
var uiDeleteCommand = new ToolBarCommand(activeContactController.DeleteContactCommand, "_Delete", "Deletes the selected contact.");
var uiNewContactCommand = new ToolBarCommand("NewContactCommand", activeContactController.NewContactCommand, "_New contact", "Creates a new contact.");
var uiDeleteCommand = new ToolBarCommand("DeleteCommand", activeContactController.DeleteContactCommand, "_Delete", "Deletes the selected contact.");
shellService.AddToolBarCommands([ uiNewContactCommand, uiDeleteCommand ]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
xmlns:dd="clr-namespace:Waf.InformationManager.AddressBook.Modules.Presentation.DesignData"
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="700"
mc:Ignorable="d" d:DesignHeight="400" d:DesignWidth="700" AutomationProperties.AutomationId="ContactLayoutView"
d:DataContext="{d:DesignInstance dd:SampleContactLayoutViewModel, IsDesignTimeCreatable=True}">
<Grid>
<Grid.ColumnDefinitions>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
xmlns:dd="clr-namespace:Waf.InformationManager.AddressBook.Modules.Presentation.DesignData"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="250" d:DesignWidth="500"
mc:Ignorable="d" d:DesignHeight="250" d:DesignWidth="500" AutomationProperties.AutomationId="ContactListView"
d:DataContext="{d:DesignInstance dd:SampleContactListViewModel, IsDesignTimeCreatable=True}">
<Grid>
<Grid.RowDefinitions>
Expand All @@ -18,7 +18,7 @@

<ListBox x:Name="contactsBox" ItemsSource="{Binding Contacts}" SelectedItem="{Binding SelectedContact}" Grid.Row="1" HorizontalContentAlignment="Stretch"
BorderThickness="0,1,0,0" BorderBrush="{x:Static SystemColors.ActiveBorderBrush}"
ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
ScrollViewer.VerticalScrollBarVisibility="Visible" ScrollViewer.HorizontalScrollBarVisibility="Disabled" AutomationProperties.AutomationId="ContactList">
<ListBox.InputBindings>
<KeyBinding Command="{Binding DeleteContactCommand}" Key="Del"/>
</ListBox.InputBindings>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
xmlns:dd="clr-namespace:Waf.InformationManager.AddressBook.Modules.Presentation.DesignData"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="500" d:DesignWidth="300"
mc:Ignorable="d" d:DesignHeight="500" d:DesignWidth="300" AutomationProperties.AutomationId="ContactView"
d:DataContext="{d:DesignInstance dd:SampleContactViewModel, IsDesignTimeCreatable=True}">

<Grid Margin="11,11,22,11">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ public void Initialize()
}
emailAccountsController.Root = Root;

var node = navigationService.AddNavigationNode("Inbox", ShowInbox, CloseCurrentView, 1, 1);
var node = navigationService.AddNavigationNode("InboxNode", "Inbox", ShowInbox, CloseCurrentView, 1, 1);
itemCountSynchronizers.Add(new(node, Root.Inbox));
node = navigationService.AddNavigationNode("Outbox", ShowOutbox, CloseCurrentView, 1, 2);
node = navigationService.AddNavigationNode("OutboxNode", "Outbox", ShowOutbox, CloseCurrentView, 1, 2);
itemCountSynchronizers.Add(new(node, Root.Outbox));
node = navigationService.AddNavigationNode("Sent", ShowSentEmails, CloseCurrentView, 1, 3);
node = navigationService.AddNavigationNode("SentNode", "Sent", ShowSentEmails, CloseCurrentView, 1, 3);
itemCountSynchronizers.Add(new(node, Root.Sent));
node = navigationService.AddNavigationNode("Drafts", ShowDrafts, CloseCurrentView, 1, 4);
node = navigationService.AddNavigationNode("DraftsNode", "Drafts", ShowDrafts, CloseCurrentView, 1, 4);
itemCountSynchronizers.Add(new(node, Root.Drafts));
node = navigationService.AddNavigationNode("Deleted", ShowDeletedEmails, CloseCurrentView, 1, 5);
node = navigationService.AddNavigationNode("DeletedNode", "Deleted", ShowDeletedEmails, CloseCurrentView, 1, 5);
itemCountSynchronizers.Add(new(node, Root.Deleted));
}

Expand All @@ -88,9 +88,9 @@ private void ShowEmails(EmailFolder emailFolder)
activeEmailFolderController.EmailFolder = emailFolder;
activeEmailFolderController.Initialize();
activeEmailFolderController.Run();
var uiNewEmailCommand = new ToolBarCommand(newEmailCommand, "_New email", "Creates a new email.");
var uiDeleteEmailCommand = new ToolBarCommand(activeEmailFolderController.DeleteEmailCommand, "_Delete", "Deletes the selected email.");
var uiEmailAccountsCommand = new ToolBarCommand(emailAccountsController.EmailAccountsCommand, "_Email accounts", "Opens a window that shows the email accounts.");
var uiNewEmailCommand = new ToolBarCommand("NewEmailCommand", newEmailCommand, "_New email", "Creates a new email.");
var uiDeleteEmailCommand = new ToolBarCommand("DeleteEmailCommand", activeEmailFolderController.DeleteEmailCommand, "_Delete", "Deletes the selected email.");
var uiEmailAccountsCommand = new ToolBarCommand("EmailAccountsCommand", emailAccountsController.EmailAccountsCommand, "_Email accounts", "Opens a window that shows the email accounts.");
shellService.AddToolBarCommands([ uiNewEmailCommand, uiDeleteEmailCommand, uiEmailAccountsCommand ]);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
/// <summary>Represents a navigation node.</summary>
public interface INavigationNode : INotifyPropertyChanged
{
/// <summary>ID used for UI Automation.</summary>
string AutomationId { get; }

/// <summary>Gets the name.</summary>
string Name { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
public interface INavigationService
{
/// <summary>Adds a navigation node in the navigation view of the shell.</summary>
/// <param name="automationId">ID used for UI Automation.</param>
/// <param name="name">The name of the node.</param>
/// <param name="showAction">The show action which is called when the user selects the node.</param>
/// <param name="closeAction">The close action which is called when the node is deselected.</param>
/// <param name="group">The group number defines the position in the navigation view. All items with the same group number are considered
/// to be in the same group. The navigation list is ordered from lower to higher numbers.</param>
/// <param name="order">The order defines the position in the group. The navigation list is ordered from lower to higher numbers.</param>
/// <returns>The created navigation node.</returns>
INavigationNode AddNavigationNode(string name, Action showAction, Action closeAction, double group, double order);
INavigationNode AddNavigationNode(string automationId, string name, Action showAction, Action closeAction, double group, double order);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
namespace Waf.InformationManager.Infrastructure.Interfaces.Applications;

/// <summary>Defines a tool bar command</summary>
/// <param name="AutomationId">ID used for UI Automation.</param>
/// <param name="Command">The command which is invoked when the user clicks on the tool bar button.</param>
/// <param name="Text">The text of the tool bar button.</param>
/// <param name="ToolTip">The tooltip of the tool bar button.</param>
public record ToolBarCommand(ICommand Command, string Text, string? ToolTip = null);
public record ToolBarCommand(string AutomationId, ICommand Command, string Text, string? ToolTip = null);
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[assembly: SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed", Scope = "member", Target = "~M:Waf.InformationManager.Infrastructure.Interfaces.Applications.ToolBarCommand.#ctor(System.Windows.Input.ICommand,System.String,System.String)")]

Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

namespace Test.InformationManager.Infrastructure.Modules.Applications.Services;

public class MockNavigationNode(string name, Action showAction, Action closeAction, double group, double order) : Model, INavigationNode
public class MockNavigationNode(string automationId, string name, Action showAction, Action closeAction, double group, double order) : Model, INavigationNode
{
public string AutomationId { get; } = automationId;

public string Name { get; } = name;

public Action ShowAction { get; } = showAction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ public class MockNavigationService : INavigationService

public IReadOnlyList<MockNavigationNode> NavigationNodes => navigationNodes;

public INavigationNode AddNavigationNode(string name, Action showAction, Action closeAction, double group, double order)
public INavigationNode AddNavigationNode(string automationId, string name, Action showAction, Action closeAction, double group, double order)
{
var node = new MockNavigationNode(name, showAction, closeAction, group, order);
var node = new MockNavigationNode(automationId, name, showAction, closeAction, group, order);
navigationNodes.Add(node);
return node;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ public class NavigationServiceTest
public void AddNavigationNodesWithWrongParameters()
{
var navigationService = new NavigationService();
AssertHelper.ExpectedException<ArgumentException>(() => navigationService.AddNavigationNode(null!, null!, null!, 0, 0));
AssertHelper.ExpectedException<ArgumentNullException>(() => navigationService.AddNavigationNode("Node 1", null!, null!, 0, 0));
AssertHelper.ExpectedException<ArgumentNullException>(() => navigationService.AddNavigationNode("Node 1", () => { }, null!, 0, 0));
AssertHelper.ExpectedException<ArgumentException>(() => navigationService.AddNavigationNode("Node 1", () => { }, () => { }, -1, -1));
AssertHelper.ExpectedException<ArgumentException>(() => navigationService.AddNavigationNode("Node 1", () => { }, () => { }, 0, -1));
AssertHelper.ExpectedException<ArgumentNullException>(() => navigationService.AddNavigationNode(null!, null!, null!, null!, 0, 0));
AssertHelper.ExpectedException<ArgumentException>(() => navigationService.AddNavigationNode("", null!, null!, null!, 0, 0));
AssertHelper.ExpectedException<ArgumentNullException>(() => navigationService.AddNavigationNode("N1", null!, null!, null!, 0, 0));
AssertHelper.ExpectedException<ArgumentException>(() => navigationService.AddNavigationNode("N1", "", null!, null!, 0, 0));
AssertHelper.ExpectedException<ArgumentNullException>(() => navigationService.AddNavigationNode("N1", "Node 1", null!, null!, 0, 0));
AssertHelper.ExpectedException<ArgumentNullException>(() => navigationService.AddNavigationNode("N1", "Node 1", () => { }, null!, 0, 0));
AssertHelper.ExpectedException<ArgumentOutOfRangeException>(() => navigationService.AddNavigationNode("N1", "Node 1", () => { }, () => { }, -1, -1));
AssertHelper.ExpectedException<ArgumentOutOfRangeException>(() => navigationService.AddNavigationNode("N1", "Node 1", () => { }, () => { }, 0, -1));
}

[TestMethod]
Expand All @@ -28,7 +31,7 @@ public void AddNavigationNode()
bool closeActionCalled = false;
void CloseAction() => closeActionCalled = true;

var node = (NavigationNode)navigationService.AddNavigationNode("Node 1", ShowAction, CloseAction, 3, 7);
var node = (NavigationNode)navigationService.AddNavigationNode("N1", "Node 1", ShowAction, CloseAction, 3, 7);

Assert.AreEqual("Node 1", node.Name);
Assert.AreEqual(3, node.Group);
Expand Down Expand Up @@ -65,10 +68,10 @@ public void AddNavigationNodes()
static void ShowAction() { }
static void CloseAction() { }

var nodeB2 = navigationService.AddNavigationNode("Node B1", ShowAction, CloseAction, 1, 1);
var nodeA1 = navigationService.AddNavigationNode("Node A1", ShowAction, CloseAction, 0, 0);
var nodeA2 = navigationService.AddNavigationNode("Node A2", ShowAction, CloseAction, 0, 1);
var nodeB1 = navigationService.AddNavigationNode("Node B1", ShowAction, CloseAction, 1, 0);
var nodeB2 = navigationService.AddNavigationNode("B2", "Node B2", ShowAction, CloseAction, 1, 1);
var nodeA1 = navigationService.AddNavigationNode("A1", "Node A1", ShowAction, CloseAction, 0, 0);
var nodeA2 = navigationService.AddNavigationNode("A2", "Node A2", ShowAction, CloseAction, 0, 1);
var nodeB1 = navigationService.AddNavigationNode("B1", "Node B1", ShowAction, CloseAction, 1, 0);

AssertHelper.SequenceEqual(new[] { nodeA1, nodeA2, nodeB1, nodeB2 }, navigationService.NavigationNodes);
Assert.IsFalse(((NavigationNode)nodeA1).IsFirstItemOfNewGroup);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public void ToolBarCommandsDelegation()
var emptyCommand = new DelegateCommand(() => { });
var newToolBarCommands = new[]
{
new ToolBarCommand(emptyCommand, "Command 1"),
new ToolBarCommand(emptyCommand, "Command 2")
new ToolBarCommand("C1", emptyCommand, "Command 1"),
new ToolBarCommand("C2", emptyCommand, "Command 2")
};

Assert.IsFalse(mockShellViewModel.ToolBarCommands.Any());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ public void ToolBarCommandsDelegation()
var emptyCommand = new DelegateCommand(() => { });
var newToolBarCommands = new[]
{
new ToolBarCommand(emptyCommand, "Command 1"),
new ToolBarCommand(emptyCommand, "Command 2")
new ToolBarCommand("C1", emptyCommand, "Command 1"),
new ToolBarCommand("C2", emptyCommand, "Command 2")
};

Assert.IsFalse(shellView.ToolBarCommands.Any());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,22 @@ public class NavigationNode : Model, INavigationNode
private bool isSelected;
private bool isFirstItemOfNewGroup;

public NavigationNode(string name, Action showAction, Action closeAction, double group, double order)
public NavigationNode(string automationId, string name, Action showAction, Action closeAction, double group, double order)
{
if (string.IsNullOrEmpty(name)) throw new ArgumentException("name must not be null or empty.", nameof(name));
if (group < 0) throw new ArgumentException("group must be equal or greater than 0.", nameof(group));
if (order < 0) throw new ArgumentException("order must be equal or greater than 0.", nameof(order));
ArgumentException.ThrowIfNullOrEmpty(automationId);
ArgumentException.ThrowIfNullOrEmpty(name);
ArgumentOutOfRangeException.ThrowIfLessThan(group, 0);
ArgumentOutOfRangeException.ThrowIfLessThan(order, 0);
AutomationId = automationId;
Name = name;
this.showAction = showAction ?? throw new ArgumentNullException(nameof(showAction));
this.closeAction = closeAction ?? throw new ArgumentNullException(nameof(closeAction));
Group = group;
Order = order;
}

public string AutomationId { get; }

public string Name { get; }

public double Group { get; }
Expand Down
Loading

0 comments on commit 2b5dd05

Please sign in to comment.