diff --git a/.github/workflows/build_uno.yml b/.github/workflows/build_uno.yml index ef2de1b4d..ce027fc36 100644 --- a/.github/workflows/build_uno.yml +++ b/.github/workflows/build_uno.yml @@ -31,4 +31,4 @@ jobs: uno-check: false uno-check-version: 1.33.1 uno-check-parameters: '--skip xcode --skip gtk3 --skip vswin --skip androidemulator --skip androidsdk --skip vsmac --skip dotnetnewunotemplates' - run-tests: false + run-tests: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6adc1e584..bac629533 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,7 +65,7 @@ jobs: uno-check: false uno-check-version: 1.33.1 uno-check-parameters: '--skip xcode --skip gtk3 --skip vswin --skip androidemulator --skip androidsdk --skip vsmac --skip dotnetnewunotemplates' - run-tests: false + run-tests: true code-sign: false artifact-name: Uno secrets: diff --git a/Directory.Packages.props b/Directory.Packages.props index ae8983617..0a5239f42 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -75,6 +75,7 @@ + diff --git a/PrismLibrary.sln b/PrismLibrary.sln index 5b2cc2f30..cd343596c 100644 --- a/PrismLibrary.sln +++ b/PrismLibrary.sln @@ -86,6 +86,16 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prism.IocContainer.Avalonia EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Avalonia", "Avalonia", "{904D5094-55F9-4581-90AC-D6C1131F8152}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Uno", "Uno", "{8B763117-8E13-0D1A-AFE0-5BC3FACCF29C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prism.Uno.WinUI.Tests", "tests\Uno\Prism.Uno.WinUI.Tests\Prism.Uno.WinUI.Tests.csproj", "{05132055-211C-42D4-B40A-39E4BB066E7B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Prism.Uno", "Prism.Uno", "{1E1EDCAE-FD32-8442-764F-7F151A12D8D4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prism.DryIoc.Uno.WinUI.Tests", "tests\Uno\Prism.DryIoc.Uno.WinUI.Tests\Prism.DryIoc.Uno.WinUI.Tests.csproj", "{158173F6-E776-422C-9127-8994C504867B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Prism.DryIoc.Uno", "Prism.DryIoc.Uno", "{064C69BC-4576-336C-79BF-6771226CD9DC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -372,6 +382,30 @@ Global {887E0794-798D-4127-847E-6F68D5C9B334}.Release|x64.Build.0 = Release|Any CPU {887E0794-798D-4127-847E-6F68D5C9B334}.Release|x86.ActiveCfg = Release|Any CPU {887E0794-798D-4127-847E-6F68D5C9B334}.Release|x86.Build.0 = Release|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Debug|x64.ActiveCfg = Debug|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Debug|x64.Build.0 = Debug|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Debug|x86.ActiveCfg = Debug|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Debug|x86.Build.0 = Debug|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Release|Any CPU.Build.0 = Release|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Release|x64.ActiveCfg = Release|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Release|x64.Build.0 = Release|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Release|x86.ActiveCfg = Release|Any CPU + {05132055-211C-42D4-B40A-39E4BB066E7B}.Release|x86.Build.0 = Release|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Debug|x64.ActiveCfg = Debug|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Debug|x64.Build.0 = Debug|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Debug|x86.ActiveCfg = Debug|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Debug|x86.Build.0 = Debug|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Release|Any CPU.Build.0 = Release|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Release|x64.ActiveCfg = Release|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Release|x64.Build.0 = Release|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Release|x86.ActiveCfg = Release|Any CPU + {158173F6-E776-422C-9127-8994C504867B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -409,6 +443,11 @@ Global {03B9C775-9582-409F-B67F-6B8A0CE0C7AF} = {904D5094-55F9-4581-90AC-D6C1131F8152} {887E0794-798D-4127-847E-6F68D5C9B334} = {904D5094-55F9-4581-90AC-D6C1131F8152} {904D5094-55F9-4581-90AC-D6C1131F8152} = {00FFDC13-7397-46F1-897E-A62A7575D28A} + {8B763117-8E13-0D1A-AFE0-5BC3FACCF29C} = {00FFDC13-7397-46F1-897E-A62A7575D28A} + {05132055-211C-42D4-B40A-39E4BB066E7B} = {8B763117-8E13-0D1A-AFE0-5BC3FACCF29C} + {1E1EDCAE-FD32-8442-764F-7F151A12D8D4} = {8F959801-D494-4CAF-9437-90F30472E169} + {158173F6-E776-422C-9127-8994C504867B} = {8B763117-8E13-0D1A-AFE0-5BC3FACCF29C} + {064C69BC-4576-336C-79BF-6771226CD9DC} = {8F959801-D494-4CAF-9437-90F30472E169} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C7433AE2-B1A0-4C1A-887E-5CAA7AAF67A6} diff --git a/PrismLibrary_Uno.slnf b/PrismLibrary_Uno.slnf index b9e1e45ef..8b995f6cb 100644 --- a/PrismLibrary_Uno.slnf +++ b/PrismLibrary_Uno.slnf @@ -7,7 +7,9 @@ "src\\Uno\\Prism.DryIoc.Uno\\Prism.DryIoc.Uno.WinUI.csproj", "src\\Uno\\Prism.Uno\\Prism.Uno.WinUI.csproj", "src\\Uno\\Prism.Uno.Markup\\Prism.Uno.WinUI.Markup.csproj", - "tests\\Prism.Core.Tests\\Prism.Core.Tests.csproj" + "tests\\Prism.Core.Tests\\Prism.Core.Tests.csproj", + "tests\\Uno\\Prism.DryIoc.Uno.WinUI.Tests\\Prism.DryIoc.Uno.WinUI.Tests.csproj", + "tests\\Uno\\Prism.Uno.WinUI.Tests\\Prism.Uno.WinUI.Tests.csproj" ] } } diff --git a/tests/Uno/Prism.DryIoc.Uno.WinUI.Tests/ContainerHelper.cs b/tests/Uno/Prism.DryIoc.Uno.WinUI.Tests/ContainerHelper.cs new file mode 100644 index 000000000..dc6f78024 --- /dev/null +++ b/tests/Uno/Prism.DryIoc.Uno.WinUI.Tests/ContainerHelper.cs @@ -0,0 +1,18 @@ +using DryIoc; +using Prism.Container.DryIoc; +using Prism.Ioc; + +namespace Prism.Container.Uno.Tests; + +public static class ContainerHelper +{ + private static Rules CreateContainerRules() => Rules.Default.WithAutoConcreteTypeResolution() + .With(Made.Of(FactoryMethod.ConstructorWithResolvableArguments)) + .WithDefaultIfAlreadyRegistered(IfAlreadyRegistered.Replace); + + public static IContainer CreateContainer() => new global::DryIoc.Container(CreateContainerRules()); + + public static IContainerExtension CreateContainerExtension() => new DryIocContainerExtension(CreateContainer()); + + public static Type RegisteredFrameworkException => typeof(ContainerException); +} diff --git a/tests/Uno/Prism.DryIoc.Uno.WinUI.Tests/Prism.DryIoc.Uno.WinUI.Tests.csproj b/tests/Uno/Prism.DryIoc.Uno.WinUI.Tests/Prism.DryIoc.Uno.WinUI.Tests.csproj new file mode 100644 index 000000000..25aa574a5 --- /dev/null +++ b/tests/Uno/Prism.DryIoc.Uno.WinUI.Tests/Prism.DryIoc.Uno.WinUI.Tests.csproj @@ -0,0 +1,35 @@ + + + + net10.0 + false + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + diff --git a/tests/Uno/Prism.DryIoc.Uno.WinUI.Tests/PrismApplicationFixture.cs b/tests/Uno/Prism.DryIoc.Uno.WinUI.Tests/PrismApplicationFixture.cs new file mode 100644 index 000000000..525f1e72d --- /dev/null +++ b/tests/Uno/Prism.DryIoc.Uno.WinUI.Tests/PrismApplicationFixture.cs @@ -0,0 +1,63 @@ +using System.Reflection; +using System.Runtime.Serialization; +using DryIoc; +using Prism.Container.DryIoc; +using Prism.Ioc; +using Xunit; +using static Prism.Container.Uno.Tests.ContainerHelper; +using ExceptionExtensions = System.ExceptionExtensions; + +namespace Prism.DryIoc.Uno.WinUI.Tests; + +public class PrismApplicationFixture +{ + [Fact] + public void CreateContainerExtensionReturnsDryIocContainerExtension() + { + var app = (PrismApplication)FormatterServices.GetUninitializedObject(typeof(PrismApplicationProxy)); + var method = typeof(PrismApplication).GetMethod("CreateContainerExtension", BindingFlags.NonPublic | BindingFlags.Instance); + + var container = method!.Invoke(app, null); + + Assert.IsType(container!); + } + + [Fact] + public void RegisterFrameworkExceptionTypesRegistersContainerException() + { + var app = (PrismApplication)FormatterServices.GetUninitializedObject(typeof(PrismApplicationProxy)); + var method = typeof(PrismApplication).GetMethod("RegisterFrameworkExceptionTypes", BindingFlags.NonPublic | BindingFlags.Instance); + + method!.Invoke(app, null); + + Assert.True(ExceptionExtensions.IsFrameworkExceptionRegistered(RegisteredFrameworkException)); + } + + [Fact] + public void CreateContainerRulesReturnsDryIocRules() + { + var app = (PrismApplication)FormatterServices.GetUninitializedObject(typeof(PrismApplicationProxy)); + var method = typeof(PrismApplication).GetMethod("CreateContainerRules", BindingFlags.NonPublic | BindingFlags.Instance); + + var rules = method!.Invoke(app, null); + + Assert.NotNull(rules); + Assert.IsType(rules); + } + + [Fact] + public void PrismApplicationRemainsAbstractAndExtendsPrismApplicationBase() + { + Assert.True(typeof(PrismApplication).IsAbstract); + Assert.Equal(typeof(PrismApplicationBase), typeof(PrismApplication).BaseType); + } + + private sealed class PrismApplicationProxy : PrismApplication + { + protected override UIElement CreateShell() => null!; + + protected override void RegisterTypes(IContainerRegistry containerRegistry) + { + } + } +} diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/ApiContractInventoryFixture.cs b/tests/Uno/Prism.Uno.WinUI.Tests/ApiContractInventoryFixture.cs new file mode 100644 index 000000000..81791ac63 --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/ApiContractInventoryFixture.cs @@ -0,0 +1,61 @@ +using System.Reflection; +using Xunit; + +namespace Prism.Uno.WinUI.Tests; + +public class ApiContractInventoryFixture +{ + private static readonly Assembly PrismAssembly = typeof(PrismApplicationBase).Assembly; + + public static IEnumerable ExportedPrismTypeCases() + { + return PrismAssembly + .GetExportedTypes() + .Where(t => t.Namespace is not null && t.Namespace.StartsWith("Prism", StringComparison.Ordinal)) + .OrderBy(t => t.FullName, StringComparer.Ordinal) + .Select(t => new object[] { t.FullName! }); + } + + public static IEnumerable PublicMemberCases() + { + const BindingFlags memberFlags = + BindingFlags.Public | + BindingFlags.Instance | + BindingFlags.Static | + BindingFlags.DeclaredOnly; + + return PrismAssembly + .GetExportedTypes() + .Where(t => t.Namespace is not null && t.Namespace.StartsWith("Prism", StringComparison.Ordinal)) + .OrderBy(t => t.FullName, StringComparer.Ordinal) + .SelectMany(t => t + .GetMembers(memberFlags) + .Where(m => m.MemberType is MemberTypes.Method or MemberTypes.Property or MemberTypes.Field or MemberTypes.Event) + .OrderBy(m => m.Name, StringComparer.Ordinal) + .Select(m => new object[] { t.FullName!, m.Name, m.MemberType.ToString() })) + .Take(450); + } + + [Theory] + [MemberData(nameof(ExportedPrismTypeCases))] + public void ExportedPrismTypeIsLoadable(string fullTypeName) + { + Assert.NotNull(PrismAssembly.GetType(fullTypeName)); + } + + [Theory] + [MemberData(nameof(PublicMemberCases))] + public void PublicContractMemberIsDiscoverable(string fullTypeName, string memberName, string memberKind) + { + var type = PrismAssembly.GetType(fullTypeName); + Assert.NotNull(type); + + const BindingFlags lookupFlags = + BindingFlags.Public | + BindingFlags.Instance | + BindingFlags.Static; + + var members = type!.GetMember(memberName, lookupFlags); + Assert.Contains(members, m => string.Equals(m.MemberType.ToString(), memberKind, StringComparison.Ordinal)); + } +} diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/CoreRegionCoverageFixture.cs b/tests/Uno/Prism.Uno.WinUI.Tests/CoreRegionCoverageFixture.cs new file mode 100644 index 000000000..5fb6eda66 --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/CoreRegionCoverageFixture.cs @@ -0,0 +1,129 @@ +using System.Collections.Specialized; +using Moq; +using Prism.Ioc; +using Prism.Navigation; +using Prism.Navigation.Regions; +using Xunit; + +namespace Prism.Uno.WinUI.Tests; + +public class CoreRegionCoverageFixture +{ + [Fact] + public void RegionManagerCanAddAndRetrieveRegionByName() + { + var regionManager = new RegionManager(); + var region = new Region { Name = "MainRegion" }; + + regionManager.Regions.Add(region); + + Assert.Same(region, regionManager.Regions["MainRegion"]); + } + + [Fact] + public void RegionManagerContainsRegionWithNameReflectsState() + { + var regionManager = new RegionManager(); + Assert.False(regionManager.Regions.ContainsRegionWithName("Target")); + + regionManager.Regions.Add(new Region { Name = "Target" }); + Assert.True(regionManager.Regions.ContainsRegionWithName("Target")); + } + + [Fact] + public void RegionManagerRemovingRegionClearsRegionManagerReference() + { + var regionManager = new RegionManager(); + var region = new Region { Name = "RemoveMe" }; + regionManager.Regions.Add(region); + + var removed = regionManager.Regions.Remove("RemoveMe"); + + Assert.True(removed); + Assert.Null(region.RegionManager); + } + + [Fact] + public void RegionManagerCreateRegionManagerReturnsNewInstance() + { + var manager = new RegionManager(); + var child = manager.CreateRegionManager(); + + Assert.NotNull(child); + Assert.NotSame(manager, child); + Assert.IsType(child); + } + + [Fact] + public void RegionManagerCollectionChangedFiresOnAddAndRemove() + { + var manager = new RegionManager(); + NotifyCollectionChangedEventArgs? lastArgs = null; + manager.Regions.CollectionChanged += (_, e) => lastArgs = e; + var region = new Region { Name = "Region1" }; + + manager.Regions.Add(region); + Assert.NotNull(lastArgs); + Assert.Equal(NotifyCollectionChangedAction.Add, lastArgs!.Action); + + manager.Regions.Remove("Region1"); + Assert.NotNull(lastArgs); + Assert.Equal(NotifyCollectionChangedAction.Remove, lastArgs!.Action); + } + + [Fact] + public void RegionGetViewReturnsNullForUnknownName() + { + var region = new Region(); + Assert.Null(region.GetView("Unknown")); + } + + [Fact] + public void RegionNameCannotBeChangedAfterFirstSet() + { + var region = new Region { Name = "Initial" }; + Assert.Throws(() => region.Name = "Changed"); + } + + [Fact] + public void RegionContextChangeRaisesPropertyChanged() + { + var region = new Region(); + var changed = false; + region.PropertyChanged += (_, e) => + { + if (e.PropertyName == nameof(region.Context)) + { + changed = true; + } + }; + + region.Context = "CTX"; + Assert.True(changed); + } + + [Fact] + public void NavigationContextParsesQueryParametersFromUri() + { + var uri = new Uri("target?name=value", UriKind.Relative); + var navService = new Mock(); + navService.SetupGet(x => x.Journal).Returns(Mock.Of()); + + var context = new NavigationContext(navService.Object, uri); + + Assert.Equal(uri, context.Uri); + Assert.NotNull(context.Parameters); + } + + [Fact] + public void NavigationContextWithoutQueryHasEmptyParameters() + { + var uri = new Uri("target", UriKind.Relative); + var navService = new Mock(); + navService.SetupGet(x => x.Journal).Returns(Mock.Of()); + + var context = new NavigationContext(navService.Object, uri); + + Assert.Empty(context.Parameters); + } +} diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/DialogAndInteractivityFixture.cs b/tests/Uno/Prism.Uno.WinUI.Tests/DialogAndInteractivityFixture.cs new file mode 100644 index 000000000..3bda29a11 --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/DialogAndInteractivityFixture.cs @@ -0,0 +1,96 @@ +#nullable enable +using Microsoft.Xaml.Interactivity; +using Prism.Dialogs; +using Prism.Interactivity; +using Xunit; + +namespace Prism.Uno.WinUI.Tests; + +public class DialogAndInteractivityFixture +{ + [Fact] + public void KnownDialogParametersContainsUnoDialogPlacementKey() + { + Assert.Equal("dialogPlacement", KnownDialogParameters.DialogPlacement); + } + + [Fact] + public void DialogServiceExposesShowDialogMethod() + { + var method = typeof(DialogService).GetMethod(nameof(DialogService.ShowDialog)); + + Assert.NotNull(method); + Assert.Equal(typeof(void), method!.ReturnType); + Assert.Equal(3, method.GetParameters().Length); + } + + [Fact] + public void InvokeCommandActionImplementsIAction() + { + Assert.Contains(typeof(IAction), typeof(InvokeCommandAction).GetInterfaces()); + } + + [Fact] + public void DialogServiceExposesExpectedInternalConfigurationMethods() + { + Assert.NotNull(typeof(DialogService).GetMethod("CreateDialogWindow", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + Assert.NotNull(typeof(DialogService).GetMethod("ConfigureDialogWindowContent", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + Assert.NotNull(typeof(DialogService).GetMethod("ConfigureDialogWindowEvents", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + } + + [Fact] + public void DialogWindowImplementsIDialogWindowContract() + { + Assert.Contains(typeof(IDialogWindow), typeof(DialogWindow).GetInterfaces()); + + var showAsync = typeof(IDialogWindow).GetMethod("ShowAsync"); + var hide = typeof(IDialogWindow).GetMethod("Hide"); + Assert.NotNull(showAsync); + Assert.NotNull(hide); + } + + [Fact] + public void CreateDialogWindowUsesContainerResolveForNamedWindow() + { + var container = new Moq.Mock(); + container.Setup(x => x.Resolve(typeof(IDialogWindow), "MainWindow")).Returns(new TestDialogWindow()); + var service = new DialogService(container.Object); + var method = typeof(DialogService).GetMethod("CreateDialogWindow", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + var result = method!.Invoke(service, new object?[] { "MainWindow" }); + + Assert.NotNull(result); + Assert.IsAssignableFrom(result); + } + + [Fact] + public void ConfigureDialogWindowContentThrowsForNonFrameworkElement() + { + var container = new Moq.Mock(); + container.Setup(x => x.Resolve(typeof(object), "SampleDialog")).Returns(new object()); + var service = new DialogService(container.Object); + var method = typeof(DialogService).GetMethod("ConfigureDialogWindowContent", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + var ex = Assert.Throws(() => + method!.Invoke(service, new object[] { "SampleDialog", new TestDialogWindow(), new DialogParameters() })); + + Assert.IsType(ex.InnerException); + } + + private sealed class TestDialogWindow : IDialogWindow + { + public object? DataContext { get; set; } + public Style Style { get; set; } = null!; + public event RoutedEventHandler? Loaded; + public event Windows.Foundation.TypedEventHandler? Closing; + public event Windows.Foundation.TypedEventHandler? Closed; + public IDialogResult? Result { get; set; } + public object? Content { get; set; } + + public Windows.Foundation.IAsyncOperation ShowAsync(ContentDialogPlacement placement) => throw new NotImplementedException(); + + public void Hide() + { + } + } +} diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/InvokeCommandActionFixture.cs b/tests/Uno/Prism.Uno.WinUI.Tests/InvokeCommandActionFixture.cs new file mode 100644 index 000000000..befde5537 --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/InvokeCommandActionFixture.cs @@ -0,0 +1,46 @@ +using Prism.Interactivity; +using Xunit; + +namespace Prism.Uno.WinUI.Tests; + +public class InvokeCommandActionFixture +{ + [Fact] + public void ImplementsIActionContract() + { + Assert.Contains(typeof(Microsoft.Xaml.Interactivity.IAction), typeof(InvokeCommandAction).GetInterfaces()); + } + + [Fact] + public void ExposesExpectedDependencyProperties() + { + Assert.NotNull(typeof(InvokeCommandAction).GetField("AutoEnableProperty")); + Assert.NotNull(typeof(InvokeCommandAction).GetField("CommandProperty")); + Assert.NotNull(typeof(InvokeCommandAction).GetField("CommandParameterProperty")); + Assert.NotNull(typeof(InvokeCommandAction).GetField("TriggerParameterPathProperty")); + } + + [Fact] + public void ExposesExecuteMethod() + { + var execute = typeof(InvokeCommandAction).GetMethod(nameof(InvokeCommandAction.Execute)); + Assert.NotNull(execute); + Assert.Equal(typeof(object), execute!.ReturnType); + } + + [Fact] + public void ExposesAutoEnableAndCommandProperties() + { + Assert.NotNull(typeof(InvokeCommandAction).GetProperty(nameof(InvokeCommandAction.AutoEnable))); + Assert.NotNull(typeof(InvokeCommandAction).GetProperty(nameof(InvokeCommandAction.Command))); + Assert.NotNull(typeof(InvokeCommandAction).GetProperty(nameof(InvokeCommandAction.CommandParameter))); + Assert.NotNull(typeof(InvokeCommandAction).GetProperty(nameof(InvokeCommandAction.TriggerParameterPath))); + } + + [Fact] + public void ContainsExecutableCommandBehaviorNestedType() + { + var nestedType = typeof(InvokeCommandAction).GetNestedType("ExecutableCommandBehavior", System.Reflection.BindingFlags.NonPublic); + Assert.NotNull(nestedType); + } +} diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/NavigationViewRegionAdapterFixture.cs b/tests/Uno/Prism.Uno.WinUI.Tests/NavigationViewRegionAdapterFixture.cs new file mode 100644 index 000000000..c12690958 --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/NavigationViewRegionAdapterFixture.cs @@ -0,0 +1,55 @@ +using Prism.Navigation.Regions; +using Xunit; + +namespace Prism.Uno.WinUI.Tests; + +public class NavigationViewRegionAdapterFixture +{ + [Fact] + public void AdapterIsSealedAndTargetsNavigationView() + { + Assert.True(typeof(NavigationViewRegionAdapter).IsSealed); + Assert.Equal(typeof(RegionAdapterBase), typeof(NavigationViewRegionAdapter).BaseType); + } + + [Fact] + public void AdapterProvidesExpectedRegionFactoryMethod() + { + var method = typeof(NavigationViewRegionAdapter).GetMethod( + "CreateRegion", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + Assert.NotNull(method); + Assert.Equal(typeof(IRegion), method!.ReturnType); + } + + [Fact] + public void AdapterExposesAdaptOverride() + { + var method = typeof(NavigationViewRegionAdapter).GetMethod( + "Adapt", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + Assert.NotNull(method); + Assert.Equal(typeof(void), method!.ReturnType); + + var parameters = method.GetParameters(); + Assert.Equal(2, parameters.Length); + Assert.Equal(typeof(IRegion), parameters[0].ParameterType); + Assert.Equal(typeof(NavigationView), parameters[1].ParameterType); + } + + [Fact] + public void CreateRegionReturnsSingleActiveRegion() + { + var behaviorFactory = new Moq.Mock(); + var adapter = new NavigationViewRegionAdapter(behaviorFactory.Object); + + var method = typeof(NavigationViewRegionAdapter).GetMethod( + "CreateRegion", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + var region = method!.Invoke(adapter, null); + Assert.IsType(region); + } +} diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/Prism.Uno.WinUI.Tests.csproj b/tests/Uno/Prism.Uno.WinUI.Tests/Prism.Uno.WinUI.Tests.csproj new file mode 100644 index 000000000..e2ed7000d --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/Prism.Uno.WinUI.Tests.csproj @@ -0,0 +1,35 @@ + + + + net10.0 + false + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/PrismApplicationBaseFixture.cs b/tests/Uno/Prism.Uno.WinUI.Tests/PrismApplicationBaseFixture.cs new file mode 100644 index 000000000..30aaf4d46 --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/PrismApplicationBaseFixture.cs @@ -0,0 +1,50 @@ +using System.Runtime.Serialization; +using Prism.Ioc; +using Xunit; + +namespace Prism.Uno.WinUI.Tests; + +public class PrismApplicationBaseFixture +{ + [Fact] + public void HostThrowsUntilShellFinalizationRuns() + { + var app = (PrismApplicationBase)FormatterServices.GetUninitializedObject(typeof(TestPrismApplication)); + + var ex = Assert.Throws(() => _ = app.Host); + Assert.Contains("host has not yet been created", ex.Message, StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public void PrismApplicationBaseExposesExpectedExtensibilitySurface() + { + Assert.True(typeof(PrismApplicationBase).IsAbstract); + Assert.True(typeof(PrismApplicationBase).GetMethod("OnLaunched", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!.IsFinal); + Assert.NotNull(typeof(PrismApplicationBase).GetMethod("CreateShell", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + Assert.NotNull(typeof(PrismApplicationBase).GetMethod("RegisterTypes", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + Assert.NotNull(typeof(PrismApplicationBase).GetMethod("ConfigureApp", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + Assert.NotNull(typeof(PrismApplicationBase).GetMethod("ConfigureHost", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + Assert.NotNull(typeof(PrismApplicationBase).GetMethod("ConfigureServices", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + Assert.NotNull(typeof(PrismApplicationBase).GetMethod("ConfigureWindow", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + Assert.NotNull(typeof(PrismApplicationBase).GetMethod("InitializeModules", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + Assert.NotNull(typeof(PrismApplicationBase).GetMethod("ConfigureModuleCatalog", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)); + } + + [Fact] + public void ContainerPropertyReturnsContainerProvider() + { + var app = (PrismApplicationBase)FormatterServices.GetUninitializedObject(typeof(TestPrismApplication)); + Assert.Null(app.Container); + } + + private sealed class TestPrismApplication : PrismApplicationBase + { + protected override IContainerExtension CreateContainerExtension() => throw new NotImplementedException(); + + protected override UIElement CreateShell() => null!; + + protected override void RegisterTypes(IContainerRegistry containerRegistry) + { + } + } +} diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/RegionManagerRequestNavigateFixture.cs b/tests/Uno/Prism.Uno.WinUI.Tests/RegionManagerRequestNavigateFixture.cs new file mode 100644 index 000000000..5ce548fbe --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/RegionManagerRequestNavigateFixture.cs @@ -0,0 +1,102 @@ +using Moq; +using Prism.Navigation; +using Prism.Navigation.Regions; +using Xunit; + +namespace Prism.Uno.WinUI.Tests; + +public class RegionManagerRequestNavigateFixture +{ + private const string Region = "Region"; + private const string NonExistentRegion = "NonExistentRegion"; + private const string Source = "Source"; + private static readonly Uri SourceUri = new("Source", UriKind.RelativeOrAbsolute); + private static readonly NavigationParameters Parameters = new(); + + [Fact] + public void ThrowsWhenNavigationCallbackIsNull() + { + var regionManager = new RegionManager(); + + Assert.Throws(() => regionManager.RequestNavigate(Region, Source, null!, Parameters)); + Assert.Throws(() => regionManager.RequestNavigate(Region, Source, navigationCallback: null!)); + Assert.Throws(() => regionManager.RequestNavigate(Region, SourceUri, null!)); + Assert.Throws(() => regionManager.RequestNavigate(Region, SourceUri, null!, Parameters)); + } + + [Fact] + public void ReturnsFailedResultWhenRegionDoesNotExist() + { + var regionManager = new RegionManager(); + NavigationResult? result = null; + + regionManager.RequestNavigate(NonExistentRegion, Source, r => result = r, Parameters); + Assert.NotNull(result); + Assert.False(result!.Success); + + result = null; + regionManager.RequestNavigate(NonExistentRegion, Source, r => result = r); + Assert.NotNull(result); + Assert.False(result!.Success); + + result = null; + regionManager.RequestNavigate(NonExistentRegion, SourceUri, r => result = r, Parameters); + + Assert.NotNull(result); + Assert.False(result!.Success); + } + + [Fact] + public void DelegatesCallToMatchingRegion() + { + var region = new Mock(); + region.SetupGet(x => x.Name).Returns(Region); + + var regionManager = new RegionManager(); + regionManager.Regions.Add(region.Object); + + regionManager.RequestNavigate(Region, SourceUri, _ => { }, Parameters); + + region.Verify(x => x.RequestNavigate(SourceUri, It.IsAny>(), Parameters), Times.Once); + } + + [Fact] + public void DelegatesCallToRegionForSourceStringOverloads() + { + var region = new Mock(); + region.SetupGet(x => x.Name).Returns(Region); + var callback = new Action(_ => { }); + + var regionManager = new RegionManager(); + regionManager.Regions.Add(region.Object); + + regionManager.RequestNavigate(Region, Source); + regionManager.RequestNavigate(Region, Source, Parameters); + regionManager.RequestNavigate(Region, Source, callback); + regionManager.RequestNavigate(Region, Source, callback, Parameters); + + region.Verify(x => x.RequestNavigate(SourceUri, It.IsAny>(), It.IsAny()), Times.AtLeastOnce); + region.Verify(x => x.RequestNavigate(SourceUri, callback, It.IsAny()), Times.AtLeastOnce); + region.Verify(x => x.RequestNavigate(SourceUri, callback, Parameters), Times.AtLeastOnce); + } + + [Fact] + public void DelegatesCallToRegionForUriOverloads() + { + var region = new Mock(); + region.SetupGet(x => x.Name).Returns(Region); + var callback = new Action(_ => { }); + + var regionManager = new RegionManager(); + regionManager.Regions.Add(region.Object); + + regionManager.RequestNavigate(Region, SourceUri); + regionManager.RequestNavigate(Region, SourceUri, Parameters); + regionManager.RequestNavigate(Region, SourceUri, callback); + regionManager.RequestNavigate(Region, SourceUri, callback, Parameters); + + region.Verify(x => x.RequestNavigate(SourceUri, It.IsAny>(), It.IsAny()), Times.AtLeastOnce); + region.Verify(x => x.RequestNavigate(SourceUri, callback, It.IsAny()), Times.AtLeastOnce); + region.Verify(x => x.RequestNavigate(SourceUri, callback, Parameters), Times.AtLeastOnce); + } +} diff --git a/tests/Uno/Prism.Uno.WinUI.Tests/UnoApiUtilityFixture.cs b/tests/Uno/Prism.Uno.WinUI.Tests/UnoApiUtilityFixture.cs new file mode 100644 index 000000000..1ebddb5d0 --- /dev/null +++ b/tests/Uno/Prism.Uno.WinUI.Tests/UnoApiUtilityFixture.cs @@ -0,0 +1,33 @@ +using Prism.Mvvm; +using Xunit; + +namespace Prism.Uno.WinUI.Tests; + +public class UnoApiUtilityFixture +{ + [Fact] + public void ViewModelLocatorExposesAttachedPropertyContract() + { + Assert.NotNull(typeof(ViewModelLocator).GetField("AutowireViewModelProperty")); + Assert.NotNull(typeof(ViewModelLocator).GetMethod(nameof(ViewModelLocator.GetAutowireViewModel))); + Assert.NotNull(typeof(ViewModelLocator).GetMethod(nameof(ViewModelLocator.SetAutowireViewModel))); + } + + [Fact] + public void UnoInternalUtilityTypesExposeExpectedMethods() + { + var prismAssembly = typeof(PrismApplicationBase).Assembly; + + var bindingOperations = prismAssembly.GetType("Prism.BindingOperations"); + var designerProperties = prismAssembly.GetType("Prism.DesignerProperties"); + var dependencyObjectExtensions = prismAssembly.GetType("Prism.DependencyObjectExtensions"); + + Assert.NotNull(bindingOperations); + Assert.NotNull(designerProperties); + Assert.NotNull(dependencyObjectExtensions); + + Assert.NotNull(bindingOperations!.GetMethod("GetBinding", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)); + Assert.NotNull(designerProperties!.GetMethod("GetIsInDesignMode", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)); + Assert.NotNull(dependencyObjectExtensions!.GetMethod("CheckAccess", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static)); + } +}