/*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.data;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.vaadin.data.Binder.Binding;
import com.vaadin.data.Binder.BindingBuilder;
import com.vaadin.data.BindingValidationStatus.Status;
import com.vaadin.tests.data.bean.Person;
import com.vaadin.ui.Label;
public class BinderValidationStatusTest
extends BinderTestBase<Binder<Person>, Person> {
protected final static BindingValidationStatusHandler NOOP = event -> {
};
@Before
public void setUp() {
binder = new Binder<>();
item = new Person();
item.setFirstName("Johannes");
item.setAge(32);
}
//
// Binding-level status handler
//
@Test
public void bindingWithStatusHandler_handlerGetsEvents() {
AtomicReference<BindingValidationStatus<?>> statusCapture = new AtomicReference<>();
BindingBuilder<Person, String> binding = binder.forField(nameField)
.withValidator(notEmpty).withValidationStatusHandler(evt -> {
Assert.assertNull(statusCapture.get());
statusCapture.set(evt);
});
binding.bind(Person::getFirstName, Person::setFirstName);
nameField.setValue("");
// First validation fails => should be event with ERROR status and
// message
binder.validate();
Assert.assertNotNull(statusCapture.get());
BindingValidationStatus<?> evt = statusCapture.get();
Assert.assertEquals(Status.ERROR, evt.getStatus());
Assert.assertEquals(EMPTY_ERROR_MESSAGE, evt.getMessage().get());
Assert.assertEquals(nameField, evt.getField());
statusCapture.set(null);
nameField.setValue("foo");
statusCapture.set(null);
// Second validation succeeds => should be event with OK status and
// no message
binder.validate();
evt = statusCapture.get();
Assert.assertNotNull(evt);
Assert.assertEquals(Status.OK, evt.getStatus());
Assert.assertFalse(evt.getMessage().isPresent());
Assert.assertEquals(nameField, evt.getField());
}
@Test
public void bindingWithStatusHandler_defaultStatusHandlerIsReplaced() {
Binding<Person, String> binding = binder.forField(nameField)
.withValidator(notEmpty).withValidationStatusHandler(evt -> {
}).bind(Person::getFirstName, Person::setFirstName);
Assert.assertNull(nameField.getComponentError());
nameField.setValue("");
// First validation fails => should be event with ERROR status and
// message
binding.validate();
// default behavior should update component error for the nameField
Assert.assertNull(nameField.getComponentError());
}
@Test
public void bindingWithStatusLabel_labelIsUpdatedAccordingStatus() {
Label label = new Label();
Binding<Person, String> binding = binder.forField(nameField)
.withValidator(notEmpty).withStatusLabel(label)
.bind(Person::getFirstName, Person::setFirstName);
nameField.setValue("");
// First validation fails => should be event with ERROR status and
// message
binding.validate();
Assert.assertTrue(label.isVisible());
Assert.assertEquals(EMPTY_ERROR_MESSAGE, label.getValue());
nameField.setValue("foo");
// Second validation succeeds => should be event with OK status and
// no message
binding.validate();
Assert.assertFalse(label.isVisible());
Assert.assertEquals("", label.getValue());
}
@Test
public void bindingWithStatusLabel_defaultStatusHandlerIsReplaced() {
Label label = new Label();
Binding<Person, String> binding = binder.forField(nameField)
.withValidator(notEmpty).withStatusLabel(label)
.bind(Person::getFirstName, Person::setFirstName);
Assert.assertNull(nameField.getComponentError());
nameField.setValue("");
// First validation fails => should be event with ERROR status and
// message
binding.validate();
// default behavior should update component error for the nameField
Assert.assertNull(nameField.getComponentError());
}
@Test(expected = IllegalStateException.class)
public void bindingWithStatusHandler_addAfterBound() {
BindingBuilder<Person, String> binding = binder.forField(nameField)
.withValidator(notEmpty);
binding.bind(Person::getFirstName, Person::setFirstName);
binding.withValidationStatusHandler(evt -> Assert.fail());
}
@Test(expected = IllegalStateException.class)
public void bindingWithStatusLabel_addAfterBound() {
Label label = new Label();
BindingBuilder<Person, String> binding = binder.forField(nameField)
.withValidator(notEmpty);
binding.bind(Person::getFirstName, Person::setFirstName);
binding.withStatusLabel(label);
}
@Test(expected = IllegalStateException.class)
public void bindingWithStatusLabel_setAfterHandler() {
Label label = new Label();
BindingBuilder<Person, String> binding = binder.forField(nameField);
binding.withValidationStatusHandler(NOOP);
binding.withStatusLabel(label);
}
@Test(expected = IllegalStateException.class)
public void bindingWithStatusHandler_setAfterLabel() {
Label label = new Label();
BindingBuilder<Person, String> binding = binder.forField(nameField);
binding.withStatusLabel(label);
binding.withValidationStatusHandler(NOOP);
}
@Test(expected = IllegalStateException.class)
public void bindingWithStatusHandler_setAfterOtherHandler() {
BindingBuilder<Person, String> binding = binder.forField(nameField);
binding.withValidationStatusHandler(NOOP);
binding.withValidationStatusHandler(NOOP);
}
//
// Binder-level status handler
//
@Test
public void binderWithStatusHandler_fieldValidationNoBeanValidation_handlerGetsStatusUpdates() {
AtomicReference<BinderValidationStatus<?>> statusCapture = new AtomicReference<>();
binder.forField(nameField).withValidator(notEmpty)
.withValidationStatusHandler(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).withValidationStatusHandler(evt -> {
Assert.fail(
"Using a custom status change handler so no change should end up here");
}).bind(Person::getAge, Person::setAge);
binder.setValidationStatusHandler(r -> {
statusCapture.set(r);
});
binder.setBean(item);
Assert.assertNull(nameField.getComponentError());
nameField.setValue("");
ageField.setValue("5");
// First binding validation fails => should be result with ERROR status
// and message
BinderValidationStatus<Person> status2 = binder.validate();
BinderValidationStatus<?> status = statusCapture.get();
Assert.assertSame(status2, status);
Assert.assertNull(nameField.getComponentError());
List<BindingValidationStatus<?>> bindingStatuses = status
.getFieldValidationStatuses();
Assert.assertNotNull(bindingStatuses);
Assert.assertEquals(1, status.getFieldValidationErrors().size());
Assert.assertEquals(2, bindingStatuses.size());
BindingValidationStatus<?> 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<BinderValidationStatus<?>> statusCapture = new AtomicReference<>();
binder.forField(nameField).withValidator(notEmpty)
.withValidationStatusHandler(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).withValidationStatusHandler(evt -> {
Assert.fail(
"Using a custom status change handler so no change should end up here");
}).bind(Person::getAge, Person::setAge);
binder.withValidator(
bean -> !bean.getFirstName().isEmpty() && bean.getAge() > 0,
"Need first name and age");
binder.setValidationStatusHandler(r -> {
statusCapture.set(r);
});
binder.setBean(item);
Assert.assertNull(nameField.getComponentError());
nameField.setValue("");
ageField.setValue("5");
// First binding validation fails => should be result with ERROR status
// and message
BinderValidationStatus<Person> status2 = binder.validate();
BinderValidationStatus<?> status = statusCapture.get();
Assert.assertSame(status2, status);
Assert.assertNull(nameField.getComponentError());
List<BindingValidationStatus<?>> bindingStatuses = status
.getFieldValidationStatuses();
Assert.assertNotNull(bindingStatuses);
Assert.assertEquals(1, status.getFieldValidationErrors().size());
Assert.assertEquals(2, bindingStatuses.size());
BindingValidationStatus<?> 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(1, status.getBeanValidationResults().size());
Assert.assertEquals(1, status.getBeanValidationErrors().size());
Assert.assertEquals("Need first name and age",
status.getBeanValidationErrors().get(0).getErrorMessage());
}
@Test
public void binderWithStatusHandler_defaultStatusHandlerIsReplaced() {
Binding<Person, String> binding = binder.forField(nameField)
.withValidator(notEmpty).withValidationStatusHandler(evt -> {
}).bind(Person::getFirstName, Person::setFirstName);
Assert.assertNull(nameField.getComponentError());
nameField.setValue("");
// First validation fails => should be event with ERROR status and
// message
binding.validate();
// no component error since default handler is replaced
Assert.assertNull(nameField.getComponentError());
}
@Test
public void binderWithStatusLabel_defaultStatusHandlerIsReplaced() {
Label label = new Label();
Binding<Person, String> binding = binder.forField(nameField)
.withValidator(notEmpty).withStatusLabel(label)
.bind(Person::getFirstName, Person::setFirstName);
Assert.assertNull(nameField.getComponentError());
nameField.setValue("");
// First validation fails => should be event with ERROR status and
// message
binding.validate();
// default behavior should update component error for the nameField
Assert.assertNull(nameField.getComponentError());
}
@Test(expected = IllegalStateException.class)
public void binderWithStatusHandler_addAfterBound() {
BindingBuilder<Person, String> binding = binder.forField(nameField)
.withValidator(notEmpty);
binding.bind(Person::getFirstName, Person::setFirstName);
binding.withValidationStatusHandler(evt -> Assert.fail());
}
@Test(expected = IllegalStateException.class)
public void binderWithStatusLabel_addAfterBound() {
Label label = new Label();
BindingBuilder<Person, String> binding = binder.forField(nameField)
.withValidator(notEmpty);
binding.bind(Person::getFirstName, Person::setFirstName);
binding.withStatusLabel(label);
}
@Test(expected = IllegalStateException.class)
public void binderWithStatusLabel_setAfterHandler() {
Label label = new Label();
BindingBuilder<Person, String> binding = binder.forField(nameField);
binding.bind(Person::getFirstName, Person::setFirstName);
binder.setValidationStatusHandler(event -> {
});
binder.setStatusLabel(label);
}
@Test(expected = IllegalStateException.class)
public void binderWithStatusHandler_setAfterLabel() {
Label label = new Label();
BindingBuilder<Person, String> binding = binder.forField(nameField);
binding.bind(Person::getFirstName, Person::setFirstName);
binder.setStatusLabel(label);
binder.setValidationStatusHandler(event -> {
});
}
@Test(expected = NullPointerException.class)
public void binderWithNullStatusHandler_throws() {
binder.setValidationStatusHandler(null);
}
@Test
public void binderWithStatusHandler_replaceHandler() {
AtomicReference<BinderValidationStatus<?>> capture = new AtomicReference<>();
BindingBuilder<Person, String> binding = binder.forField(nameField);
binding.bind(Person::getFirstName, Person::setFirstName);
binder.setValidationStatusHandler(results -> {
Assert.fail();
});
binder.setValidationStatusHandler(results -> {
capture.set(results);
});
nameField.setValue("foo");
binder.validate();
List<BindingValidationStatus<?>> results = capture.get()
.getFieldValidationStatuses();
Assert.assertNotNull(results);
Assert.assertEquals(1, results.size());
Assert.assertFalse(results.get(0).isError());
}
}