diff --git a/Directory.build.props b/Directory.build.props index d981f6c..81b41b8 100644 --- a/Directory.build.props +++ b/Directory.build.props @@ -69,4 +69,9 @@ True + + + + \ No newline at end of file diff --git a/src/Prism.Container.Extensions/Prism.Container.Extensions.csproj b/src/Prism.Container.Extensions/Prism.Container.Extensions.csproj index 8ba07b5..4f4cdb7 100644 --- a/src/Prism.Container.Extensions/Prism.Container.Extensions.csproj +++ b/src/Prism.Container.Extensions/Prism.Container.Extensions.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Prism.DryIoc.Extensions/Prism.DryIoc.Extensions.csproj b/src/Prism.DryIoc.Extensions/Prism.DryIoc.Extensions.csproj index e158bb5..e06e866 100644 --- a/src/Prism.DryIoc.Extensions/Prism.DryIoc.Extensions.csproj +++ b/src/Prism.DryIoc.Extensions/Prism.DryIoc.Extensions.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Prism.Forms.Extended/Ioc/RequiredTypesExtensions.cs b/src/Prism.Forms.Extended/Ioc/RequiredTypesExtensions.cs index 0cb64f8..7499b3a 100644 --- a/src/Prism.Forms.Extended/Ioc/RequiredTypesExtensions.cs +++ b/src/Prism.Forms.Extended/Ioc/RequiredTypesExtensions.cs @@ -20,7 +20,7 @@ public static class RequiredTypesExtensions public static void RegisterRequiredTypes(this IContainerRegistry containerRegistry) { - containerRegistry.RegisterManySingletonOnce(); + containerRegistry.RegisterManySingletonOnce(typeof(IAggregateLogger), typeof(ILogger), typeof(IAnalyticsService), typeof(ICrashesService)); containerRegistry.RegisterSingletonOnce(); containerRegistry.RegisterSingletonOnce(); containerRegistry.RegisterSingletonOnce(); @@ -32,17 +32,17 @@ public static void RegisterRequiredTypes(this IContainerRegistry containerRegist containerRegistry.RegisterSingletonOnce(); containerRegistry.RegisterSingletonOnce(); containerRegistry.RegisterSingletonOnce(); - //containerRegistry.RegisterScoped(NavigationServiceName); + containerRegistry.Register(NavigationServiceName); containerRegistry.RegisterScoped(); var isRegistered = containerRegistry.IsRegistered(NavigationServiceName); } public static void RegisterPrismCoreServices(this IServiceCollection services) { - services.RegisterSingletonIfNotRegistered(); - services.RegisterSingletonIfNotRegistered(sp => (ILogger)sp.GetService(typeof(ILogger))); - services.RegisterSingletonIfNotRegistered(sp => (ILogger)sp.GetService(typeof(ILogger))); - services.RegisterSingletonIfNotRegistered(sp => (ILogger)sp.GetService(typeof(ILogger))); + services.RegisterSingletonIfNotRegistered(); + services.RegisterSingletonIfNotRegistered(sp => (IAggregableLogger)sp.GetService(typeof(IAggregableLogger))); + services.RegisterSingletonIfNotRegistered(sp => (IAggregableLogger)sp.GetService(typeof(IAggregableLogger))); + services.RegisterSingletonIfNotRegistered(sp => (IAggregableLogger)sp.GetService(typeof(IAggregableLogger))); services.RegisterSingletonIfNotRegistered(); services.RegisterSingletonIfNotRegistered(); services.RegisterSingletonIfNotRegistered(); diff --git a/src/Prism.Forms.Extended/Navigation/ErrorReportingNavigationService.cs b/src/Prism.Forms.Extended/Navigation/ErrorReportingNavigationService.cs index 4d5a63a..9b20ea3 100644 --- a/src/Prism.Forms.Extended/Navigation/ErrorReportingNavigationService.cs +++ b/src/Prism.Forms.Extended/Navigation/ErrorReportingNavigationService.cs @@ -15,9 +15,8 @@ public class ErrorReportingNavigationService : PageNavigationService public ErrorReportingNavigationService(IContainerExtension container, IApplicationProvider applicationProvider, IPageBehaviorFactory pageBehaviorFactory, - ILoggerFacade logger, IEventAggregator eventAggregator) - : base(container, applicationProvider, pageBehaviorFactory, logger) + : base(container, applicationProvider, pageBehaviorFactory) { EventAggregator = eventAggregator; } diff --git a/src/Prism.Forms.Extended/Prism.Forms.Extended.csproj b/src/Prism.Forms.Extended/Prism.Forms.Extended.csproj index bf99c4c..24d8ebc 100644 --- a/src/Prism.Forms.Extended/Prism.Forms.Extended.csproj +++ b/src/Prism.Forms.Extended/Prism.Forms.Extended.csproj @@ -29,8 +29,8 @@ - - + + diff --git a/src/Prism.Forms.Extended/PrismApplication.android.cs b/src/Prism.Forms.Extended/PrismApplication.android.cs index 8e71631..b8cee6f 100644 --- a/src/Prism.Forms.Extended/PrismApplication.android.cs +++ b/src/Prism.Forms.Extended/PrismApplication.android.cs @@ -28,9 +28,8 @@ protected override void Initialize() base.Initialize(); Logger = Container.Resolve(); - Log.Listeners.Add(Container.Resolve()); + Log.Listeners.Add(new FormsLogListener(Logger)); Container.Resolve().GetEvent().Subscribe(OnNavigationError); - ConfigureAggregateLogger(Container.Resolve(), Container); } private void AndroidEnvironment_UnhandledExceptionRaiser(object sender, RaiseThrowableEventArgs args) diff --git a/src/Prism.Forms.Extended/PrismApplication.cs b/src/Prism.Forms.Extended/PrismApplication.cs index cb3f2ce..d6e44a2 100644 --- a/src/Prism.Forms.Extended/PrismApplication.cs +++ b/src/Prism.Forms.Extended/PrismApplication.cs @@ -2,12 +2,10 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization.Json; using System.Text; using System.Threading.Tasks; -using Prism.Container.Extensions; using Prism.Events; using Prism.Forms.Extended.ViewModels; using Prism.Ioc; @@ -34,15 +32,11 @@ protected PrismApplication(IPlatformInitializer platformInitializer) : base(plat { } - protected PrismApplication(IPlatformInitializer platformInitializer, bool setFormsDependencyResolver) : base(platformInitializer, setFormsDependencyResolver) - { - } - public ILogger Logger { get; private set; } protected IModuleCatalog ModuleCatalog { get; private set; } - protected override sealed void RegisterRequiredTypes(IContainerRegistry containerRegistry) + protected override void RegisterRequiredTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterRequiredTypes(); ViewModelLocationProvider.Register(); @@ -53,14 +47,6 @@ protected override IContainerExtension CreateContainerExtension() return ContainerLocator.Current ?? throw new NullReferenceException("Call PrismContainerExtension.Init() prior to initializing PrismApplication"); } - protected virtual void ConfigureAggregateLogger(IAggregateLogger aggregateLogger, IContainerProvider container) - { - if(!aggregateLogger.Loggers.Any()) - { - aggregateLogger.AddLogger(container.Resolve()); - } - } - protected sealed override void InitializeModules() { if (ModuleCatalog is null) diff --git a/src/Prism.Forms.Extended/PrismApplication.iosmac.cs b/src/Prism.Forms.Extended/PrismApplication.iosmac.cs index 7537e83..eeca4fa 100644 --- a/src/Prism.Forms.Extended/PrismApplication.iosmac.cs +++ b/src/Prism.Forms.Extended/PrismApplication.iosmac.cs @@ -25,9 +25,8 @@ protected override void Initialize() base.Initialize(); Logger = Container.Resolve(); - Log.Listeners.Add(Container.Resolve()); + Log.Listeners.Add(new FormsLogListener(Logger)); Container.Resolve().GetEvent().Subscribe(OnNavigationError); - ConfigureAggregateLogger(Container.Resolve(), Container); } private void Runtime_MarshalObjectiveCException(object sender, MarshalObjectiveCExceptionEventArgs args) diff --git a/src/Prism.Forms.Extended/PrismApplication.netstandard.cs b/src/Prism.Forms.Extended/PrismApplication.netstandard.cs index e4874f5..8b43b70 100644 --- a/src/Prism.Forms.Extended/PrismApplication.netstandard.cs +++ b/src/Prism.Forms.Extended/PrismApplication.netstandard.cs @@ -22,9 +22,8 @@ protected override void Initialize() base.Initialize(); Logger = Container.Resolve(); - Log.Listeners.Add(Container.Resolve()); + Log.Listeners.Add(new FormsLogListener(Logger)); Container.Resolve().GetEvent().Subscribe(OnNavigationError); - ConfigureAggregateLogger(Container.Resolve(), Container); } } } \ No newline at end of file diff --git a/src/Prism.Forms.Extended/PrismApplicationBaseExtended.cs b/src/Prism.Forms.Extended/PrismApplicationBaseExtended.cs deleted file mode 100644 index 18d46e5..0000000 --- a/src/Prism.Forms.Extended/PrismApplicationBaseExtended.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using Xamarin.Forms; - -[assembly: InternalsVisibleTo("Prism.DryIoc.Forms.Extended.Tests")] -[assembly: InternalsVisibleTo("Prism.Unity.Forms.Extended.Tests")] -[assembly: XmlnsDefinition("http://prismlibrary.com", "Prism")] -[assembly: XmlnsDefinition("http://prismlibrary.com", "Prism.Platform")] -namespace Prism -{ - [Obsolete("PrismApplication is now directly provided in Prism.Forms.Extended.")] - public abstract class PrismApplicationBaseExtended : PrismApplication - { - protected PrismApplicationBaseExtended() - { - } - - protected PrismApplicationBaseExtended(IPlatformInitializer platformInitializer) : base(platformInitializer) - { - } - - protected PrismApplicationBaseExtended(IPlatformInitializer platformInitializer, bool setFormsDependencyResolver) : base(platformInitializer, setFormsDependencyResolver) - { - } - } -} diff --git a/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareOverrideProvider.cs b/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareOverrideProvider.cs new file mode 100644 index 0000000..243b0d9 --- /dev/null +++ b/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareOverrideProvider.cs @@ -0,0 +1,57 @@ +using System; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; + +namespace Prism.Microsoft.DependencyInjection.Extensions +{ + internal class ConcreteAwareOverrideProvider : IServiceProvider + { + private IServiceProvider _rootProvider { get; } + private IServiceCollection _services { get; } + private (Type type, object instance)[] _overrides { get; } + + public ConcreteAwareOverrideProvider(IServiceProvider serviceProvider, IServiceCollection services, (Type type, object instance)[] overrides) + { + _rootProvider = serviceProvider; + _overrides = overrides; + } + + public object GetService(Type serviceType) + { + if (!serviceType.IsAbstract && serviceType.IsClass && serviceType != typeof(object)) + { + return BuildInstance(serviceType); + } + + var serviceDescriptor = _services.LastOrDefault(x => x.ServiceType == serviceType); + + if (serviceDescriptor?.ImplementationType is null) + return _rootProvider.GetService(serviceType); + + var implType = serviceDescriptor.ImplementationType; + return BuildInstance(implType); + } + + private object BuildInstance(Type implType) + { + var constructors = implType.GetConstructors(); + + if (constructors is null || !constructors.Any()) + return Activator.CreateInstance(implType); + + var ctor = constructors.OrderByDescending(x => x.GetParameters().Length).First(); + var parameters = ctor.GetParameters().Select(x => + { + (var t, var instance) = _overrides.FirstOrDefault(o => x.ParameterType == o.type); + if (t != null) + { + return instance; + } + + return _rootProvider.GetService(x.ParameterType); + }).ToArray(); + + return ctor.Invoke(parameters); + } + } +} diff --git a/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareServiceProvider.cs b/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareServiceProvider.cs index b6f1b05..4f21a18 100644 --- a/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareServiceProvider.cs +++ b/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareServiceProvider.cs @@ -6,11 +6,19 @@ namespace Prism.Microsoft.DependencyInjection { public class ConcreteAwareServiceProvider : IServiceProvider { + private bool _isScoped { get; } + public ConcreteAwareServiceProvider(IServiceProvider serviceProvider) { ServiceProvider = serviceProvider; } + public ConcreteAwareServiceProvider(IServiceScope serviceScope) + { + ServiceProvider = serviceScope.ServiceProvider; + _isScoped = true; + } + public IServiceProvider ServiceProvider { get; } public object GetService(Type serviceType) => @@ -22,6 +30,9 @@ private object GetConcreteImplementation(Type serviceType) if (serviceType.IsClass) { + if (_isScoped) + BuildConcreteImplementation(serviceType); + PrismContainerExtension.Current.Register(serviceType, serviceType); var sp = PrismContainerExtension.Current.ServiceCollection().BuildServiceProvider(); return sp.GetService(serviceType); @@ -34,5 +45,18 @@ private object GetConcreteImplementation(Type serviceType) return null; } + + private object BuildConcreteImplementation(Type serviceType) + { + var constructors = serviceType.GetConstructors(); + + if (!constructors.Any()) + return Activator.CreateInstance(serviceType); + + var ctor = constructors.OrderByDescending(x => x.GetParameters().Length).First(); + + var parameters = ctor.GetParameters().Select(p => GetService(p.ParameterType)).ToArray(); + return ctor.Invoke(parameters); + } } } diff --git a/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareServiceScope.cs b/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareServiceScope.cs index beff183..230e0fc 100644 --- a/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareServiceScope.cs +++ b/src/Prism.Microsoft.DependencyInjection.Extensions/ConcreteAwareServiceScope.cs @@ -10,9 +10,10 @@ public class ConcreteAwareServiceScope : IServiceScope public ConcreteAwareServiceScope(IServiceScope serviceScope) { _serviceScope = serviceScope; + ServiceProvider = new ConcreteAwareServiceProvider(_serviceScope.ServiceProvider); ; } - public IServiceProvider ServiceProvider => _serviceScope.ServiceProvider; + public IServiceProvider ServiceProvider { get; } #region IDisposable Support private bool disposedValue = false; // To detect redundant calls diff --git a/src/Prism.Microsoft.DependencyInjection.Extensions/Prism.Microsoft.DependencyInjection.Extensions.csproj b/src/Prism.Microsoft.DependencyInjection.Extensions/Prism.Microsoft.DependencyInjection.Extensions.csproj index 1f2d95d..eb03433 100644 --- a/src/Prism.Microsoft.DependencyInjection.Extensions/Prism.Microsoft.DependencyInjection.Extensions.csproj +++ b/src/Prism.Microsoft.DependencyInjection.Extensions/Prism.Microsoft.DependencyInjection.Extensions.csproj @@ -4,7 +4,7 @@ netstandard2.0 prism di iserviceprovider iservicecollection microsoft-dependencyinjection dansiegel - Prism Container Extensions for the Microsoft.Extensions.DependencyInjection implementations of IServiceCollection / IServiceProvider + Prism Container Extensions for the Microsoft.Extensions.DependencyInjection implementations of IServiceCollection / IServiceProvider. NOTE: This is an EXPERIMENTAL Container! While this may basic basic tests, this container may still have unknown issues as the Microsoft.Extensions.DependencyInjection package inheriently does not support either Named Types or Container Mutability. diff --git a/src/Prism.Microsoft.DependencyInjection.Extensions/PrismContainerExtension.cs b/src/Prism.Microsoft.DependencyInjection.Extensions/PrismContainerExtension.cs index 70ba858..b042e03 100644 --- a/src/Prism.Microsoft.DependencyInjection.Extensions/PrismContainerExtension.cs +++ b/src/Prism.Microsoft.DependencyInjection.Extensions/PrismContainerExtension.cs @@ -1,21 +1,33 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Prism.Container.Extensions; using Prism.Ioc; -using Prism.Microsoft.DependencyInjection; +using Prism.Ioc.Internals; +using Prism.Microsoft.DependencyInjection.Extensions; [assembly: InternalsVisibleTo("Prism.Microsoft.DependencyInjection.Extensions.Tests")] [assembly: InternalsVisibleTo("Prism.Microsoft.DependencyInjection.Forms.Extended.Tests")] [assembly: InternalsVisibleTo("Shiny.Prism.Tests")] namespace Prism.Microsoft.DependencyInjection { - public class PrismContainerExtension : IContainerExtension, IServiceCollectionAware, IServiceProvider + public class PrismContainerExtension : IContainerExtension, IServiceCollectionAware, IServiceProvider, IContainerInfo { - public static IContainerExtension Current => - ContainerLocator.Current ?? Init(); + public static IContainerExtension Current + { + get + { + var container = TryGetContainer(); + if (container != null) + return container; + + Init(); + return ContainerLocator.Current; + } + } public IScopedProvider CurrentScope { get; private set; } @@ -30,7 +42,7 @@ public static IContainerExtension Init() => public static IContainerExtension Init(IServiceCollection services) { - if (ContainerLocator.Current != null) + if (TryGetContainer() != null) throw new NotSupportedException("The PrismContainerExtension has already been initialized."); var extension = new PrismContainerExtension(services); @@ -38,6 +50,21 @@ public static IContainerExtension Init(IServiceCollection services) return extension; } + private static IContainerExtension TryGetContainer() + { + try + { + return ContainerLocator.Current; + } + catch + { + return null; + } + } + + private Dictionary _typeFactories { get; } + private Func _defaultViewTypeToViewModelTypeResolver { get; } + private IServiceScope _serviceScope; private bool requiresRebuild; private NamedServiceRegistry NamedServiceRegistry { get; } @@ -178,7 +205,14 @@ public IContainerRegistry Register(Type from, Type to) public IContainerRegistry Register(Type from, Type to, string name) { requiresRebuild = true; + Services.AddTransient(to); NamedServiceRegistry.Add(name, from, to); + + if(from == typeof(object) && to.Namespace.Contains("Views")) + { + + } + return this; } @@ -335,12 +369,26 @@ private IServiceProvider GetChildProvider((Type Type, object Instance)[] paramet var rootSP = services.BuildServiceProvider(); - if(_serviceScope is null) + if (_serviceScope is null) { return new ConcreteAwareServiceProvider(rootSP); } - throw new NotSupportedException("We do not currently support using a child container within a ServiceScope"); + return new ConcreteAwareOverrideProvider(Instance, Services, parameters); + } + + Type IContainerInfo.GetRegistrationType(string key) + { + var matchingRegistration = NamedServiceRegistry.GetRegistrationType(key); + if (matchingRegistration != null) + return matchingRegistration; + + return Services.FirstOrDefault(r => key.Equals(r.ImplementationType.Name, StringComparison.Ordinal))?.ImplementationType; + } + + Type IContainerInfo.GetRegistrationType(Type serviceType) + { + return Services.FirstOrDefault(x => x.ServiceType == serviceType)?.ImplementationType; } private class ScopedProvider : IScopedProvider diff --git a/src/Shiny.Prism/Navigation/INavigationServiceDelegate.cs b/src/Shiny.Prism/Navigation/INavigationServiceDelegate.cs index 727d4c0..9e89eb1 100644 --- a/src/Shiny.Prism/Navigation/INavigationServiceDelegate.cs +++ b/src/Shiny.Prism/Navigation/INavigationServiceDelegate.cs @@ -1,6 +1,6 @@ namespace Prism.Navigation { - public interface INavigationServiceDelegate : INavigationService, IPlatformNavigationService + public interface INavigationServiceDelegate : INavigationService { } diff --git a/src/Shiny.Prism/Navigation/NavigationServiceDelegate.cs b/src/Shiny.Prism/Navigation/NavigationServiceDelegate.cs index db510f6..ed8237b 100644 --- a/src/Shiny.Prism/Navigation/NavigationServiceDelegate.cs +++ b/src/Shiny.Prism/Navigation/NavigationServiceDelegate.cs @@ -86,7 +86,7 @@ Task INavigationService.NavigateAsync(string name, INavigatio #endregion #region IPlatformNavigationService - Task IPlatformNavigationService.GoBackAsync(INavigationParameters parameters, bool? useModalNavigation, bool animated) + Task INavigationService.GoBackAsync(INavigationParameters parameters, bool? useModalNavigation, bool animated) { var navService = GetNavigationService(); if (navService is null) @@ -96,7 +96,7 @@ Task IPlatformNavigationService.GoBackAsync(INavigationParame return navService.GoBackAsync(parameters, useModalNavigation, animated); } - Task IPlatformNavigationService.GoBackToRootAsync(INavigationParameters parameters) + Task INavigationService.GoBackToRootAsync(INavigationParameters parameters) { var navService = GetNavigationService(); if (navService is null) @@ -106,7 +106,7 @@ Task IPlatformNavigationService.GoBackToRootAsync(INavigation return navService.GoBackToRootAsync(parameters); } - Task IPlatformNavigationService.NavigateAsync(string name, INavigationParameters parameters, bool? useModalNavigation, bool animated) + Task INavigationService.NavigateAsync(string name, INavigationParameters parameters, bool? useModalNavigation, bool animated) { var navService = GetNavigationService(); if (navService is null) @@ -116,7 +116,7 @@ Task IPlatformNavigationService.NavigateAsync(string name, IN return navService.NavigateAsync(name, parameters, useModalNavigation, animated); } - Task IPlatformNavigationService.NavigateAsync(Uri uri, INavigationParameters parameters, bool? useModalNavigation, bool animated) + Task INavigationService.NavigateAsync(Uri uri, INavigationParameters parameters, bool? useModalNavigation, bool animated) { var navService = GetNavigationService(); if (navService is null) diff --git a/tests/Prism.Container.Extensions.Mocks/Prism.Container.Extensions.Mocks.csproj b/tests/Prism.Container.Extensions.Mocks/Prism.Container.Extensions.Mocks.csproj index 8a7e813..9124f55 100644 --- a/tests/Prism.Container.Extensions.Mocks/Prism.Container.Extensions.Mocks.csproj +++ b/tests/Prism.Container.Extensions.Mocks/Prism.Container.Extensions.Mocks.csproj @@ -6,4 +6,8 @@ Prism.Container.Extensions.Tests + + + + diff --git a/tests/Prism.DryIoc.Extensions.Tests/Prism.DryIoc.Extensions.Tests.csproj b/tests/Prism.DryIoc.Extensions.Tests/Prism.DryIoc.Extensions.Tests.csproj index 0096520..60dc612 100644 --- a/tests/Prism.DryIoc.Extensions.Tests/Prism.DryIoc.Extensions.Tests.csproj +++ b/tests/Prism.DryIoc.Extensions.Tests/Prism.DryIoc.Extensions.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 false $(DefineConstants);DRYIOC @@ -12,6 +12,7 @@ + all diff --git a/tests/Prism.DryIoc.Forms.Extended.Tests/Prism.DryIoc.Forms.Extended.Tests.csproj b/tests/Prism.DryIoc.Forms.Extended.Tests/Prism.DryIoc.Forms.Extended.Tests.csproj index 1b34b92..39e41a7 100644 --- a/tests/Prism.DryIoc.Forms.Extended.Tests/Prism.DryIoc.Forms.Extended.Tests.csproj +++ b/tests/Prism.DryIoc.Forms.Extended.Tests/Prism.DryIoc.Forms.Extended.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 false Prism.DryIoc.Forms.Extended $(DefineConstants);DRYIOC @@ -9,6 +9,7 @@ + diff --git a/tests/Prism.Forms.Extended.Mocks/Mocks/Navigation/MockNavigationService.cs b/tests/Prism.Forms.Extended.Mocks/Mocks/Navigation/MockNavigationService.cs index c290169..a04961e 100644 --- a/tests/Prism.Forms.Extended.Mocks/Mocks/Navigation/MockNavigationService.cs +++ b/tests/Prism.Forms.Extended.Mocks/Mocks/Navigation/MockNavigationService.cs @@ -23,6 +23,24 @@ public Task GoBackAsync(INavigationParameters parameters) return Task.FromResult(Result); } + public Task GoBackAsync(INavigationParameters parameters, bool? useModalNavigation, bool animated) + { + if(parameters is null) + { + parameters = new NavigationParameters(); + } + if(useModalNavigation.HasValue) + { + parameters.Add(KnownNavigationParameters.UseModalNavigation, useModalNavigation); + } + return GoBackAsync(parameters); + } + + public Task GoBackToRootAsync(INavigationParameters parameters) + { + return GoBackAsync(parameters); + } + public Task NavigateAsync(Uri uri) => NavigateAsync(uri, null); public Task NavigateAsync(Uri uri, INavigationParameters parameters) @@ -42,6 +60,32 @@ public Task NavigateAsync(string name, INavigationParameters ParametersUsed = parameters; return Task.FromResult(Result); } + + public Task NavigateAsync(string name, INavigationParameters parameters, bool? useModalNavigation, bool animated) + { + if (parameters is null) + { + parameters = new NavigationParameters(); + } + if (useModalNavigation.HasValue) + { + parameters.Add(KnownNavigationParameters.UseModalNavigation, useModalNavigation); + } + return NavigateAsync(name, parameters); + } + + public Task NavigateAsync(Uri uri, INavigationParameters parameters, bool? useModalNavigation, bool animated) + { + if (parameters is null) + { + parameters = new NavigationParameters(); + } + if (useModalNavigation.HasValue) + { + parameters.Add(KnownNavigationParameters.UseModalNavigation, useModalNavigation); + } + return NavigateAsync(uri, parameters); + } } public class MockNavigationResult : INavigationResult diff --git a/tests/Prism.Forms.Extended.Mocks/Prism.Forms.Extended.Mocks.projitems b/tests/Prism.Forms.Extended.Mocks/Prism.Forms.Extended.Mocks.projitems index 1aa9ff2..780d398 100644 --- a/tests/Prism.Forms.Extended.Mocks/Prism.Forms.Extended.Mocks.projitems +++ b/tests/Prism.Forms.Extended.Mocks/Prism.Forms.Extended.Mocks.projitems @@ -23,6 +23,7 @@ Code + diff --git a/tests/Prism.Forms.Extended.Mocks/Tests/ExtendedPageBehaviorFactoryTests.cs b/tests/Prism.Forms.Extended.Mocks/Tests/ExtendedPageBehaviorFactoryTests.cs new file mode 100644 index 0000000..180e55f --- /dev/null +++ b/tests/Prism.Forms.Extended.Mocks/Tests/ExtendedPageBehaviorFactoryTests.cs @@ -0,0 +1,101 @@ +using System.Reflection; +using Moq; +using Prism.Behaviors; +using Prism.Forms.Extended.ViewModels; +using Prism.Navigation; +using Xamarin.Forms; +using Xamarin.Forms.PlatformConfiguration; +using Xamarin.Forms.PlatformConfiguration.AndroidSpecific; +using Xamarin.Forms.PlatformConfiguration.iOSSpecific; +using Xunit; +using AndroidTabbedPage = Xamarin.Forms.PlatformConfiguration.AndroidSpecific.TabbedPage; +using NavigationPage = Xamarin.Forms.NavigationPage; +using TabbedPage = Xamarin.Forms.TabbedPage; + +namespace Prism.Forms.Extended.Tests +{ + public class ExtendedPageBehaviorFactoryTests + { + [Fact] + public void SetsPreferLargeTitles() + { + Xamarin.Forms.Mocks.MockForms.Init(Device.iOS); + var _ = new Xamarin.Forms.Application(); + IPageBehaviorFactory factory = new ExtendedPageBehaviorFactory(new DefaultPageBehaviorFactoryOptions()); + + var page = new NavigationPage(); + ConfigurePage(page); + factory.ApplyPageBehaviors(page); + + Assert.True(page.On().PrefersLargeTitles()); + } + + [Fact] + public void SetsUseSafeArea() + { + Xamarin.Forms.Mocks.MockForms.Init(Device.iOS); + var _ = new Xamarin.Forms.Application(); + IPageBehaviorFactory factory = new ExtendedPageBehaviorFactory(new DefaultPageBehaviorFactoryOptions()); + + var page = new ContentPage(); + ConfigurePage(page); + factory.ApplyPageBehaviors(page); + + Assert.True(page.On().UsingSafeArea()); + } + + [Fact] + public void SetUseBottomTabs() + { + Xamarin.Forms.Mocks.MockForms.Init(Device.Android); + var _ = new Xamarin.Forms.Application(); + IPageBehaviorFactory factory = new ExtendedPageBehaviorFactory(new DefaultPageBehaviorFactoryOptions()); + + var page = new TabbedPage(); + ConfigurePage(page); + factory.ApplyPageBehaviors(page); + + Assert.Equal(ToolbarPlacement.Bottom, AndroidTabbedPage.GetToolbarPlacement(page)); + } + + [Fact] + public void SetsTitleBinding() + { + Xamarin.Forms.Mocks.MockForms.Init(); + var _ = new Xamarin.Forms.Application(); + IPageBehaviorFactory factory = new ExtendedPageBehaviorFactory(new DefaultPageBehaviorFactoryOptions()); + + var page = new TabbedPage + { + BindingContext = new DefaultViewModel() + }; + + ConfigurePage(page); + factory.ApplyPageBehaviors(page); + + Assert.True(page.IsSet(Xamarin.Forms.Page.TitleProperty)); + } + + [Fact] + public void AddsChildTitleBehavior() + { + Xamarin.Forms.Mocks.MockForms.Init(); + var _ = new Xamarin.Forms.Application(); + IPageBehaviorFactory factory = new ExtendedPageBehaviorFactory(new DefaultPageBehaviorFactoryOptions()); + + var page = new TabbedPage(); + ConfigurePage(page); + factory.ApplyPageBehaviors(page); + + Assert.Contains(page.Behaviors, b => b.GetType() == typeof(TabbedPageChildTitleBehavior)); + } + + private void ConfigurePage(Xamarin.Forms.Page page) + { + var prismNavigationType = typeof(Prism.Navigation.Xaml.Navigation); + var navServicePropInfo = prismNavigationType.GetField("NavigationServiceProperty", BindingFlags.Static | BindingFlags.NonPublic); + var navServiceProp = (BindableProperty)navServicePropInfo.GetValue(null); + page.SetValue(navServiceProp, Mock.Of()); + } + } +} diff --git a/tests/Prism.Forms.Extended.Mocks/Tests/PrismApplicationTests.cs b/tests/Prism.Forms.Extended.Mocks/Tests/PrismApplicationTests.cs index 53ad19c..86b1ca6 100644 --- a/tests/Prism.Forms.Extended.Mocks/Tests/PrismApplicationTests.cs +++ b/tests/Prism.Forms.Extended.Mocks/Tests/PrismApplicationTests.cs @@ -29,11 +29,18 @@ namespace Prism.Forms.Extended.Tests { - public class PrismApplicationTests : IDisposable + public sealed class PrismApplicationTests : IDisposable { + public PrismApplicationTests() + { + Xamarin.Forms.Mocks.MockForms.Init(); + PrismContainerExtension.Init(); + } + [Fact] public void PageBehaviorFactorySetsTabbedPageTitle() { + Assert.Null(Record.Exception(() => PrismContainerExtension.Current)); var app = CreateApp(); var pageBehaviorFactory = app.Container.Resolve(); @@ -61,7 +68,7 @@ public async Task TabbedPageGetsTitleSetFromNavigationUri() var app = CreateApp(); var result = await app.NavigationService.NavigateAsync("/TabbedPage?createTab=ViewA&createTab=ViewB&title=Title%20From%20Uri"); - Assert.True(result.Success); + Assert.Null(result.Exception); Assert.IsType(app.MainPage); Assert.IsType(app.MainPage.BindingContext); Assert.Equal("Title From Uri", app.MainPage.Title); @@ -73,7 +80,7 @@ public async Task AndroidTabbedPageHasBottomTabs() var app = CreateApp(Device.Android); var result = await app.NavigationService.NavigateAsync("/TabbedPage?createTab=ViewA&createTab=ViewB"); - Assert.True(result.Success); + Assert.Null(result.Exception); Assert.IsType(app.MainPage); var toolbarPlacement = AndroidTabbedPage.GetToolbarPlacement(app.MainPage); @@ -128,19 +135,41 @@ public void ErrorReportingNavigationServiceIsRegistered() var app = CreateApp(); INavigationService navService = null; - // TODO: Remove internal constants - var ex = Record.Exception(() => navService = app.Container.Resolve(PrismApplicationBase.NavigationServiceName)); + var ex = Record.Exception(() => navService = app.Container.Resolve()); + + Assert.Null(ex); + Assert.NotNull(navService); + navService = null; + + // The unnamed resolve is Scoped while the Named is transient + ex = Record.Exception(() => navService = app.Container.Resolve(PrismApplicationBase.NavigationServiceName)); + + if(ex is ContainerResolutionException cre) + { + var errors = cre.GetErrors(); + } Assert.Null(ex); Assert.NotNull(navService); Assert.IsType(navService); } -#if MICROSOFT_DI - [Fact(Skip = "Not supported")] -#else [Fact] -#endif + public async Task AppRegistersDefaultPageBehaviorFactoryOptions() + { + var app = CreateApp(); + IPageBehaviorFactoryOptions options = null; + var ex = Record.Exception(() => options = app.Container.Resolve()); + + Assert.Null(ex); + + Assert.True(options.PreferLargeTitles); + Assert.True(options.UseBottomTabs); + Assert.True(options.UseChildTitle); + Assert.True(options.UseSafeArea); + } + + [Fact] public async Task NavigationServiceHasExpectedPage() { var app = CreateApp(); @@ -168,14 +197,15 @@ public async Task NavigationServiceHasExpectedPage() private AppMock CreateApp(string runtimePlatform = "Test") { + Assert.Null(Record.Exception(() => PrismContainerExtension.Current)); Xamarin.Forms.Mocks.MockForms.Init(runtimePlatform); - PrismContainerExtension.Reset(); return new AppMock(); } public void Dispose() { PrismContainerExtension.Reset(); + PageNavigationRegistry.ClearRegistrationCache(); } } } diff --git a/tests/Prism.Microsoft.DependencyInjection.Extensions.Forms.Tests/Prism.Microsoft.DependencyInjection.Forms.Extended.Tests.csproj b/tests/Prism.Microsoft.DependencyInjection.Extensions.Forms.Tests/Prism.Microsoft.DependencyInjection.Forms.Extended.Tests.csproj index 96077bb..d71ccab 100644 --- a/tests/Prism.Microsoft.DependencyInjection.Extensions.Forms.Tests/Prism.Microsoft.DependencyInjection.Forms.Extended.Tests.csproj +++ b/tests/Prism.Microsoft.DependencyInjection.Extensions.Forms.Tests/Prism.Microsoft.DependencyInjection.Forms.Extended.Tests.csproj @@ -1,13 +1,14 @@  - netcoreapp3.0 + netcoreapp3.1 false $(DefineConstants);MICROSOFT_DI + diff --git a/tests/Prism.Microsoft.DependencyInjection.Extensions.Tests/Prism.Microsoft.DependencyInjection.Extensions.Tests.csproj b/tests/Prism.Microsoft.DependencyInjection.Extensions.Tests/Prism.Microsoft.DependencyInjection.Extensions.Tests.csproj index 43a43d3..8280672 100644 --- a/tests/Prism.Microsoft.DependencyInjection.Extensions.Tests/Prism.Microsoft.DependencyInjection.Extensions.Tests.csproj +++ b/tests/Prism.Microsoft.DependencyInjection.Extensions.Tests/Prism.Microsoft.DependencyInjection.Extensions.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 false $(DefineConstants);MICROSOFT_DI @@ -11,6 +11,7 @@ + all diff --git a/tests/Prism.Unity.Extensions.Tests/Prism.Unity.Extensions.Tests.csproj b/tests/Prism.Unity.Extensions.Tests/Prism.Unity.Extensions.Tests.csproj index 7ffd530..4ac58e0 100644 --- a/tests/Prism.Unity.Extensions.Tests/Prism.Unity.Extensions.Tests.csproj +++ b/tests/Prism.Unity.Extensions.Tests/Prism.Unity.Extensions.Tests.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + netcoreapp3.1 false $(DefineConstants);UNITY @@ -12,6 +12,7 @@ + all diff --git a/tests/Prism.Unity.Forms.Extended.Tests/Prism.Unity.Forms.Extended.Tests.csproj b/tests/Prism.Unity.Forms.Extended.Tests/Prism.Unity.Forms.Extended.Tests.csproj index cdf7d50..86ebd30 100644 --- a/tests/Prism.Unity.Forms.Extended.Tests/Prism.Unity.Forms.Extended.Tests.csproj +++ b/tests/Prism.Unity.Forms.Extended.Tests/Prism.Unity.Forms.Extended.Tests.csproj @@ -1,12 +1,13 @@  - netcoreapp3.0 + netcoreapp3.1 false $(DefineConstants);UNITY + diff --git a/tests/Shiny.Prism.Tests/Shiny.Prism.Tests.csproj b/tests/Shiny.Prism.Tests/Shiny.Prism.Tests.csproj index 3700846..649c21c 100644 --- a/tests/Shiny.Prism.Tests/Shiny.Prism.Tests.csproj +++ b/tests/Shiny.Prism.Tests/Shiny.Prism.Tests.csproj @@ -1,13 +1,14 @@  - netcoreapp3.0 + netcoreapp3.1 false Shiny.Prism +