diff --git a/.gitignore b/.gitignore index 3e759b75..f7e3a3e8 100644 --- a/.gitignore +++ b/.gitignore @@ -328,3 +328,8 @@ ASALocalRun/ # MFractors (Xamarin productivity tool) working folder .mfractor/ + + +#dont save code coverage results +coverage.opencover.xml +coverage.json diff --git a/src/Caliburn.Micro.Maui.Tests/ActionMessageTests.cs b/src/Caliburn.Micro.Maui.Tests/ActionMessageTests.cs new file mode 100644 index 00000000..32d77ece --- /dev/null +++ b/src/Caliburn.Micro.Maui.Tests/ActionMessageTests.cs @@ -0,0 +1,49 @@ +namespace Caliburn.Micro.Maui.Tests +{ + public class ActionMessageTests + { + [Fact] + public void MethodName_ShouldBeSetAndRetrievedCorrectly() + { + // Arrange + var actionMessage = new ActionMessage(); + var expectedMethodName = "TestMethod"; + + // Act + actionMessage.MethodName = expectedMethodName; + var actualMethodName = actionMessage.MethodName; + + // Assert + Assert.Equal(expectedMethodName, actualMethodName); + } + + [Fact] + public void Handler_ShouldBeSetAndRetrievedCorrectly() + { + // Arrange + var actionMessage = new ActionMessage(); + var expectedHandler = new object(); + + // Act + actionMessage.Handler = expectedHandler; + var actualHandler = actionMessage.Handler; + + // Assert + Assert.Equal(expectedHandler, actualHandler); + } + + [Fact] + public void Parameters_ShouldBeInitialized() + { + // Arrange + var actionMessage = new ActionMessage(); + + // Act + var parameters = actionMessage.Parameters; + + // Assert + Assert.NotNull(parameters); + } + + } +} diff --git a/src/Caliburn.Micro.Maui.Tests/AssemblyCacheTests.cs b/src/Caliburn.Micro.Maui.Tests/AssemblyCacheTests.cs new file mode 100644 index 00000000..afa1aac5 --- /dev/null +++ b/src/Caliburn.Micro.Maui.Tests/AssemblyCacheTests.cs @@ -0,0 +1,34 @@ +namespace Caliburn.Micro.Maui.Tests +{ + public class AssemblyCacheTests + { + [Fact] + public void AddingTheSameAssemblyMoreThanOneShouldNotThrow() + { + AssemblySourceCache.Install(); + + var testAssembly = typeof(AssemblyCacheTests).Assembly; + AssemblySource.Instance.Add(testAssembly); + + //Re-add the same assembly + var exception = Record.Exception(() => AssemblySource.Instance.Add(testAssembly)); + Assert.Null(exception); + } + + [Fact] + public void ResettingTheCacheWithMoreThanOneAssemblyShouldNotThrow() + { + AssemblySourceCache.Install(); + + var testAssembly = typeof(AssemblyCacheTests).Assembly; + + AssemblySource.Instance.AddRange(new[] { testAssembly, testAssembly }); + + //Refresh clears and re-creates the cache + var exception = Record.Exception(() => AssemblySource.Instance.Refresh()); + Assert.Null(exception); + } + } + + +} diff --git a/src/Caliburn.Micro.Maui.Tests/Caliburn.Micro.Maui.Tests.csproj b/src/Caliburn.Micro.Maui.Tests/Caliburn.Micro.Maui.Tests.csproj new file mode 100644 index 00000000..341ab555 --- /dev/null +++ b/src/Caliburn.Micro.Maui.Tests/Caliburn.Micro.Maui.Tests.csproj @@ -0,0 +1,37 @@ + + + + net8.0 + enable + enable + true + false + true + + + 10.0 + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + diff --git a/src/Caliburn.Micro.Maui.Tests/FormsPlatformProviderTests.cs b/src/Caliburn.Micro.Maui.Tests/FormsPlatformProviderTests.cs new file mode 100644 index 00000000..8cbf616a --- /dev/null +++ b/src/Caliburn.Micro.Maui.Tests/FormsPlatformProviderTests.cs @@ -0,0 +1,126 @@ +using Moq; + +namespace Caliburn.Micro.Maui.Tests +{ + public class FormsPlatformProviderTests + { + private readonly Mock mockPlatformProvider; + private readonly FormsPlatformProvider formsPlatformProvider; + + public FormsPlatformProviderTests() + { + mockPlatformProvider = new Mock(); + formsPlatformProvider = new FormsPlatformProvider(mockPlatformProvider.Object); + } + + [Fact] + public void InDesignMode_ShouldReturnPlatformProviderInDesignMode() + { + // Arrange + mockPlatformProvider.Setup(p => p.InDesignMode).Returns(true); + + // Act + var result = formsPlatformProvider.InDesignMode; + + // Assert + Assert.True(result); + } + + [Fact] + public void PropertyChangeNotificationsOnUIThread_ShouldReturnPlatformProviderPropertyChangeNotificationsOnUIThread() + { + // Arrange + mockPlatformProvider.Setup(p => p.PropertyChangeNotificationsOnUIThread).Returns(true); + + // Act + var result = formsPlatformProvider.PropertyChangeNotificationsOnUIThread; + + // Assert + Assert.True(result); + } + + [Fact] + public void BeginOnUIThread_ShouldCallPlatformProviderBeginOnUIThread() + { + // Arrange + System.Action action = () => { }; + + // Act + formsPlatformProvider.BeginOnUIThread(action); + + // Assert + mockPlatformProvider.Verify(p => p.BeginOnUIThread(action), Times.Once); + } + + [Fact] + public async Task OnUIThreadAsync_ShouldCallPlatformProviderOnUIThreadAsync() + { + // Arrange + Func action = async () => await Task.CompletedTask; + + // Act + await formsPlatformProvider.OnUIThreadAsync(action); + + // Assert + mockPlatformProvider.Verify(p => p.OnUIThreadAsync(action), Times.Once); + } + + [Fact] + public void OnUIThread_ShouldCallPlatformProviderOnUIThread() + { + // Arrange + System.Action action = () => { }; + + // Act + formsPlatformProvider.OnUIThread(action); + + // Assert + mockPlatformProvider.Verify(p => p.OnUIThread(action), Times.Once); + } + + [Fact] + public void ExecuteOnFirstLoad_ShouldCallPlatformProviderExecuteOnFirstLoad_WhenViewIsNotPage() + { + // Arrange + var view = new object(); + Action handler = _ => { }; + + // Act + formsPlatformProvider.ExecuteOnFirstLoad(view, handler); + + // Assert + mockPlatformProvider.Verify(p => p.ExecuteOnFirstLoad(view, handler), Times.Once); + } + + + [Fact] + public void ExecuteOnLayoutUpdated_ShouldCallPlatformProviderExecuteOnLayoutUpdated() + { + // Arrange + var view = new object(); + Action handler = _ => { }; + + // Act + formsPlatformProvider.ExecuteOnLayoutUpdated(view, handler); + + // Assert + mockPlatformProvider.Verify(p => p.ExecuteOnLayoutUpdated(view, handler), Times.Once); + } + + [Fact] + public void GetViewCloseAction_ShouldCallPlatformProviderGetViewCloseAction() + { + // Arrange + var viewModel = new object(); + var views = new List(); + bool? dialogResult = true; + + // Act + formsPlatformProvider.GetViewCloseAction(viewModel, views, dialogResult); + + // Assert + mockPlatformProvider.Verify(p => p.GetViewCloseAction(viewModel, views, dialogResult), Times.Once); + } + } + +} diff --git a/src/Caliburn.Micro.Maui.Tests/MessageBinderTests.cs b/src/Caliburn.Micro.Maui.Tests/MessageBinderTests.cs new file mode 100644 index 00000000..b9d7ceb5 --- /dev/null +++ b/src/Caliburn.Micro.Maui.Tests/MessageBinderTests.cs @@ -0,0 +1,17 @@ +namespace Caliburn.Micro.Maui.Tests +{ + public class MessageBinderTests + { + [Fact] + public void EvaluateParameterCaseInsensitive() + { + MessageBinder.SpecialValues.Add("$sampleParameter", context => 42); + var caseSensitiveValue = MessageBinder.EvaluateParameter("$sampleParameter", typeof(int), new ActionExecutionContext()); + + Assert.NotEqual("$sampleParameter", caseSensitiveValue); + + var caseInsensitiveValue = MessageBinder.EvaluateParameter("$sampleparameter", typeof(int), new ActionExecutionContext()); + Assert.NotEqual("$sampleparameter", caseInsensitiveValue); + } + } +} diff --git a/src/Caliburn.Micro.Maui.Tests/ParameterTests.cs b/src/Caliburn.Micro.Maui.Tests/ParameterTests.cs new file mode 100644 index 00000000..2f0ae317 --- /dev/null +++ b/src/Caliburn.Micro.Maui.Tests/ParameterTests.cs @@ -0,0 +1,90 @@ +namespace Caliburn.Micro.Maui.Tests +{ + using Microsoft.Maui.Controls; + using Moq; + using Xunit; + + + public class ParameterTests + { + [Fact] + public void ValueProperty_Should_SetAndGetValue() + { + // Arrange + var parameter = new Parameter(); + var expectedValue = "TestValue"; + + // Act + parameter.Value = expectedValue; + var actualValue = parameter.Value; + + // Assert + Assert.Equal(expectedValue, actualValue); + } + + [Fact] + public void Attach_Should_SetAssociatedObject() + { + // Arrange + var parameter = new Parameter(); + var bindableObject = new CheckBox(); + + // Act + ((IAttachedObject)parameter).Attach(bindableObject); + + // Assert + Assert.Equal(bindableObject, ((IAttachedObject)parameter).AssociatedObject); + } + + [Fact] + public void Detach_Should_ClearAssociatedObject() + { + // Arrange + var parameter = new Parameter(); + var bindableObject = new TextCell(); + ((IAttachedObject)parameter).Attach(bindableObject); + + // Act + ((IAttachedObject)parameter).Detach(); + + // Assert + Assert.Null(((IAttachedObject)parameter).AssociatedObject); + } + + [Fact] + public void OnValueChanged_Should_UpdateAvailability() + { + // Arrange + var parameter = new Parameter(); + var mockActionMessage = new Mock(); + mockActionMessage.Setup(x => x.UpdateAvailability()); + var actionMessage = mockActionMessage.Object; + parameter.MakeAwareOf(actionMessage); + + // Act + Parameter.OnValueChanged(parameter, new DependencyPropertyChangedEventArgs(null, null, Parameter.ValueProperty)); + + // Assert + mockActionMessage.Verify(v => v.UpdateAvailability()); + } + + + [Fact] + public void ExecuteOnFirstLoad_ShouldCallBaseMethod_WhenViewIsNotPage() + { + // Arrange + var mockPlatformProvider = new Mock(); + var formsPlatformProvider = new FormsPlatformProvider(mockPlatformProvider.Object); + var mockView = new Mock(); + Action handler = (view) => { }; + + // Act + formsPlatformProvider.ExecuteOnFirstLoad(mockView.Object, handler); + + // Assert + mockPlatformProvider.Verify(p => p.ExecuteOnFirstLoad(mockView.Object, handler), Times.Once); + } + } + + +} diff --git a/src/Caliburn.Micro.Maui.Tests/ParserTests.cs b/src/Caliburn.Micro.Maui.Tests/ParserTests.cs new file mode 100644 index 00000000..e3e15d37 --- /dev/null +++ b/src/Caliburn.Micro.Maui.Tests/ParserTests.cs @@ -0,0 +1,19 @@ +using System.Globalization; + +namespace Caliburn.Micro.Maui.Tests +{ + public class ParserTests + { + [Fact] + public void CreateParametersWithOddNumbers() + { + var evenResult = Parser.CreateParameter(null, "0.1"); + var oddResult = Parser.CreateParameter(null, "-0.1"); + var nanResult = Parser.CreateParameter(null, "-0.1abc"); + + Assert.Equal(0.1, double.Parse((string)evenResult.Value, CultureInfo.InvariantCulture)); + Assert.Equal(-0.1, double.Parse((string)oddResult.Value, CultureInfo.InvariantCulture)); + Assert.Null(nanResult.Value); + } + } +} diff --git a/src/Caliburn.Micro.Maui.Tests/StringParserTests.cs b/src/Caliburn.Micro.Maui.Tests/StringParserTests.cs new file mode 100644 index 00000000..47439b2e --- /dev/null +++ b/src/Caliburn.Micro.Maui.Tests/StringParserTests.cs @@ -0,0 +1,54 @@ +namespace Caliburn.Micro.Maui.Tests +{ + public class StringSplitterTests + { + [Fact] + public void SplitSimpleString() + { + var output = StringSplitter.Split("MyMethodName", ';'); + + Assert.Collection(output, o => Assert.Equal("MyMethodName", o)); + } + + [Fact] + public void SplitSeparatedString() + { + var output = StringSplitter.Split("First;Second", ';'); + + Assert.Collection(output, + o => Assert.Equal("First", o), + o => Assert.Equal("Second", o)); + } + + [Fact] + public void TrimsSplitSimpleString() + { + var output = StringSplitter.Split("MyMethodName ", ';'); + + Assert.Collection(output, o => Assert.Equal("MyMethodName", o)); + } + + [Fact] + public void RemovesEmptySplitsSeparatedString() + { + var output = StringSplitter.Split("First;Second;", ';'); + + Assert.Collection(output, + o => Assert.Equal("First", o), + o => Assert.Equal("Second", o)); + } + + [Fact] + public void HandlesNewLinesInSeparatedString() + { + var output = StringSplitter.Split(@" + First; + Second; + ", ';'); + + Assert.Collection(output, + o => Assert.Equal("First", o), + o => Assert.Equal("Second", o)); + } + } +} diff --git a/src/Caliburn.Micro.Maui.Tests/ViewLocatorTests.cs b/src/Caliburn.Micro.Maui.Tests/ViewLocatorTests.cs new file mode 100644 index 00000000..d231f4af --- /dev/null +++ b/src/Caliburn.Micro.Maui.Tests/ViewLocatorTests.cs @@ -0,0 +1,91 @@ +namespace Caliburn.Micro.Maui.Tests +{ + public class ViewLocatorTests + { + [Fact] + public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewModelsIsEmpty() + { + var config = new TypeMappingConfiguration + { + DefaultSubNamespaceForViews = "not empty", + DefaultSubNamespaceForViewModels = null, + NameFormat = "not{1}{0} Empty" + }; + + Assert.Throws(() => ViewLocator.ConfigureTypeMappings(config)); + } + + [Fact] + public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewModelsIsNull() + { + var config = new TypeMappingConfiguration + { + DefaultSubNamespaceForViews = "not null", + DefaultSubNamespaceForViewModels = null, + NameFormat = "not{0} null{1}" + }; + + Assert.Throws(() => ViewLocator.ConfigureTypeMappings(config)); + } + + [Fact] + public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewsIsEmpty() + { + var config = new TypeMappingConfiguration + { + DefaultSubNamespaceForViews = string.Empty, + DefaultSubNamespaceForViewModels = "not Empty", + NameFormat = "{0}not Empty{1}" + }; + + Assert.Throws(() => ViewLocator.ConfigureTypeMappings(config)); + } + + [Fact] + public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewsIsNull() + { + var config = new TypeMappingConfiguration + { + DefaultSubNamespaceForViews = null, + DefaultSubNamespaceForViewModels = "not null", + NameFormat = "{1}not {0}null" + }; + + Assert.Throws(() => ViewLocator.ConfigureTypeMappings(config)); + } + + [Fact] + public void ConfigureTypeMappingsShouldThrowWhenNameFormatIsEmpty() + { + var config = new TypeMappingConfiguration + { + DefaultSubNamespaceForViews = "not Empty", + DefaultSubNamespaceForViewModels = "not Empty", + NameFormat = string.Empty + }; + + Assert.Throws(() => ViewLocator.ConfigureTypeMappings(config)); + } + + [Fact] + public void ConfigureTypeMappingsShouldThrowWhenNameFormatIsNull() + { + var config = new TypeMappingConfiguration + { + DefaultSubNamespaceForViews = "not null", + DefaultSubNamespaceForViewModels = "not null", + NameFormat = null + }; + + Assert.Throws(() => ViewLocator.ConfigureTypeMappings(config)); + } + + [Fact] + public void ConfigureTypeMappingsWithDefaultValuesShouldNotThrow() + { + var typeMappingConfiguration = new TypeMappingConfiguration(); + + ViewLocator.ConfigureTypeMappings(typeMappingConfiguration); + } + } +} diff --git a/src/Caliburn.Micro.Maui.Tests/ViewModelLocatorTests.cs b/src/Caliburn.Micro.Maui.Tests/ViewModelLocatorTests.cs new file mode 100644 index 00000000..a089bcde --- /dev/null +++ b/src/Caliburn.Micro.Maui.Tests/ViewModelLocatorTests.cs @@ -0,0 +1,96 @@ +namespace Caliburn.Micro.Maui.Tests +{ + public class ViewModelLocatorTests + { + [Fact] + public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewModelsIsEmpty() + { + var config = new TypeMappingConfiguration + { + DefaultSubNamespaceForViews = "not empty", + DefaultSubNamespaceForViewModels = string.Empty, + NameFormat = "not Empty{1}{0}" + }; + + Assert.Throws(() => ViewModelLocator.ConfigureTypeMappings(config)); + } + + [Fact] + public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewModelsIsNull() + { + var config = new TypeMappingConfiguration + { + DefaultSubNamespaceForViews = "not null", + DefaultSubNamespaceForViewModels = null, + NameFormat = "not null{1}{0}" + }; + + Assert.Throws(() => ViewModelLocator.ConfigureTypeMappings(config)); + } + + [Fact] + public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewsIsEmpty() + { + var config = new TypeMappingConfiguration + { + DefaultSubNamespaceForViews = string.Empty, + DefaultSubNamespaceForViewModels = "not Empty", + NameFormat = "{0}{1}Empty" + }; + + Assert.Throws(() => ViewModelLocator.ConfigureTypeMappings(config)); + } + + [Fact] + public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewsIsNull() + { + var config = new TypeMappingConfiguration + { + DefaultSubNamespaceForViews = null, + DefaultSubNamespaceForViewModels = "not null", + NameFormat = "{1}not {0}null" + }; + + Assert.Throws(() => ViewModelLocator.ConfigureTypeMappings(config)); + } + + + [Fact] + public void COnfigureTypeMappingsWithDefaultValuesShouldNotThrow() + { + var typeMappingConfiguration = new TypeMappingConfiguration(); + + ViewModelLocator.ConfigureTypeMappings(typeMappingConfiguration); + } + + [Fact] + public void MakeInterface_ShouldReturnInterfaceName() + { + // Arrange + string typeName = "MyClass"; + string expectedInterfaceName = "IMyClass"; + + // Act + string result = ViewModelLocator.MakeInterface(typeName); + + // Assert + Assert.Equal(expectedInterfaceName, result); + } + + [Fact] + public void MakeInterface_ShouldHandleEmptyString() + { + // Arrange + string typeName = ""; + string expectedInterfaceName = "I"; + + // Act + string result = ViewModelLocator.MakeInterface(typeName); + + // Assert + Assert.Equal(expectedInterfaceName, result); + } + + + } +} diff --git a/src/Caliburn.Micro.Maui/Caliburn.Micro.Maui.csproj b/src/Caliburn.Micro.Maui/Caliburn.Micro.Maui.csproj index 9500eb51..d7256417 100644 --- a/src/Caliburn.Micro.Maui/Caliburn.Micro.Maui.csproj +++ b/src/Caliburn.Micro.Maui/Caliburn.Micro.Maui.csproj @@ -1,20 +1,22 @@  - - net8.0;net8.0-android;net8.0-ios;net8.0-maccatalyst - $(TargetFrameworks);net8.0-windows10.0.19041 + + + net8.0;net8.0-android;net8.0-ios;net8.0-maccatalyst;net8.0-windows10.0.19041 + + $(TargetFrameworks);net8.0-windows10.0.19041 $(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage - + true - true + true MAUI - 14.2 - 14.0 - 21.0 - 10.0.17763.0 - 10.0.17763.0 + 14.2 + 14.0 + 21.0 + 10.0.17763.0 + 10.0.17763.0 Caliburn.Micro.Maui Caliburn.Micro.Maui @@ -112,7 +114,7 @@ - + True @@ -124,5 +126,9 @@ - + + + <_Parameter1>Caliburn.Micro.Maui.Tests + + diff --git a/src/Caliburn.Micro.Platform.Tests/ViewLocatorTests.cs b/src/Caliburn.Micro.Platform.Tests/ViewLocatorTests.cs index f2f4de0e..28f5948f 100644 --- a/src/Caliburn.Micro.Platform.Tests/ViewLocatorTests.cs +++ b/src/Caliburn.Micro.Platform.Tests/ViewLocatorTests.cs @@ -11,8 +11,8 @@ public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewModels var config = new TypeMappingConfiguration { DefaultSubNamespaceForViews = "not empty", - DefaultSubNamespaceForViewModels = string.Empty, - NameFormat = "not Empty" + DefaultSubNamespaceForViewModels = null, + NameFormat = "not{1}{0} Empty" }; Assert.Throws(() => ViewLocator.ConfigureTypeMappings(config)); @@ -25,7 +25,7 @@ public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewModels { DefaultSubNamespaceForViews = "not null", DefaultSubNamespaceForViewModels = null, - NameFormat = "not null" + NameFormat = "not{0} null{1}" }; Assert.Throws(() => ViewLocator.ConfigureTypeMappings(config)); @@ -38,7 +38,7 @@ public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewsIsEmp { DefaultSubNamespaceForViews = string.Empty, DefaultSubNamespaceForViewModels = "not Empty", - NameFormat = "not Empty" + NameFormat = "{0}not Empty{1}" }; Assert.Throws(() => ViewLocator.ConfigureTypeMappings(config)); @@ -51,7 +51,7 @@ public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewsIsNul { DefaultSubNamespaceForViews = null, DefaultSubNamespaceForViewModels = "not null", - NameFormat = "not null" + NameFormat = "{1}not {0}null" }; Assert.Throws(() => ViewLocator.ConfigureTypeMappings(config)); diff --git a/src/Caliburn.Micro.Platform.Tests/ViewModelLocatorTests.cs b/src/Caliburn.Micro.Platform.Tests/ViewModelLocatorTests.cs index abb89088..efa9f159 100644 --- a/src/Caliburn.Micro.Platform.Tests/ViewModelLocatorTests.cs +++ b/src/Caliburn.Micro.Platform.Tests/ViewModelLocatorTests.cs @@ -12,7 +12,7 @@ public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewModels { DefaultSubNamespaceForViews = "not empty", DefaultSubNamespaceForViewModels = string.Empty, - NameFormat = "not Empty" + NameFormat = "not Empty{1}{0}" }; Assert.Throws(() => ViewModelLocator.ConfigureTypeMappings(config)); @@ -25,7 +25,7 @@ public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewModels { DefaultSubNamespaceForViews = "not null", DefaultSubNamespaceForViewModels = null, - NameFormat = "not null" + NameFormat = "not null{1}{0}" }; Assert.Throws(() => ViewModelLocator.ConfigureTypeMappings(config)); @@ -38,7 +38,7 @@ public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewsIsEmp { DefaultSubNamespaceForViews = string.Empty, DefaultSubNamespaceForViewModels = "not Empty", - NameFormat = "not Empty" + NameFormat = "{0}{1}Empty" }; Assert.Throws(() => ViewModelLocator.ConfigureTypeMappings(config)); @@ -51,44 +51,47 @@ public void ConfigureTypeMappingsShouldThrowWhenDefaultSubNamespaceForViewsIsNul { DefaultSubNamespaceForViews = null, DefaultSubNamespaceForViewModels = "not null", - NameFormat = "not null" + NameFormat = "{1}not {0}null" }; Assert.Throws(() => ViewModelLocator.ConfigureTypeMappings(config)); } + [Fact] - public void ConfigureTypeMappingsShouldThrowWhenNameFormatIsEmpty() + public void COnfigureTypeMappingsWithDefaultValuesShouldNotThrow() { - var config = new TypeMappingConfiguration - { - DefaultSubNamespaceForViews = "not Empty", - DefaultSubNamespaceForViewModels = "not Empty", - NameFormat = string.Empty - }; + var typeMappingConfiguration = new TypeMappingConfiguration(); - Assert.Throws(() => ViewModelLocator.ConfigureTypeMappings(config)); + ViewModelLocator.ConfigureTypeMappings(typeMappingConfiguration); } - [Fact] - public void ConfigureTypeMappingsShouldThrowWhenNameFormatIsNull() + public void MakeInterface_ShouldReturnInterfaceName() { - var config = new TypeMappingConfiguration - { - DefaultSubNamespaceForViews = "not null", - DefaultSubNamespaceForViewModels = "not null", - NameFormat = null - }; + // Arrange + string typeName = "MyClass"; + string expectedInterfaceName = "IMyClass"; - Assert.Throws(() => ViewModelLocator.ConfigureTypeMappings(config)); + // Act + string result = ViewModelLocator.MakeInterface(typeName); + + // Assert + Assert.Equal(expectedInterfaceName, result); } [Fact] - public void COnfigureTypeMappingsWithDefaultValuesShouldNotThrow() + public void MakeInterface_ShouldHandleEmptyString() { - var typeMappingConfiguration = new TypeMappingConfiguration(); + // Arrange + string typeName = ""; + string expectedInterfaceName = "I"; - ViewModelLocator.ConfigureTypeMappings(typeMappingConfiguration); + // Act + string result = ViewModelLocator.MakeInterface(typeName); + + // Assert + Assert.Equal(expectedInterfaceName, result); } + } } diff --git a/src/Caliburn.Micro.Platform/Platforms/Maui/ActionMessage.cs b/src/Caliburn.Micro.Platform/Platforms/Maui/ActionMessage.cs index 66b8dd88..7e295f99 100644 --- a/src/Caliburn.Micro.Platform/Platforms/Maui/ActionMessage.cs +++ b/src/Caliburn.Micro.Platform/Platforms/Maui/ActionMessage.cs @@ -6,10 +6,8 @@ using System.Linq; using System.Reflection; using global::Microsoft.Maui.Controls; - using UIElement = global::Microsoft.Maui.Controls.Element; using FrameworkElement = global::Microsoft.Maui.Controls.VisualElement; - using DependencyProperty = global::Microsoft.Maui.Controls.BindableProperty; - using DependencyObject = global::Microsoft.Maui.Controls.BindableObject; + using UIElement = global::Microsoft.Maui.Controls.Element; /// /// Used to send a message from the UI to a presentation model class, indicating that a particular Action should be invoked. @@ -57,7 +55,7 @@ public ActionMessage() public object Handler { get { return handler; } - private set + internal set { if (handler == value) return; @@ -81,8 +79,10 @@ private set /// /// Called after the action is attached to an AssociatedObject. /// - protected override void OnAttached() { - if (!View.InDesignMode) { + protected override void OnAttached() + { + if (!View.InDesignMode) + { Parameters.Attach(AssociatedObject); Parameters.OfType().Apply(x => x.MakeAwareOf(this)); @@ -92,14 +92,15 @@ protected override void OnAttached() { EventHandler bindingContextChanged = null; - bindingContextChanged = (s, e) => { + bindingContextChanged = (s, e) => + { AssociatedObject.BindingContextChanged -= bindingContextChanged; ElementLoaded(); }; AssociatedObject.BindingContextChanged += bindingContextChanged; - + } base.OnAttached(); @@ -137,7 +138,8 @@ private void ElementLoaded() currentElement = currentElement.Parent; } } - else currentElement = context.View as FrameworkElement; + else + currentElement = context.View as FrameworkElement; Handler = currentElement; } @@ -165,7 +167,8 @@ protected override void Invoke(VisualElement sender) { Log.Info("Invoking {0}.", this); - if (AssociatedObject == null) { + if (AssociatedObject == null) + { AssociatedObject = sender; } @@ -228,7 +231,7 @@ public virtual void UpdateAvailability() UpdateAvailabilityCore(); } - bool UpdateAvailabilityCore() + internal bool UpdateAvailabilityCore() { Log.Info("{0} availability update.", this); return ApplyAvailabilityEffect(context); @@ -375,7 +378,8 @@ public override string ToString() public static Action PrepareContext = context => { SetMethodBinding(context); - if (context.Target == null || context.Method == null) { + if (context.Target == null || context.Method == null) + { return; } @@ -395,17 +399,20 @@ public override string ToString() { matchingGuardName = possibleGuardName; guard = GetMethodInfo(targetType, "get_" + matchingGuardName); - if (guard != null) break; + if (guard != null) + break; } if (guard == null) return; PropertyChangedEventHandler handler = null; - handler = (s, e) => { + handler = (s, e) => + { if (string.IsNullOrEmpty(e.PropertyName) || e.PropertyName == matchingGuardName) { - Caliburn.Micro.Execute.OnUIThread(() => { + Caliburn.Micro.Execute.OnUIThread(() => + { var message = context.Message; if (message == null) { @@ -418,8 +425,10 @@ public override string ToString() }; inpc.PropertyChanged += handler; - context.Disposing += delegate { inpc.PropertyChanged -= handler; }; - context.Message.Detaching += delegate { inpc.PropertyChanged -= handler; }; + context.Disposing += delegate + { inpc.PropertyChanged -= handler; }; + context.Message.Detaching += delegate + { inpc.PropertyChanged -= handler; }; } context.CanExecute = () => (bool)guard.Invoke( @@ -438,23 +447,30 @@ public override string ToString() /// The execution context /// Method names to look for. /// A MethodInfo, if found; null otherwise - static MethodInfo TryFindGuardMethod(ActionExecutionContext context, IEnumerable possibleGuardNames) { + internal static MethodInfo TryFindGuardMethod(ActionExecutionContext context, IEnumerable possibleGuardNames) + { var targetType = context.Target.GetType(); MethodInfo guard = null; foreach (string possibleGuardName in possibleGuardNames) { guard = GetMethodInfo(targetType, possibleGuardName); - if (guard != null) break; + if (guard != null) + break; } - if (guard == null) return null; - if (guard.ContainsGenericParameters) return null; - if (!typeof(bool).Equals(guard.ReturnType)) return null; + if (guard == null) + return null; + if (guard.ContainsGenericParameters) + return null; + if (!typeof(bool).Equals(guard.ReturnType)) + return null; var guardPars = guard.GetParameters(); var actionPars = context.Method.GetParameters(); - if (guardPars.Length == 0) return guard; - if (guardPars.Length != actionPars.Length) return null; + if (guardPars.Length == 0) + return guard; + if (guardPars.Length != actionPars.Length) + return null; var comparisons = guardPars.Zip( context.Method.GetParameters(), @@ -472,7 +488,8 @@ static MethodInfo TryFindGuardMethod(ActionExecutionContext context, IEnumerable /// /// Returns the list of possible names of guard methods / properties for the given method. /// - public static Func> BuildPossibleGuardNames = method => { + public static Func> BuildPossibleGuardNames = method => + { var guardNames = new List(); @@ -492,7 +509,8 @@ static MethodInfo TryFindGuardMethod(ActionExecutionContext context, IEnumerable return guardNames; }; - static MethodInfo GetMethodInfo(Type t, string methodName) { + static MethodInfo GetMethodInfo(Type t, string methodName) + { return t.GetRuntimeMethods().SingleOrDefault(m => m.Name == methodName); } } diff --git a/src/Caliburn.Micro.Platform/Platforms/Maui/Parameter.cs b/src/Caliburn.Micro.Platform/Platforms/Maui/Parameter.cs index f1a58d3b..962c6e3c 100644 --- a/src/Caliburn.Micro.Platform/Platforms/Maui/Parameter.cs +++ b/src/Caliburn.Micro.Platform/Platforms/Maui/Parameter.cs @@ -6,10 +6,8 @@ namespace Caliburn.Micro { using System; #if MAUI - using global::Microsoft.Maui.Controls; using DependencyObject = global::Microsoft.Maui.Controls.BindableObject; using DependencyProperty = global::Microsoft.Maui.Controls.BindableProperty; - using FrameworkElement = global::Microsoft.Maui.Controls.VisualElement; #else using Windows.UI.Xaml; #endif @@ -18,7 +16,8 @@ namespace Caliburn.Micro /// Represents a parameter of an . /// #if WINDOWS_UWP || MAUI - public class Parameter : DependencyObject, IAttachedObject { + public class Parameter : DependencyObject, IAttachedObject + { DependencyObject associatedObject; #else public class Parameter : FrameworkElement, IAttachedObject @@ -35,7 +34,7 @@ public class Parameter : FrameworkElement, IAttachedObject "Value", typeof(object), typeof(Parameter), - null, + null, OnValueChanged ); @@ -50,7 +49,8 @@ public object Value } #if WINDOWS_UWP || MAUI - DependencyObject IAttachedObject.AssociatedObject { + DependencyObject IAttachedObject.AssociatedObject + { #else FrameworkElement IAttachedObject.AssociatedObject { @@ -69,7 +69,8 @@ protected ActionMessage Owner } #if WINDOWS_UWP || MAUI - void IAttachedObject.Attach(DependencyObject dependencyObject) { + void IAttachedObject.Attach(DependencyObject dependencyObject) + { #else void IAttachedObject.Attach(FrameworkElement dependencyObject) { @@ -91,7 +92,7 @@ internal void MakeAwareOf(ActionMessage owner) Owner = owner; } - static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + internal static void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var parameter = (Parameter)d; var owner = parameter.Owner; diff --git a/src/Caliburn.Micro.sln b/src/Caliburn.Micro.sln index 7173a0cb..18d0507a 100644 --- a/src/Caliburn.Micro.sln +++ b/src/Caliburn.Micro.sln @@ -15,7 +15,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Caliburn.Micro.Platform.Cor EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Caliburn.Micro.Xamarin.Forms", "Caliburn.Micro.Xamarin.Forms\Caliburn.Micro.Xamarin.Forms.csproj", "{A570D69E-B33E-4CDE-86BF-E5B66DF4B0AE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Caliburn.Micro.Maui", "Caliburn.Micro.Maui\Caliburn.Micro.Maui.csproj", "{50F9A3FD-FCC8-40CD-906E-A4FC6FC40286}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Caliburn.Micro.Maui", "Caliburn.Micro.Maui\Caliburn.Micro.Maui.csproj", "{50F9A3FD-FCC8-40CD-906E-A4FC6FC40286}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Caliburn.Micro.Maui.Tests", "Caliburn.Micro.Maui.Tests\Caliburn.Micro.Maui.Tests.csproj", "{55521CEA-F8EB-44DD-8D9D-F28F46407F59}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -111,6 +113,18 @@ Global {50F9A3FD-FCC8-40CD-906E-A4FC6FC40286}.Release|x64.Build.0 = Release|Any CPU {50F9A3FD-FCC8-40CD-906E-A4FC6FC40286}.Release|x86.ActiveCfg = Release|Any CPU {50F9A3FD-FCC8-40CD-906E-A4FC6FC40286}.Release|x86.Build.0 = Release|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Debug|x64.ActiveCfg = Debug|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Debug|x64.Build.0 = Debug|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Debug|x86.ActiveCfg = Debug|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Debug|x86.Build.0 = Debug|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Release|Any CPU.Build.0 = Release|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Release|x64.ActiveCfg = Release|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Release|x64.Build.0 = Release|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Release|x86.ActiveCfg = Release|Any CPU + {55521CEA-F8EB-44DD-8D9D-F28F46407F59}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE