public void ShouldDeliverErrorsWhenModelObservableValidationTriggers()
        {
            var viewModel = new IndeiTestViewModel();

            string namesShouldMatchMessage = "names should match.";
            var    validation = new ModelObservableValidation <IndeiTestViewModel, string>(
                viewModel,
                vm => vm.OtherName,
                vm => vm.WhenAnyValue(
                    m => m.Name,
                    m => m.OtherName,
                    (n, on) => new { n, on })
                .Select(bothNames => bothNames.n == bothNames.on),
                (_, isValid) => isValid ? string.Empty : namesShouldMatchMessage);

            viewModel.ValidationContext.Add(validation);

            Assert.False(viewModel.HasErrors);
            Assert.True(viewModel.ValidationContext.IsValid);
            Assert.Single(viewModel.ValidationContext.Validations);
            Assert.Empty(viewModel.GetErrors(nameof(viewModel.Name)).Cast <string>());
            Assert.Empty(viewModel.GetErrors(nameof(viewModel.OtherName)).Cast <string>());

            viewModel.Name      = "JoJo";
            viewModel.OtherName = "NoNo";

            Assert.True(viewModel.HasErrors);
            Assert.Empty(viewModel.GetErrors(nameof(viewModel.Name)).Cast <string>());
            Assert.Single(viewModel.GetErrors(nameof(viewModel.OtherName)).Cast <string>());
            Assert.Single(validation.Text);
            Assert.Equal(namesShouldMatchMessage, validation.Text.Single());
        }
        /// <summary>
        /// Setup a validation rule with a general observable indicating validity.
        /// </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 referenced in viewModelObservableProperty.</param>
        /// <param name="viewModelObservableProperty">Func to define if the viewModel is valid or not.</param>
        /// <param name="messageFunc">Func to define the validation error message based on the viewModel and viewModelObservableProperty values.</param>
        /// <returns>Returns a <see cref="ValidationHelper"/> object.</returns>
        /// <remarks>
        /// It should be noted that the observable should provide an initial value, otherwise that can result
        /// in an inconsistent performance.
        /// </remarks>
        public static ValidationHelper ValidationRule <TViewModel, TViewModelProp>(
            this TViewModel viewModel,
            Expression <Func <TViewModel, TViewModelProp> > viewModelProperty,
            Func <TViewModel, IObservable <bool> > viewModelObservableProperty,
            Func <TViewModel, bool, string> messageFunc)
            where TViewModel : ReactiveObject, IValidatableViewModel
        {
            if (viewModel is null)
            {
                throw new ArgumentNullException(nameof(viewModel));
            }

            if (viewModelProperty is null)
            {
                throw new ArgumentNullException(nameof(viewModelProperty));
            }

            if (viewModelObservableProperty is null)
            {
                throw new ArgumentNullException(nameof(viewModelObservableProperty));
            }

            if (messageFunc is null)
            {
                throw new ArgumentNullException(nameof(messageFunc));
            }

            var validation = new ModelObservableValidation <TViewModel, TViewModelProp>(viewModel, viewModelProperty, viewModelObservableProperty, messageFunc);

            viewModel.ValidationContext.Add(validation);

            return(new ValidationHelper(validation));
        }
        /// <summary>
        /// Setup a validation rule with a general observable indicating validity.
        /// </summary>
        /// <typeparam name="TViewModel">ViewModel type.</typeparam>
        /// <param name="viewModel">ViewModel instance.</param>
        /// <param name="viewModelObservableProperty">Func to define if the viewModel is valid or not.</param>
        /// <param name="messageFunc">Func to define the validation error message based on the viewModel and viewModelObservableProperty values.</param>
        /// <returns>Returns a <see cref="ValidationHelper"/> object.</returns>
        /// <remarks>
        /// It should be noted that the observable should provide an initial value, otherwise that can result
        /// in an inconsistent performance.
        /// </remarks>
        public static ValidationHelper ValidationRule <TViewModel>(
            this TViewModel viewModel,
            Func <TViewModel, IObservable <bool> > viewModelObservableProperty,
            Func <TViewModel, bool, string> messageFunc)
            where TViewModel : ReactiveObject, ISupportsValidation
        {
            var validation =
                new ModelObservableValidation <TViewModel>(viewModel, viewModelObservableProperty, messageFunc);

            viewModel.ValidationContext.Add(validation);

            return(new ValidationHelper(validation));
        }
        public void InitialValidStateIsCorrect()
        {
            var model = new TestViewModel()
            {
                Name = "name", Name2 = "name2"
            };

            Subject <bool> validState = new Subject <bool>();

            var v = new ModelObservableValidation <TestViewModel>(model, (m) => validState.StartWith(true), (m, s) => "broken");

            Assert.True(v.IsValid);
        }
        public void ObservableToInvalid()
        {
            var model = new TestViewModel()
            {
                Name = "name", Name2 = "name2"
            };

            ReplaySubject <bool> validState = new ReplaySubject <bool>(1);

            var v = new ModelObservableValidation <TestViewModel>(model, (m) => validState, (m, s) => "broken");

            validState.OnNext(false);
            validState.OnNext(true);
            validState.OnNext(false);

            Assert.False(v.IsValid);
            Assert.Equal("broken", v.Text.ToSingleLine());
        }