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/Common/ParametersFixture.cs b/Source/Prism.Tests/Common/ParametersFixture.cs new file mode 100644 index 000000000..abd2aa8cf --- /dev/null +++ b/Source/Prism.Tests/Common/ParametersFixture.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Linq; +using Prism.Tests.Common.Mocks; +using Xunit; + +namespace Prism.Tests.Common +{ + public class ParametersFixture + { + [Fact] + public void TryGetValueOfT() + { + var parameters = new MockParameters("mock=Foo&mock2=1"); + bool success = false; + MockEnum value = default; + MockEnum value1 = default; + + var ex = Record.Exception(() => success = parameters.TryGetValue("mock", out value)); + var ex2 = Record.Exception(() => success = parameters.TryGetValue("mock2", out value1)); + Assert.Null(ex); + Assert.True(success); + Assert.Equal(MockEnum.Foo, value); + Assert.Equal(value, value1); + } + + [Fact] + public void GetValuesOfT() + { + var parameters = new MockParameters("mock=Foo&mock=2&mock=Fizz"); + + IEnumerable values = default; + + var ex = Record.Exception(() => values = parameters.GetValues("mock")); + Assert.Null(ex); + Assert.Equal(3, values.Count()); + Assert.Equal(MockEnum.Foo, values.ElementAt(0)); + Assert.Equal(MockEnum.Bar, values.ElementAt(1)); + Assert.Equal(MockEnum.Fizz, values.ElementAt(2)); + } + + + [Fact] + public void GetValue() + { + var parameters = new MockParameters("mock=Foo&mock1=2&mock2=Fizz"); + MockEnum value = default; + MockEnum value1 = default; + + var ex = Record.Exception(() => value = parameters.GetValue("mock")); + var ex2 = Record.Exception(() => value1 = parameters.GetValue("mock1")); + Assert.Null(ex); + Assert.Equal(MockEnum.Foo, value); + Assert.Equal(MockEnum.Bar, value1); + } + } +} diff --git a/Source/Prism/Common/ParametersExtensions.cs b/Source/Prism/Common/ParametersExtensions.cs index 5222fffcc..774bb6a2c 100644 --- a/Source/Prism/Common/ParametersExtensions.cs +++ b/Source/Prism/Common/ParametersExtensions.cs @@ -19,14 +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 - 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}' "); } } @@ -36,20 +32,15 @@ public static object GetValue(this IEnumerable> par [EditorBrowsable(EditorBrowsableState.Never)] public static bool TryGetValue(this IEnumerable> parameters, string key, out T value) { + var type = typeof(T); + foreach (var kvp in parameters) { if (string.Compare(kvp.Key, key, StringComparison.Ordinal) == 0) { - if (kvp.Value == null) - value = default; - else if (kvp.Value.GetType() == typeof(T)) - value = (T)kvp.Value; - else if (typeof(T).IsAssignableFrom(kvp.Value.GetType())) - value = (T)kvp.Value; - else - value = (T)Convert.ChangeType(kvp.Value, typeof(T)); - - return true; + var success = TryGetValueInternal(kvp, typeof(T), out object valueAsObject); + value = (T)valueAsObject; + return success; } } @@ -61,25 +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); 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() == typeof(T)) - values.Add((T)kvp.Value); - else if (typeof(T).IsAssignableFrom(kvp.Value.GetType())) - values.Add((T)kvp.Value); - else - values.Add((T)Convert.ChangeType(kvp.Value, typeof(T))); + 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 + } +}