diff --git a/Directory.Build.props b/Directory.Build.props index 5d3a1861f..430f44d86 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ net8.0 - 8.0.14 + 8.0.21 latest enable enable diff --git a/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementPage.xaml.cs b/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementPage.xaml.cs index 1ffcb95ac..0f140212c 100644 --- a/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementPage.xaml.cs +++ b/samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementPage.xaml.cs @@ -212,7 +212,7 @@ async void ChangeAspectClicked(object? sender, EventArgs e) "Cancel", null, Aspect.AspectFit.ToString(), Aspect.AspectFill.ToString(), Aspect.Fill.ToString()); - if (resultAspect.Equals("Cancel")) + if (resultAspect is null || resultAspect.Equals("Cancel")) { return; } diff --git a/src/CommunityToolkit.Maui.UnitTests/BaseHandlerTest.cs b/src/CommunityToolkit.Maui.UnitTests/BaseHandlerTest.cs index 578ba9fb7..270dc5a87 100644 --- a/src/CommunityToolkit.Maui.UnitTests/BaseHandlerTest.cs +++ b/src/CommunityToolkit.Maui.UnitTests/BaseHandlerTest.cs @@ -13,7 +13,7 @@ protected BaseHandlerTest() protected IServiceProvider ServiceProvider { get; } - protected static TElementHandler CreateElementHandler(Microsoft.Maui.IElement view, bool hasMauiContext = true) + protected static TElementHandler CreateElementHandler(IElement view, bool hasMauiContext = true) where TElementHandler : IElementHandler, new() { var mockElementHandler = new TElementHandler(); diff --git a/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs b/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs index af9ddcfd6..7f291210d 100644 --- a/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs +++ b/src/CommunityToolkit.Maui.UnitTests/BaseTest.cs @@ -18,8 +18,14 @@ protected enum TestDuration protected BaseTest() { + CompatibilityCheck.UseCompatibility(); + defaultCulture = Thread.CurrentThread.CurrentCulture; defaultUiCulture = Thread.CurrentThread.CurrentUICulture; + + DeviceDisplay.SetCurrent(null); + DeviceInfo.SetCurrent(null); + AppInfo.SetCurrent(null); DispatcherProvider.SetCurrent(new MockDispatcherProvider()); DeviceDisplay.SetCurrent(null); diff --git a/src/CommunityToolkit.Maui.UnitTests/Essentials/AppThemeTests.cs b/src/CommunityToolkit.Maui.UnitTests/Essentials/AppThemeTests.cs index 75454d249..fa2ae2087 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Essentials/AppThemeTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Essentials/AppThemeTests.cs @@ -4,15 +4,21 @@ namespace CommunityToolkit.Maui.UnitTests.Essentials; -public class AppThemeTests : BaseTest +public class AppThemeTests : BaseHandlerTest { readonly MockAppInfo mockAppInfo; readonly Application app; public AppThemeTests() { - AppInfo.SetCurrent(mockAppInfo = new() { RequestedTheme = AppTheme.Light }); + const AppTheme initialAppTheme = AppTheme.Light; + AppInfo.SetCurrent(mockAppInfo = new() { RequestedTheme = initialAppTheme }); + Application.Current = app = new Application(); + + SetAppTheme(initialAppTheme); + + Assert.Equal(initialAppTheme, app.PlatformAppTheme); } [Fact] @@ -28,10 +34,12 @@ public void AppThemeColorUsesCorrectColorForTheme() { Text = "Green on Light, Red on Dark" }; - label.SetAppThemeColor(Label.TextColorProperty, color); - Application.Current = null; + app.MainPage = new ContentPage + { + Content = label + }; Assert.Equal(Colors.Green, label.TextColor); @@ -56,7 +64,10 @@ public void AppThemeColorUsesDefaultColorWhenDarkColorNotSet() label.SetAppThemeColor(Label.TextColorProperty, color); - Application.Current = null; + app.MainPage = new ContentPage + { + Content = label + }; Assert.Equal(Colors.Green, label.TextColor); @@ -81,7 +92,10 @@ public void AppThemeColorUsesDefaultColorWhenLightColorNotSet() label.SetAppThemeColor(Label.TextColorProperty, color); - Application.Current = null; + app.MainPage = new ContentPage + { + Content = label + }; Assert.Equal(Colors.Blue, label.TextColor); @@ -103,7 +117,11 @@ public void AppThemeResourceUpdatesLabelText() label.SetAppTheme(Label.TextProperty, resource); - Application.Current = null; + app.MainPage = new ContentPage + { + Content = label + }; + Assert.Equal("Light Theme", label.Text); SetAppTheme(AppTheme.Dark); @@ -111,7 +129,7 @@ public void AppThemeResourceUpdatesLabelText() Assert.Equal("Dark Theme", label.Text); } - void SetAppTheme(AppTheme theme) + void SetAppTheme(in AppTheme theme) { mockAppInfo.RequestedTheme = theme; ((IApplication)app).ThemeChanged(); diff --git a/src/CommunityToolkit.Maui.UnitTests/Mocks/MockApplication.cs b/src/CommunityToolkit.Maui.UnitTests/Mocks/MockApplication.cs index 08706b960..919276b6c 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Mocks/MockApplication.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Mocks/MockApplication.cs @@ -1,21 +1,26 @@ -using Microsoft.Maui.Handlers; +using System.Reflection; +using Microsoft.Maui.Controls.Internals; +using Microsoft.Maui.Handlers; namespace CommunityToolkit.Maui.UnitTests.Mocks; -class MockApplication(IServiceProvider serviceProvider) : Application, IPlatformApplication +class MockApplication : Application, IPlatformApplication { - public new Application? Current = null; - public IServiceProvider Services { get; } = serviceProvider; +#pragma warning disable CS0612 // Type or member is obsolete + public MockApplication(IServiceProvider serviceProvider) +#pragma warning restore CS0612 // Type or member is obsolete + { + Services = serviceProvider; + DependencyService.Register(); + } + public IApplication Application => this; + public IServiceProvider Services { get; } } // Inspired by https://github.com/dotnet/maui/blob/main/src/Controls/tests/Core.UnitTests/TestClasses/ApplicationHandlerStub.cs -class ApplicationHandlerStub : ElementHandler +class ApplicationHandlerStub() : ElementHandler(Mapper) { - public ApplicationHandlerStub() : base(Mapper) - { - } - public static IPropertyMapper Mapper = new PropertyMapper(ElementMapper); protected override object CreatePlatformElement() diff --git a/src/CommunityToolkit.Maui.UnitTests/Mocks/MockResourcesProvider.cs b/src/CommunityToolkit.Maui.UnitTests/Mocks/MockResourcesProvider.cs new file mode 100644 index 000000000..9d738515a --- /dev/null +++ b/src/CommunityToolkit.Maui.UnitTests/Mocks/MockResourcesProvider.cs @@ -0,0 +1,13 @@ +using Microsoft.Maui.Controls.Internals; +namespace CommunityToolkit.Maui.UnitTests.Mocks; + +// Inspired by https://github.com/dotnet/maui/blob/79695fbb7ba6517a334c795ecf0a1d6358ef309a/src/Controls/Foldable/test/MockPlatformServices.cs#L145-L176 + +#pragma warning disable CS0612 // Type or member is obsolete +class MockResourcesProvider : ISystemResourcesProvider +#pragma warning restore CS0612 // Type or member is obsolete +{ + readonly ResourceDictionary dictionary = new(); + + public IResourceDictionary GetSystemResources() => dictionary; +} \ No newline at end of file diff --git a/src/CommunityToolkit.Maui/Extensions/AppThemeResourceExtension.shared.cs b/src/CommunityToolkit.Maui/Extensions/AppThemeResourceExtension.shared.cs index 692b8e574..8b1ecf466 100644 --- a/src/CommunityToolkit.Maui/Extensions/AppThemeResourceExtension.shared.cs +++ b/src/CommunityToolkit.Maui/Extensions/AppThemeResourceExtension.shared.cs @@ -17,7 +17,7 @@ public sealed class AppThemeResourceExtension : IMarkupExtension /// Thrown if does no implement . public BindingBase ProvideValue(IServiceProvider serviceProvider) { - ArgumentNullException.ThrowIfNull(serviceProvider, nameof(serviceProvider)); + ArgumentNullException.ThrowIfNull(serviceProvider); if (Key is null) { @@ -35,19 +35,20 @@ public BindingBase ProvideValue(IServiceProvider serviceProvider) var xmlLineInfo = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) is IXmlLineInfoProvider xmlLineInfoProvider ? xmlLineInfoProvider.XmlLineInfo : null; throw new XamlParseException($"Resource not found for key {Key}", xmlLineInfo); } - else if (resource is AppThemeColor color) + + switch (resource) { - return color.GetBinding(); - } - else if (resource is AppThemeObject themeResource) - { - return themeResource.GetBinding(); - } - else - { - var xmlLineInfo = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) is IXmlLineInfoProvider xmlLineInfoProvider ? xmlLineInfoProvider.XmlLineInfo : null; - throw new XamlParseException($"Resource found for key {Key} is not of type {nameof(AppThemeColor)} or {nameof(AppThemeObject)}", xmlLineInfo); + case AppThemeColor color: + return color.GetBinding(); + case AppThemeObject themeResource: + return themeResource.GetBinding(); + default: + { + var xmlLineInfo = serviceProvider.GetService(typeof(IXmlLineInfoProvider)) is IXmlLineInfoProvider xmlLineInfoProvider ? xmlLineInfoProvider.XmlLineInfo : null; + throw new XamlParseException($"Resource found for key {Key} is not of type {nameof(AppThemeColor)} or {nameof(AppThemeObject)}", xmlLineInfo); + } } + } static bool TryGetResource(string key, IEnumerable parentObjects, out object? resource, out ResourceDictionary? resourceDictionary) @@ -57,8 +58,8 @@ static bool TryGetResource(string key, IEnumerable parentObjects, out ob foreach (var parentObject in parentObjects) { - ResourceDictionary? resDict = parentObject is IResourcesProvider resoiurceProvider && resoiurceProvider.IsResourcesCreated - ? resoiurceProvider.Resources + var resDict = parentObject is IResourcesProvider { IsResourcesCreated: true } resourcesProvider + ? resourcesProvider.Resources : parentObject as ResourceDictionary; if (resDict is null) {