public void CanAddValidationComponentsTest() { var vc = new ValidationContext(ImmediateScheduler.Instance); var invalidName = string.Empty; var vm = new TestViewModel { Name = "valid" }; var v1 = new BasePropertyValidation <TestViewModel, string>( vm, v => v.Name, s => !string.IsNullOrEmpty(s), msg => $"{msg} isn't valid"); vc.Add(v1); Assert.True(vc.IsValid); vm.Name = invalidName; Assert.False(v1.IsValid); Assert.False(vc.IsValid); Assert.Equal(1, vc.Text.Count); }
public void RegisterValidationsWithDifferentLambdaNameWorksTest() { const string validName = "valid"; const int minimumLength = 5; var viewModel = new TestViewModel { Name = validName }; var view = new TestView(viewModel); var firstValidation = new BasePropertyValidation <TestViewModel, string>( viewModel, viewModelProperty => viewModelProperty.Name, s => !string.IsNullOrEmpty(s), s => $"Name {s} isn't valid"); // Add validations viewModel.ValidationContext.Add(firstValidation); // View bindings view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); // This was throwing exception due to naming problems with lambdas expressions view.BindValidation( view.ViewModel, vm => vm.Name, v => v.NameErrorLabel); Assert.True(viewModel.ValidationContext.IsValid); Assert.Equal(1, viewModel.ValidationContext.Validations.Count); }
public void ValidationMessagesDefaultConcatenationTest() { var viewModel = new TestViewModel { Name = string.Empty }; var view = new TestView(viewModel); var nameValidation = new BasePropertyValidation <TestViewModel, string>( viewModel, viewModelProperty => viewModelProperty.Name, s => !string.IsNullOrEmpty(s), "Name should not be empty."); var name2Validation = new BasePropertyValidation <TestViewModel, string>( viewModel, viewModelProperty => viewModelProperty.Name2, s => !string.IsNullOrEmpty(s), "Name2 should not be empty."); viewModel.ValidationContext.Add(nameValidation); viewModel.ValidationContext.Add(name2Validation); view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); view.Bind(view.ViewModel, vm => vm.Name2, v => v.Name2Label); view.BindValidation(view.ViewModel, v => v.NameErrorLabel); Assert.False(viewModel.ValidationContext.IsValid); Assert.Equal(2, viewModel.ValidationContext.Validations.Count); Assert.NotEmpty(view.NameErrorLabel); Assert.Equal("Name should not be empty. Name2 should not be empty.", view.NameErrorLabel); }
public void MessageUpdatedWhenPropertyChanged() { const string testRoot = "bon"; const string testValue = testRoot + "go"; var model = new TestViewModel(); var validation = new BasePropertyValidation <TestViewModel, string>( model, vm => vm.Name, n => n != null && n.Length > testValue.Length, v => $"The value '{v}' is incorrect"); model.Name = testValue; var changes = new List <ValidationState>(); validation.ValidationStatusChange.Subscribe(v => changes.Add(v)); Assert.Equal("The value 'bongo' is incorrect", validation.Text.ToSingleLine()); Assert.Single(changes); Assert.Equal(new ValidationState(false, "The value 'bongo' is incorrect", validation), changes[0], new ValidationStateComparer()); model.Name = testRoot; Assert.Equal("The value 'bon' is incorrect", validation.Text.ToSingleLine()); Assert.Equal(2, changes.Count); Assert.Equal(new ValidationState(false, "The value 'bon' is incorrect", validation), changes[1], new ValidationStateComparer()); }
public void StateTransitionsWhenValidityChangesTest() { const string testValue = "test"; var model = new TestViewModel(); var validation = new BasePropertyValidation <TestViewModel, string>( model, vm => vm.Name, n => n != null && n.Length >= testValue.Length, "broken"); bool?lastVal = null; var unused = validation .ValidationStatusChange .Subscribe(v => lastVal = v.IsValid); Assert.False(validation.IsValid); Assert.False(lastVal); Assert.True(lastVal.HasValue); model.Name = testValue + "-" + testValue; Assert.True(validation.IsValid); Assert.True(lastVal); }
public void ShouldFireErrorsChangedEventWhenValidationStateChanges() { var viewModel = new IndeiTestViewModel(); DataErrorsChangedEventArgs arguments = null; viewModel.ErrorsChanged += (_, args) => arguments = args; using var firstValidation = new BasePropertyValidation <IndeiTestViewModel, string>( viewModel, vm => vm.Name, s => !string.IsNullOrEmpty(s), NameShouldNotBeEmptyMessage); viewModel.ValidationContext.Add(firstValidation); Assert.True(viewModel.HasErrors); Assert.False(viewModel.ValidationContext.IsValid); Assert.Single(viewModel.ValidationContext.Validations); Assert.Single(viewModel.GetErrors("Name").Cast <string>()); viewModel.Name = "JoJo"; Assert.False(viewModel.HasErrors); Assert.Empty(viewModel.GetErrors("Name").Cast <string>()); Assert.NotNull(arguments); Assert.Equal("Name", arguments.PropertyName); }
public void ShouldSupportBindingToValidationContextWrappedInValidationHelper() { const string nameValidationError = "Name should not be empty."; var view = new TestView(new TestViewModel { Name = string.Empty }); var outerContext = new ValidationContext(ImmediateScheduler.Instance); var validation = new BasePropertyValidation <TestViewModel, string>( view.ViewModel, vm => vm.Name, name => !string.IsNullOrWhiteSpace(name), nameValidationError); outerContext.Add(validation); view.ViewModel.NameRule = new ValidationHelper(outerContext); view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); view.BindValidation(view.ViewModel, vm => vm.NameRule, v => v.NameErrorLabel); Assert.False(view.ViewModel.NameRule.IsValid); Assert.NotEmpty(view.NameErrorLabel); Assert.Equal(nameValidationError, view.NameErrorLabel); view.ViewModel.Name = "Jotaro"; Assert.True(view.ViewModel.NameRule.IsValid); Assert.Empty(view.NameErrorLabel); }
public void ValidModelDefaultState() { var model = CreateDefaultValidModel(); var validation = new BasePropertyValidation <TestViewModel, string>(model, vm => vm.Name, (n) => !string.IsNullOrEmpty(n), "broken"); Assert.True(validation.IsValid); Assert.True(string.IsNullOrEmpty(validation.Text.ToSingleLine())); }
public void PropertyContentsProvidedToMessageTest() { const string testValue = "bongo"; var model = new TestViewModel(); var validation = new BasePropertyValidation <TestViewModel, string>( model, vm => vm.Name, n => n != null && n.Length > testValue.Length, v => $"The value '{v}' is incorrect"); model.Name = testValue; Assert.Equal("The value 'bongo' is incorrect", validation.Text.ToSingleLine()); }
public void TwoValidationPropertiesInSamePropertyThrowsExceptionTest() { const string validName = "valid"; const int minimumLength = 5; var viewModel = new TestViewModel { Name = validName }; var view = new TestView(viewModel); var firstValidation = new BasePropertyValidation <TestViewModel, string>( viewModel, vm => vm.Name, s => !string.IsNullOrEmpty(s), s => $"Name {s} isn't valid"); var secondValidation = new BasePropertyValidation <TestViewModel, string>( viewModel, vm => vm.Name, s => s.Length > minimumLength, _ => $"Minimum length is {minimumLength}"); // Add validations viewModel.ValidationContext.Add(firstValidation); viewModel.ValidationContext.Add(secondValidation); // View bindings view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); // View validations bindings var ex = Assert.Throws <MultipleValidationNotSupportedException>(() => { return(view.BindValidation( view.ViewModel, vm => vm.Name, v => v.NameErrorLabel)); }); Assert.False(viewModel.ValidationContext.IsValid); Assert.Equal(2, viewModel.ValidationContext.Validations.Count); // Checks if second validation error message is shown var expectedError = $"Property {nameof(viewModel.Name)} has more than one validation rule associated. Consider using {nameof(ValidationBindingEx)} methods."; Assert.Equal(expectedError, ex.Message); }
public void TwoValidationComponentsCorrectlyResultInContextTest() { const string validName = "valid"; var invalidName = string.Empty; var vc = new ValidationContext(ImmediateScheduler.Instance); var vm = new TestViewModel { Name = validName, Name2 = validName }; var firstValidation = new BasePropertyValidation <TestViewModel, string>( vm, v => v.Name, s => !string.IsNullOrEmpty(s), s => $"Name {s} isn't valid"); var secondValidation = new BasePropertyValidation <TestViewModel, string>( vm, v => v.Name2, s => !string.IsNullOrEmpty(s), s => $"Name 2 {s} isn't valid"); vc.Add(firstValidation); vc.Add(secondValidation); Assert.True(vc.IsValid); Assert.Equal(0, vc.Text.Count); vm.Name = invalidName; Assert.False(vc.IsValid); Assert.Equal(1, vc.Text.Count); Assert.Equal("Name " + invalidName + " isn't valid", vc.Text[0]); vm.Name2 = invalidName; Assert.False(vc.IsValid); Assert.Equal(2, vc.Text.Count); Assert.Equal("Name " + invalidName + " isn't valid", vc.Text[0]); Assert.Equal("Name 2 " + invalidName + " isn't valid", vc.Text[1]); vm.Name = validName; vm.Name2 = validName; Assert.True(vc.IsValid); Assert.Equal(0, vc.Text.Count); }
/// <summary> /// Setup a validation rule for a specified ViewModel property with dynamic error message. /// </summary> /// <typeparam name="TViewModel">ViewModel type.</typeparam> /// <typeparam name="TViewModelProp">ViewModel property type.</typeparam> /// <param name="viewModel">ViewModel instance.</param> /// <param name="viewModelProperty">ViewModel property.</param> /// <param name="isPropertyValid">Func to define if the viewModelProperty is valid or not.</param> /// <param name="message">Func to define the validation error message based on the viewModelProperty value.</param> /// <returns>Returns a <see cref="ValidationHelper"/> object.</returns> public static ValidationHelper ValidationRule <TViewModel, TViewModelProp>( this TViewModel viewModel, Expression <Func <TViewModel, TViewModelProp> > viewModelProperty, Func <TViewModelProp, bool> isPropertyValid, Func <TViewModelProp, string> message) where TViewModel : ReactiveObject, ISupportsValidation { // We need to associate the ViewModel property // with something that can be easily looked up and bound to var propValidation = new BasePropertyValidation <TViewModel, TViewModelProp>( viewModel, viewModelProperty, isPropertyValid, message); viewModel.ValidationContext.Add(propValidation); return(new ValidationHelper(propValidation)); }
public void ShouldClearAttachedValidationRulesForTheGivenProperty() { var viewModel = new TestViewModel { Name = string.Empty }; using var nameValidation = new BasePropertyValidation <TestViewModel, string>( viewModel, viewModelProperty => viewModelProperty.Name, s => !string.IsNullOrEmpty(s), "Name should not be empty."); const string name2ErrorMessage = "Name2 should not be empty."; using var name2Validation = new BasePropertyValidation <TestViewModel, string>( viewModel, viewModelProperty => viewModelProperty.Name2, s => !string.IsNullOrEmpty(s), name2ErrorMessage); viewModel.ValidationContext.Add(nameValidation); viewModel.ValidationContext.Add(name2Validation); Assert.Equal(2, viewModel.ValidationContext.Validations.Count); Assert.False(viewModel.ValidationContext.IsValid); Assert.NotEmpty(viewModel.ValidationContext.Text); Assert.Throws <ArgumentNullException>(() => viewModel.ClearValidationRules <TestViewModel, string>(null !)); viewModel.ClearValidationRules(x => x.Name); Assert.Equal(1, viewModel.ValidationContext.Validations.Count); Assert.False(viewModel.ValidationContext.IsValid); Assert.NotEmpty(viewModel.ValidationContext.Text); Assert.Equal(name2ErrorMessage, viewModel.ValidationContext.Text.ToSingleLine()); // Verify that the method is idempotent. viewModel.ClearValidationRules(x => x.Name); Assert.Equal(1, viewModel.ValidationContext.Validations.Count); Assert.False(viewModel.ValidationContext.IsValid); Assert.NotEmpty(viewModel.ValidationContext.Text); Assert.Equal(name2ErrorMessage, viewModel.ValidationContext.Text.ToSingleLine()); }
public void ShouldSynchronizeNotifyDataErrorInfoWithValidationContext() { var viewModel = new IndeiTestViewModel(); var view = new IndeiTestView(viewModel); using var firstValidation = new BasePropertyValidation <IndeiTestViewModel, string>( viewModel, vm => vm.Name, s => !string.IsNullOrEmpty(s), NameShouldNotBeEmptyMessage); viewModel.ValidationContext.Add(firstValidation); view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); view.BindValidation(view.ViewModel, vm => vm.Name, v => v.NameErrorLabel); // Verify the initial state. Assert.True(viewModel.HasErrors); Assert.False(viewModel.ValidationContext.IsValid); Assert.Single(viewModel.ValidationContext.Validations); Assert.Equal(NameShouldNotBeEmptyMessage, viewModel.GetErrors("Name").Cast <string>().First()); Assert.Equal(NameShouldNotBeEmptyMessage, view.NameErrorLabel); // Send INotifyPropertyChanged. viewModel.Name = "JoJo"; // Verify the changed state. Assert.False(viewModel.HasErrors); Assert.True(viewModel.ValidationContext.IsValid); Assert.Empty(viewModel.GetErrors("Name").Cast <string>()); Assert.Empty(view.NameErrorLabel); // Send INotifyPropertyChanged. viewModel.Name = string.Empty; // Verify the changed state. Assert.True(viewModel.HasErrors); Assert.False(viewModel.ValidationContext.IsValid); Assert.Single(viewModel.ValidationContext.Validations); Assert.Equal(NameShouldNotBeEmptyMessage, viewModel.GetErrors("Name").Cast <string>().First()); Assert.Equal(NameShouldNotBeEmptyMessage, view.NameErrorLabel); }
public void DualStateMessageTest() { const string testRoot = "bon"; const string testValue = testRoot + "go"; var model = new TestViewModel { Name = testValue }; var validation = new BasePropertyValidation <TestViewModel, string>( model, vm => vm.Name, n => n != null && n.Length > testRoot.Length, (p, v) => v ? "cool" : $"The value '{p}' is incorrect"); Assert.Equal("cool", validation.Text.ToSingleLine()); model.Name = testRoot; Assert.Equal("The value 'bon' is incorrect", validation.Text.ToSingleLine()); }
public void ShouldSupportBindingTwoValidationsForOneProperty() { const int minimumLength = 5; var viewModel = new TestViewModel { Name = "some" }; var view = new TestView(viewModel); var firstValidation = new BasePropertyValidation <TestViewModel, string>( viewModel, vm => vm.Name, s => !string.IsNullOrEmpty(s), "Name is required."); var minimumLengthErrorMessage = $"Minimum length is {minimumLength}"; var secondValidation = new BasePropertyValidation <TestViewModel, string>( viewModel, vm => vm.Name, s => s.Length > minimumLength, s => minimumLengthErrorMessage); // Add validations viewModel.ValidationContext.Add(firstValidation); viewModel.ValidationContext.Add(secondValidation); // View bindings view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); // View validations bindings view.BindValidation(view.ViewModel, vm => vm.Name, v => v.NameErrorLabel); viewModel.Name = "som"; Assert.False(viewModel.ValidationContext.IsValid); Assert.Equal(2, viewModel.ValidationContext.Validations.Count); // Checks if second validation error message is shown Assert.Equal(minimumLengthErrorMessage, view.NameErrorLabel); }
/// <summary> /// Setup a validation rule for a specified ViewModel property with dynamic error message. /// </summary> /// <typeparam name="TViewModel">ViewModel type.</typeparam> /// <typeparam name="TViewModelProp">ViewModel property type.</typeparam> /// <param name="viewModel">ViewModel instance.</param> /// <param name="viewModelProperty">ViewModel property.</param> /// <param name="isPropertyValid">Func to define if the viewModelProperty is valid or not.</param> /// <param name="message">Func to define the validation error message based on the viewModelProperty value.</param> /// <returns>Returns a <see cref="ValidationHelper"/> object.</returns> public static ValidationHelper ValidationRule <TViewModel, TViewModelProp>( this TViewModel viewModel, Expression <Func <TViewModel, TViewModelProp> > viewModelProperty, Func <TViewModelProp, bool> isPropertyValid, Func <TViewModelProp, string> message) where TViewModel : ReactiveObject, IValidatableViewModel { if (viewModel is null) { throw new ArgumentNullException(nameof(viewModel)); } if (viewModelProperty is null) { throw new ArgumentNullException(nameof(viewModelProperty)); } if (isPropertyValid is null) { throw new ArgumentNullException(nameof(isPropertyValid)); } if (message is null) { throw new ArgumentNullException(nameof(message)); } // We need to associate the ViewModel property // with something that can be easily looked up and bound to var propValidation = new BasePropertyValidation <TViewModel, TViewModelProp>( viewModel, viewModelProperty, isPropertyValid, message); viewModel.ValidationContext.Add(propValidation); return(new ValidationHelper(propValidation)); }
public void ShouldClearAttachedValidationRules() { var viewModel = new TestViewModel { Name = string.Empty }; using var nameValidation = new BasePropertyValidation <TestViewModel, string>( viewModel, viewModelProperty => viewModelProperty.Name, s => !string.IsNullOrEmpty(s), "Name should not be empty."); using var name2Validation = new BasePropertyValidation <TestViewModel, string>( viewModel, viewModelProperty => viewModelProperty.Name2, s => !string.IsNullOrEmpty(s), "Name2 should not be empty."); viewModel.ValidationContext.Add(nameValidation); viewModel.ValidationContext.Add(name2Validation); Assert.Equal(2, viewModel.ValidationContext.Validations.Count); Assert.False(viewModel.ValidationContext.IsValid); Assert.NotEmpty(viewModel.ValidationContext.Text); viewModel.ClearValidationRules(); Assert.Equal(0, viewModel.ValidationContext.Validations.Count); Assert.True(viewModel.ValidationContext.IsValid); Assert.Empty(viewModel.ValidationContext.Text); // Verify that the method is idempotent. viewModel.ClearValidationRules(); Assert.Equal(0, viewModel.ValidationContext.Validations.Count); Assert.True(viewModel.ValidationContext.IsValid); Assert.Empty(viewModel.ValidationContext.Text); }
public void ShouldMarkPropertiesAsInvalidOnInit() { var viewModel = new IndeiTestViewModel(); var view = new IndeiTestView(viewModel); using var firstValidation = new BasePropertyValidation <IndeiTestViewModel, string>( viewModel, vm => vm.Name, s => !string.IsNullOrEmpty(s), NameShouldNotBeEmptyMessage); viewModel.ValidationContext.Add(firstValidation); view.Bind(view.ViewModel, vm => vm.Name, v => v.NameLabel); view.BindValidation(view.ViewModel, vm => vm.Name, v => v.NameErrorLabel); // Verify validation context behavior. Assert.False(viewModel.ValidationContext.IsValid); Assert.Single(viewModel.ValidationContext.Validations); Assert.Equal(NameShouldNotBeEmptyMessage, view.NameErrorLabel); // Verify INotifyDataErrorInfo behavior. Assert.True(viewModel.HasErrors); Assert.Equal(NameShouldNotBeEmptyMessage, viewModel.GetErrors("Name").Cast <string>().First()); }
public void IsValidShouldNotifyOfValidityChange() { var viewModel = new TestViewModel { Name = string.Empty }; using var nameValidation = new BasePropertyValidation <TestViewModel, string>( viewModel, viewModelProperty => viewModelProperty.Name, s => !string.IsNullOrEmpty(s), "Name should not be empty."); viewModel.ValidationContext.Add(nameValidation); var latestValidity = false; viewModel.IsValid().Subscribe(isValid => latestValidity = isValid); Assert.False(latestValidity); viewModel.Name = "Jonathan"; Assert.True(latestValidity); viewModel.Name = string.Empty; Assert.False(latestValidity); }