diff --git a/Source/Prism.Tests/Common/Mocks/MockEnum.cs b/Source/Prism.Tests/Common/Mocks/MockEnum.cs new file mode 100644 index 000000000..74ebae993 --- /dev/null +++ b/Source/Prism.Tests/Common/Mocks/MockEnum.cs @@ -0,0 +1,11 @@ +namespace Prism.Tests.Common.Mocks +{ + internal enum MockEnum + { + None = 0, + Foo = 1, + Bar = 2, + Fizz = 3, + SomethingElse = 99 + } +} diff --git a/Source/Prism.Tests/Common/Mocks/MockParameters.cs b/Source/Prism.Tests/Common/Mocks/MockParameters.cs new file mode 100644 index 000000000..671f99014 --- /dev/null +++ b/Source/Prism.Tests/Common/Mocks/MockParameters.cs @@ -0,0 +1,10 @@ +using Prism.Common; + +namespace Prism.Tests.Common.Mocks +{ + internal class MockParameters : ParametersBase + { + public MockParameters() : base() { } + public MockParameters(string query) : base(query) { } + } +} diff --git a/Source/Prism.Tests/Mvvm/AutoInitializeViewModelFixture.cs b/Source/Prism.Tests/Common/ParametersFixture.cs similarity index 81% rename from Source/Prism.Tests/Mvvm/AutoInitializeViewModelFixture.cs rename to Source/Prism.Tests/Common/ParametersFixture.cs index a3c28c178..abd2aa8cf 100644 --- a/Source/Prism.Tests/Mvvm/AutoInitializeViewModelFixture.cs +++ b/Source/Prism.Tests/Common/ParametersFixture.cs @@ -1,25 +1,11 @@ using System.Collections.Generic; using System.Linq; -using Prism.Common; +using Prism.Tests.Common.Mocks; using Xunit; -namespace Prism.Tests.Mvvm +namespace Prism.Tests.Common { - internal class MockParameters : ParametersBase - { - public MockParameters() : base() { } - public MockParameters(string query) : base(query) { } - } - - internal enum MockEnum - { - None = 0, - Foo = 1, - Bar = 2, - Fizz = 3 - } - - public class AutoInitializeViewModelFixture + public class ParametersFixture { [Fact] public void TryGetValueOfT() @@ -41,7 +27,7 @@ public void TryGetValueOfT() public void GetValuesOfT() { var parameters = new MockParameters("mock=Foo&mock=2&mock=Fizz"); - bool success = false; + IEnumerable values = default; var ex = Record.Exception(() => values = parameters.GetValues("mock")); diff --git a/Source/Prism/Common/ParametersExtensions.cs b/Source/Prism/Common/ParametersExtensions.cs index a2e0d33fe..774bb6a2c 100644 --- a/Source/Prism/Common/ParametersExtensions.cs +++ b/Source/Prism/Common/ParametersExtensions.cs @@ -19,16 +19,10 @@ public static object GetValue(this IEnumerable> par { if (string.Compare(kvp.Key, key, StringComparison.Ordinal) == 0) { - if (kvp.Value == null) - return GetDefault(type); - else if (kvp.Value.GetType() == type) - return kvp.Value; - else if (type.IsAssignableFrom(kvp.Value.GetType())) - return kvp.Value; - else if (type.IsEnum && Enum.IsDefined(type, kvp.Value.ToString())) - return Enum.Parse(type, kvp.Value.ToString()); - else - return Convert.ChangeType(kvp.Value, type); + if(TryGetValueInternal(kvp, type, out var value)) + return value; + + throw new InvalidCastException($"Unable to convert the value of Type '{kvp.Value.GetType().FullName}' to '{type.FullName}' for the key '{key}' "); } } @@ -44,18 +38,9 @@ public static bool TryGetValue(this IEnumerable> { if (string.Compare(kvp.Key, key, StringComparison.Ordinal) == 0) { - if (kvp.Value == null) - value = default; - else if (kvp.Value.GetType() == type) - value = (T)kvp.Value; - else if (type.IsAssignableFrom(kvp.Value.GetType())) - value = (T)kvp.Value; - else if (type.IsEnum && Enum.IsDefined(type, kvp.Value.ToString())) - value = (T)Enum.Parse(type, kvp.Value.ToString()); - else - value = (T)Convert.ChangeType(kvp.Value, type); - - return true; + var success = TryGetValueInternal(kvp, typeof(T), out object valueAsObject); + value = (T)valueAsObject; + return success; } } @@ -67,28 +52,62 @@ public static bool TryGetValue(this IEnumerable> public static IEnumerable GetValues(this IEnumerable> parameters, string key) { List values = new List(); - var type = typeof(T); + var type = typeof(T); foreach (var kvp in parameters) { if (string.Compare(kvp.Key, key, StringComparison.Ordinal) == 0) { - if (kvp.Value == null) - values.Add(default); - else if (kvp.Value.GetType() == type) - values.Add((T)kvp.Value); - else if (type.IsAssignableFrom(kvp.Value.GetType())) - values.Add((T)kvp.Value); - else if (type.IsEnum && Enum.IsDefined(type, kvp.Value.ToString())) - values.Add((T)Enum.Parse(type, kvp.Value.ToString())); - else - values.Add((T)Convert.ChangeType(kvp.Value, type)); + TryGetValueInternal(kvp, type, out var value); + values.Add((T)value); } } return values.AsEnumerable(); } + private static bool TryGetValueInternal(KeyValuePair kvp, Type type, out object value) + { + value = GetDefault(type); + var success = false; + if (kvp.Value == null) + { + success = true; + } + else if (kvp.Value.GetType() == type) + { + success = true; + value = kvp.Value; + } + else if (type.IsAssignableFrom(kvp.Value.GetType())) + { + success = true; + value = kvp.Value; + } + else if (type.IsEnum) + { + var valueAsString = kvp.Value.ToString(); + if (Enum.IsDefined(type, valueAsString)) + { + success = true; + value = Enum.Parse(type, valueAsString); + } + else if (int.TryParse(valueAsString, out var numericValue)) + { + success = true; + value = Enum.ToObject(type, numericValue); + } + } + + if (!success && type.GetInterface("System.IConvertible") != null) + { + success = true; + value = Convert.ChangeType(kvp.Value, type); + } + + return success; + } + [EditorBrowsable(EditorBrowsableState.Never)] public static bool ContainsKey(this IEnumerable> parameters, string key) => parameters.Any(x => string.Compare(x.Key, key, StringComparison.Ordinal) == 0); diff --git a/Source/Xamarin/Prism.Forms.Tests/Mvvm/AutoInitializeFixture.cs b/Source/Xamarin/Prism.Forms.Tests/Mvvm/AutoInitializeFixture.cs new file mode 100644 index 000000000..33dea9b38 --- /dev/null +++ b/Source/Xamarin/Prism.Forms.Tests/Mvvm/AutoInitializeFixture.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Prism.Common; +using Prism.Forms.Tests.Mvvm.Mocks.ViewModels; +using Prism.Navigation; +using Xunit; + +namespace Prism.Forms.Tests.Mvvm +{ + public class AutoInitializeFixture + { + [Fact] + public void ThrowsAnExceptionWithoutRequiredParameter() + { + var vm = new AutoInitializedViewModelMock(); + var parameters = new NavigationParameters("?foo=bar"); + var ex = Record.Exception(() => PageUtilities.Abracadabra(vm, parameters)); + + Assert.NotNull(ex); + Assert.IsType(ex); + } + + [Fact] + public void NoExceptionWhenTitleIsProvided() + { + var vm = new AutoInitializedViewModelMock(); + var parameters = new NavigationParameters("?success=true&title=Hello"); + var ex = Record.Exception(() => PageUtilities.Abracadabra(vm, parameters)); + + Assert.Null(ex); + Assert.Equal("Hello", vm.Title); + } + + [Fact] + public void SetsBooleanFromQueryString() + { + var vm = new AutoInitializedViewModelMock(); + var parameters = new NavigationParameters("?success=true&title=Hello"); + var ex = Record.Exception(() => PageUtilities.Abracadabra(vm, parameters)); + + Assert.Null(ex); + Assert.True(vm.Success); + } + + [Fact] + public void SetsDateTimeFromQueryString() + { + var vm = new AutoInitializedViewModelMock(); + var parameters = new NavigationParameters("?someDate=July 11, 2019 08:00&title=Hello"); + var ex = Record.Exception(() => PageUtilities.Abracadabra(vm, parameters)); + + Assert.Null(ex); + var expected = new DateTime(2019, 7, 11, 8, 0, 0); + Assert.Equal(expected, vm.SomeDate); + } + + [Theory] + [InlineData("status=OK", MockHttpStatus.OK)] + [InlineData("status=201", MockHttpStatus.Created)] + [InlineData("status=500", (MockHttpStatus)500)] + public void SetsEnumFromQueryString(string queryString, MockHttpStatus status) + { + var vm = new AutoInitializedViewModelMock(); + var parameters = new NavigationParameters($"?{queryString}&title=Hello"); + var ex = Record.Exception(() => PageUtilities.Abracadabra(vm, parameters)); + + Assert.Null(ex); + Assert.Equal(status, vm.Status); + } + } +} diff --git a/Source/Xamarin/Prism.Forms.Tests/Mvvm/Mocks/ViewModels/AutoInitializedViewModelMock.cs b/Source/Xamarin/Prism.Forms.Tests/Mvvm/Mocks/ViewModels/AutoInitializedViewModelMock.cs new file mode 100644 index 000000000..0d2029a89 --- /dev/null +++ b/Source/Xamarin/Prism.Forms.Tests/Mvvm/Mocks/ViewModels/AutoInitializedViewModelMock.cs @@ -0,0 +1,17 @@ +using System; +using Prism.AppModel; + +namespace Prism.Forms.Tests.Mvvm.Mocks.ViewModels +{ + public class AutoInitializedViewModelMock : IAutoInitialize + { + [AutoInitialize(true)] + public string Title { get; set; } + + public bool Success { get; set; } + + public DateTime SomeDate { get; set; } + + public MockHttpStatus Status { get; set; } + } +} diff --git a/Source/Xamarin/Prism.Forms.Tests/Mvvm/Mocks/ViewModels/MockHttpStatus.cs b/Source/Xamarin/Prism.Forms.Tests/Mvvm/Mocks/ViewModels/MockHttpStatus.cs new file mode 100644 index 000000000..fbe8b9258 --- /dev/null +++ b/Source/Xamarin/Prism.Forms.Tests/Mvvm/Mocks/ViewModels/MockHttpStatus.cs @@ -0,0 +1,10 @@ +namespace Prism.Forms.Tests.Mvvm.Mocks.ViewModels +{ + public enum MockHttpStatus + { + Continue = 100, + OK = 200, + Created = 201, + MultipleChoices = 300 + } +}