Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Enum support from QueryStrings #1953

Merged
merged 3 commits into from Nov 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions 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
}
}
10 changes: 10 additions & 0 deletions 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) { }
}
}
56 changes: 56 additions & 0 deletions 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<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
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
@@ -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);
}
}
}
@@ -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; }
}
}
@@ -0,0 +1,10 @@
namespace Prism.Forms.Tests.Mvvm.Mocks.ViewModels
{
public enum MockHttpStatus
{
Continue = 100,
OK = 200,
Created = 201,
MultipleChoices = 300
}
}