Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
Added TextCharactersValidationBehavior (#363)
Browse files Browse the repository at this point in the history
* Added TextCharactersValidationBehavior

* Added unit tests

* Fixed props naming

* Update names. Added basic latin letters

* Fixed build on Mac

* Fixed warning

* Removed redundant conditions

* Restored removed line

* Renamed class

* Updated character type
  • Loading branch information
AndreiMisiukevich authored Oct 21, 2020
1 parent 81f9899 commit b983c2d
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Xamarin.CommunityToolkit.Behaviors;
using Xamarin.CommunityToolkit.UnitTests.Mocks;
using Xamarin.Forms;
using Xunit;

namespace Xamarin.CommunityToolkit.UnitTests.Behaviors
{
public class CharactersValidationBehavior_Tests
{
public CharactersValidationBehavior_Tests()
=> Device.PlatformServices = new MockPlatformServices();

[Theory]
[InlineData(CharacterType.Any, 1, 2, "A", true)]
[InlineData(CharacterType.Any, 0, int.MaxValue, "", true)]
[InlineData(CharacterType.LowercaseLetter, 1, int.MaxValue, "WWWWWaWWWW", true)]
[InlineData(CharacterType.UppercaseLetter, 1, int.MaxValue, "aaaaaaRRaaaa", true)]
[InlineData(CharacterType.Letter, 4, int.MaxValue, "aaaaaaRRaaaa", true)]
[InlineData(CharacterType.Digit, 1, int.MaxValue, "-1d", true)]
[InlineData(CharacterType.Alphanumeric, 2, int.MaxValue, "@-3r", true)]
[InlineData(CharacterType.NonAlphanumericSymbol, 10, int.MaxValue, "@-&^%!+()/", true)]
[InlineData(CharacterType.LowercaseLatinLetter, 2, int.MaxValue, "HHHH a r.", true)]
[InlineData(CharacterType.UppercaseLatinLetter, 2, int.MaxValue, "aaaaaa....R.R.R.aaaa", true)]
[InlineData(CharacterType.LatinLetter, 5, int.MaxValue, "12345bBbBb", true)]
[InlineData(CharacterType.Whitespace, 0, int.MaxValue, ";lkjhgfd@+fasf", true)]
[InlineData(CharacterType.Any, 2, 2, "A", false)]
[InlineData(CharacterType.Any, 2, 2, "AaA", false)]
[InlineData(CharacterType.Any, 1, int.MaxValue, "", false)]
[InlineData(CharacterType.Any, 1, int.MaxValue, null, false)]
[InlineData(CharacterType.LowercaseLetter, 1, int.MaxValue, "WWWWWW", false)]
[InlineData(CharacterType.UppercaseLetter, 1, int.MaxValue, "aaaaaa", false)]
[InlineData(CharacterType.Letter, 4, int.MaxValue, "wHo", false)]
[InlineData(CharacterType.Digit, 1, int.MaxValue, "-d", false)]
[InlineData(CharacterType.Alphanumeric, 2, int.MaxValue, "@-3", false)]
[InlineData(CharacterType.NonAlphanumericSymbol, 1, int.MaxValue, "WWWWWWWW", false)]
[InlineData(CharacterType.LowercaseLatinLetter, 1, int.MaxValue, "Кириллица", false)]
[InlineData(CharacterType.UppercaseLatinLetter, 1, int.MaxValue, "КИРИЛЛИЦА", false)]
[InlineData(CharacterType.LatinLetter, 1, int.MaxValue, "Это Кириллица!", false)]
[InlineData(CharacterType.Whitespace, 0, 0, "WWWWWW WWWWW", false)]
public void IsValid(CharacterType characterType, int minimumCharactersNumber, int maximumCharactersNumber, string value, bool expectedValue)
{
var behavior = new CharactersValidationBehavior
{
CharacterType = characterType,
MinimumCharacterCount = minimumCharactersNumber,
MaximumCharacterCount = maximumCharactersNumber
};
var entry = new Entry
{
Text = value
};
entry.Behaviors.Add(behavior);
behavior.ForceValidate();
Assert.Equal(expectedValue, behavior.IsValid);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ public NumericValidationBehavior_Tests()
=> Device.PlatformServices = new MockPlatformServices();

[Theory]
// Positive
[InlineData("en-US", "15.2", 1.0, 16.0, 0, 16, true)]
[InlineData("en-US", "15.", 1.0, 16.0, 0, 1, true)]
[InlineData("en-US", "15.88", 1.0, 16.0, 2, 2, true)]
Expand All @@ -25,7 +24,6 @@ public NumericValidationBehavior_Tests()
[InlineData("de-DE", "0,99", 0.9, 2.0, 0, 16, true)]
[InlineData("de-DE", ",99", 0.9, 2.0, 0, 16, true)]
[InlineData("de-DE", "1.115,2", 1.0, 2000.0, 0, 16, true)]
// Negative
[InlineData("en-US", "15.3", 16.0, 20.0, 0, 16, false)]
[InlineData("en-US", "15.3", 0.0, 15.0, 0, 16, false)]
[InlineData("en-US", "15.", 1.0, 16.0, 0, 0, false)]
Expand Down Expand Up @@ -60,14 +58,11 @@ public void IsValid(string culture, string value, double minValue, double maxVal
MinimumDecimalPlaces = minDecimalPlaces,
MaximumDecimalPlaces = maxDecimalPlaces
};
new Entry
var entry = new Entry
{
Text = value,
Behaviors =
{
behavior
}
Text = value
};
entry.Behaviors.Add(behavior);
behavior.ForceValidate();
Assert.Equal(expectedValue, behavior.IsValid);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,15 @@ public void IsValid(string value, UriKind uriKind, bool expectedValue)
{
UriKind = uriKind,
};
new Entry
var entry = new Entry
{
Text = value,
Behaviors =
{
behavior
}
};
entry.Behaviors.Add(behavior);
behavior.ForceValidate();
Assert.Equal(expectedValue, behavior.IsValid);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace Xamarin.CommunityToolkit.Behaviors
{
[Flags]
public enum CharacterType
{
LowercaseLetter = 1,
UppercaseLetter = 2,
Letter = LowercaseLetter | UppercaseLetter,
Digit = 4,
Alphanumeric = Letter | Digit,
Whitespace = 8,
NonAlphanumericSymbol = 16,
LowercaseLatinLetter = 32,
UppercaseLatinLetter = 64,
LatinLetter = LowercaseLatinLetter | UppercaseLatinLetter,
Any = Alphanumeric | NonAlphanumericSymbol | Whitespace
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Xamarin.Forms;

namespace Xamarin.CommunityToolkit.Behaviors
{
public class CharactersValidationBehavior : TextValidationBehavior
{
List<Predicate<char>> characterPredicates;

public static readonly BindableProperty CharacterTypeProperty =
BindableProperty.Create(nameof(CharacterType), typeof(CharacterType), typeof(CharactersValidationBehavior), CharacterType.Any, propertyChanged: OnCharacterTypePropertyChanged);

public static readonly BindableProperty MinimumCharacterCountProperty =
BindableProperty.Create(nameof(MinimumCharacterCount), typeof(int), typeof(CharactersValidationBehavior), 0, propertyChanged: OnValidationPropertyChanged);

public static readonly BindableProperty MaximumCharacterCountProperty =
BindableProperty.Create(nameof(MaximumCharacterCount), typeof(int), typeof(CharactersValidationBehavior), int.MaxValue, propertyChanged: OnValidationPropertyChanged);

public CharactersValidationBehavior()
=> OnCharacterTypePropertyChanged();

public CharacterType CharacterType
{
get => (CharacterType)GetValue(CharacterTypeProperty);
set => SetValue(CharacterTypeProperty, value);
}

public int MinimumCharacterCount
{
get => (int)GetValue(MinimumCharacterCountProperty);
set => SetValue(MinimumCharacterCountProperty, value);
}

public int MaximumCharacterCount
{
get => (int)GetValue(MaximumCharacterCountProperty);
set => SetValue(MaximumCharacterCountProperty, value);
}

protected override bool Validate(object value)
=> base.Validate(value) && Validate(value?.ToString());

static void OnCharacterTypePropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
((CharactersValidationBehavior)bindable).OnCharacterTypePropertyChanged();
OnValidationPropertyChanged(bindable, oldValue, newValue);
}

static IEnumerable<Predicate<char>> GetCharacterPredicates(CharacterType characterType)
{
if (characterType.HasFlag(CharacterType.LowercaseLetter))
yield return char.IsLower;

if (characterType.HasFlag(CharacterType.UppercaseLetter))
yield return char.IsUpper;

if (characterType.HasFlag(CharacterType.Digit))
yield return char.IsDigit;

if (characterType.HasFlag(CharacterType.Whitespace))
yield return char.IsWhiteSpace;

if (characterType.HasFlag(CharacterType.NonAlphanumericSymbol))
yield return c => !char.IsLetterOrDigit(c) && !char.IsWhiteSpace(c);

if (characterType.HasFlag(CharacterType.LowercaseLatinLetter))
yield return c => c >= 'a' && c <= 'z';

if (characterType.HasFlag(CharacterType.UppercaseLatinLetter))
yield return c => c >= 'A' && c <= 'Z';
}

void OnCharacterTypePropertyChanged()
=> characterPredicates = GetCharacterPredicates(CharacterType).ToList();

bool Validate(string value)
{
var count = value?.ToCharArray().Count(character => characterPredicates.Any(predicate => predicate.Invoke(character))) ?? 0;
return count >= MinimumCharacterCount
&& count <= MaximumCharacterCount;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void DoCameraThings_Clicked(object sender, EventArgs e)
: AppResources.CameraViewSampleSnapPicture;
}

void CameraView_OnAvailable(object _, bool e)
void CameraView_OnAvailable(object sender, bool e)
{
if (e)
{
Expand Down

0 comments on commit b983c2d

Please sign in to comment.