diff --git a/.github/workflows/System.Waf.CI.yml b/.github/workflows/System.Waf.CI.yml
index 1ca11385..954ca688 100644
--- a/.github/workflows/System.Waf.CI.yml
+++ b/.github/workflows/System.Waf.CI.yml
@@ -27,7 +27,7 @@ jobs:
src/System.Waf/System.Waf/**/*.snupkg
- name: UI Test
- run: dotnet test ./src/Samples.UITest/Samples.UITest.sln --logger "console;verbosity=detailed"
+ run: dotnet test ./src/Samples.UITest/Samples.UITest.sln --logger "console;verbosity=detailed" -maxCpuCount:1
- name: Upload UI Test results
uses: actions/upload-artifact@v4
if: always()
diff --git a/src/Samples.UITest/BookLibrary.Test/BookLibrary.Test.csproj b/src/Samples.UITest/BookLibrary.Test/BookLibrary.Test.csproj
new file mode 100644
index 00000000..e46fb508
--- /dev/null
+++ b/src/Samples.UITest/BookLibrary.Test/BookLibrary.Test.csproj
@@ -0,0 +1,19 @@
+
+
+ net8.0-windows
+ UITest.BookLibrary
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Samples.UITest/BookLibrary.Test/Tests/BookLibraryTest.cs b/src/Samples.UITest/BookLibrary.Test/Tests/BookLibraryTest.cs
new file mode 100644
index 00000000..afba64cf
--- /dev/null
+++ b/src/Samples.UITest/BookLibrary.Test/Tests/BookLibraryTest.cs
@@ -0,0 +1,93 @@
+using FlaUI.Core.AutomationElements;
+using FlaUI.Core.Capturing;
+using UITest.BookLibrary.Views;
+using UITest.SystemViews;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace UITest.BookLibrary.Tests;
+
+public class BookLibraryTest(ITestOutputHelper log) : UITest(log)
+{
+ [Fact]
+ public void AboutTest() => Run(() =>
+ {
+ Launch();
+ var window = GetShellWindow();
+
+ var helpMenu = window.HelpMenu;
+ helpMenu.Click();
+ helpMenu.AboutMenuItem.Click();
+
+ var messageBox = window.FirstModalWindow().As();
+ Assert.Equal("Waf Book Library", messageBox.Title);
+ Log.WriteLine(messageBox.Message);
+ Assert.StartsWith("Waf Book Library ", messageBox.Message);
+ Capture.Screen().ToFile(GetScreenshotFile("About.png"));
+ messageBox.Buttons[0].Click();
+
+ var dataMenu = window.DataMenu;
+ dataMenu.Click();
+ dataMenu.ExitMenuItem.Click();
+ });
+
+ [Fact]
+ public void SearchBookListAndChangeEntriesTest() => Run(() =>
+ {
+ Launch();
+ var window = GetShellWindow();
+ var bookListView = window.TabControl.BookLibraryTabItem.BookListView;
+ var bookView = window.TabControl.BookLibraryTabItem.BookView;
+
+ Assert.Equal(41, bookListView.BookDataGrid.RowCount);
+ bookListView.SearchBox.Text = "Ha";
+ Assert.Equal(13, bookListView.BookDataGrid.RowCount);
+ bookListView.SearchBox.Text = "Harr";
+ Assert.Equal(7, bookListView.BookDataGrid.RowCount);
+ var bookRow2 = bookListView.BookDataGrid.GetRowByIndex(1).As();
+ bookRow2.Select();
+
+ AssertEqual("Harry Potter and the Deathly Hallows", bookRow2.TitleCell.Name, bookView.TitleTextBox.Text);
+ AssertEqual("J.K. Rowling", bookRow2.AuthorCell.Name, bookView.AuthorTextBox.Text);
+ Assert.Equal("Bloomsbury", bookView.PublisherTextBox.Text);
+ Assert.Equal("1/1/2007", bookRow2.PublishDateCell.Name);
+ Assert.Equal(new DateTime(2007, 1, 1), bookView.PublishDatePicker.SelectedDate);
+ Assert.Equal("9780747591054", bookView.IsbnTextBox.Text);
+ Assert.Equal("English", bookView.LanguageComboBox.SelectedItem.Text);
+ Assert.Equal("607", bookView.PagesTextBox.Text);
+ AssertEqual("Ginny Weasley", bookRow2.LendToCell.LendToLabel.Name, bookView.LendToTextBox.Text);
+
+ bookView.TitleTextBox.Text = "Test Title";
+ Assert.Equal("Test Title", bookRow2.TitleCell.Name);
+ bookView.AuthorTextBox.Text = "TAuthor";
+ Assert.Equal("TAuthor", bookRow2.AuthorCell.Name);
+ bookView.PublishDatePicker.SelectedDate = new DateTime(2024, 3, 2);
+ Assert.Equal("3/2/2024", bookRow2.PublishDateCell.Name);
+ Assert.Equal(["Undefined", "English", "German", "French", "Spanish", "Chinese", "Japanese"], bookView.LanguageComboBox.Items.Select(x => x.Name));
+ bookView.LanguageComboBox.Select(2);
+ bookView.LanguageComboBox.Click(); // To close the combo box popup
+ Assert.Equal("German", bookView.LanguageComboBox.SelectedItem.Text);
+
+ bookView.LendToButton.Click();
+ var lendToWindow = window.FirstModalWindow().As();
+ Assert.True(lendToWindow.WasReturnedRadioButton.IsChecked);
+ Assert.False(lendToWindow.LendToRadioButton.IsChecked);
+ Assert.False(lendToWindow.PersonListBox.IsEnabled);
+ lendToWindow.LendToRadioButton.Click();
+ Assert.True(lendToWindow.PersonListBox.IsEnabled);
+ Assert.Equal(["Ginny", "Hermione", "Harry", "Ron"], lendToWindow.PersonListBox.Items.Select(x => x.Text));
+ lendToWindow.PersonListBox.Items[2].Select();
+ lendToWindow.OkButton.Click();
+ AssertEqual("Harry Potter", bookRow2.LendToCell.LendToLabel.Name, bookView.LendToTextBox.Text);
+
+ window.Close();
+ var messageBox = window.FirstModalWindow().As(); // MessageBox that asks user to save the changes
+ messageBox.Buttons[1].Click(); // No button
+
+ void AssertEqual(string expected, string actual1, string actual2)
+ {
+ Assert.Equal(expected, actual1);
+ Assert.Equal(expected, actual2);
+ }
+ });
+}
diff --git a/src/Samples.UITest/BookLibrary.Test/UITest.cs b/src/Samples.UITest/BookLibrary.Test/UITest.cs
new file mode 100644
index 00000000..54afc756
--- /dev/null
+++ b/src/Samples.UITest/BookLibrary.Test/UITest.cs
@@ -0,0 +1,41 @@
+using FlaUI.Core;
+using FlaUI.Core.AutomationElements;
+using System.Diagnostics;
+using UITest.BookLibrary.Views;
+using Xunit;
+using Xunit.Abstractions;
+
+[assembly: CollectionBehavior(DisableTestParallelization = true)]
+
+namespace UITest.BookLibrary;
+
+public abstract class UITest(ITestOutputHelper log) : UITestBase(log, "BookLibrary.exe",
+ Environment.GetEnvironmentVariable("UITestExePath") ?? "out/BookLibrary/Release/net8.0-windows/",
+ Environment.GetEnvironmentVariable("UITestOutputPath") ?? "out/Samples.UITest/BookLibrary/")
+{
+ public Application Launch(LaunchArguments? arguments = null, bool resetSettings = true)
+ {
+ Log.WriteLine("");
+ if (resetSettings)
+ {
+ var productName = FileVersionInfo.GetVersionInfo(Executable).ProductName ?? throw new InvalidOperationException("Could not read the ProductName from the exe.");
+ var settingsFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), productName, "Settings", "Settings.xml");
+ if (File.Exists(settingsFile)) File.Delete(settingsFile);
+ Log.WriteLine($"Delete settings: {settingsFile}");
+ }
+ var args = (arguments ?? new LaunchArguments()).ToArguments();
+ Log.WriteLine($"Launch: {args}");
+ return App = Application.Launch(Executable, args);
+ }
+
+ public ShellWindow GetShellWindow() => App!.GetMainWindow(Automation).As();
+}
+
+public record LaunchArguments(string? UICulture = "en-US", string? Culture = "en-US", string? AdditionalArguments = null) : LaunchArgumentsBase
+{
+ public override string ToArguments()
+ {
+ string?[] args = [CreateArg(UICulture), CreateArg(Culture), AdditionalArguments];
+ return string.Join(" ", args.Where(x => !string.IsNullOrEmpty(x)));
+ }
+}
\ No newline at end of file
diff --git a/src/Samples.UITest/BookLibrary.Test/Views/BookListView.cs b/src/Samples.UITest/BookLibrary.Test/Views/BookListView.cs
new file mode 100644
index 00000000..652ae4be
--- /dev/null
+++ b/src/Samples.UITest/BookLibrary.Test/Views/BookListView.cs
@@ -0,0 +1,29 @@
+using FlaUI.Core;
+using FlaUI.Core.AutomationElements;
+
+namespace UITest.BookLibrary.Views;
+
+public class BookListView(FrameworkAutomationElementBase element) : AutomationElement(element)
+{
+ public TextBox SearchBox => this.Find("SearchBox").AsTextBox();
+
+ public Grid BookDataGrid => this.Find("BookDataGrid").AsGrid();
+}
+
+public class BookGridRow(FrameworkAutomationElementBase element) : GridRow(element)
+{
+ public GridCell TitleCell => Cells[0];
+
+ public GridCell AuthorCell => Cells[1];
+
+ public GridCell PublishDateCell => Cells[2];
+
+ public LendToGridCell LendToCell => Cells[3].As();
+}
+
+public class LendToGridCell(FrameworkAutomationElementBase element) : GridCell(element)
+{
+ public Label LendToLabel => this.Find("LendToLabel").AsLabel();
+
+ public Button LendToButton => this.Find("LendToButton").AsButton();
+}
\ No newline at end of file
diff --git a/src/Samples.UITest/BookLibrary.Test/Views/BookView.cs b/src/Samples.UITest/BookLibrary.Test/Views/BookView.cs
new file mode 100644
index 00000000..f4276d2b
--- /dev/null
+++ b/src/Samples.UITest/BookLibrary.Test/Views/BookView.cs
@@ -0,0 +1,25 @@
+using FlaUI.Core.AutomationElements;
+using FlaUI.Core;
+
+namespace UITest.BookLibrary.Views;
+
+public class BookView(FrameworkAutomationElementBase element) : AutomationElement(element)
+{
+ public TextBox TitleTextBox => this.Find("TitleTextBox").AsTextBox();
+
+ public TextBox AuthorTextBox => this.Find("AuthorTextBox").AsTextBox();
+
+ public TextBox PublisherTextBox => this.Find("PublisherTextBox").AsTextBox();
+
+ public DateTimePicker PublishDatePicker => this.Find("PublishDatePicker").AsDateTimePicker();
+
+ public TextBox IsbnTextBox => this.Find("IsbnTextBox").AsTextBox();
+
+ public ComboBox LanguageComboBox => this.Find("LanguageComboBox").AsComboBox();
+
+ public TextBox PagesTextBox => this.Find("PagesTextBox").AsTextBox();
+
+ public TextBox LendToTextBox => this.Find("LendToTextBox").AsTextBox();
+
+ public Button LendToButton => this.Find("LendToButton").AsButton();
+}
diff --git a/src/Samples.UITest/BookLibrary.Test/Views/LendToWindow.cs b/src/Samples.UITest/BookLibrary.Test/Views/LendToWindow.cs
new file mode 100644
index 00000000..48fd4e4a
--- /dev/null
+++ b/src/Samples.UITest/BookLibrary.Test/Views/LendToWindow.cs
@@ -0,0 +1,17 @@
+using FlaUI.Core;
+using FlaUI.Core.AutomationElements;
+
+namespace UITest.BookLibrary.Views;
+
+public class LendToWindow(FrameworkAutomationElementBase element) : Window(element)
+{
+ public RadioButton WasReturnedRadioButton => this.Find("WasReturnedRadioButton").AsRadioButton();
+
+ public RadioButton LendToRadioButton => this.Find("LendToRadioButton").AsRadioButton();
+
+ public ListBox PersonListBox => this.Find("PersonListBox").AsListBox();
+
+ public Button OkButton => this.Find("OkButton").AsButton();
+
+ public Button CancelButton => this.Find("CancelButton").AsButton();
+}
diff --git a/src/Samples.UITest/BookLibrary.Test/Views/ShellWindow.cs b/src/Samples.UITest/BookLibrary.Test/Views/ShellWindow.cs
new file mode 100644
index 00000000..4537016d
--- /dev/null
+++ b/src/Samples.UITest/BookLibrary.Test/Views/ShellWindow.cs
@@ -0,0 +1,41 @@
+using FlaUI.Core;
+using FlaUI.Core.AutomationElements;
+
+namespace UITest.BookLibrary.Views;
+
+public class ShellWindow(FrameworkAutomationElementBase element) : Window(element)
+{
+ public DataMenu DataMenu => this.Find("DataMenu").As();
+
+ public HelpMenu HelpMenu => this.Find("HelpMenu").As();
+
+ public TabControl TabControl => this.Find("TabControl").As();
+}
+
+public class DataMenu(FrameworkAutomationElementBase element) : Menu(element)
+{
+ public MenuItem SaveMenuItem => this.Find("SaveMenuItem").AsMenuItem();
+
+ public MenuItem ExitMenuItem => this.Find("ExitMenuItem").AsMenuItem();
+}
+
+public class HelpMenu(FrameworkAutomationElementBase element) : Menu(element)
+{
+ public MenuItem AboutMenuItem => this.Find("AboutMenuItem").AsMenuItem();
+}
+
+public class TabControl(FrameworkAutomationElementBase element) : Tab(element)
+{
+ public BookLibraryTabItem BookLibraryTabItem => this.Find("BookLibraryTabItem").As();
+
+ public TabItem AddressBookTabItem => this.Find("AddressBookTabItem").AsTabItem();
+
+ public TabItem ReportingTabItem => this.Find("ReportingTabItem").AsTabItem();
+}
+
+public class BookLibraryTabItem(FrameworkAutomationElementBase element) : TabItem(element)
+{
+ public BookListView BookListView => this.Find("BookListView").As();
+
+ public BookView BookView => this.Find("BookView").As();
+}
\ No newline at end of file
diff --git a/src/Samples.UITest/Samples.UITest.sln b/src/Samples.UITest/Samples.UITest.sln
index 9d83d639..cb64ab9f 100644
--- a/src/Samples.UITest/Samples.UITest.sln
+++ b/src/Samples.UITest/Samples.UITest.sln
@@ -3,7 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.9.34714.143
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Writer.Test", "Writer.Test\Writer.Test.csproj", "{8FBAB64A-51F5-40F8-8B56-4AEE7BE49838}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Writer.Test", "Writer.Test\Writer.Test.csproj", "{8FBAB64A-51F5-40F8-8B56-4AEE7BE49838}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BookLibrary.Test", "BookLibrary.Test\BookLibrary.Test.csproj", "{A239DB72-F7D9-4DE9-83F9-E0D852FA5F8F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UITest.Core", "UITest.Core\UITest.Core.csproj", "{4B2174D9-B723-42AA-90DA-2F556409F19A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -15,6 +19,14 @@ Global
{8FBAB64A-51F5-40F8-8B56-4AEE7BE49838}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8FBAB64A-51F5-40F8-8B56-4AEE7BE49838}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8FBAB64A-51F5-40F8-8B56-4AEE7BE49838}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A239DB72-F7D9-4DE9-83F9-E0D852FA5F8F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A239DB72-F7D9-4DE9-83F9-E0D852FA5F8F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A239DB72-F7D9-4DE9-83F9-E0D852FA5F8F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A239DB72-F7D9-4DE9-83F9-E0D852FA5F8F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {4B2174D9-B723-42AA-90DA-2F556409F19A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {4B2174D9-B723-42AA-90DA-2F556409F19A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {4B2174D9-B723-42AA-90DA-2F556409F19A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {4B2174D9-B723-42AA-90DA-2F556409F19A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Samples.UITest/UITest.Core/LaunchArgumentsBase.cs b/src/Samples.UITest/UITest.Core/LaunchArgumentsBase.cs
new file mode 100644
index 00000000..eb5200d2
--- /dev/null
+++ b/src/Samples.UITest/UITest.Core/LaunchArgumentsBase.cs
@@ -0,0 +1,10 @@
+using System.Runtime.CompilerServices;
+
+namespace UITest;
+
+public abstract record LaunchArgumentsBase
+{
+ protected string? CreateArg(object? value, [CallerArgumentExpression(nameof(value))] string propertyName = null!) => value is null ? null : $"--{propertyName}=\"{value}\"";
+
+ public abstract string ToArguments();
+}
diff --git a/src/Samples.UITest/Writer.Test/Views/MessageBox.cs b/src/Samples.UITest/UITest.Core/SystemViews/MessageBox.cs
similarity index 66%
rename from src/Samples.UITest/Writer.Test/Views/MessageBox.cs
rename to src/Samples.UITest/UITest.Core/SystemViews/MessageBox.cs
index 524c373e..501548b8 100644
--- a/src/Samples.UITest/Writer.Test/Views/MessageBox.cs
+++ b/src/Samples.UITest/UITest.Core/SystemViews/MessageBox.cs
@@ -2,7 +2,7 @@
using FlaUI.Core.AutomationElements;
using FlaUI.Core.Definitions;
-namespace UITest.Writer.Views;
+namespace UITest.SystemViews;
public class MessageBox(FrameworkAutomationElementBase element) : AutomationElement(element)
{
@@ -10,5 +10,5 @@ public class MessageBox(FrameworkAutomationElementBase element) : AutomationElem
public string Message => this.Find(x => x.ByControlType(ControlType.Text)).Name;
- public Button OkButton => this.Find(x => x.ByControlType(ControlType.Button)).AsButton();
+ public Button[] Buttons => this.FindAll(x => x.ByControlType(ControlType.Button)).Select(x => x.AsButton()).ToArray();
}
diff --git a/src/Samples.UITest/Writer.Test/Views/SaveFileDialog.cs b/src/Samples.UITest/UITest.Core/SystemViews/SaveFileDialog.cs
similarity index 96%
rename from src/Samples.UITest/Writer.Test/Views/SaveFileDialog.cs
rename to src/Samples.UITest/UITest.Core/SystemViews/SaveFileDialog.cs
index d1c5642f..d2096e2e 100644
--- a/src/Samples.UITest/Writer.Test/Views/SaveFileDialog.cs
+++ b/src/Samples.UITest/UITest.Core/SystemViews/SaveFileDialog.cs
@@ -4,7 +4,7 @@
using FlaUI.Core.Input;
using FlaUI.Core.WindowsAPI;
-namespace UITest.Writer.Views;
+namespace UITest.SystemViews;
public class SaveFileDialog(FrameworkAutomationElementBase element) : Window(element)
{
diff --git a/src/Samples.UITest/Writer.Test/UIAssert.cs b/src/Samples.UITest/UITest.Core/UIAssert.cs
similarity index 97%
rename from src/Samples.UITest/Writer.Test/UIAssert.cs
rename to src/Samples.UITest/UITest.Core/UIAssert.cs
index 6d739fb9..7281377e 100644
--- a/src/Samples.UITest/Writer.Test/UIAssert.cs
+++ b/src/Samples.UITest/UITest.Core/UIAssert.cs
@@ -1,7 +1,7 @@
using FlaUI.Core.Tools;
using System.Runtime.CompilerServices;
-namespace UITest.Writer;
+namespace UITest;
public class ElementFoundException(string message, Exception? innerException = null) : Exception(message, innerException) { }
diff --git a/src/Samples.UITest/UITest.Core/UITest.Core.csproj b/src/Samples.UITest/UITest.Core/UITest.Core.csproj
new file mode 100644
index 00000000..69ec98c9
--- /dev/null
+++ b/src/Samples.UITest/UITest.Core/UITest.Core.csproj
@@ -0,0 +1,14 @@
+
+
+ net8.0-windows
+ UITest
+
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/src/Samples.UITest/UITest.Core/UITestBase.cs b/src/Samples.UITest/UITest.Core/UITestBase.cs
new file mode 100644
index 00000000..03ec4cbf
--- /dev/null
+++ b/src/Samples.UITest/UITest.Core/UITestBase.cs
@@ -0,0 +1,114 @@
+using FlaUI.Core;
+using FlaUI.Core.Capturing;
+using FlaUI.Core.Input;
+using FlaUI.Core.Tools;
+using FlaUI.UIA3;
+using System.Globalization;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using Xunit.Abstractions;
+
+namespace UITest;
+
+public abstract class UITestBase : IDisposable
+{
+ private readonly List usedFiles = [];
+ private string? testMethodName;
+
+ static UITestBase()
+ {
+ CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
+
+ Mouse.MovePixelsPerMillisecond = 2;
+ Retry.DefaultTimeout = TimeSpan.FromSeconds(5);
+ Retry.DefaultInterval = TimeSpan.FromMilliseconds(250);
+ }
+
+ protected UITestBase(ITestOutputHelper log, string executableFileName, string executablePath, string testOutputPath)
+ {
+ Log = log;
+ var assemblyPath = Assembly.GetAssembly(typeof(UITestBase))!.Location;
+ var rootPath = Path.GetFullPath(Path.Combine(assemblyPath, "../../../../../../../"));
+ Executable = Path.GetFullPath(Path.Combine(Path.IsPathFullyQualified(executablePath) ? executablePath : Path.Combine(rootPath, executablePath), executableFileName));
+ TestOutPath = Path.GetFullPath(Path.IsPathFullyQualified(testOutputPath) ? testOutputPath : Path.Combine(rootPath, testOutputPath));
+ Directory.CreateDirectory(TestOutPath);
+ Log.WriteLine($"OSVersion: {Environment.OSVersion}");
+ Log.WriteLine($"ProcessorCount: {Environment.ProcessorCount}");
+ Log.WriteLine($"MachineName: {Environment.MachineName}");
+ Log.WriteLine($"UserInteractive: {Environment.UserInteractive}");
+ Log.WriteLine($"Executable: {Executable}");
+ Log.WriteLine($"TestOutPath: {TestOutPath}");
+ Automation = new()
+ {
+ ConnectionTimeout = TimeSpan.FromSeconds(5)
+ };
+ }
+
+ public ITestOutputHelper Log { get; }
+
+ public string Executable { get; }
+
+ public string TestOutPath { get; }
+
+ public string TestMethodName => testMethodName ?? throw new InvalidOperationException("Test context not available. Use the Run method for your test code.");
+
+ public UIA3Automation Automation { get; }
+
+ public Application? App { get; protected set; }
+
+ public bool SkipAppClose { get; set; } = false;
+
+ public void Run(Action action, [CallerMemberName] string? memberName = null)
+ {
+ try
+ {
+ testMethodName = memberName;
+ action();
+ }
+ catch (Exception)
+ {
+ TryGetScreenshot();
+ throw;
+ }
+ finally
+ {
+ testMethodName = null;
+ }
+
+ void TryGetScreenshot()
+ {
+ try
+ {
+ Capture.Screen().ToFile(GetScreenshotFile("Fail"));
+ }
+ catch { }
+ }
+ }
+
+ public string GetTempFileName(string fileExtension)
+ {
+ var file = $"UITest_{Path.GetRandomFileName()}.{fileExtension}";
+ file = Path.Combine(Path.GetTempPath(), file);
+ usedFiles.Add(file);
+ Log.WriteLine($"TempFile: {file}");
+ return file;
+ }
+
+ public string GetScreenshotFile(string fileName)
+ {
+ var file = Path.Combine(TestOutPath, string.Join("-", TestMethodName, fileName));
+ if (string.IsNullOrEmpty(Path.GetExtension(file))) file += ".png";
+ return file;
+ }
+
+ public void Dispose()
+ {
+ if (!SkipAppClose) App?.Close();
+ App?.Dispose();
+ Automation.Dispose();
+ foreach (var file in usedFiles)
+ {
+ if (File.Exists(file)) File.Delete(file);
+ }
+ }
+}
diff --git a/src/Samples.UITest/Writer.Test/UITestHelper.cs b/src/Samples.UITest/UITest.Core/UITestHelper.cs
similarity index 99%
rename from src/Samples.UITest/Writer.Test/UITestHelper.cs
rename to src/Samples.UITest/UITest.Core/UITestHelper.cs
index 479ffb79..c0e06383 100644
--- a/src/Samples.UITest/Writer.Test/UITestHelper.cs
+++ b/src/Samples.UITest/UITest.Core/UITestHelper.cs
@@ -4,7 +4,7 @@
using FlaUI.Core.Tools;
using System.Text;
-namespace UITest.Writer;
+namespace UITest;
public class ElementNotFoundException(string message, Exception? innerException = null) : Exception(message, innerException) { }
diff --git a/src/Samples.UITest/Writer.Test/LaunchArguments.cs b/src/Samples.UITest/Writer.Test/LaunchArguments.cs
deleted file mode 100644
index d4d29e1b..00000000
--- a/src/Samples.UITest/Writer.Test/LaunchArguments.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System.Runtime.CompilerServices;
-
-namespace UITest.Writer;
-
-public record LaunchArguments(string? UICulture = "en-US", string? Culture = "en-US", bool? DefaultSettings = true, string? AdditionalArguments = null)
-{
- private string? CreateArg(object? value, [CallerArgumentExpression(nameof(value))] string propertyName = null!) => value is null ? null : $"--{propertyName}=\"{value}\"";
-
- public string ToArguments()
- {
- string?[] args = [ CreateArg(UICulture), CreateArg(Culture), CreateArg(DefaultSettings), CreateArg(AdditionalArguments) ];
- return string.Join(" ", args.Where(x => !string.IsNullOrEmpty(x)));
- }
-}
diff --git a/src/Samples.UITest/Writer.Test/Tests/WriterTest.cs b/src/Samples.UITest/Writer.Test/Tests/WriterTest.cs
index 37c76366..afafe4ae 100644
--- a/src/Samples.UITest/Writer.Test/Tests/WriterTest.cs
+++ b/src/Samples.UITest/Writer.Test/Tests/WriterTest.cs
@@ -2,6 +2,7 @@
using FlaUI.Core.Capturing;
using FlaUI.Core.Definitions;
using FlaUI.Core.Tools;
+using UITest.SystemViews;
using UITest.Writer.Views;
using Xunit;
using Xunit.Abstractions;
@@ -21,7 +22,7 @@ public WriterTest(ITestOutputHelper log) : base(log)
protected void AssertTextEqual(string expected, TextBox actual) { if (!SkipTextBoxReadText) Assert.Equal(expected, actual.Text); }
[Fact]
- public void AboutTest()
+ public void AboutTest() => Run(() =>
{
Launch();
var window = GetShellWindow();
@@ -33,15 +34,15 @@ public void AboutTest()
Log.WriteLine(messageBox.Message);
Assert.StartsWith("Waf Writer ", messageBox.Message);
Capture.Screen().ToFile(GetScreenshotFile("About.png"));
- messageBox.OkButton.Click();
+ messageBox.Buttons[0].Click();
var fileRibbonMenu = window.FileRibbonMenu;
fileRibbonMenu.MenuButton.Click();
fileRibbonMenu.ExitMenuItem.Invoke();
- }
+ });
[Fact]
- public void NewZoomWritePrintPreviewExitWithoutSave()
+ public void NewZoomWritePrintPreviewExitWithoutSave() => Run(() =>
{
Launch();
var window = GetShellWindow();
@@ -85,17 +86,17 @@ public void NewZoomWritePrintPreviewExitWithoutSave()
var firstItem = saveChangesWindow.FilesToSaveList.Items.Single();
Assert.Equal("Document 1.rtf", firstItem.Text);
saveChangesWindow.NoButton.Click();
- }
+ });
[Fact]
- public void MultipleNewSaveRestartOpenRecentChangeAskToSave()
+ public void MultipleNewSaveRestartOpenRecentChangeAskToSave() => Run(() =>
{
Launch();
var window = GetShellWindow();
var startView = window.StartView;
Assert.False(startView.IsOffscreen);
-
+
// Create new document, add text, check tab name with dirty flag indicator '*'
startView.NewButton.Click();
var tab1 = window.DocumentTabItems.Single();
@@ -146,20 +147,20 @@ public void MultipleNewSaveRestartOpenRecentChangeAskToSave()
// Restart the app and check recent file list, pin second item -> moved to top
- Launch(new LaunchArguments(DefaultSettings: false));
+ Launch(resetSettings: false);
window = GetShellWindow();
startView = window.StartView;
Capture.Screen().ToFile(GetScreenshotFile("RestartScreen.png"));
- Assert.Equal([ fileName2, fileName ], startView.RecentFileListItems.Select(x => x.ToolTip));
+ Assert.Equal([fileName2, fileName], startView.RecentFileListItems.Select(x => x.ToolTip));
startView.RecentFileListItems[1].PinButton.Click();
- Assert.Equal([ fileName, fileName2 ], startView.RecentFileListItems.Select(x => x.ToolTip));
+ Assert.Equal([fileName, fileName2], startView.RecentFileListItems.Select(x => x.ToolTip));
Assert.True(startView.RecentFileListItems[0].PinButton.IsToggled);
Assert.False(startView.RecentFileListItems[1].PinButton.IsToggled);
// Check that the recent file list within the ribbon menu has the same content
fileRibbonMenu = window.FileRibbonMenu;
fileRibbonMenu.MenuButton.Click();
- Assert.Equal([ fileName, fileName2 ], fileRibbonMenu.RecentFileListItems.Select(x => x.ToolTip));
+ Assert.Equal([fileName, fileName2], fileRibbonMenu.RecentFileListItems.Select(x => x.ToolTip));
Assert.True(fileRibbonMenu.RecentFileListItems[0].PinButton.IsToggled);
Assert.False(fileRibbonMenu.RecentFileListItems[1].PinButton.IsToggled);
fileRibbonMenu.MenuButton.Toggle();
@@ -188,27 +189,27 @@ public void MultipleNewSaveRestartOpenRecentChangeAskToSave()
Assert.Equal(fileName, firstItem.Text);
saveChangesWindow.NoButton.Click();
-
+
// Restart the app and check recent file list, use context menu to unpin, remove and open
- Launch(new LaunchArguments(DefaultSettings: false));
+ Launch(resetSettings: false);
window = GetShellWindow();
startView = window.StartView;
- Assert.Equal([ fileName, fileName2 ], startView.RecentFileListItems.Select(x => x.ToolTip));
+ Assert.Equal([fileName, fileName2], startView.RecentFileListItems.Select(x => x.ToolTip));
Assert.True(startView.RecentFileListItems[0].PinButton.IsToggled);
var contextMenu = startView.RecentFileListItems[0].ShowContextMenu();
- UIAssert.NotExists(() => _ = contextMenu.PinFileMenuItem);
+ UIAssert.NotExists(() => _ = contextMenu.PinFileMenuItem);
contextMenu.UnpinFileMenuItem.Invoke();
Assert.False(startView.RecentFileListItems[0].PinButton.IsToggled);
-
+
contextMenu = startView.RecentFileListItems[0].ShowContextMenu();
UIAssert.NotExists(() => _ = contextMenu.UnpinFileMenuItem);
contextMenu.RemoveFileMenuItem.Invoke();
- Assert.Equal([ fileName2 ], startView.RecentFileListItems.Select(x => x.ToolTip));
+ Assert.Equal([fileName2], startView.RecentFileListItems.Select(x => x.ToolTip));
contextMenu = startView.RecentFileListItems[0].ShowContextMenu();
contextMenu.OpenFileMenuItem.Invoke();
tab1 = window.DocumentTab.SelectedTabItem.As();
Assert.Equal(Path.GetFileName(fileName2), tab1.TabName);
AssertTextEqual("Hello World 2", tab1.RichTextView.RichTextBox);
- }
+ });
}
diff --git a/src/Samples.UITest/Writer.Test/UITest.cs b/src/Samples.UITest/Writer.Test/UITest.cs
index caf17c7e..6bee544a 100644
--- a/src/Samples.UITest/Writer.Test/UITest.cs
+++ b/src/Samples.UITest/Writer.Test/UITest.cs
@@ -1,10 +1,6 @@
using FlaUI.Core;
using FlaUI.Core.AutomationElements;
-using FlaUI.Core.Input;
-using FlaUI.Core.Tools;
-using FlaUI.UIA3;
-using System.Reflection;
-using System.Runtime.CompilerServices;
+using System.Diagnostics;
using UITest.Writer.Views;
using Xunit;
using Xunit.Abstractions;
@@ -13,77 +9,33 @@
namespace UITest.Writer;
-public class UITest : IDisposable
+public abstract class UITest(ITestOutputHelper log) : UITestBase(log, "Writer.exe",
+ Environment.GetEnvironmentVariable("UITestExePath") ?? "out/Writer/Release/net8.0-windows/",
+ Environment.GetEnvironmentVariable("UITestOutputPath") ?? "out/Samples.UITest/Writer/")
{
- private readonly string outPath;
- private readonly string executable;
- private readonly string testOutPath;
- private readonly List usedFiles = [];
- private Application? app;
-
- static UITest()
+ public Application Launch(LaunchArguments? arguments = null, bool resetSettings = true)
{
- Mouse.MovePixelsPerMillisecond = 2;
- Retry.DefaultTimeout = TimeSpan.FromSeconds(5);
- Retry.DefaultInterval = TimeSpan.FromMilliseconds(250);
- }
-
- public UITest(ITestOutputHelper log)
- {
- Log = log;
- var assemblyPath = Assembly.GetAssembly(typeof(UITest))!.Location;
- outPath = Path.GetFullPath(Path.Combine(assemblyPath, "../../../../../../../out/"));
- executable = Path.Combine(outPath, "Writer/Release/net8.0-windows/writer.exe");
- testOutPath = Path.Combine(outPath, "Samples.UITest/Writer/");
- Directory.CreateDirectory(testOutPath);
- Log.WriteLine($"OSVersion: {Environment.OSVersion}");
- Log.WriteLine($"ProcessorCount: {Environment.ProcessorCount}");
- Log.WriteLine($"MachineName: {Environment.MachineName}");
- Log.WriteLine($"UserInteractive: {Environment.UserInteractive}");
- Log.WriteLine($"AssemblyPath: {assemblyPath}");
- Log.WriteLine($"Executable: {executable}");
- Log.WriteLine($"TestOutPath: {testOutPath}");
- Automation = new()
+ Log.WriteLine("");
+ if (resetSettings)
{
- ConnectionTimeout = TimeSpan.FromSeconds(5)
- };
- }
-
- public ITestOutputHelper Log { get; }
-
- public UIA3Automation Automation { get; }
-
- public bool SkipAppClose { get; set; } = false;
-
- public Application Launch(LaunchArguments? arguments = null)
- {
+ var productName = FileVersionInfo.GetVersionInfo(Executable).ProductName ?? throw new InvalidOperationException("Could not read the ProductName from the exe.");
+ var settingsFile = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), productName, "Settings", "Settings.xml");
+ if (File.Exists(settingsFile)) File.Delete(settingsFile);
+ Log.WriteLine($"Delete settings: {settingsFile}");
+ }
var args = (arguments ?? new LaunchArguments()).ToArguments();
Log.WriteLine($"Launch: {args}");
- return app = Application.Launch(executable, args);
- }
-
- public ShellWindow GetShellWindow() => app!.GetMainWindow(Automation).As();
-
- public string GetTempFileName(string fileExtension)
- {
- var file = $"UITest_{Path.GetRandomFileName()}.{fileExtension}";
- file = Path.Combine(Path.GetTempPath(), file);
- usedFiles.Add(file);
- Log.WriteLine($"TempFile: {file}");
- return file;
+ return App = Application.Launch(Executable, args);
}
- public string GetScreenshotFile(string fileName, [CallerMemberName]string? memberName = null)
- => Path.Combine(testOutPath, string.Join("-", new[] { memberName, fileName }.Where(x => !string.IsNullOrEmpty(x))));
+ public ShellWindow GetShellWindow() => App!.GetMainWindow(Automation).As();
+}
- public void Dispose()
+public record LaunchArguments(string? UICulture = "en-US", string? Culture = "en-US", string? AdditionalArguments = null) : LaunchArgumentsBase
+{
+ public override string ToArguments()
{
- if (!SkipAppClose) app?.Close();
- app?.Dispose();
- Automation.Dispose();
- foreach (var file in usedFiles)
- {
- if (File.Exists(file)) File.Delete(file);
- }
+ string?[] args = [CreateArg(UICulture), CreateArg(Culture), AdditionalArguments];
+ return string.Join(" ", args.Where(x => !string.IsNullOrEmpty(x)));
}
-}
+}
\ No newline at end of file
diff --git a/src/Samples.UITest/Writer.Test/Writer.Test.csproj b/src/Samples.UITest/Writer.Test/Writer.Test.csproj
index eb8f2100..8c0708d6 100644
--- a/src/Samples.UITest/Writer.Test/Writer.Test.csproj
+++ b/src/Samples.UITest/Writer.Test/Writer.Test.csproj
@@ -8,9 +8,12 @@
-
-
-
-
+
+
+
+
+
+
+
diff --git a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/App.config b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/App.config
index a62adb65..7ead96e9 100644
--- a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/App.config
+++ b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/App.config
@@ -14,8 +14,7 @@
-
+
Waf.BookLibrary.Reporting.Applications.dll
Waf.BookLibrary.Reporting.Presentation.dll
diff --git a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/App.xaml.cs b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/App.xaml.cs
index e2941c4d..ea4533da 100644
--- a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/App.xaml.cs
+++ b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/App.xaml.cs
@@ -1,4 +1,5 @@
-using NLog;
+using Microsoft.Extensions.Configuration;
+using NLog;
using NLog.Targets;
using NLog.Targets.Wrappers;
using System.ComponentModel.Composition;
@@ -30,7 +31,7 @@ private static readonly (string loggerNamePattern, LogLevel minLevel)[] logSetti
private AggregateCatalog? catalog;
private CompositionContainer? container;
- private IEnumerable moduleControllers = Array.Empty();
+ private IEnumerable moduleControllers = [];
public App()
{
@@ -73,19 +74,36 @@ protected override void OnStartup(StartupEventArgs e)
DispatcherUnhandledException += AppDispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += AppDomainUnhandledException;
#endif
+ AppConfig appConfig;
+ try
+ {
+ var config = new ConfigurationBuilder().AddCommandLine(Environment.GetCommandLineArgs()).Build();
+ appConfig = config.Get() ?? new AppConfig();
+ }
+ catch (Exception ex)
+ {
+ Log.Default.Error(ex, "Command line parsing error");
+ appConfig = new AppConfig();
+ }
+
catalog = new();
catalog.Catalogs.Add(new AssemblyCatalog(typeof(IMessageService).Assembly)); // WinApplicationFramework
catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); // Waf.BookLibrary.Library.Presentation
catalog.Catalogs.Add(new AssemblyCatalog(typeof(ShellViewModel).Assembly)); // Waf.BookLibrary.Library.Applications
// Load module assemblies as well (e.g. Reporting extension). See App.config file.
- foreach (var x in Settings.Default.ModuleAssemblies) catalog.Catalogs.Add(new AssemblyCatalog(x));
+ var baseDir = AppContext.BaseDirectory;
+ foreach (var x in Settings.Default.ModuleAssemblies)
+ {
+ catalog.Catalogs.Add(new AssemblyCatalog(Path.Combine(baseDir, x!)));
+ }
container = new CompositionContainer(catalog, CompositionOptions.DisableSilentRejection);
var batch = new CompositionBatch();
batch.AddExportedValue(container);
container.Compose(batch);
+ InitializeCultures(appConfig);
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
moduleControllers = container.GetExportedValues();
@@ -102,6 +120,19 @@ protected override void OnExit(ExitEventArgs e)
base.OnExit(e);
}
+ private static void InitializeCultures(AppConfig appConfig)
+ {
+ try
+ {
+ if (!string.IsNullOrEmpty(appConfig.Culture)) Thread.CurrentThread.CurrentCulture = CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(appConfig.Culture);
+ if (!string.IsNullOrEmpty(appConfig.UICulture)) Thread.CurrentThread.CurrentUICulture = CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(appConfig.UICulture);
+ }
+ catch (Exception ex)
+ {
+ Log.Default.Error(ex, "The specified culture code is invalid");
+ }
+ }
+
private void AppDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) => HandleException(e.Exception, false);
private static void AppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e) => HandleException(e.ExceptionObject as Exception, e.IsTerminating);
diff --git a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/BookLibrary.Library.Presentation.csproj b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/BookLibrary.Library.Presentation.csproj
index c01f0883..7479b4d8 100644
--- a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/BookLibrary.Library.Presentation.csproj
+++ b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/BookLibrary.Library.Presentation.csproj
@@ -11,6 +11,8 @@
+
+
diff --git a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/AppConfig.cs b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/AppConfig.cs
new file mode 100644
index 00000000..3c37d697
--- /dev/null
+++ b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/AppConfig.cs
@@ -0,0 +1,8 @@
+namespace Waf.BookLibrary.Library.Presentation.Properties;
+
+public class AppConfig
+{
+ public string? Culture { get; init; }
+
+ public string? UICulture { get; init; }
+}
diff --git a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/Settings.Designer.cs b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/Settings.Designer.cs
index 77ead131..f69d25d4 100644
--- a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/Settings.Designer.cs
+++ b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/Settings.Designer.cs
@@ -12,7 +12,7 @@ namespace Waf.BookLibrary.Library.Presentation.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.9.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -26,7 +26,7 @@ public static Settings Default {
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute(@"
-
+
Waf.BookLibrary.Reporting.Applications.dll
Waf.BookLibrary.Reporting.Presentation.dll
")]
diff --git a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/Settings.settings b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/Settings.settings
index 3f7046f3..ee0ba01e 100644
--- a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/Settings.settings
+++ b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Properties/Settings.settings
@@ -4,7 +4,7 @@
<?xml version="1.0" encoding="utf-16"?>
-<ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+<ArrayOfString xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<string>Waf.BookLibrary.Reporting.Applications.dll</string>
<string>Waf.BookLibrary.Reporting.Presentation.dll</string>
</ArrayOfString>
diff --git a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/BookListView.xaml b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/BookListView.xaml
index fdbb23dd..71c788be 100644
--- a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/BookListView.xaml
+++ b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/BookListView.xaml
@@ -7,7 +7,7 @@
xmlns:waf="http://waf.codeplex.com/schemas"
xmlns:dd="clr-namespace:Waf.BookLibrary.Library.Presentation.DesignData"
mc:Ignorable="d" d:DataContext="{d:DesignInstance dd:SampleBookListViewModel, IsDesignTimeCreatable=True}"
- d:DesignWidth="550" d:DesignHeight="150"
+ d:DesignWidth="550" d:DesignHeight="150" AutomationProperties.AutomationId="BookListView"
waf:ValidationHelper.IsEnabled="true" waf:ValidationHelper.IsValid="{Binding IsValid, Mode=OneWayToSource}">
@@ -23,12 +23,12 @@
+ Text="{Binding Path=FilterText, UpdateSourceTrigger=PropertyChanged}" AutomationProperties.AutomationId="SearchBox"/>
+ SelectionChanged="DataGridSelectionChanged" Sorting="DataGridSorting" CanUserDeleteRows="False" BorderThickness="0" AutomationProperties.AutomationId="BookDataGrid">
@@ -51,7 +51,7 @@
-
+
@@ -60,7 +60,7 @@
-
+
diff --git a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/BookView.xaml b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/BookView.xaml
index 300d9c42..601c8b93 100644
--- a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/BookView.xaml
+++ b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/BookView.xaml
@@ -7,7 +7,7 @@
xmlns:waf="http://waf.codeplex.com/schemas"
xmlns:dd="clr-namespace:Waf.BookLibrary.Library.Presentation.DesignData"
mc:Ignorable="d" d:DataContext="{d:DesignInstance dd:SampleBookViewModel, IsDesignTimeCreatable=True}"
- MinWidth="250" MinHeight="270" IsEnabled="{Binding IsEnabled}"
+ MinWidth="250" MinHeight="270" IsEnabled="{Binding IsEnabled}" AutomationProperties.AutomationId="BookView"
waf:ValidationHelper.IsEnabled="true" waf:ValidationHelper.IsValid="{Binding IsValid, Mode=OneWayToSource}">
@@ -40,30 +40,30 @@
+ MaxLength="100" HorizontalAlignment="Stretch" AutomationProperties.AutomationId="TitleTextBox"/>
+ MaxLength="100" HorizontalAlignment="Stretch" AutomationProperties.AutomationId="AuthorTextBox"/>
+ MaxLength="100" HorizontalAlignment="Stretch" AutomationProperties.AutomationId="PublisherTextBox"/>
+ HorizontalAlignment="Stretch" AutomationProperties.AutomationId="PublishDatePicker"/>
+ MaxLength="14" HorizontalAlignment="Stretch" AutomationProperties.AutomationId="IsbnTextBox"/>
+ SelectedItem="{Binding Book.Language}" Height="24" AutomationProperties.AutomationId="LanguageComboBox">
@@ -74,10 +74,10 @@
+ HorizontalAlignment="Stretch" AutomationProperties.AutomationId="PagesTextBox"/>
-
+
@@ -87,7 +87,7 @@
+ MinWidth="25" AutomationProperties.AutomationId="LendToButton"/>
diff --git a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/LendToWindow.xaml b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/LendToWindow.xaml
index 89b1b302..64ede51f 100644
--- a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/LendToWindow.xaml
+++ b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/LendToWindow.xaml
@@ -11,19 +11,21 @@
-
-
+ Padding="0" DockPanel.Dock="Top" Style="{StaticResource HeaderLabel}"/>
+
+
+ Style="{StaticResource DialogButton}" AutomationProperties.AutomationId="OkButton"/>
+ Style="{StaticResource DialogButton}" Margin="11,0,0,0" AutomationProperties.AutomationId="CancelButton"/>
+ MouseDoubleClick="PersonsListMouseDoubleClick" TabIndex="2" Margin="0,11,0,0" AutomationProperties.AutomationId="PersonListBox">
diff --git a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/ShellWindow.xaml b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/ShellWindow.xaml
index 8013c84e..0f97ed7f 100644
--- a/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/ShellWindow.xaml
+++ b/src/System.Waf/Samples/BookLibrary/BookLibrary.Library.Presentation/Views/ShellWindow.xaml
@@ -19,11 +19,11 @@
@@ -65,8 +65,8 @@
-
-
+
+
@@ -83,7 +83,7 @@
-
+
@@ -100,7 +100,8 @@
-
+