From 8bd1962b15ae86443c6192864ba022e719d61d23 Mon Sep 17 00:00:00 2001 From: rabbitism Date: Tue, 8 Oct 2024 20:58:40 +0800 Subject: [PATCH 1/2] feat: 1. fix #427. 2. add headless test for this fix. --- Ursa.sln | 16 +++++ src/Ursa/Controls/Breadcrumb/Breadcrumb.cs | 15 ++++- .../Controls/Breadcrumb/BreadcrumbItem.cs | 5 ++ tests/HeadlessTest.Ursa/App.axaml | 10 +++ tests/HeadlessTest.Ursa/App.axaml.cs | 13 ++++ .../BreadcrumbTests/BreadcrumbTests.cs | 66 +++++++++++++++++++ .../HeadlessTest.Ursa.csproj | 30 +++++++++ tests/HeadlessTest.Ursa/TestAppBuilder.cs | 13 ++++ tests/Test.Ursa/Test.Ursa.csproj | 27 ++++++++ 9 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 tests/HeadlessTest.Ursa/App.axaml create mode 100644 tests/HeadlessTest.Ursa/App.axaml.cs create mode 100644 tests/HeadlessTest.Ursa/Controls/BreadcrumbTests/BreadcrumbTests.cs create mode 100644 tests/HeadlessTest.Ursa/HeadlessTest.Ursa.csproj create mode 100644 tests/HeadlessTest.Ursa/TestAppBuilder.cs create mode 100644 tests/Test.Ursa/Test.Ursa.csproj diff --git a/Ursa.sln b/Ursa.sln index 67d3ba1..7b48952 100644 --- a/Ursa.sln +++ b/Ursa.sln @@ -33,6 +33,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sandbox", "demo\Sandbox\San EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ursa.ReactiveUIExtension", "src\Ursa.ReactiveUIExtension\Ursa.ReactiveUIExtension.csproj", "{8DD57D6B-1C44-4E42-9541-8380DD17A3A4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{49243F14-2D63-4361-BE31-BAF63951DE6F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Test.Ursa", "tests\Test.Ursa\Test.Ursa.csproj", "{AC7B37DC-9B88-42C0-B176-0C274D14C68D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HeadlessTest.Ursa", "tests\HeadlessTest.Ursa\HeadlessTest.Ursa.csproj", "{2D1C279E-7FFF-4630-92CC-CB2C33C3F80F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -83,6 +89,14 @@ Global {8DD57D6B-1C44-4E42-9541-8380DD17A3A4}.Debug|Any CPU.Build.0 = Debug|Any CPU {8DD57D6B-1C44-4E42-9541-8380DD17A3A4}.Release|Any CPU.ActiveCfg = Release|Any CPU {8DD57D6B-1C44-4E42-9541-8380DD17A3A4}.Release|Any CPU.Build.0 = Release|Any CPU + {AC7B37DC-9B88-42C0-B176-0C274D14C68D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC7B37DC-9B88-42C0-B176-0C274D14C68D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC7B37DC-9B88-42C0-B176-0C274D14C68D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC7B37DC-9B88-42C0-B176-0C274D14C68D}.Release|Any CPU.Build.0 = Release|Any CPU + {2D1C279E-7FFF-4630-92CC-CB2C33C3F80F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2D1C279E-7FFF-4630-92CC-CB2C33C3F80F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D1C279E-7FFF-4630-92CC-CB2C33C3F80F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2D1C279E-7FFF-4630-92CC-CB2C33C3F80F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -95,5 +109,7 @@ Global {D1942476-8473-4608-BB9F-5AC01083BBDA} = {A41BAF0D-DA61-4A63-889A-084BAD36FD66} {F99B3D07-4560-4B05-892C-0FF2757FEF2E} = {A41BAF0D-DA61-4A63-889A-084BAD36FD66} {1E94BAFD-867E-425F-8E12-7F416D523C94} = {A41BAF0D-DA61-4A63-889A-084BAD36FD66} + {AC7B37DC-9B88-42C0-B176-0C274D14C68D} = {49243F14-2D63-4361-BE31-BAF63951DE6F} + {2D1C279E-7FFF-4630-92CC-CB2C33C3F80F} = {49243F14-2D63-4361-BE31-BAF63951DE6F} EndGlobalSection EndGlobal diff --git a/src/Ursa/Controls/Breadcrumb/Breadcrumb.cs b/src/Ursa/Controls/Breadcrumb/Breadcrumb.cs index c020dfe..a95369c 100644 --- a/src/Ursa/Controls/Breadcrumb/Breadcrumb.cs +++ b/src/Ursa/Controls/Breadcrumb/Breadcrumb.cs @@ -1,3 +1,4 @@ +using System.Collections.Specialized; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Templates; @@ -83,9 +84,19 @@ public class Breadcrumb: ItemsControl return new BreadcrumbItem(); } + protected override void LogicalChildrenCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e) + { + base.LogicalChildrenCollectionChanged(sender, e); + var breadcrumbItems = LogicalChildren.OfType().ToList(); + for (var i = 0; i < breadcrumbItems.Count; i++) + { + var breadcrumbItem = breadcrumbItems[i]; + breadcrumbItem.SetPseudoClassLast(i == breadcrumbItems.Count - 1); + } + } + protected override void PrepareContainerForItemOverride(Control container, object? item, int index) { - // base.PrepareContainerForItemOverride(container, item, index); if (container is not BreadcrumbItem breadcrumbItem) return; if (!breadcrumbItem.IsSet(BreadcrumbItem.SeparatorProperty)) { @@ -100,8 +111,6 @@ public class Breadcrumb: ItemsControl }); } - PseudolassesExtensions.Set(container.Classes, BreadcrumbItem.PC_Last, index == ItemCount - 1); - if (container == item) return; if(!breadcrumbItem.IsSet(ContentControl.ContentProperty)) { diff --git a/src/Ursa/Controls/Breadcrumb/BreadcrumbItem.cs b/src/Ursa/Controls/Breadcrumb/BreadcrumbItem.cs index ce87a47..4e93a35 100644 --- a/src/Ursa/Controls/Breadcrumb/BreadcrumbItem.cs +++ b/src/Ursa/Controls/Breadcrumb/BreadcrumbItem.cs @@ -74,4 +74,9 @@ public class BreadcrumbItem: ContentControl Command?.Execute(CommandParameter); } } + + internal void SetPseudoClassLast(bool isLast) + { + PseudoClasses.Set(PC_Last, isLast); + } } \ No newline at end of file diff --git a/tests/HeadlessTest.Ursa/App.axaml b/tests/HeadlessTest.Ursa/App.axaml new file mode 100644 index 0000000..a5c889d --- /dev/null +++ b/tests/HeadlessTest.Ursa/App.axaml @@ -0,0 +1,10 @@ + + + + + + diff --git a/tests/HeadlessTest.Ursa/App.axaml.cs b/tests/HeadlessTest.Ursa/App.axaml.cs new file mode 100644 index 0000000..eaed88e --- /dev/null +++ b/tests/HeadlessTest.Ursa/App.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace HeadlessTest.Ursa; + +public partial class App : Application +{ + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + } +} \ No newline at end of file diff --git a/tests/HeadlessTest.Ursa/Controls/BreadcrumbTests/BreadcrumbTests.cs b/tests/HeadlessTest.Ursa/Controls/BreadcrumbTests/BreadcrumbTests.cs new file mode 100644 index 0000000..9ca19c5 --- /dev/null +++ b/tests/HeadlessTest.Ursa/Controls/BreadcrumbTests/BreadcrumbTests.cs @@ -0,0 +1,66 @@ +using System.Collections.ObjectModel; +using Avalonia.Controls; +using Avalonia.Headless.XUnit; +using Avalonia.LogicalTree; +using Ursa.Controls; + +namespace HeadlessTest.Ursa.Controls; + +public class BreadcrumbTests +{ + [AvaloniaFact] + public void BreadcrumbItem_Should_Have_Correct_PseudoClasses() + { + var window = new Window(); + var breadcrumb = new Breadcrumb(); + window.Content = breadcrumb; + window.Show(); + var item1 = new BreadcrumbItem(); + var item2 = new BreadcrumbItem(); + breadcrumb.Items.Add(item1); + Assert.Contains(":last", item1.Classes); + breadcrumb.Items.Add(item2); + Assert.Contains(":last", item2.Classes); + Assert.DoesNotContain(":last", item1.Classes); + } + + [AvaloniaFact] + public void Generated_BreadcrumbItem_Should_Have_Correct_PseudoClasses() + { + var window = new Window(); + var breadcrumb = new Breadcrumb(); + window.Content = breadcrumb; + window.Show(); + var item1 = new TextBlock(); + var item2 = new TextBlock(); + breadcrumb.Items.Add(item1); + var firstItem = breadcrumb.GetLogicalChildren().OfType().FirstOrDefault(); + Assert.NotNull(firstItem); + Assert.Contains(":last", firstItem.Classes); + breadcrumb.Items.Add(item2); + var lastItem = breadcrumb.GetLogicalChildren().OfType().LastOrDefault(); + Assert.NotNull(lastItem); + Assert.Contains(":last", lastItem.Classes); + Assert.DoesNotContain(":last", firstItem.Classes); + } + + [AvaloniaFact] + public void BreadcrumbItem_FromItemsSource_Should_Have_Correct_PseudoClasses() + { + var window = new Window(); + var breadcrumb = new Breadcrumb(); + window.Content = breadcrumb; + window.Show(); + var items = new ObservableCollection(); + breadcrumb.ItemsSource = items; + items.Add("Item 1"); + var item1 = breadcrumb.GetLogicalChildren().OfType().FirstOrDefault(); + Assert.NotNull(item1); + Assert.Contains(":last", item1.Classes); + items.Add("Item 2"); + var item2 = breadcrumb.GetLogicalChildren().OfType().LastOrDefault(); + Assert.NotNull(item2); + Assert.Contains(":last", item2.Classes); + Assert.DoesNotContain(":last", item1.Classes); + } +} \ No newline at end of file diff --git a/tests/HeadlessTest.Ursa/HeadlessTest.Ursa.csproj b/tests/HeadlessTest.Ursa/HeadlessTest.Ursa.csproj new file mode 100644 index 0000000..edb294c --- /dev/null +++ b/tests/HeadlessTest.Ursa/HeadlessTest.Ursa.csproj @@ -0,0 +1,30 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/HeadlessTest.Ursa/TestAppBuilder.cs b/tests/HeadlessTest.Ursa/TestAppBuilder.cs new file mode 100644 index 0000000..e0cc73e --- /dev/null +++ b/tests/HeadlessTest.Ursa/TestAppBuilder.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Headless; +using HeadlessTest.Ursa; + +[assembly: AvaloniaTestApplication(typeof(TestAppBuilder))] + +namespace HeadlessTest.Ursa; + +public class TestAppBuilder +{ + public static AppBuilder BuildAvaloniaApp() => + AppBuilder.Configure().UseHeadless(new AvaloniaHeadlessPlatformOptions()); +} \ No newline at end of file diff --git a/tests/Test.Ursa/Test.Ursa.csproj b/tests/Test.Ursa/Test.Ursa.csproj new file mode 100644 index 0000000..ab4c0f3 --- /dev/null +++ b/tests/Test.Ursa/Test.Ursa.csproj @@ -0,0 +1,27 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + From b733621362fcfaf6573b868b3f59cc2ccfcb91eb Mon Sep 17 00:00:00 2001 From: rabbitism Date: Tue, 8 Oct 2024 22:46:38 +0800 Subject: [PATCH 2/2] test: add bulk assignment test case. --- .../BreadcrumbTests/BreadcrumbTests.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/HeadlessTest.Ursa/Controls/BreadcrumbTests/BreadcrumbTests.cs b/tests/HeadlessTest.Ursa/Controls/BreadcrumbTests/BreadcrumbTests.cs index 9ca19c5..d7d7ed1 100644 --- a/tests/HeadlessTest.Ursa/Controls/BreadcrumbTests/BreadcrumbTests.cs +++ b/tests/HeadlessTest.Ursa/Controls/BreadcrumbTests/BreadcrumbTests.cs @@ -63,4 +63,25 @@ public class BreadcrumbTests Assert.Contains(":last", item2.Classes); Assert.DoesNotContain(":last", item1.Classes); } + + [AvaloniaFact] + public void Bulk_Added_BreadcrumbItem_Should_Have_Correct_PseudoClasses() + { + var window = new Window(); + var breadcrumb = new Breadcrumb(); + window.Content = breadcrumb; + window.Show(); + var items = new ObservableCollection + { + "Item 1", + "Item 2" + }; + breadcrumb.ItemsSource = items; + var item1 = breadcrumb.GetLogicalChildren().OfType().FirstOrDefault(); + var item2 = breadcrumb.GetLogicalChildren().OfType().LastOrDefault(); + Assert.NotNull(item1); + Assert.NotNull(item2); + Assert.Contains(":last", item2.Classes); + Assert.DoesNotContain(":last", item1.Classes); + } } \ No newline at end of file