package com.vaadin.data;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.vaadin.data.Binder.BindingBuilder;
import com.vaadin.data.converter.StringToIntegerConverter;
import com.vaadin.data.validator.NotEmptyValidator;
import com.vaadin.server.ErrorMessage;
import com.vaadin.tests.data.bean.Person;
import com.vaadin.tests.data.bean.Sex;
import com.vaadin.ui.TextField;
public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
@Before
public void setUp() {
binder = new Binder<>();
item = new Person();
item.setFirstName("Johannes");
item.setAge(32);
}
@Test
public void bindNullBean_noBeanPresent() {
binder.setBean(item);
assertNotNull(binder.getBean());
binder.setBean(null);
assertNull(binder.getBean());
}
@Test
public void bindNullBean_FieldsAreCleared() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
binder.forField(ageField)
.withConverter(new StringToIntegerConverter(""))
.bind(Person::getAge, Person::setAge);
binder.setBean(item);
assertEquals("No name field value","Johannes", nameField.getValue());
assertEquals("No age field value", "32", ageField.getValue());
binder.setBean(null);
assertEquals("Name field not empty", "", nameField.getValue());
assertEquals("Age field not empty", "", ageField.getValue());
}
@Test
public void clearForReadBean_boundFieldsAreCleared() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
binder.forField(ageField)
.withConverter(new StringToIntegerConverter(""))
.bind(Person::getAge, Person::setAge);
binder.readBean(item);
assertEquals("No name field value","Johannes", nameField.getValue());
assertEquals("No age field value", "32", ageField.getValue());
binder.readBean(null);
assertEquals("Name field not empty", "", nameField.getValue());
assertEquals("Age field not empty", "", ageField.getValue());
}
@Test
public void clearReadOnlyField_shouldClearField() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
// Make name field read only
nameField.setReadOnly(true);
binder.setBean(item);
assertEquals("No name field value", "Johannes", nameField.getValue());
binder.setBean(null);
assertEquals("ReadOnly field not empty","", nameField.getValue());
}
@Test
public void clearBean_setsHasChangesToFalse() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
// Make name field read only
nameField.setReadOnly(true);
binder.readBean(item);
assertEquals("No name field value", "Johannes", nameField.getValue());
nameField.setValue("James");
assertTrue("Binder did not have value changes", binder.hasChanges());
binder.readBean(null);
assertFalse("Binder has changes after clearing all fields",
binder.hasChanges());
}
@Test
public void clearReadOnlyBinder_shouldClearFields() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
binder.forField(ageField)
.withConverter(new StringToIntegerConverter(""))
.bind(Person::getAge, Person::setAge);
binder.setReadOnly(true);
binder.setBean(item);
binder.setBean(null);
assertEquals("ReadOnly name field not empty", "", nameField.getValue());
assertEquals("ReadOnly age field not empty", "", ageField.getValue());
}
@Test(expected = NullPointerException.class)
public void bindNullField_throws() {
binder.forField(null);
}
@Test(expected = NullPointerException.class)
public void bindNullGetter_throws() {
binder.bind(nameField, null, Person::setFirstName);
}
@Test
public void fieldBound_bindItem_fieldValueUpdated() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
binder.setBean(item);
assertEquals("Johannes", nameField.getValue());
}
@Test
public void fieldBoundWithShortcut_bindBean_fieldValueUpdated() {
bindName();
assertEquals("Johannes", nameField.getValue());
}
@Test
public void beanBound_updateFieldValue_beanValueUpdated() {
binder.setBean(item);
binder.bind(nameField, Person::getFirstName, Person::setFirstName);
assertEquals("Johannes", nameField.getValue());
nameField.setValue("Artur");
assertEquals("Artur", item.getFirstName());
}
@Test
public void bound_getBean_returnsBoundBean() {
assertNull(binder.getBean());
binder.setBean(item);
assertSame(item, binder.getBean());
}
@Test
public void unbound_getBean_returnsNothing() {
binder.setBean(item);
binder.removeBean();
assertNull(binder.getBean());
}
@Test
public void bound_changeFieldValue_beanValueUpdated() {
bindName();
nameField.setValue("Henri");
assertEquals("Henri", item.getFirstName());
}
@Test
public void unbound_changeFieldValue_beanValueNotUpdated() {
bindName();
nameField.setValue("Henri");
binder.removeBean();
nameField.setValue("Aleksi");
assertEquals("Henri", item.getFirstName());
}
@Test
public void bindNullSetter_valueChangesIgnored() {
binder.bind(nameField, Person::getFirstName, null);
binder.setBean(item);
nameField.setValue("Artur");
assertEquals(item.getFirstName(), "Johannes");
}
@Test
public void bound_bindToAnotherBean_stopsUpdatingOriginal() {
bindName();
nameField.setValue("Leif");
Person p2 = new Person();
p2.setFirstName("Marlon");
binder.setBean(p2);
assertEquals("Marlon", nameField.getValue());
assertEquals("Leif", item.getFirstName());
assertSame(p2, binder.getBean());
nameField.setValue("Ilia");
assertEquals("Ilia", p2.getFirstName());
assertEquals("Leif", item.getFirstName());
}
@Test
public void save_unbound_noChanges() throws ValidationException {
Binder<Person> binder = new Binder<>();
Person person = new Person();
int age = 10;
person.setAge(age);
binder.writeBean(person);
Assert.assertEquals(age, person.getAge());
}
@Test
public void save_bound_beanIsUpdated() throws ValidationException {
Binder<Person> binder = new Binder<>();
binder.bind(nameField, Person::getFirstName, Person::setFirstName);
Person person = new Person();
String fieldValue = "bar";
nameField.setValue(fieldValue);
person.setFirstName("foo");
binder.writeBean(person);
Assert.assertEquals(fieldValue, person.getFirstName());
}
@Test
public void load_bound_fieldValueIsUpdated() {
binder.bind(nameField, Person::getFirstName, Person::setFirstName);
Person person = new Person();
String name = "bar";
person.setFirstName(name);
binder.readBean(person);
Assert.assertEquals(name, nameField.getValue());
}
@Test
public void load_unbound_noChanges() {
nameField.setValue("");
Person person = new Person();
String name = "bar";
person.setFirstName(name);
binder.readBean(person);
Assert.assertEquals("", nameField.getValue());
}
protected void bindName() {
binder.bind(nameField, Person::getFirstName, Person::setFirstName);
binder.setBean(item);
}
@Test
public void binding_with_null_representation() {
String nullRepresentation = "Some arbitrary text";
String realName = "John";
Person namelessPerson = new Person(null, "Doe", "", 25, Sex.UNKNOWN,
null);
binder.forField(nameField).withNullRepresentation(nullRepresentation)
.bind(Person::getFirstName, Person::setFirstName);
// Bind a person with null value and check that null representation is
// used
binder.setBean(namelessPerson);
Assert.assertEquals(
"Null value from bean was not converted to explicit null representation",
nullRepresentation, nameField.getValue());
// Verify that changes are applied to bean
nameField.setValue(realName);
Assert.assertEquals(
"Bean was not correctly updated from a change in the field",
realName, namelessPerson.getFirstName());
// Verify conversion back to null
nameField.setValue(nullRepresentation);
Assert.assertEquals(
"Two-way null representation did not change value back to null",
null, namelessPerson.getFirstName());
}
@Test
public void binding_with_default_null_representation() {
TextField nullTextField = new TextField() {
@Override
public String getEmptyValue() {
return "null";
}
};
Person namelessPerson = new Person(null, "Doe", "", 25, Sex.UNKNOWN,
null);
binder.bind(nullTextField, Person::getFirstName, Person::setFirstName);
binder.setBean(namelessPerson);
assertTrue(nullTextField.isEmpty());
Assert.assertEquals(null, namelessPerson.getFirstName());
// Change value, see that textfield is not empty and bean is updated.
nullTextField.setValue("");
assertFalse(nullTextField.isEmpty());
Assert.assertEquals("First name of person was not properly updated", "",
namelessPerson.getFirstName());
// Verify that default null representation does not map back to null
nullTextField.setValue("null");
assertTrue(nullTextField.isEmpty());
Assert.assertEquals("Default one-way null representation failed.",
"null", namelessPerson.getFirstName());
}
@Test
public void binding_with_null_representation_value_not_null() {
String nullRepresentation = "Some arbitrary text";
binder.forField(nameField).withNullRepresentation(nullRepresentation)
.bind(Person::getFirstName, Person::setFirstName);
assertFalse("First name in item should not be null",
Objects.isNull(item.getFirstName()));
binder.setBean(item);
Assert.assertEquals("Field value was not set correctly",
item.getFirstName(), nameField.getValue());
}
@Test
public void withConverter_disablesDefaulNullRepresentation() {
Integer customNullConverter = 0;
binder.forField(ageField).withNullRepresentation("foo")
.withConverter(new StringToIntegerConverter(""))
.withConverter(age -> age,
age -> age == null ? customNullConverter : age)
.bind(Person::getSalary, Person::setSalary);
binder.setBean(item);
Assert.assertEquals(customNullConverter.toString(),
ageField.getValue());
Integer salary = 11;
ageField.setValue(salary.toString());
Assert.assertEquals(11, salary.intValue());
}
@Test
public void beanBinder_nullRepresentationIsNotDisabled() {
Binder<Person> binder = new Binder<>(Person.class);
binder.forField(nameField).bind("firstName");
Person person = new Person();
binder.setBean(person);
Assert.assertEquals("", nameField.getValue());
}
@Test
public void beanBinder_withConverter_nullRepresentationIsNotDisabled() {
String customNullPointerRepresentation = "foo";
Binder<Person> binder = new Binder<>(Person.class);
binder.forField(nameField)
.withConverter(value -> value, value -> value == null
? customNullPointerRepresentation : value)
.bind("firstName");
Person person = new Person();
binder.setBean(person);
Assert.assertEquals(customNullPointerRepresentation,
nameField.getValue());
}
@Test
public void withValidator_doesNotDisablesDefaulNullRepresentation() {
String nullRepresentation = "foo";
binder.forField(nameField).withNullRepresentation(nullRepresentation)
.withValidator(new NotEmptyValidator<>(""))
.bind(Person::getFirstName, Person::setFirstName);
item.setFirstName(null);
binder.setBean(item);
Assert.assertEquals(nullRepresentation, nameField.getValue());
String newValue = "bar";
nameField.setValue(newValue);
Assert.assertEquals(newValue, item.getFirstName());
}
@Test
public void setRequired_withErrorMessage_fieldGetsRequiredIndicatorAndValidator() {
TextField textField = new TextField();
assertFalse(textField.isRequiredIndicatorVisible());
BindingBuilder<Person, String> binding = binder.forField(textField);
assertFalse(textField.isRequiredIndicatorVisible());
binding.asRequired("foobar");
assertTrue(textField.isRequiredIndicatorVisible());
binding.bind(Person::getFirstName, Person::setFirstName);
binder.setBean(item);
Assert.assertNull(textField.getErrorMessage());
textField.setValue(textField.getEmptyValue());
ErrorMessage errorMessage = textField.getErrorMessage();
Assert.assertNotNull(errorMessage);
Assert.assertEquals("foobar", errorMessage.getFormattedHtmlMessage());
textField.setValue("value");
Assert.assertNull(textField.getErrorMessage());
assertTrue(textField.isRequiredIndicatorVisible());
}
@Test
public void setRequired_withErrorMessageProvider_fieldGetsRequiredIndicatorAndValidator() {
TextField textField = new TextField();
textField.setLocale(Locale.CANADA);
assertFalse(textField.isRequiredIndicatorVisible());
BindingBuilder<Person, String> binding = binder.forField(textField);
assertFalse(textField.isRequiredIndicatorVisible());
AtomicInteger invokes = new AtomicInteger();
binding.asRequired(context -> {
invokes.incrementAndGet();
Assert.assertSame(Locale.CANADA, context.getLocale().get());
return "foobar";
});
assertTrue(textField.isRequiredIndicatorVisible());
binding.bind(Person::getFirstName, Person::setFirstName);
binder.setBean(item);
Assert.assertNull(textField.getErrorMessage());
Assert.assertEquals(0, invokes.get());
textField.setValue(textField.getEmptyValue());
ErrorMessage errorMessage = textField.getErrorMessage();
Assert.assertNotNull(errorMessage);
Assert.assertEquals("foobar", errorMessage.getFormattedHtmlMessage());
// validation is run twice, once for the field, then for all the fields
// for cross field validation...
Assert.assertEquals(2, invokes.get());
textField.setValue("value");
Assert.assertNull(textField.getErrorMessage());
assertTrue(textField.isRequiredIndicatorVisible());
}
@Test
public void validationStatusHandler_onlyRunForChangedField() {
TextField firstNameField = new TextField();
TextField lastNameField = new TextField();
AtomicInteger invokes = new AtomicInteger();
binder.forField(firstNameField)
.withValidator(new NotEmptyValidator<>(""))
.withValidationStatusHandler(
validationStatus -> invokes.addAndGet(1))
.bind(Person::getFirstName, Person::setFirstName);
binder.forField(lastNameField)
.withValidator(new NotEmptyValidator<>(""))
.bind(Person::getLastName, Person::setLastName);
binder.setBean(item);
// setting the bean causes 2:
Assert.assertEquals(2, invokes.get());
lastNameField.setValue("");
Assert.assertEquals(2, invokes.get());
firstNameField.setValue("");
Assert.assertEquals(3, invokes.get());
binder.removeBean();
Person person = new Person();
person.setFirstName("a");
person.setLastName("a");
binder.readBean(person);
// reading from a bean causes 2:
Assert.assertEquals(5, invokes.get());
lastNameField.setValue("");
Assert.assertEquals(5, invokes.get());
firstNameField.setValue("");
Assert.assertEquals(6, invokes.get());
}
@Test(expected = IllegalStateException.class)
public void noArgsConstructor_stringBind_throws() {
binder.bind(new TextField(), "firstName");
}
@Test
public void setReadOnly_unboundBinder() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
binder.forField(ageField);
binder.setReadOnly(true);
assertTrue(nameField.isReadOnly());
assertFalse(ageField.isReadOnly());
binder.setReadOnly(false);
assertFalse(nameField.isReadOnly());
assertFalse(ageField.isReadOnly());
}
@Test
public void setReadOnly_boundBinder() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
binder.forField(ageField)
.withConverter(new StringToIntegerConverter(""))
.bind(Person::getAge, Person::setAge);
binder.setBean(new Person());
binder.setReadOnly(true);
assertTrue(nameField.isReadOnly());
assertTrue(ageField.isReadOnly());
binder.setReadOnly(false);
assertFalse(nameField.isReadOnly());
assertFalse(ageField.isReadOnly());
}
@Test
public void setReadOnly_binderLoadedByReadBean() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
binder.forField(ageField)
.withConverter(new StringToIntegerConverter(""))
.bind(Person::getAge, Person::setAge);
binder.readBean(new Person());
binder.setReadOnly(true);
assertTrue(nameField.isReadOnly());
assertTrue(ageField.isReadOnly());
binder.setReadOnly(false);
assertFalse(nameField.isReadOnly());
assertFalse(ageField.isReadOnly());
}
@Test
public void isValidTest_bound_binder() {
binder.forField(nameField)
.withValidator(
Validator.from(
name -> !name.equals("fail field validation"),
""))
.bind(Person::getFirstName, Person::setFirstName);
binder.withValidator(
Validator.from(person -> !person.getFirstName()
.equals("fail bean validation"), ""));
binder.setBean(item);
Assert.assertTrue(binder.isValid());
nameField.setValue("fail field validation");
Assert.assertFalse(binder.isValid());
nameField.setValue("");
Assert.assertTrue(binder.isValid());
nameField.setValue("fail bean validation");
Assert.assertFalse(binder.isValid());
}
@Test
public void isValidTest_unbound_binder() {
binder.forField(nameField)
.withValidator(Validator.from(
name -> !name.equals("fail field validation"), ""))
.bind(Person::getFirstName, Person::setFirstName);
Assert.assertTrue(binder.isValid());
nameField.setValue("fail field validation");
Assert.assertFalse(binder.isValid());
nameField.setValue("");
Assert.assertTrue(binder.isValid());
}
@Test(expected = IllegalStateException.class)
public void isValidTest_unbound_binder_throws_with_bean_level_validation() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
binder.withValidator(Validator.from(
person -> !person.getFirstName().equals("fail bean validation"),
""));
binder.isValid();
}
}