mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-27 04:09:37 +08:00
465 lines
14 KiB
C#
465 lines
14 KiB
C#
using Shouldly;
|
|
using Avalonia;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Interactivity;
|
|
using Avalonia.Input;
|
|
using Avalonia.Threading;
|
|
using Avalonia.VisualTree;
|
|
using Xunit;
|
|
|
|
namespace AtomUI.Desktop.Controls.Tests.Dialog;
|
|
|
|
public class DialogSurfaceTests
|
|
{
|
|
static DialogSurfaceTests()
|
|
{
|
|
AvaloniaTestApp.EnsureInitialized();
|
|
}
|
|
|
|
[Fact]
|
|
public void Content_Binding_Preserves_Data_Objects_And_Clears_On_Dispose()
|
|
{
|
|
var initialContent = new object();
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog
|
|
{
|
|
Content = initialContent
|
|
};
|
|
var surface = new DialogSurface(dialog);
|
|
|
|
surface.Content.ShouldBeSameAs(initialContent);
|
|
|
|
dialog.Content = "updated content";
|
|
surface.Content.ShouldBe("updated content");
|
|
|
|
surface.Dispose();
|
|
dialog.Content = "after dispose";
|
|
surface.Content.ShouldBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Visual_State_And_Effective_Footer_Follow_The_Dialog()
|
|
{
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog
|
|
{
|
|
Title = "Initial",
|
|
IsFooterVisible = true,
|
|
StandardButtons = DialogStandardButton.NoButton,
|
|
IsLoading = false,
|
|
IsConfirmLoading = false,
|
|
IsClosable = true,
|
|
IsMaximizable = false,
|
|
IsMotionEnabled = true
|
|
};
|
|
var surface = new DialogSurface(dialog);
|
|
|
|
surface.Title.ShouldBe("Initial");
|
|
surface.IsEffectiveFooterVisible.ShouldBeFalse();
|
|
|
|
dialog.CustomButtons.Add(new DialogButton { Content = "Custom" });
|
|
surface.IsEffectiveFooterVisible.ShouldBeTrue();
|
|
|
|
dialog.IsLoading = true;
|
|
surface.IsEffectiveFooterVisible.ShouldBeFalse();
|
|
|
|
dialog.CustomButtons.Clear();
|
|
dialog.IsLoading = false;
|
|
dialog.StandardButtons = DialogStandardButton.Ok;
|
|
dialog.Title = "Updated";
|
|
dialog.IsConfirmLoading = true;
|
|
|
|
surface.Title.ShouldBe("Updated");
|
|
surface.IsConfirmLoading.ShouldBeTrue();
|
|
surface.StandardButtons.ShouldBe(DialogStandardButton.Ok);
|
|
surface.IsEffectiveFooterVisible.ShouldBeTrue();
|
|
|
|
surface.Dispose();
|
|
}
|
|
|
|
[Fact]
|
|
public void Custom_Buttons_Are_Synchronized_Through_The_Surface_Button_Box()
|
|
{
|
|
var first = new DialogButton { Content = "First" };
|
|
var second = new DialogButton { Content = "Second" };
|
|
var replacement = new DialogButton { Content = "Replacement" };
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog();
|
|
dialog.CustomButtons.Add(first);
|
|
dialog.CustomButtons.Add(second);
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window
|
|
{
|
|
Content = surface
|
|
};
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
|
|
var buttonBox = surface.ButtonBox.ShouldNotBeNull();
|
|
buttonBox.CustomButtons.ShouldBe(new[] { first, second });
|
|
|
|
dialog.CustomButtons[0] = replacement;
|
|
dialog.CustomButtons.Move(1, 0);
|
|
buttonBox.CustomButtons.ShouldBe(new[] { second, replacement });
|
|
|
|
dialog.CustomButtons.Clear();
|
|
buttonBox.CustomButtons.ShouldBeEmpty();
|
|
|
|
surface.Dispose();
|
|
surface.ButtonBox.ShouldBeNull();
|
|
}
|
|
finally
|
|
{
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Assigning_ButtonsConfigure_While_Open_Configures_The_Current_Sequence()
|
|
{
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog
|
|
{
|
|
StandardButtons = DialogStandardButton.Ok | DialogStandardButton.Cancel
|
|
};
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window { Content = surface };
|
|
IReadOnlyList<DialogButton>? configuredButtons = null;
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
|
|
dialog.ButtonsConfigure = buttons => configuredButtons = buttons;
|
|
|
|
configuredButtons.ShouldNotBeNull();
|
|
configuredButtons.Select(button => button.StandardButtonType)
|
|
.ShouldBe(new DialogStandardButton?[]
|
|
{
|
|
DialogStandardButton.Ok,
|
|
DialogStandardButton.Cancel
|
|
}, ignoreOrder: true);
|
|
}
|
|
finally
|
|
{
|
|
surface.Dispose();
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Button_Click_Raises_Dialog_Event_Before_Requesting_Close()
|
|
{
|
|
var button = new DialogButton
|
|
{
|
|
Content = "Accept",
|
|
Role = DialogButtonRole.AcceptRole
|
|
};
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog();
|
|
dialog.CustomButtons.Add(button);
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window
|
|
{
|
|
Content = surface
|
|
};
|
|
var buttonClickedCount = 0;
|
|
DialogSurfaceCloseRequestedEventArgs? closeRequest = null;
|
|
dialog.ButtonClicked += (_, _) => buttonClickedCount++;
|
|
surface.CloseRequested += (_, e) => closeRequest = e;
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
|
|
button.RaiseEvent(new RoutedEventArgs(AtomUI.Desktop.Controls.Button.ClickEvent));
|
|
|
|
buttonClickedCount.ShouldBe(1);
|
|
closeRequest.ShouldNotBeNull();
|
|
closeRequest.Reason.ShouldBe(DialogCloseReason.Accepted);
|
|
closeRequest.Result.ShouldBe(DialogCode.Accepted);
|
|
closeRequest.SourceButton.ShouldBeSameAs(button);
|
|
}
|
|
finally
|
|
{
|
|
surface.Dispose();
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Handled_Button_Click_Does_Not_Request_Close()
|
|
{
|
|
var button = new DialogButton
|
|
{
|
|
Content = "Accept",
|
|
Role = DialogButtonRole.AcceptRole
|
|
};
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog();
|
|
dialog.CustomButtons.Add(button);
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window
|
|
{
|
|
Content = surface
|
|
};
|
|
var closeRequestCount = 0;
|
|
dialog.ButtonClicked += (_, e) => e.Handled = true;
|
|
surface.CloseRequested += (_, _) => closeRequestCount++;
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
|
|
button.RaiseEvent(new RoutedEventArgs(AtomUI.Desktop.Controls.Button.ClickEvent));
|
|
|
|
closeRequestCount.ShouldBe(0);
|
|
}
|
|
finally
|
|
{
|
|
surface.Dispose();
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Escape_Invokes_The_Real_Default_Escape_Button_Once()
|
|
{
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog
|
|
{
|
|
StandardButtons = DialogStandardButton.Ok | DialogStandardButton.Cancel
|
|
};
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window
|
|
{
|
|
Content = surface
|
|
};
|
|
var buttonClickedCount = 0;
|
|
DialogSurfaceCloseRequestedEventArgs? closeRequest = null;
|
|
dialog.ButtonClicked += (_, _) => buttonClickedCount++;
|
|
surface.CloseRequested += (_, e) => closeRequest = e;
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
|
|
surface.TryInvokeStandardButton(Key.Escape).ShouldBeTrue();
|
|
|
|
buttonClickedCount.ShouldBe(1);
|
|
closeRequest.ShouldNotBeNull();
|
|
closeRequest.Result.ShouldBe(DialogCode.Rejected);
|
|
closeRequest.SourceButton.StandardButtonType.ShouldBe(DialogStandardButton.Cancel);
|
|
}
|
|
finally
|
|
{
|
|
surface.Dispose();
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Close_Caption_Raises_A_Host_Close_Request()
|
|
{
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog();
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window
|
|
{
|
|
Content = surface
|
|
};
|
|
var closeRequestCount = 0;
|
|
surface.HostCloseRequested += (_, _) => closeRequestCount++;
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
|
|
var header = surface.Header.ShouldNotBeNull();
|
|
var closeButton = header.GetVisualDescendants()
|
|
.OfType<DialogCaptionButton>()
|
|
.Single(button => button.Name == "PART_CloseButton");
|
|
closeButton.RaiseEvent(new RoutedEventArgs(AtomUI.Desktop.Controls.Button.ClickEvent));
|
|
|
|
closeRequestCount.ShouldBe(1);
|
|
}
|
|
finally
|
|
{
|
|
surface.Dispose();
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Resizable_Surface_Owns_One_Template_Resizer()
|
|
{
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog
|
|
{
|
|
IsResizable = true
|
|
};
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window
|
|
{
|
|
Content = surface
|
|
};
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
|
|
var resizer = surface.Resizer.ShouldNotBeNull();
|
|
resizer.IsVisible.ShouldBeTrue();
|
|
|
|
dialog.IsResizable = false;
|
|
resizer.IsVisible.ShouldBeFalse();
|
|
}
|
|
finally
|
|
{
|
|
surface.Dispose();
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Structural_Minimum_Combines_Header_Content_Viewport_And_Visible_Footer()
|
|
{
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog
|
|
{
|
|
Title = "Structural minimum",
|
|
StandardButtons = DialogStandardButton.Ok | DialogStandardButton.Cancel
|
|
};
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window { Content = surface };
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
|
|
var withFooter = surface.MeasureStructuralMinimum();
|
|
withFooter.Width.ShouldBeGreaterThanOrEqualTo(surface.ContentViewportMinWidth);
|
|
withFooter.Height.ShouldBeGreaterThan(surface.ContentViewportMinHeight);
|
|
|
|
dialog.IsFooterVisible = false;
|
|
Dispatcher.UIThread.RunJobs();
|
|
|
|
var withoutFooter = surface.MeasureStructuralMinimum();
|
|
withoutFooter.Height.ShouldBeLessThan(withFooter.Height);
|
|
}
|
|
finally
|
|
{
|
|
surface.Dispose();
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Structural_Minimum_Does_Not_Include_Arbitrary_Content_DesiredSize()
|
|
{
|
|
var content = new Border { Width = 900, Height = 700 };
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog
|
|
{
|
|
Content = content,
|
|
IsFooterVisible = false
|
|
};
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window { Content = surface };
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
|
|
var minimum = surface.MeasureStructuralMinimum();
|
|
|
|
minimum.Width.ShouldBeLessThan(content.Width);
|
|
minimum.Height.ShouldBeLessThan(content.Height);
|
|
}
|
|
finally
|
|
{
|
|
surface.Dispose();
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Structural_Minimum_Tracks_Effective_Button_Content()
|
|
{
|
|
var customButton = new DialogButton { Content = "OK" };
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog();
|
|
dialog.CustomButtons.Add(customButton);
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window { Content = surface };
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
var initialMinimum = surface.MeasureStructuralMinimum();
|
|
|
|
customButton.Content = new string('W', 80);
|
|
Dispatcher.UIThread.RunJobs();
|
|
var updatedMinimum = surface.MeasureStructuralMinimum();
|
|
|
|
updatedMinimum.Width.ShouldBeGreaterThan(initialMinimum.Width);
|
|
}
|
|
finally
|
|
{
|
|
surface.Dispose();
|
|
window.Close();
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Structural_Minimum_Change_Is_Published_When_Template_Spacing_Changes()
|
|
{
|
|
var dialog = new AtomUI.Desktop.Controls.Dialog
|
|
{
|
|
StandardButtons = DialogStandardButton.Ok | DialogStandardButton.Cancel
|
|
};
|
|
var surface = new DialogSurface(dialog);
|
|
var window = new AtomUI.Desktop.Controls.Window { Content = surface };
|
|
|
|
try
|
|
{
|
|
window.Show();
|
|
Dispatcher.UIThread.RunJobs();
|
|
surface.ApplyTemplate();
|
|
Dispatcher.UIThread.RunJobs();
|
|
|
|
var footerFrame = surface.GetVisualDescendants()
|
|
.OfType<Border>()
|
|
.Single(border => border.Name == "FooterFrame");
|
|
var initialMinimum = surface.MeasureStructuralMinimum();
|
|
var changeCount = 0;
|
|
surface.StructuralMinimumChanged += (_, _) => changeCount++;
|
|
|
|
footerFrame.Padding = new Thickness(
|
|
footerFrame.Padding.Left,
|
|
footerFrame.Padding.Top + 20,
|
|
footerFrame.Padding.Right,
|
|
footerFrame.Padding.Bottom + 20);
|
|
Dispatcher.UIThread.RunJobs();
|
|
|
|
surface.MeasureStructuralMinimum().Height.ShouldBeGreaterThan(initialMinimum.Height);
|
|
changeCount.ShouldBeGreaterThan(0);
|
|
}
|
|
finally
|
|
{
|
|
surface.Dispose();
|
|
window.Close();
|
|
}
|
|
}
|
|
}
|