public void CancelEdit_Restores_ValidationErrors_ForAddedEntity() { // Start with a valid entity Cities.City city = new Cities.City() { Name = "Cincinnati", StateName = "OH", CountyName = "Hamilton" }; IEditableObject editableCity = (IEditableObject)city; string customEntityError = "Entity-level error that was added manually"; city.ValidationErrors.Add(new ValidationResult(customEntityError)); string customPropertyError = "Property-level error that was added manually"; city.ValidationErrors.Add(new ValidationResult(customPropertyError, new string[] { "ZoneID", "CountyName" })); Assert.AreEqual<int>(2, city.ValidationErrors.Count, "Error count before editing"); // Edit the city, making changes that will replace the property errors for Name and CountyName (because it was tied to the Name member too) // with new errors, leaving the entity-level error in place. editableCity.BeginEdit(); city.ZoneID = -1; // Out of range city.ZoneName = Cities.CityPropertyValidator.InvalidZoneName; Assert.AreEqual<int>(3, city.ValidationErrors.Count, "Error count before CancelEdit"); INotifyDataErrorInfo notifier = (INotifyDataErrorInfo)city; List<Tuple<string, IEnumerable<ValidationResult>>> errorNotifications = new List<Tuple<string, IEnumerable<ValidationResult>>>(); notifier.ErrorsChanged += (s, e) => { errorNotifications.Add(new Tuple<string, IEnumerable<ValidationResult>>(e.PropertyName, notifier.GetErrors(e.PropertyName).Cast<ValidationResult>())); }; /// When we cancel the edit, the following changes will occur to the validation errors, in no particular order /// - StateName is no longer invalid; notification, with no errors for that property /// - Name loses its required error but regains the manually-added custom error /// - CountyName regains the manually-added error /// - The custom entity-level error message that was added will be restored editableCity.CancelEdit(); // Verify our validation errors count reverted back to the 2 errors we had before editing Assert.AreEqual<int>(2, city.ValidationErrors.Count, "Error count after CancelEdit"); // Verify the entity-level error that we manually added still shows up Assert.AreEqual<string>(customEntityError, city.ValidationErrors.Single(e => e.MemberNames.Count() == 0 || (e.MemberNames.Count() == 1 && string.IsNullOrEmpty(e.MemberNames.Single()))).ErrorMessage, "ErrorMessage after CancelEdit"); // Verify the property-level error that we manually added still shows up Assert.AreEqual<string>(customPropertyError, city.ValidationErrors.Single(e => e.MemberNames.Contains("ZoneID")).ErrorMessage, "ErrorMessage for ZoneID after CancelEdit"); Assert.AreEqual<string>(customPropertyError, city.ValidationErrors.Single(e => e.MemberNames.Contains("CountyName")).ErrorMessage, "ErrorMessage for CountyName after CancelEdit"); // Verify that we got the 4 expected notifications from INotifyDataErrorInfo Assert.AreEqual<int>(4, errorNotifications.Count, "Error notification count"); // One of which should have been for Name, one for StateName, one for CountyName, and one for the entity-level error Assert.AreEqual<int>(1, errorNotifications.Count(e => e.Item1 == "ZoneID"), "Count of Name notifications"); Assert.AreEqual<int>(1, errorNotifications.Count(e => e.Item1 == "ZoneName"), "Count of ZoneName notifications"); Assert.AreEqual<int>(1, errorNotifications.Count(e => e.Item1 == "CountyName"), "Count of CountyName notifications"); Assert.AreEqual<int>(1, errorNotifications.Count(e => string.IsNullOrEmpty(e.Item1)), "Count of entity-level notifications"); // Verify that when the notification occurred for Name, CountyName, and the entity, we had a single error for each, and there was no error for StateName Assert.AreEqual<int>(1, errorNotifications.Single(e => e.Item1 == "ZoneID").Item2.Count(), "Error count for ZoneID at time of notification"); Assert.AreEqual<int>(1, errorNotifications.Single(e => e.Item1 == "CountyName").Item2.Count(), "Error count for CountyName at time of notification"); Assert.AreEqual<int>(1, errorNotifications.Single(e => string.IsNullOrEmpty(e.Item1)).Item2.Count(), "Error count for entity errors at time of notification"); Assert.AreEqual<int>(0, errorNotifications.Single(e => e.Item1 == "ZoneName").Item2.Count(), "Error count for ZoneName at time of notification"); // Verify the manually-added errors were in place at the time of the notifications Assert.AreEqual<string>(customPropertyError, errorNotifications.Single(e => e.Item1 == "ZoneID").Item2.Single().ErrorMessage, "ErrorMessage of the ZoneID error when its notification was raised"); Assert.AreEqual<string>(customPropertyError, errorNotifications.Single(e => e.Item1 == "CountyName").Item2.Single().ErrorMessage, "ErrorMessage of the CountyName error when its notification was raised"); Assert.AreEqual<string>(customEntityError, errorNotifications.Single(e => string.IsNullOrEmpty(e.Item1)).Item2.Single().ErrorMessage, "ErrorMessage of the entity-level error when its notification was raised"); }
public void ErrorsChanged_Event_Subscription() { // Start with a valid entity Cities.City city = new Cities.City() { Name = "Cincinnati", StateName = "OH", CountyName = "Hamilton" }; INotifyDataErrorInfo notifier = city as INotifyDataErrorInfo; int errorsChangedCount = 0; EventHandler<DataErrorsChangedEventArgs> handler = (s, e) => ++errorsChangedCount; notifier.ErrorsChanged += handler; city.ValidationErrors.Add(new ValidationResult("Foo")); Assert.AreEqual<int>(1, errorsChangedCount, "Error count after subscribing"); notifier.ErrorsChanged -= handler; city.ValidationErrors.Add(new ValidationResult("Bar")); Assert.AreEqual<int>(1, errorsChangedCount, "Error count after unsubscribing"); }
public void GetErrors_Prevents_Deferred_Enumeration() { Cities.City city = new Cities.City(); INotifyDataErrorInfo notifier = (INotifyDataErrorInfo)city; System.Collections.IEnumerable noErrors = notifier.GetErrors("Name"); city.ValidationErrors.Add(new ValidationResult("Error", new string[] { "Name" })); System.Collections.IEnumerable withError = notifier.GetErrors("Name"); Assert.AreEqual<int>(0, noErrors.Cast<ValidationResult>().Count(), "Count from first enumerable"); Assert.AreEqual<int>(1, withError.Cast<ValidationResult>().Count(), "Count from second enumerable"); }
public void ValidatePropertyOverrideCanThrowValidationException() { Cities.City city = new Cities.City() { Name = "Cincinnati" }; city.ThrowValidationExceptions = true; string invalidName = "This 1 is an invalid city name"; ExceptionHelper.ExpectValidationException(() => city.Name = invalidName, "The field CityName must match the regular expression '^[A-Z]+[a-z A-Z]*$'.", typeof(RegularExpressionAttribute), invalidName); Assert.AreEqual<string>("Cincinnati", city.Name, "The city name should be unchanged when invalid"); }
public void Entity_RejectChanges_Clears_ValidationErrors() { // This test requires an entity that is attached Cities.CityDomainContext domainContext = new Cities.CityDomainContext(); Cities.City entity = new Cities.City(); domainContext.Cities.Add(entity); INotifyDataErrorInfo notifier = (INotifyDataErrorInfo)entity; List<string> actualErrors = new List<string>(); notifier.ErrorsChanged += (s, e) => actualErrors.Add(e.PropertyName); entity.StateName = "Not a State Name"; // Marks the entity as changed and adds a validation result for StateName entity.ValidationErrors.Add(new ValidationResult("Invalid Property Error", new string[] { "Foo" })); entity.ValidationErrors.Add(new ValidationResult("Entity Error", null)); string[] membersNotifed = new string[] { "StateName", "Foo", null }; Assert.IsTrue(actualErrors.OrderBy(s => s).SequenceEqual(membersNotifed.OrderBy(s => s)), "The list of errors when adding errors"); actualErrors.Clear(); ((IRevertibleChangeTracking)entity).RejectChanges(); Assert.IsTrue(actualErrors.OrderBy(s => s).SequenceEqual(membersNotifed.OrderBy(s => s)), "The list of errors when rejecting changes"); }
public void ValidationContextUpdatedForEntityWhenChangedForDomainContext() { Cities.CityDomainContext domainContext = new Cities.CityDomainContext(TestURIs.Cities); Cities.City newCity = new Cities.City(); domainContext.Cities.Add(newCity); // Set up the ValidationContext after adding the entity into the domain context // to ensure that the updated validation context is plumbed through Dictionary<object, object> items = new Dictionary<object, object>(); items.Add("TestMethod", "ValidationContextUsedForPropertyValidation"); ValidationContext providedValidationContext = new ValidationContext(this, null, items); domainContext.ValidationContext = providedValidationContext; bool callbackCalled = false; Action<ValidationContext> assertValidationContext = validationValidationContext => { Assert.AreNotSame(providedValidationContext, validationValidationContext, "The ValidationContext provided to ValidationProperty should not be the same actual instance of the ValidationContext we provided"); Assert.IsTrue(validationValidationContext.Items.ContainsKey("TestMethod"), "The ValidationContext provided should have the items we provided"); Assert.AreEqual(providedValidationContext.Items["TestMethod"], validationValidationContext.Items["TestMethod"], "The ValidationContext provided should have the items we provided"); callbackCalled = true; }; newCity.ValidatePropertyCallback = assertValidationContext; newCity.ValidateCityCallback = assertValidationContext; // Entity-level validation is performed by calling EndEdit with valid properties IEditableObject editableCity = (IEditableObject)newCity; editableCity.BeginEdit(); newCity.Name = "Cincinnati"; newCity.StateName = "OH"; newCity.CountyName = "Hamilton"; editableCity.EndEdit(); Assert.IsTrue(callbackCalled, "Make sure our callback was called to perform the test"); }
public void ValidationContextUsedForPropertyValidation() { Dictionary<object, object> items = new Dictionary<object,object>(); items.Add("TestMethod", "ValidationContextUsedForPropertyValidation"); ValidationContext providedValidationContext = new ValidationContext(this, null, items); Cities.CityDomainContext domainContext = new Cities.CityDomainContext(TestURIs.Cities); domainContext.ValidationContext = providedValidationContext; bool callbackCalled = false; Cities.City newCity = new Cities.City(); domainContext.Cities.Add(newCity); newCity.ValidatePropertyCallback = validationValidationContext => { Assert.AreNotSame(providedValidationContext, validationValidationContext, "The ValidationContext provided to ValidationProperty should not be the same actual instance of the ValidationContext we provided"); Assert.IsTrue(validationValidationContext.Items.ContainsKey("TestMethod"), "The ValidationContext provided should have the items we provided"); Assert.AreEqual(providedValidationContext.Items["TestMethod"], validationValidationContext.Items["TestMethod"], "The ValidationContext provided should have the items we provided"); callbackCalled = true; }; // Set a property, triggering property validation newCity.Name = "Foo"; Assert.IsTrue(callbackCalled, "Make sure our callback was called to perform the test"); }