Skip to content

Commit f7729b2

Browse files
committed
Add AddressBookTest; extend BookLibraryTest
1 parent 77068f7 commit f7729b2

File tree

10 files changed

+281
-13
lines changed

10 files changed

+281
-13
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using FlaUI.Core;
2+
using FlaUI.Core.AutomationElements;
3+
using FlaUI.Core.Definitions;
4+
5+
namespace UITest.BookLibrary.Controls;
6+
7+
public class HyperlinkGridCell(FrameworkAutomationElementBase element) : GridCell(element)
8+
{
9+
public AutomationElement RootElement => FindFirstChild();
10+
11+
public AutomationElement Hyperlink => this.Find(x => x.ByControlType(ControlType.Hyperlink));
12+
13+
public Label Label => Hyperlink.Find(x => x.ByControlType(ControlType.Text)).AsLabel();
14+
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
using FlaUI.Core;
22
using FlaUI.Core.AutomationElements;
3+
using FlaUI.Core.Definitions;
34

45
namespace UITest.BookLibrary.Controls;
56

67
public class TextGridCell(FrameworkAutomationElementBase element) : GridCell(element)
78
{
8-
public Label Label => this.Find(x => x.ByControlType(FlaUI.Core.Definitions.ControlType.Text)).AsLabel();
9+
public Label Label => this.Find(x => x.ByControlType(ControlType.Text)).AsLabel();
910
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
using FlaUI.Core.AutomationElements;
2+
using UITest.BookLibrary.Views;
3+
using Xunit.Abstractions;
4+
using Xunit;
5+
using UITest.SystemViews;
6+
7+
namespace UITest.BookLibrary.Tests;
8+
9+
public class AddressBookTest(ITestOutputHelper log) : UITest(log)
10+
{
11+
[Fact]
12+
public void SearchPersonListAndChangeEntriesTest() => Run(() =>
13+
{
14+
Launch();
15+
var window = GetShellWindow();
16+
window.TabControl.AddressBookTabItem.Select();
17+
var personListView = window.TabControl.AddressBookTabItem.PersonListView;
18+
var personView = window.TabControl.AddressBookTabItem.PersonView;
19+
20+
var rowCount = personListView.PersonDataGrid.RowCount;
21+
Assert.Equal(4, rowCount);
22+
Log.WriteLine($"List of Persons ({rowCount}):");
23+
for (int i = 0; i < rowCount; i++)
24+
{
25+
Log.WriteLine($"{i:00}: {personListView.PersonDataGrid.GetRowByIndex(i).As<PersonGridRow>().FirstnameCell.Label.Text}");
26+
}
27+
28+
personListView.SearchBox.Text = "H";
29+
Assert.Equal(2, personListView.PersonDataGrid.RowCount);
30+
personListView.SearchBox.Text = "Ha";
31+
Assert.Equal(1, personListView.PersonDataGrid.RowCount);
32+
var personRow1 = personListView.PersonDataGrid.GetRowByIndex(0).As<PersonGridRow>();
33+
personRow1.Select();
34+
35+
AssertEqual("Harry", personRow1.FirstnameCell.Name, personView.FirstnameTextBox.Text);
36+
AssertEqual("Potter", personRow1.LastnameCell.Name, personView.LastnameTextBox.Text);
37+
AssertEqual("[email protected]", personRow1.EmailCell.Label.Text, personView.EmailTextBox.Text);
38+
39+
personView.FirstnameTextBox.Text = "TFirstname";
40+
Assert.Equal("TFirstname", personRow1.FirstnameCell.Name);
41+
personView.LastnameTextBox.Text = "TLastname";
42+
Assert.Equal("TLastname", personRow1.LastnameCell.Name);
43+
personView.EmailTextBox.Text = "[email protected]";
44+
Assert.Equal("[email protected]", personRow1.EmailCell.Label.Text);
45+
46+
window.Close();
47+
var messageBox = window.FirstModalWindow().As<MessageBox>(); // MessageBox that asks user to save the changes
48+
messageBox.Buttons[1].Click(); // No button
49+
});
50+
51+
[Fact]
52+
public void AddAndRemoveEntriesTest() => Run(() =>
53+
{
54+
Launch();
55+
var window = GetShellWindow();
56+
window.TabControl.AddressBookTabItem.Select();
57+
var personListView = window.TabControl.AddressBookTabItem.PersonListView;
58+
var personView = window.TabControl.AddressBookTabItem.PersonView;
59+
60+
Assert.Equal(4, personListView.PersonDataGrid.RowCount);
61+
personListView.AddButton.Click();
62+
Assert.Equal(5, personListView.PersonDataGrid.RowCount);
63+
var newRow = personListView.PersonDataGrid.SelectedItem.As<PersonGridRow>();
64+
Assert.Equal(personListView.PersonDataGrid.Rows[0], newRow);
65+
66+
// ItemStatus contains the validation error message or string.Empty if no error exists
67+
AssertEqual("", personView.FirstnameTextBox.Text, newRow.FirstnameCell.Label.Text);
68+
AssertEqual("Firstname is mandatory.", personView.FirstnameTextBox.ItemStatus, newRow.FirstnameCell.Label.ItemStatus);
69+
AssertEqual("", personView.LastnameTextBox.Text, newRow.LastnameCell.Label.Text);
70+
AssertEqual("Lastname is mandatory.", personView.LastnameTextBox.ItemStatus, newRow.LastnameCell.Label.ItemStatus);
71+
AssertEqual("", personView.EmailTextBox.ItemStatus, newRow.EmailCell.Label.Text);
72+
73+
personView.FirstnameTextBox.Text = "AFirstname";
74+
Assert.Equal("AFirstname", newRow.FirstnameCell.Name);
75+
AssertEqual("", personView.FirstnameTextBox.ItemStatus, newRow.FirstnameCell.Label.ItemStatus);
76+
personView.LastnameTextBox.Text = "ALastname";
77+
Assert.Equal("ALastname", newRow.LastnameCell.Name);
78+
AssertEqual("", personView.LastnameTextBox.ItemStatus, newRow.LastnameCell.Label.ItemStatus);
79+
80+
var lastRow = personListView.PersonDataGrid.GetRowByIndex(personListView.PersonDataGrid.RowCount - 1).As<PersonGridRow>();
81+
Assert.False(lastRow.IsOffscreen);
82+
Assert.StartsWith("Ron", lastRow.FirstnameCell.Name);
83+
84+
var lastNotRemovedRow = personListView.PersonDataGrid.GetRowByIndex(personListView.PersonDataGrid.RowCount - 3);
85+
personListView.PersonDataGrid.Select(personListView.PersonDataGrid.RowCount - 2);
86+
personListView.PersonDataGrid.AddToSelection(personListView.PersonDataGrid.RowCount - 1);
87+
personListView.RemoveButton.Click();
88+
Assert.Equal(3, personListView.PersonDataGrid.RowCount);
89+
Assert.Equal(lastNotRemovedRow, personListView.PersonDataGrid.SelectedItem);
90+
Assert.Equal(lastNotRemovedRow, personListView.PersonDataGrid.Rows[^1]);
91+
92+
window.DataMenu.Click();
93+
window.DataMenu.SaveMenuItem.Click();
94+
window.Close();
95+
96+
97+
Launch(resetSettings: false, resetDatabase: false);
98+
window = GetShellWindow();
99+
window.TabControl.AddressBookTabItem.Select();
100+
personListView = window.TabControl.AddressBookTabItem.PersonListView;
101+
Assert.Equal(3, personListView.PersonDataGrid.RowCount);
102+
103+
personListView.PersonDataGrid.Select(0);
104+
for (int i = 1; i < personListView.PersonDataGrid.RowCount; i++) personListView.PersonDataGrid.AddToSelection(i);
105+
Assert.Equal(3, personListView.PersonDataGrid.SelectedItems.Length);
106+
Assert.True(personListView.RemoveButton.IsEnabled);
107+
personListView.RemoveButton.Click();
108+
Assert.False(personListView.RemoveButton.IsEnabled);
109+
Assert.Null(personListView.PersonDataGrid.SelectedItem);
110+
111+
window.Close();
112+
var messageBox = window.FirstModalWindow().As<MessageBox>(); // MessageBox that asks user to save the changes
113+
messageBox.Buttons[1].Click(); // No button
114+
});
115+
116+
[Fact]
117+
public void ValidateFieldsTest() => Run(() =>
118+
{
119+
Launch();
120+
var window = GetShellWindow();
121+
window.TabControl.AddressBookTabItem.Select();
122+
var personListView = window.TabControl.AddressBookTabItem.PersonListView;
123+
var personView = window.TabControl.AddressBookTabItem.PersonView;
124+
var row1 = personListView.PersonDataGrid.SelectedItem.As<PersonGridRow>();
125+
126+
var text31 = new string('a', 31);
127+
var text101 = new string('a', 101);
128+
129+
// Note: "Required" validation of Title and Author are covered by the AddAndRemoveEntriesTest test
130+
// ItemStatus contains the validation error message or string.Empty if no error exists
131+
personView.FirstnameTextBox.Text = text31;
132+
AssertEqual("Firstname can contain 30 characters at maximum.", personView.FirstnameTextBox.ItemStatus, row1.FirstnameCell.Label.ItemStatus);
133+
134+
personView.LastnameTextBox.Text = text31;
135+
AssertEqual("Lastname can contain 30 characters at maximum.", personView.LastnameTextBox.ItemStatus, row1.LastnameCell.Label.ItemStatus);
136+
137+
personView.EmailTextBox.Text = text101;
138+
Assert.Equal("Email can contain 100 characters at maximum.\r\nThe provided email address is invalid.", personView.EmailTextBox.ItemStatus);
139+
personView.EmailTextBox.Text = "test";
140+
Assert.Equal("The provided email address is invalid.", personView.EmailTextBox.ItemStatus);
141+
personView.EmailTextBox.Text = "[email protected]";
142+
Assert.Empty(personView.EmailTextBox.ItemStatus);
143+
144+
window.Close();
145+
var messageBox = window.FirstModalWindow().As<MessageBox>(); // MessageBox that asks user to really close the app although unsafed changes exist
146+
messageBox.Buttons[0].Click(); // Yes button
147+
});
148+
149+
private static void AssertEqual(string expected, string actual1, string actual2)
150+
{
151+
Assert.Equal(expected, actual1);
152+
Assert.Equal(expected, actual2);
153+
}
154+
}

src/Samples.UITest/BookLibrary.Test/Tests/BookLibraryTest.cs

+45-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public void SearchBookListAndChangeEntriesTest() => Run(() =>
2323
{
2424
Log.WriteLine($"{i:00}: {bookListView.BookDataGrid.GetRowByIndex(i).As<BookGridRow>().TitleCell.Label.Text}");
2525
}
26-
26+
2727
bookListView.SearchBox.Text = "Ha";
2828
Assert.Equal(13, bookListView.BookDataGrid.RowCount);
2929
bookListView.SearchBox.Text = "Harr";
@@ -144,9 +144,53 @@ public void AddAndRemoveEntriesTest() => Run(() =>
144144
messageBox.Buttons[1].Click(); // No button
145145
});
146146

147+
[Fact]
148+
public void ValidateFieldsTest() => Run(() =>
149+
{
150+
Launch();
151+
var window = GetShellWindow();
152+
var bookListView = window.TabControl.BookLibraryTabItem.BookListView;
153+
var bookView = window.TabControl.BookLibraryTabItem.BookView;
154+
var row1 = bookListView.BookDataGrid.SelectedItem.As<BookGridRow>();
155+
156+
var text101 = new string('a', 101);
157+
158+
// Note: "Required" validation of Title and Author are covered by the AddAndRemoveEntriesTest test
159+
// ItemStatus contains the validation error message or string.Empty if no error exists
160+
bookView.TitleTextBox.Text = text101;
161+
AssertEqual("Title can contain 100 characters at maximum.", bookView.TitleTextBox.ItemStatus, row1.TitleCell.Label.ItemStatus);
162+
163+
bookView.AuthorTextBox.Text = text101;
164+
AssertEqual("Author can contain 100 characters at maximum.", bookView.AuthorTextBox.ItemStatus, row1.AuthorCell.Label.ItemStatus);
165+
166+
bookView.PublisherTextBox.Text = text101;
167+
Assert.Equal("Publisher can contain 100 characters at maximum.", bookView.PublisherTextBox.ItemStatus);
168+
169+
bookView.PublishDatePicker.SelectedDate = new DateTime(1752, 12, 31);
170+
Assert.Equal("Value for 12/31/1752 12:00:00 AM must be between 1/1/1753 12:00:00 AM and 12/31/9999 12:00:00 AM.", bookView.PublishDatePicker.ItemStatus);
171+
bookView.PublishDatePicker.SelectedDate = new DateTime(2025, 1, 1);
172+
Assert.Empty(bookView.PublishDatePicker.ItemStatus);
173+
bookView.PublishDatePicker.SelectedDate = new DateTime(9999, 12, 31, 23, 59, 59);
174+
Assert.Equal("Value for 12/31/9999 11:59:59 PM must be between 1/1/1753 12:00:00 AM and 12/31/9999 12:00:00 AM.", bookView.PublishDatePicker.ItemStatus);
175+
176+
bookView.IsbnTextBox.Text = new string('a', 15);
177+
Assert.Equal("Isbn can contain 14 characters at maximum.", bookView.IsbnTextBox.ItemStatus);
178+
179+
bookView.PagesTextBox.Text = "-1";
180+
Assert.Equal("Pages must be equal or larger than 0.", bookView.PagesTextBox.ItemStatus);
181+
182+
window.Close();
183+
var messageBox = window.FirstModalWindow().As<MessageBox>(); // MessageBox that asks user to really close the app although unsafed changes exist
184+
messageBox.Buttons[0].Click(); // Yes button
185+
});
186+
147187
private static void AssertEqual(string expected, string actual1, string actual2)
148188
{
149189
Assert.Equal(expected, actual1);
150190
Assert.Equal(expected, actual2);
151191
}
192+
193+
194+
195+
// TODO: remove Person that was assigned for "Lend To"
152196
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using FlaUI.Core.AutomationElements;
2+
using FlaUI.Core;
3+
using UITest.BookLibrary.Controls;
4+
5+
namespace UITest.BookLibrary.Views;
6+
7+
public class PersonListView(FrameworkAutomationElementBase element) : AutomationElement(element)
8+
{
9+
public Button AddButton => this.Find("AddButton").AsButton();
10+
11+
public Button RemoveButton => this.Find("RemoveButton").AsButton();
12+
13+
public TextBox SearchBox => this.Find("SearchBox").AsTextBox();
14+
15+
public Grid PersonDataGrid => this.Find("PersonDataGrid").AsGrid();
16+
}
17+
18+
public class PersonGridRow(FrameworkAutomationElementBase element) : GridRow(element)
19+
{
20+
public TextGridCell FirstnameCell => Cells[0].As<TextGridCell>();
21+
22+
public TextGridCell LastnameCell => Cells[1].As<TextGridCell>();
23+
24+
public HyperlinkGridCell EmailCell => Cells[2].As<HyperlinkGridCell>();
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using FlaUI.Core.AutomationElements;
2+
using FlaUI.Core;
3+
4+
namespace UITest.BookLibrary.Views;
5+
6+
public class PersonView(FrameworkAutomationElementBase element) : AutomationElement(element)
7+
{
8+
public TextBox FirstnameTextBox => this.Find("FirstnameTextBox").AsTextBox();
9+
10+
public TextBox LastnameTextBox => this.Find("LastnameTextBox").AsTextBox();
11+
12+
public TextBox EmailTextBox => this.Find("EmailTextBox").AsTextBox();
13+
14+
public Button CreateNewEmailButton => this.Find("CreateNewEmailButton").AsButton();
15+
}

src/Samples.UITest/BookLibrary.Test/Views/ShellWindow.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class TabControl(FrameworkAutomationElementBase element) : Tab(element)
2828
{
2929
public BookLibraryTabItem BookLibraryTabItem => this.Find("BookLibraryTabItem").As<BookLibraryTabItem>();
3030

31-
public TabItem AddressBookTabItem => this.Find("AddressBookTabItem").AsTabItem();
31+
public AddressBookTabItem AddressBookTabItem => this.Find("AddressBookTabItem").As<AddressBookTabItem>();
3232

3333
public TabItem ReportingTabItem => this.Find("ReportingTabItem").AsTabItem();
3434
}
@@ -38,4 +38,11 @@ public class BookLibraryTabItem(FrameworkAutomationElementBase element) : TabIte
3838
public BookListView BookListView => this.Find("BookListView").As<BookListView>();
3939

4040
public BookView BookView => this.Find("BookView").As<BookView>();
41+
}
42+
43+
public class AddressBookTabItem(FrameworkAutomationElementBase element) : TabItem(element)
44+
{
45+
public PersonListView PersonListView => this.Find("PersonListView").As<PersonListView>();
46+
47+
public PersonView PersonView => this.Find("PersonView").As<PersonView>();
4148
}

src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Resources/ControlResources.xaml

+8
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,14 @@
5757

5858
<Style TargetType="DatePicker">
5959
<Setter Property="Validation.ErrorTemplate" Value="{StaticResource ErrorTemplate}"/>
60+
<Setter Property="AutomationProperties.ItemStatus">
61+
<Setter.Value>
62+
<MultiBinding Converter="{StaticResource ValidationErrorsConverter}">
63+
<Binding Path="(Validation.Errors)" RelativeSource="{RelativeSource Self}"/>
64+
<Binding Path="(Validation.Errors).Count" RelativeSource="{RelativeSource Self}"/>
65+
</MultiBinding>
66+
</Setter.Value>
67+
</Setter>
6068
</Style>
6169

6270
<Style TargetType="ToolBarTray">

src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/PersonListView.xaml

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,28 @@
77
xmlns:waf="http://waf.codeplex.com/schemas"
88
xmlns:dd="clr-namespace:Waf.BookLibrary.Library.Presentation.DesignData"
99
mc:Ignorable="d" d:DataContext="{d:DesignInstance dd:SamplePersonListViewModel, IsDesignTimeCreatable=True}"
10-
d:DesignWidth="500" d:DesignHeight="150"
10+
d:DesignWidth="500" d:DesignHeight="150" AutomationProperties.AutomationId="PersonListView"
1111
waf:ValidationHelper.IsEnabled="true" waf:ValidationHelper.IsValid="{Binding IsValid, Mode=OneWayToSource}">
1212

1313
<DockPanel>
1414
<ToolBarTray DockPanel.Dock="Top" Margin="0,1,0,1">
1515
<ToolBar>
16-
<Button Command="{Binding AddNewCommand}">
16+
<Button Command="{Binding AddNewCommand}" AutomationProperties.AutomationId="AddButton">
1717
<AccessText Text="{x:Static p:Resources.AddMenu}"/>
1818
</Button>
19-
<Button Command="{Binding RemoveCommand}">
19+
<Button Command="{Binding RemoveCommand}" AutomationProperties.AutomationId="RemoveButton">
2020
<AccessText Text="{x:Static p:Resources.RemoveMenu}"/>
2121
</Button>
2222
<Separator/>
2323
<Label Content="{x:Static p:Resources.SearchMenu}" Target="{Binding ElementName=searchBox}" Padding="5,0,7,0" Margin="0"
2424
VerticalAlignment="Center"/>
2525
<TextBox x:Name="searchBox" Width="150" BorderBrush="LightGray" BorderThickness="1"
26-
Text="{Binding Path=FilterText, UpdateSourceTrigger=PropertyChanged}"/>
26+
Text="{Binding Path=FilterText, UpdateSourceTrigger=PropertyChanged}" AutomationProperties.AutomationId="SearchBox"/>
2727
</ToolBar>
2828
</ToolBarTray>
2929

3030
<DataGrid x:Name="personTable" ItemsSource="{Binding Persons}" SelectedItem="{Binding SelectedPerson, ValidatesOnNotifyDataErrors=False}"
31-
SelectionChanged="DataGridSelectionChanged" Sorting="DataGridSorting" CanUserDeleteRows="False" BorderThickness="0">
31+
SelectionChanged="DataGridSelectionChanged" Sorting="DataGridSorting" CanUserDeleteRows="False" BorderThickness="0" AutomationProperties.AutomationId="PersonDataGrid">
3232
<DataGrid.InputBindings>
3333
<KeyBinding Command="{Binding RemoveCommand}" Key="Del"/>
3434
</DataGrid.InputBindings>

src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/PersonView.xaml

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
xmlns:waf="http://waf.codeplex.com/schemas"
88
xmlns:dd="clr-namespace:Waf.BookLibrary.Library.Presentation.DesignData"
99
mc:Ignorable="d" d:DataContext="{d:DesignInstance dd:SamplePersonViewModel, IsDesignTimeCreatable=True}"
10-
MinWidth="250" MinHeight="122" IsEnabled="{Binding IsEnabled}"
10+
MinWidth="250" MinHeight="122" IsEnabled="{Binding IsEnabled}" AutomationProperties.AutomationId="PersonView"
1111
waf:ValidationHelper.IsEnabled="true" waf:ValidationHelper.IsValid="{Binding IsValid, Mode=OneWayToSource}">
1212

1313
<Grid Margin="11,11,22,11">
@@ -30,16 +30,16 @@
3030
<Label Grid.Column="0" Grid.Row="0" Content="{x:Static p:Resources.FirstnameMenu}" Target="{Binding ElementName=firstnameBox}"/>
3131
<TextBox x:Name="firstnameBox" Grid.Column="2" Grid.Row="0" Grid.ColumnSpan="3"
3232
Text="{Binding Person.Firstname, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=true, NotifyOnValidationError=true}"
33-
MaxLength="30" HorizontalAlignment="Stretch"/>
33+
MaxLength="30" HorizontalAlignment="Stretch" AutomationProperties.AutomationId="FirstnameTextBox"/>
3434

3535
<Label Grid.Column="0" Grid.Row="2" Content="{x:Static p:Resources.Lastname}"/>
3636
<TextBox Grid.Column="2" Grid.Row="2" Grid.ColumnSpan="3" Text="{Binding Person.Lastname, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=true, NotifyOnValidationError=true}"
37-
MaxLength="30" HorizontalAlignment="Stretch"/>
37+
MaxLength="30" HorizontalAlignment="Stretch" AutomationProperties.AutomationId="LastnameTextBox"/>
3838

3939
<Label Grid.Column="0" Grid.Row="4" Content="{x:Static p:Resources.Email}"/>
4040
<TextBox Grid.Column="2" Grid.Row="4" Text="{Binding Person.Email, UpdateSourceTrigger=PropertyChanged, ValidatesOnExceptions=true, NotifyOnValidationError=true}"
41-
MaxLength="100" HorizontalAlignment="Stretch"/>
41+
MaxLength="100" HorizontalAlignment="Stretch" AutomationProperties.AutomationId="EmailTextBox"/>
4242
<Button Grid.Column="4" Grid.Row="4" Command="{Binding CreateNewEmailCommand}" CommandParameter="{Binding Person, ValidatesOnNotifyDataErrors=False}" Content="{StaticResource EmailImage}"
43-
ToolTip="{x:Static p:Resources.NewEmailMessage}" MinWidth="25" UseLayoutRounding="True"/>
43+
ToolTip="{x:Static p:Resources.NewEmailMessage}" MinWidth="25" UseLayoutRounding="True" AutomationProperties.AutomationId="CreateNewEmailButton"/>
4444
</Grid>
4545
</UserControl>

0 commit comments

Comments
 (0)