From 28f5c845793be9a0db3f2acef914a68ccc91ce80 Mon Sep 17 00:00:00 2001 From: Pekka Hyvönen Date: Mon, 12 Sep 2016 12:05:19 +0300 Subject: Refactor Binder Status Handling API BinderStatusHandler is now triggered only once per validation. Unified ValidationError and BinderResult into BinderValidationStatus. Renamed ValidationStatusChangeEvent into ValidationStatus. Unified handler names for validation status. Next patch will fix resetting of field errors on reset. Change-Id: I9536d554d781fe599fbd7e5bcb5a9ffebe675ca0 --- .../test/java/com/vaadin/data/BeanBinderTest.java | 7 +- .../com/vaadin/data/BinderBookOfVaadinTest.java | 194 +++++++------ .../src/test/java/com/vaadin/data/BinderTest.java | 306 ++++++++++++++------- 3 files changed, 327 insertions(+), 180 deletions(-) (limited to 'server/src/test/java') diff --git a/server/src/test/java/com/vaadin/data/BeanBinderTest.java b/server/src/test/java/com/vaadin/data/BeanBinderTest.java index 1788c3a9e8..84a414edf1 100644 --- a/server/src/test/java/com/vaadin/data/BeanBinderTest.java +++ b/server/src/test/java/com/vaadin/data/BeanBinderTest.java @@ -163,9 +163,10 @@ public class BeanBinderTest { } private void assertInvalid(HasValue field, String message) { - List> errors = binder.validate(); + BinderValidationStatus status = binder.validate(); + List> errors = status.getFieldValidationErrors(); assertEquals(1, errors.size()); - assertSame(field, errors.get(0).getField().get()); - assertEquals(message, errors.get(0).getMessage()); + assertSame(field, errors.get(0).getField()); + assertEquals(message, errors.get(0).getMessage().get()); } } diff --git a/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java b/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java index 6861903132..d11a0ed6da 100644 --- a/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java +++ b/server/src/test/java/com/vaadin/data/BinderBookOfVaadinTest.java @@ -27,12 +27,14 @@ import org.junit.Before; import org.junit.Test; import com.vaadin.data.Binder.Binding; +import com.vaadin.data.ValidationStatus.Status; import com.vaadin.data.util.converter.Converter; import com.vaadin.data.util.converter.StringToIntegerConverter; import com.vaadin.data.validator.EmailValidator; import com.vaadin.server.AbstractErrorMessage; import com.vaadin.ui.Button; import com.vaadin.ui.Label; +import com.vaadin.ui.Notification; import com.vaadin.ui.PopupDateField; import com.vaadin.ui.Slider; import com.vaadin.ui.TextField; @@ -139,6 +141,30 @@ public class BinderBookOfVaadinTest { emailField = new TextField(); } + @Test + public void loadingFromBusinessObjects() { + // this test is just to make sure the code snippet in the book compiles + binder.load(new BookPerson(1969, 50000)); + + BinderValidationStatus status = binder.validate(); + + if (status.hasErrors()) { + Notification.show("Validation error count: " + + status.getValidationErrors().size()); + } + } + + @Test + public void handlingCheckedException() { + // another test just to verify that book examples actually compile + try { + binder.save(new BookPerson(2000, 50000)); + } catch (ValidationException e) { + Notification.show("Validation error count: " + + e.getValidationErrors().size()); + } + } + @Test public void simpleEmailValidator() { binder.forField(field) @@ -148,16 +174,16 @@ public class BinderBookOfVaadinTest { .bind(BookPerson::getEmail, BookPerson::setEmail); field.setValue("not-email"); - List> errors = binder.validate(); - Assert.assertEquals(1, errors.size()); + BinderValidationStatus status = binder.validate(); + Assert.assertEquals(1, status.getFieldValidationErrors().size()); Assert.assertEquals("This doesn't look like a valid email address", - errors.get(0).getMessage()); + status.getFieldValidationErrors().get(0).getMessage().get()); Assert.assertEquals("This doesn't look like a valid email address", ((AbstractErrorMessage) field.getErrorMessage()).getMessage()); field.setValue("abc@vaadin.com"); - errors = binder.validate(); - Assert.assertEquals(0, errors.size()); + status = binder.validate(); + Assert.assertEquals(0, status.getBeanValidationErrors().size()); Assert.assertNull(field.getErrorMessage()); } @@ -170,16 +196,16 @@ public class BinderBookOfVaadinTest { .bind(BookPerson::getLastName, BookPerson::setLastName); field.setValue("a"); - List> errors = binder.validate(); - Assert.assertEquals(1, errors.size()); + BinderValidationStatus status = binder.validate(); + Assert.assertEquals(1, status.getFieldValidationErrors().size()); Assert.assertEquals("Last name must contain at least three characters", - errors.get(0).getMessage()); + status.getFieldValidationErrors().get(0).getMessage().get()); Assert.assertEquals("Last name must contain at least three characters", ((AbstractErrorMessage) field.getErrorMessage()).getMessage()); field.setValue("long last name"); - errors = binder.validate(); - Assert.assertEquals(0, errors.size()); + status = binder.validate(); + Assert.assertEquals(0, status.getFieldValidationErrors().size()); Assert.assertNull(field.getErrorMessage()); } @@ -194,25 +220,25 @@ public class BinderBookOfVaadinTest { .bind(BookPerson::getEmail, BookPerson::setEmail); field.setValue("not-email"); - List> errors = binder.validate(); + BinderValidationStatus status = binder.validate(); // Only one error per field should be reported - Assert.assertEquals(1, errors.size()); + Assert.assertEquals(1, status.getFieldValidationErrors().size()); Assert.assertEquals("This doesn't look like a valid email address", - errors.get(0).getMessage()); + status.getFieldValidationErrors().get(0).getMessage().get()); Assert.assertEquals("This doesn't look like a valid email address", ((AbstractErrorMessage) field.getErrorMessage()).getMessage()); field.setValue("abc@vaadin.com"); - errors = binder.validate(); - Assert.assertEquals(1, errors.size()); + status = binder.validate(); + Assert.assertEquals(1, status.getFieldValidationErrors().size()); Assert.assertEquals("Only acme.com email addresses are allowed", - errors.get(0).getMessage()); + status.getFieldValidationErrors().get(0).getMessage().get()); Assert.assertEquals("Only acme.com email addresses are allowed", ((AbstractErrorMessage) field.getErrorMessage()).getMessage()); field.setValue("abc@acme.com"); - errors = binder.validate(); - Assert.assertEquals(0, errors.size()); + status = binder.validate(); + Assert.assertEquals(0, status.getFieldValidationErrors().size()); Assert.assertNull(field.getErrorMessage()); } @@ -296,32 +322,32 @@ public class BinderBookOfVaadinTest { departing.setValue(before); returning.setValue(after); - List> errors = binder.validate(); - Assert.assertTrue(errors.isEmpty()); + BinderValidationStatus status = binder.validate(); + Assert.assertTrue(status.getBeanValidationErrors().isEmpty()); Assert.assertNull(departing.getComponentError()); Assert.assertNull(returning.getComponentError()); // update returning => validation is done against this field returning.setValue(past); - errors = binder.validate(); + status = binder.validate(); - Assert.assertFalse(errors.isEmpty()); + Assert.assertFalse(status.getFieldValidationErrors().isEmpty()); Assert.assertNotNull(returning.getComponentError()); Assert.assertNull(departing.getComponentError()); // set correct value back returning.setValue(before); - errors = binder.validate(); + status = binder.validate(); - Assert.assertTrue(errors.isEmpty()); + Assert.assertTrue(status.getFieldValidationErrors().isEmpty()); Assert.assertNull(departing.getComponentError()); Assert.assertNull(returning.getComponentError()); // update departing => validation is done because of listener added departing.setValue(after); - errors = binder.validate(); + status = binder.validate(); - Assert.assertFalse(errors.isEmpty()); + Assert.assertFalse(status.getFieldValidationErrors().isEmpty()); Assert.assertNotNull(returning.getComponentError()); Assert.assertNull(departing.getComponentError()); @@ -351,7 +377,7 @@ public class BinderBookOfVaadinTest { departing.setValue(before); returning.setValue(after); - Result result = returnBinding.validate(); + ValidationStatus result = returnBinding.validate(); Assert.assertFalse(result.isError()); Assert.assertNull(departing.getComponentError()); @@ -402,17 +428,16 @@ public class BinderBookOfVaadinTest { @Test public void withBindingStatusChangeHandlerExample() { Label nameStatus = new Label(); - AtomicReference event = new AtomicReference<>(); + AtomicReference> statusCapture = new AtomicReference<>(); String msg = "Full name must contain at least three characters"; binder.forField(field).withValidator(name -> name.length() >= 3, msg) - .withStatusChangeHandler(statusChange -> { + .withStatusHandler(statusChange -> { nameStatus.setValue(statusChange.getMessage().orElse("")); // Only show the label when validation has failed - boolean error = statusChange - .getStatus() == ValidationStatus.ERROR; + boolean error = statusChange.getStatus() == Status.ERROR; nameStatus.setVisible(error); - event.set(statusChange); + statusCapture.set(statusChange); }).bind(BookPerson::getLastName, BookPerson::setLastName); field.setValue("aa"); @@ -420,22 +445,22 @@ public class BinderBookOfVaadinTest { Assert.assertTrue(nameStatus.isVisible()); Assert.assertEquals(msg, nameStatus.getValue()); - Assert.assertNotNull(event.get()); - ValidationStatusChangeEvent evt = event.get(); - Assert.assertEquals(ValidationStatus.ERROR, evt.getStatus()); - Assert.assertEquals(msg, evt.getMessage().get()); - Assert.assertEquals(field, evt.getSource()); + Assert.assertNotNull(statusCapture.get()); + ValidationStatus status = statusCapture.get(); + Assert.assertEquals(Status.ERROR, status.getStatus()); + Assert.assertEquals(msg, status.getMessage().get()); + Assert.assertEquals(field, status.getField()); field.setValue("foo"); binder.validate(); Assert.assertFalse(nameStatus.isVisible()); Assert.assertEquals("", nameStatus.getValue()); - Assert.assertNotNull(event.get()); - evt = event.get(); - Assert.assertEquals(ValidationStatus.OK, evt.getStatus()); - Assert.assertFalse(evt.getMessage().isPresent()); - Assert.assertEquals(field, evt.getSource()); + Assert.assertNotNull(statusCapture.get()); + status = statusCapture.get(); + Assert.assertEquals(Status.OK, status.getStatus()); + Assert.assertFalse(status.getMessage().isPresent()); + Assert.assertEquals(field, status.getField()); } @Test @@ -495,17 +520,18 @@ public class BinderBookOfVaadinTest { .bind(BookPerson::getYearOfBirth, BookPerson::setYearOfBirth); yearOfBirthField.setValue("abc"); - Assert.assertEquals("Doesn't look like a year", - binder.validate().get(0).getMessage()); + Assert.assertEquals("Doesn't look like a year", binder.validate() + .getFieldValidationErrors().get(0).getMessage().get()); yearOfBirthField.setValue("abcd"); - Assert.assertEquals("Must enter a number", - binder.validate().get(0).getMessage()); + Assert.assertEquals("Must enter a number", binder.validate() + .getFieldValidationErrors().get(0).getMessage().get()); yearOfBirthField.setValue("1200"); Assert.assertEquals("Person must be born in the 20th century", - binder.validate().get(0).getMessage()); + binder.validate().getFieldValidationErrors().get(0).getMessage() + .get()); yearOfBirthField.setValue("1950"); - Assert.assertTrue(binder.validate().isEmpty()); + Assert.assertFalse(binder.validate().hasErrors()); BookPerson person = new BookPerson(1500, 12); binder.save(person); Assert.assertEquals(1950, person.getYearOfBirth()); @@ -546,15 +572,17 @@ public class BinderBookOfVaadinTest { binder.bind(p); yearOfBirthField.setValue("abc"); - Assert.assertEquals("Please enter a number", - binder.validate().get(0).getMessage()); + Assert.assertTrue(binder.validate().hasErrors()); + Assert.assertEquals("Please enter a number", binder.validate() + .getFieldValidationErrors().get(0).getMessage().get()); yearOfBirthField.setValue("123"); - Assert.assertTrue(binder.validate().isEmpty()); + Assert.assertTrue(binder.validate().isOk()); p.setYearOfBirth(12500); binder.load(p); Assert.assertEquals("12500", yearOfBirthField.getValue()); + Assert.assertTrue(binder.validate().isOk()); } @Test @@ -582,25 +610,30 @@ public class BinderBookOfVaadinTest { // first bean validator fails and passes error message to status label yearOfBirth.setValue("2001"); - List> errors = binder.validate(); - Assert.assertEquals(1, errors.size()); - Assert.assertEquals(errors.get(0).getMessage(), message); + BinderValidationStatus status = binder.validate(); + Assert.assertEquals(0, status.getFieldValidationErrors().size()); + Assert.assertEquals(1, status.getBeanValidationErrors().size()); + Assert.assertEquals( + status.getBeanValidationErrors().get(0).getMessage().get(), + message); Assert.assertEquals(message, formStatusLabel.getValue()); // value is correct, status label is cleared yearOfBirth.setValue("1999"); - errors = binder.validate(); - Assert.assertEquals(0, errors.size()); + status = binder.validate(); + Assert.assertFalse(status.hasErrors()); Assert.assertEquals("", formStatusLabel.getValue()); // both bean validators fail, should be two error messages chained yearOfBirth.setValue("2000"); - errors = binder.validate(); - Assert.assertEquals(2, errors.size()); + status = binder.validate(); + Assert.assertEquals(2, status.getBeanValidationResults().size()); + Assert.assertEquals(0, status.getFieldValidationErrors().size()); + Assert.assertEquals(2, status.getBeanValidationErrors().size()); // only first error is shown Assert.assertEquals(message, formStatusLabel.getValue()); @@ -612,21 +645,17 @@ public class BinderBookOfVaadinTest { BinderStatusHandler defaultHandler = binder.getStatusHandler(); - binder.setStatusHandler(results -> { - String errorMessage = results.stream() - // Ignore confirmation messages - .filter(BinderResult::isError) - // Ignore messages that belong to a specific field - .filter(error -> !error.getField().isPresent()) - // Create a string out of the remaining messages - .map(Result::getMessage).map(o -> o.get()) - .collect(Collectors.joining("\n")); - + binder.setStatusHandler(status -> { + // create an error message on failed bean level validations + List> errors = status.getBeanValidationErrors(); + String errorMessage = errors.stream().map(Result::getMessage) + .map(o -> o.get()).collect(Collectors.joining("\n")); + // show error in a label formStatusLabel.setValue(errorMessage); formStatusLabel.setVisible(!errorMessage.isEmpty()); // Let the default handler show messages for each field - defaultHandler.accept(results); + defaultHandler.accept(status); }); final String bindingMessage = "uneven"; @@ -648,34 +677,41 @@ public class BinderBookOfVaadinTest { // first binding validation fails, no bean level validation is done yearOfBirth.setValue("2001"); - List> errors = binder.validate(); - Assert.assertEquals(1, errors.size()); - Assert.assertEquals(errors.get(0).getMessage(), bindingMessage); + BinderValidationStatus status = binder.validate(); + Assert.assertEquals(1, status.getFieldValidationErrors().size()); + Assert.assertEquals(bindingMessage, + status.getFieldValidationErrors().get(0).getMessage().get()); Assert.assertEquals("", formStatusLabel.getValue()); // first bean validator fails and passes error message to status label yearOfBirth.setValue("2002"); - errors = binder.validate(); - Assert.assertEquals(1, errors.size()); - Assert.assertEquals(errors.get(0).getMessage(), message); + status = binder.validate(); + Assert.assertEquals(0, status.getFieldValidationErrors().size()); + Assert.assertEquals(1, status.getBeanValidationErrors().size()); + Assert.assertEquals(message, + status.getBeanValidationErrors().get(0).getMessage().get()); Assert.assertEquals(message, formStatusLabel.getValue()); // value is correct, status label is cleared yearOfBirth.setValue("1998"); - errors = binder.validate(); - Assert.assertEquals(0, errors.size()); + status = binder.validate(); + Assert.assertTrue(status.isOk()); + Assert.assertFalse(status.hasErrors()); + Assert.assertEquals(0, status.getFieldValidationErrors().size()); + Assert.assertEquals(0, status.getBeanValidationErrors().size()); Assert.assertEquals("", formStatusLabel.getValue()); // both bean validators fail, should be two error messages chained yearOfBirth.setValue("2000"); - errors = binder.validate(); - Assert.assertEquals(2, errors.size()); + status = binder.validate(); + Assert.assertEquals(0, status.getFieldValidationErrors().size()); + Assert.assertEquals(2, status.getBeanValidationErrors().size()); Assert.assertEquals(message + "\n" + message2, formStatusLabel.getValue()); diff --git a/server/src/test/java/com/vaadin/data/BinderTest.java b/server/src/test/java/com/vaadin/data/BinderTest.java index 4b69fdf50e..0a82b527bb 100644 --- a/server/src/test/java/com/vaadin/data/BinderTest.java +++ b/server/src/test/java/com/vaadin/data/BinderTest.java @@ -5,17 +5,15 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertSame; import java.util.List; -import java.util.Optional; -import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import com.vaadin.data.Binder.Binding; +import com.vaadin.data.ValidationStatus.Status; import com.vaadin.data.util.converter.Converter; import com.vaadin.data.validator.NotEmptyValidator; import com.vaadin.server.AbstractErrorMessage; @@ -27,6 +25,8 @@ import com.vaadin.ui.TextField; public class BinderTest { + private static final String EMPTY_ERROR_MESSAGE = "Value cannot be empty"; + private static class StatusBean { private String status; @@ -48,7 +48,7 @@ public class BinderTest { Person p = new Person(); Validator notEmpty = Validator.from(val -> !val.isEmpty(), - "Value cannot be empty"); + EMPTY_ERROR_MESSAGE); Converter stringToInteger = Converter.from( Integer::valueOf, String::valueOf, e -> "Value must be a number"); Validator notNegative = Validator.from(x -> x >= 0, @@ -327,18 +327,17 @@ public class BinderTest { binder.save(person); Assert.fail(); } catch (ValidationException exception) { - List> validationErrors = exception - .getValidationError(); + List> validationErrors = exception + .getFieldValidationErrors(); Assert.assertEquals(2, validationErrors.size()); - ValidationError error = validationErrors.get(0); - Assert.assertEquals(nameField, error.getField().get()); - Assert.assertEquals(msg, error.getMessage()); - Assert.assertEquals("", error.getValue()); + ValidationStatus error = validationErrors.get(0); + Assert.assertEquals(nameField, error.getField()); + Assert.assertEquals(msg, error.getMessage().get()); error = validationErrors.get(1); - Assert.assertEquals(ageField, error.getField().get()); - Assert.assertEquals("Value must be positive", error.getMessage()); - Assert.assertEquals(ageField.getValue(), error.getValue()); + Assert.assertEquals(ageField, error.getField()); + Assert.assertEquals("Value must be positive", + error.getMessage().get()); } } @@ -375,9 +374,9 @@ public class BinderTest { public void validate_notBound_noErrors() { Binder binder = new Binder<>(); - List> errors = binder.validate(); + BinderValidationStatus status = binder.validate(); - Assert.assertTrue(errors.isEmpty()); + Assert.assertTrue(status.isOk()); } @Test @@ -388,9 +387,9 @@ public class BinderTest { Person::setFirstName); nameField.setComponentError(new UserError("")); - List> errors = binder.validate(); + BinderValidationStatus status = binder.validate(); - Assert.assertTrue(errors.isEmpty()); + Assert.assertTrue(status.isOk()); Assert.assertNull(nameField.getComponentError()); } @@ -411,19 +410,19 @@ public class BinderTest { binding.withValidator(value -> false, msg2); binding.bind(Person::getFirstName, Person::setFirstName); - List> errors = binder.validate(); + BinderValidationStatus status = binder.validate(); + List> errors = status.getFieldValidationErrors(); Assert.assertEquals(1, errors.size()); - Set errorMessages = errors.stream() - .map(ValidationError::getMessage).collect(Collectors.toSet()); - Assert.assertTrue(errorMessages.contains(msg1)); + ValidationStatus validationStatus = errors.stream().findFirst() + .get(); + String msg = validationStatus.getMessage().get(); + Assert.assertEquals(msg1, msg); + + HasValue field = validationStatus.getField(); - Set fields = errors.stream().map(ValidationError::getField) - .filter(Optional::isPresent).map(Optional::get) - .collect(Collectors.toSet()); - Assert.assertEquals(1, fields.size()); - Assert.assertTrue(fields.contains(nameField)); + Assert.assertEquals(nameField, field); ErrorMessage componentError = nameField.getComponentError(); Assert.assertNotNull(componentError); @@ -490,19 +489,25 @@ public class BinderTest { ageField.setValue(""); assertEquals(32, p.getAge()); - assertValidationErrors(binder.validate(), "Value cannot be empty"); + assertValidationErrors(binder.validate(), EMPTY_ERROR_MESSAGE); } private void assertValidationErrors( - List> validationErrors, + List> validationErrors, String... errorMessages) { Assert.assertEquals(errorMessages.length, validationErrors.size()); for (int i = 0; i < errorMessages.length; i++) { Assert.assertEquals(errorMessages[i], - validationErrors.get(i).getMessage()); + validationErrors.get(i).getMessage().get()); } } + private void assertValidationErrors(BinderValidationStatus status, + String... errorMessages) { + assertValidationErrors(status.getFieldValidationErrors(), + errorMessages); + } + @Test public void convertToModelConversionFails() { bindAgeWithValidatorConverterValidator(); @@ -571,11 +576,11 @@ public class BinderTest { @Test public void bindingWithStatusChangeHandler_handlerGetsEvents() { - AtomicReference event = new AtomicReference<>(); + AtomicReference> statusCapture = new AtomicReference<>(); Binding binding = binder.forField(nameField) - .withValidator(notEmpty).withStatusChangeHandler(evt -> { - Assert.assertNull(event.get()); - event.set(evt); + .withValidator(notEmpty).withStatusHandler(evt -> { + Assert.assertNull(statusCapture.get()); + statusCapture.set(evt); }); binding.bind(Person::getFirstName, Person::setFirstName); @@ -585,30 +590,30 @@ public class BinderTest { // message binder.validate(); - Assert.assertNotNull(event.get()); - ValidationStatusChangeEvent evt = event.get(); - Assert.assertEquals(ValidationStatus.ERROR, evt.getStatus()); - Assert.assertEquals("Value cannot be empty", evt.getMessage().get()); - Assert.assertEquals(nameField, evt.getSource()); + Assert.assertNotNull(statusCapture.get()); + ValidationStatus evt = statusCapture.get(); + Assert.assertEquals(Status.ERROR, evt.getStatus()); + Assert.assertEquals(EMPTY_ERROR_MESSAGE, evt.getMessage().get()); + Assert.assertEquals(nameField, evt.getField()); nameField.setValue("foo"); - event.set(null); + statusCapture.set(null); // Second validation succeeds => should be event with OK status and // no message binder.validate(); - evt = event.get(); + evt = statusCapture.get(); Assert.assertNotNull(evt); - Assert.assertEquals(ValidationStatus.OK, evt.getStatus()); + Assert.assertEquals(Status.OK, evt.getStatus()); Assert.assertFalse(evt.getMessage().isPresent()); - Assert.assertEquals(nameField, evt.getSource()); + Assert.assertEquals(nameField, evt.getField()); } @Test public void bindingWithStatusChangeHandler_defaultStatusChangeHandlerIsReplaced() { Binding binding = binder.forField(nameField) - .withValidator(notEmpty).withStatusChangeHandler(evt -> { + .withValidator(notEmpty).withStatusHandler(evt -> { }); binding.bind(Person::getFirstName, Person::setFirstName); @@ -639,7 +644,7 @@ public class BinderTest { binding.validate(); Assert.assertTrue(label.isVisible()); - Assert.assertEquals("Value cannot be empty", label.getValue()); + Assert.assertEquals(EMPTY_ERROR_MESSAGE, label.getValue()); nameField.setValue("foo"); @@ -677,7 +682,7 @@ public class BinderTest { .withValidator(notEmpty); binding.bind(Person::getFirstName, Person::setFirstName); - binding.withStatusChangeHandler(evt -> Assert.fail()); + binding.withStatusHandler(evt -> Assert.fail()); } @Test(expected = IllegalStateException.class) @@ -697,7 +702,7 @@ public class BinderTest { Binding binding = binder.forField(nameField); - binding.withStatusChangeHandler(event -> { + binding.withStatusHandler(event -> { }); binding.withStatusLabel(label); @@ -711,7 +716,7 @@ public class BinderTest { binding.withStatusLabel(label); - binding.withStatusChangeHandler(event -> { + binding.withStatusHandler(event -> { }); } @@ -720,10 +725,10 @@ public class BinderTest { Binding binding = binder.forField(nameField); - binding.withStatusChangeHandler(event -> { + binding.withStatusHandler(event -> { }); - binding.withStatusChangeHandler(event -> { + binding.withStatusHandler(event -> { }); } @@ -738,9 +743,9 @@ public class BinderTest { Person person = new Person(); binder.bind(person); - List> errors = binder.validate(); - Assert.assertEquals(1, errors.size()); - Assert.assertFalse(errors.get(0).getField().isPresent()); + List> errors = binder.validate() + .getFieldValidationErrors(); + Assert.assertEquals(0, errors.size()); } @Test @@ -756,12 +761,12 @@ public class BinderTest { Person person = new Person(); binder.bind(person); - List> errors = binder.validate(); + List> errors = binder.validate() + .getFieldValidationErrors(); Assert.assertEquals(1, errors.size()); - ValidationError error = errors.get(0); - Assert.assertEquals(msg, error.getMessage()); - Assert.assertTrue(error.getField().isPresent()); - Assert.assertEquals(nameField.getValue(), error.getValue()); + ValidationStatus error = errors.get(0); + Assert.assertEquals(msg, error.getMessage().get()); + Assert.assertEquals(nameField, error.getField()); } @Test @@ -778,14 +783,14 @@ public class BinderTest { Person person = new Person(); binder.bind(person); - List> errors = binder.validate(); + List> errors = binder.validate() + .getFieldValidationErrors(); Assert.assertEquals(1, errors.size()); - ValidationError error = errors.get(0); + ValidationStatus error = errors.get(0); - Assert.assertEquals(msg1, error.getMessage()); - Assert.assertEquals(nameField, error.getField().get()); - Assert.assertEquals(nameField.getValue(), error.getValue()); + Assert.assertEquals(msg1, error.getMessage().get()); + Assert.assertEquals(nameField, error.getField()); } @Test @@ -799,8 +804,8 @@ public class BinderTest { Person person = new Person(); binder.bind(person); - List> errors = binder.validate(); - Assert.assertEquals(0, errors.size()); + Assert.assertFalse(binder.validate().hasErrors()); + Assert.assertTrue(binder.validate().isOk()); } @Test @@ -863,15 +868,105 @@ public class BinderTest { } @Test - public void binderWithStatusChangeHandler_handlerGetsEvents() { - AtomicReference>> resultsCapture = new AtomicReference<>(); + public void binderWithStatusHandler_fieldValidationNoBeanValidation_handlerGetsStatusUpdates() { + AtomicReference> statusCapture = new AtomicReference<>(); binder.forField(nameField).withValidator(notEmpty) - .withStatusChangeHandler(evt -> { + .withStatusHandler(evt -> { Assert.fail( "Using a custom status change handler so no change should end up here"); }).bind(Person::getFirstName, Person::setFirstName); binder.forField(ageField).withConverter(stringToInteger) - .withValidator(notNegative).withStatusChangeHandler(evt -> { + .withValidator(notNegative).withStatusHandler(evt -> { + Assert.fail( + "Using a custom status change handler so no change should end up here"); + }).bind(Person::getAge, Person::setAge); + + binder.setStatusHandler(r -> { + statusCapture.set(r); + }); + binder.bind(p); + Assert.assertNull(nameField.getComponentError()); + + nameField.setValue(""); + ageField.setValue("5"); + + // First binding validation fails => should be result with ERROR status + // and message + BinderValidationStatus status2 = binder.validate(); + BinderValidationStatus status = statusCapture.get(); + Assert.assertSame(status2, status); + + Assert.assertNull(nameField.getComponentError()); + + List> bindingStatuses = status + .getFieldValidationStatuses(); + Assert.assertNotNull(bindingStatuses); + Assert.assertEquals(1, status.getFieldValidationErrors().size()); + Assert.assertEquals(2, bindingStatuses.size()); + + ValidationStatus r = bindingStatuses.get(0); + Assert.assertTrue(r.isError()); + Assert.assertEquals(EMPTY_ERROR_MESSAGE, r.getMessage().get()); + Assert.assertEquals(nameField, r.getField()); + + r = bindingStatuses.get(1); + Assert.assertFalse(r.isError()); + Assert.assertFalse(r.getMessage().isPresent()); + Assert.assertEquals(ageField, r.getField()); + + Assert.assertEquals(0, status.getBeanValidationResults().size()); + Assert.assertEquals(0, status.getBeanValidationErrors().size()); + + nameField.setValue("foo"); + ageField.setValue(""); + + statusCapture.set(null); + // Second validation succeeds => should be result with OK status and + // no message, and error result for age + binder.validate(); + + status = statusCapture.get(); + bindingStatuses = status.getFieldValidationStatuses(); + Assert.assertEquals(1, status.getFieldValidationErrors().size()); + Assert.assertEquals(2, bindingStatuses.size()); + + r = bindingStatuses.get(0); + Assert.assertFalse(r.isError()); + Assert.assertFalse(r.getMessage().isPresent()); + Assert.assertEquals(nameField, r.getField()); + + r = bindingStatuses.get(1); + Assert.assertTrue(r.isError()); + Assert.assertEquals("Value must be a number", r.getMessage().get()); + Assert.assertEquals(ageField, r.getField()); + + Assert.assertEquals(0, status.getBeanValidationResults().size()); + Assert.assertEquals(0, status.getBeanValidationErrors().size()); + + statusCapture.set(null); + // binding validations pass, binder validation fails + ageField.setValue("0"); + binder.validate(); + + status = statusCapture.get(); + bindingStatuses = status.getFieldValidationStatuses(); + Assert.assertEquals(0, status.getFieldValidationErrors().size()); + Assert.assertEquals(2, bindingStatuses.size()); + + Assert.assertEquals(0, status.getBeanValidationResults().size()); + Assert.assertEquals(0, status.getBeanValidationErrors().size()); + } + + @Test + public void binderWithStatusHandler_fieldAndBeanLevelValidation_handlerGetsStatusUpdates() { + AtomicReference> statusCapture = new AtomicReference<>(); + binder.forField(nameField).withValidator(notEmpty) + .withStatusHandler(evt -> { + Assert.fail( + "Using a custom status change handler so no change should end up here"); + }).bind(Person::getFirstName, Person::setFirstName); + binder.forField(ageField).withConverter(stringToInteger) + .withValidator(notNegative).withStatusHandler(evt -> { Assert.fail( "Using a custom status change handler so no change should end up here"); }).bind(Person::getAge, Person::setAge); @@ -881,7 +976,7 @@ public class BinderTest { : Result.error("Need first name and age")); binder.setStatusHandler(r -> { - resultsCapture.set(r); + statusCapture.set(r); }); binder.bind(p); Assert.assertNull(nameField.getComponentError()); @@ -891,65 +986,78 @@ public class BinderTest { // First binding validation fails => should be result with ERROR status // and message - binder.validate(); + BinderValidationStatus status2 = binder.validate(); + BinderValidationStatus status = statusCapture.get(); + Assert.assertSame(status2, status); Assert.assertNull(nameField.getComponentError()); - List> results = resultsCapture.get(); - Assert.assertNotNull(results); - Assert.assertEquals(2, results.size()); + List> bindingStatuses = status + .getFieldValidationStatuses(); + Assert.assertNotNull(bindingStatuses); + Assert.assertEquals(1, status.getFieldValidationErrors().size()); + Assert.assertEquals(2, bindingStatuses.size()); - BinderResult r = results.get(0); + ValidationStatus r = bindingStatuses.get(0); Assert.assertTrue(r.isError()); - Assert.assertEquals("Value cannot be empty", r.getMessage().get()); - Assert.assertEquals(nameField, r.getField().get()); + Assert.assertEquals(EMPTY_ERROR_MESSAGE, r.getMessage().get()); + Assert.assertEquals(nameField, r.getField()); - r = results.get(1); + r = bindingStatuses.get(1); Assert.assertFalse(r.isError()); Assert.assertFalse(r.getMessage().isPresent()); - Assert.assertEquals(ageField, r.getField().get()); + Assert.assertEquals(ageField, r.getField()); + + Assert.assertEquals(0, status.getBeanValidationResults().size()); + Assert.assertEquals(0, status.getBeanValidationErrors().size()); nameField.setValue("foo"); ageField.setValue(""); - resultsCapture.set(null); + statusCapture.set(null); // Second validation succeeds => should be result with OK status and // no message, and error result for age binder.validate(); - results = resultsCapture.get(); - Assert.assertNotNull(results); - Assert.assertEquals(2, results.size()); + status = statusCapture.get(); + bindingStatuses = status.getFieldValidationStatuses(); + Assert.assertEquals(1, status.getFieldValidationErrors().size()); + Assert.assertEquals(2, bindingStatuses.size()); - r = results.get(0); + r = bindingStatuses.get(0); Assert.assertFalse(r.isError()); Assert.assertFalse(r.getMessage().isPresent()); - Assert.assertEquals(nameField, r.getField().get()); + Assert.assertEquals(nameField, r.getField()); - r = results.get(1); + r = bindingStatuses.get(1); Assert.assertTrue(r.isError()); Assert.assertEquals("Value must be a number", r.getMessage().get()); - Assert.assertEquals(ageField, r.getField().get()); + Assert.assertEquals(ageField, r.getField()); + + Assert.assertEquals(0, status.getBeanValidationResults().size()); + Assert.assertEquals(0, status.getBeanValidationErrors().size()); - resultsCapture.set(null); + statusCapture.set(null); // binding validations pass, binder validation fails ageField.setValue("0"); binder.validate(); - results = resultsCapture.get(); - Assert.assertNotNull(results); - Assert.assertEquals(1, results.size()); + status = statusCapture.get(); + bindingStatuses = status.getFieldValidationStatuses(); + Assert.assertEquals(0, status.getFieldValidationErrors().size()); + Assert.assertEquals(2, bindingStatuses.size()); - r = results.get(0); - Assert.assertTrue(r.isError()); - Assert.assertTrue(r.getMessage().isPresent()); - Assert.assertFalse(r.getField().isPresent()); + Assert.assertEquals(1, status.getBeanValidationResults().size()); + Assert.assertEquals(1, status.getBeanValidationErrors().size()); + + Assert.assertEquals("Need first name and age", + status.getBeanValidationErrors().get(0).getMessage().get()); } @Test public void binderWithStatusChangeHandler_defaultStatusChangeHandlerIsReplaced() { Binding binding = binder.forField(nameField) - .withValidator(notEmpty).withStatusChangeHandler(evt -> { + .withValidator(notEmpty).withStatusHandler(evt -> { }); binding.bind(Person::getFirstName, Person::setFirstName); @@ -991,7 +1099,7 @@ public class BinderTest { .withValidator(notEmpty); binding.bind(Person::getFirstName, Person::setFirstName); - binding.withStatusChangeHandler(evt -> Assert.fail()); + binding.withStatusHandler(evt -> Assert.fail()); } @Test(expected = IllegalStateException.class) @@ -1038,7 +1146,7 @@ public class BinderTest { @Test public void binderWithStatusChangeHandler_replaceHandler() { - AtomicReference>> capture = new AtomicReference<>(); + AtomicReference> capture = new AtomicReference<>(); Binding binding = binder.forField(nameField); binding.bind(Person::getFirstName, Person::setFirstName); @@ -1054,9 +1162,11 @@ public class BinderTest { nameField.setValue("foo"); binder.validate(); - List> results = capture.get(); + List> results = capture.get() + .getFieldValidationStatuses(); Assert.assertNotNull(results); Assert.assertEquals(1, results.size()); + Assert.assertFalse(results.get(0).isError()); } @Test -- cgit v1.2.3