Skip to content

Commit

Permalink
Merge pull request #1953 from PrismLibrary/enum-autoinitialize
Browse files Browse the repository at this point in the history
Adding Enum support from QueryStrings
  • Loading branch information
dansiegel authored Nov 21, 2019
2 parents ed16196 + 053eb30 commit a2231be
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 26 deletions.
11 changes: 11 additions & 0 deletions Source/Prism.Tests/Common/Mocks/MockEnum.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Prism.Tests.Common.Mocks
{
internal enum MockEnum
{
None = 0,
Foo = 1,
Bar = 2,
Fizz = 3,
SomethingElse = 99
}
}
10 changes: 10 additions & 0 deletions Source/Prism.Tests/Common/Mocks/MockParameters.cs
Original file line number Diff line number Diff line change
@@ -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) { }
}
}
56 changes: 56 additions & 0 deletions Source/Prism.Tests/Common/ParametersFixture.cs
Original file line number Diff line number Diff line change
@@ -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<MockEnum>("mock", out value));
var ex2 = Record.Exception(() => success = parameters.TryGetValue<MockEnum>("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<MockEnum> values = default;

var ex = Record.Exception(() => values = parameters.GetValues<MockEnum>("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<MockEnum>("mock"));
var ex2 = Record.Exception(() => value1 = parameters.GetValue<MockEnum>("mock1"));
Assert.Null(ex);
Assert.Equal(MockEnum.Foo, value);
Assert.Equal(MockEnum.Bar, value1);
}
}
}
80 changes: 54 additions & 26 deletions Source/Prism/Common/ParametersExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,10 @@ public static object GetValue(this IEnumerable<KeyValuePair<string, object>> 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}' ");
}
}

Expand All @@ -36,20 +32,15 @@ public static object GetValue(this IEnumerable<KeyValuePair<string, object>> par
[EditorBrowsable(EditorBrowsableState.Never)]
public static bool TryGetValue<T>(this IEnumerable<KeyValuePair<string, object>> 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;
}
}

Expand All @@ -61,25 +52,62 @@ public static bool TryGetValue<T>(this IEnumerable<KeyValuePair<string, object>>
public static IEnumerable<T> GetValues<T>(this IEnumerable<KeyValuePair<string, object>> parameters, string key)
{
List<T> values = new List<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() == 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<string, object> 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<KeyValuePair<string, object>> parameters, string key) =>
parameters.Any(x => string.Compare(x.Key, key, StringComparison.Ordinal) == 0);
Expand Down
72 changes: 72 additions & 0 deletions Source/Xamarin/Prism.Forms.Tests/Mvvm/AutoInitializeFixture.cs
Original file line number Diff line number Diff line change
@@ -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<ArgumentNullException>(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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Prism.Forms.Tests.Mvvm.Mocks.ViewModels
{
public enum MockHttpStatus
{
Continue = 100,
OK = 200,
Created = 201,
MultipleChoices = 300
}
}

0 comments on commit a2231be

Please sign in to comment.