You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BinderTest.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. package com.vaadin.data;
  2. import static org.junit.Assert.assertEquals;
  3. import static org.junit.Assert.assertFalse;
  4. import static org.junit.Assert.assertSame;
  5. import java.util.List;
  6. import java.util.Set;
  7. import java.util.stream.Collectors;
  8. import org.junit.Assert;
  9. import org.junit.Before;
  10. import org.junit.Test;
  11. import com.vaadin.data.Binder.Binding;
  12. import com.vaadin.data.util.converter.Converter;
  13. import com.vaadin.server.AbstractErrorMessage;
  14. import com.vaadin.server.ErrorMessage;
  15. import com.vaadin.server.UserError;
  16. import com.vaadin.tests.data.bean.Person;
  17. import com.vaadin.ui.TextField;
  18. public class BinderTest {
  19. private static class StatusBean {
  20. private String status;
  21. public String getStatus() {
  22. return status;
  23. }
  24. public void setStatus(String status) {
  25. this.status = status;
  26. }
  27. }
  28. Binder<Person> binder;
  29. TextField nameField;
  30. TextField ageField;
  31. Person p = new Person();
  32. Validator<String> notEmpty = Validator.from(val -> !val.isEmpty(),
  33. "Value cannot be empty");
  34. Converter<String, Integer> stringToInteger = Converter.from(
  35. Integer::valueOf, String::valueOf, e -> "Value must be a number");
  36. Validator<Integer> notNegative = Validator.from(x -> x >= 0,
  37. "Value must be positive");
  38. @Before
  39. public void setUp() {
  40. binder = new Binder<>();
  41. p.setFirstName("Johannes");
  42. p.setAge(32);
  43. nameField = new TextField();
  44. ageField = new TextField();
  45. }
  46. @Test(expected = NullPointerException.class)
  47. public void bindingNullBeanThrows() {
  48. binder.bind(null);
  49. }
  50. @Test(expected = NullPointerException.class)
  51. public void bindingNullFieldThrows() {
  52. binder.forField(null);
  53. }
  54. @Test(expected = NullPointerException.class)
  55. public void bindingNullGetterThrows() {
  56. binder.bind(nameField, null, Person::setFirstName);
  57. }
  58. @Test
  59. public void fieldValueUpdatedOnBeanBind() {
  60. binder.forField(nameField).bind(Person::getFirstName,
  61. Person::setFirstName);
  62. binder.bind(p);
  63. assertEquals("Johannes", nameField.getValue());
  64. }
  65. @Test
  66. public void fieldValueUpdatedWithShortcutBind() {
  67. bindName();
  68. assertEquals("Johannes", nameField.getValue());
  69. }
  70. @Test
  71. public void fieldValueUpdatedIfBeanAlreadyBound() {
  72. binder.bind(p);
  73. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  74. assertEquals("Johannes", nameField.getValue());
  75. nameField.setValue("Artur");
  76. assertEquals("Artur", p.getFirstName());
  77. }
  78. @Test
  79. public void getBeanReturnsBoundBeanOrNothing() {
  80. assertFalse(binder.getBean().isPresent());
  81. binder.bind(p);
  82. assertSame(p, binder.getBean().get());
  83. binder.unbind();
  84. assertFalse(binder.getBean().isPresent());
  85. }
  86. @Test
  87. public void fieldValueSavedToPropertyOnChange() {
  88. bindName();
  89. nameField.setValue("Henri");
  90. assertEquals("Henri", p.getFirstName());
  91. }
  92. @Test
  93. public void fieldValueNotSavedAfterUnbind() {
  94. bindName();
  95. nameField.setValue("Henri");
  96. binder.unbind();
  97. nameField.setValue("Aleksi");
  98. assertEquals("Henri", p.getFirstName());
  99. }
  100. @Test
  101. public void bindNullSetterIgnoresValueChange() {
  102. binder.bind(nameField, Person::getFirstName, null);
  103. binder.bind(p);
  104. nameField.setValue("Artur");
  105. assertEquals(p.getFirstName(), "Johannes");
  106. }
  107. @Test
  108. public void bindToAnotherBeanStopsUpdatingOriginalBean() {
  109. bindName();
  110. nameField.setValue("Leif");
  111. Person p2 = new Person();
  112. p2.setFirstName("Marlon");
  113. binder.bind(p2);
  114. assertEquals("Marlon", nameField.getValue());
  115. assertEquals("Leif", p.getFirstName());
  116. assertSame(p2, binder.getBean().get());
  117. nameField.setValue("Ilia");
  118. assertEquals("Ilia", p2.getFirstName());
  119. assertEquals("Leif", p.getFirstName());
  120. }
  121. @Test
  122. public void save_unbound_noChanges() {
  123. Binder<Person> binder = new Binder<>();
  124. Person person = new Person();
  125. int age = 10;
  126. person.setAge(age);
  127. binder.save(person);
  128. Assert.assertEquals(age, person.getAge());
  129. }
  130. @Test
  131. public void save_bound_beanIsUpdated() {
  132. Binder<Person> binder = new Binder<>();
  133. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  134. Person person = new Person();
  135. String fieldValue = "bar";
  136. nameField.setValue(fieldValue);
  137. person.setFirstName("foo");
  138. binder.save(person);
  139. Assert.assertEquals(fieldValue, person.getFirstName());
  140. }
  141. @Test
  142. public void load_bound_fieldValueIsUpdated() {
  143. Binder<Person> binder = new Binder<>();
  144. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  145. Person person = new Person();
  146. String name = "bar";
  147. person.setFirstName(name);
  148. binder.load(person);
  149. Assert.assertEquals(name, nameField.getValue());
  150. }
  151. @Test
  152. public void load_unbound_noChanges() {
  153. Binder<Person> binder = new Binder<>();
  154. nameField.setValue("");
  155. Person person = new Person();
  156. String name = "bar";
  157. person.setFirstName(name);
  158. binder.load(person);
  159. Assert.assertEquals("", nameField.getValue());
  160. }
  161. @Test
  162. public void validate_notBound_noErrors() {
  163. Binder<Person> binder = new Binder<>();
  164. List<ValidationError<?>> errors = binder.validate();
  165. Assert.assertTrue(errors.isEmpty());
  166. }
  167. @Test
  168. public void bound_validatorsAreOK_noErrors() {
  169. Binder<Person> binder = new Binder<>();
  170. Binding<Person, String, String> binding = binder.forField(nameField);
  171. binding.withValidator(Validator.alwaysPass()).bind(Person::getFirstName,
  172. Person::setFirstName);
  173. nameField.setComponentError(new UserError(""));
  174. List<ValidationError<?>> errors = binder.validate();
  175. Assert.assertTrue(errors.isEmpty());
  176. Assert.assertNull(nameField.getComponentError());
  177. }
  178. @SuppressWarnings("serial")
  179. @Test
  180. public void bound_validatorsFail_errors() {
  181. Binder<Person> binder = new Binder<>();
  182. Binding<Person, String, String> binding = binder.forField(nameField);
  183. binding.withValidator(Validator.alwaysPass());
  184. String msg1 = "foo";
  185. String msg2 = "bar";
  186. binding.withValidator(new Validator<String>() {
  187. @Override
  188. public Result<String> apply(String value) {
  189. return new SimpleResult<>(null, msg1);
  190. }
  191. });
  192. binding.withValidator(value -> false, msg2);
  193. binding.bind(Person::getFirstName, Person::setFirstName);
  194. List<ValidationError<?>> errors = binder.validate();
  195. Assert.assertEquals(1, errors.size());
  196. Set<String> errorMessages = errors.stream()
  197. .map(ValidationError::getMessage).collect(Collectors.toSet());
  198. Assert.assertTrue(errorMessages.contains(msg1));
  199. Set<?> fields = errors.stream().map(ValidationError::getField)
  200. .collect(Collectors.toSet());
  201. Assert.assertEquals(1, fields.size());
  202. Assert.assertTrue(fields.contains(nameField));
  203. ErrorMessage componentError = nameField.getComponentError();
  204. Assert.assertNotNull(componentError);
  205. Assert.assertEquals("foo",
  206. ((AbstractErrorMessage) componentError).getMessage());
  207. }
  208. private void bindName() {
  209. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  210. binder.bind(p);
  211. }
  212. private void bindAgeWithValidatorConverterValidator() {
  213. binder.forField(ageField).withValidator(notEmpty)
  214. .withConverter(stringToInteger).withValidator(notNegative)
  215. .bind(Person::getAge, Person::setAge);
  216. binder.bind(p);
  217. }
  218. @Test
  219. public void validatorForSuperTypeCanBeUsed() {
  220. // Validates that a validator for a super type can be used, e.g.
  221. // validator for Number can be used on a Double
  222. TextField salaryField = new TextField();
  223. Binder<Person> binder = new Binder<>();
  224. Validator<Number> positiveNumberValidator = value -> {
  225. if (value.doubleValue() >= 0) {
  226. return Result.ok(value);
  227. } else {
  228. return Result.error("Number must be positive");
  229. }
  230. };
  231. binder.forField(salaryField)
  232. .withConverter(Double::valueOf, String::valueOf)
  233. .withValidator(positiveNumberValidator)
  234. .bind(Person::getSalaryDouble, Person::setSalaryDouble);
  235. Person person = new Person();
  236. binder.bind(person);
  237. salaryField.setValue("10");
  238. Assert.assertEquals(10, person.getSalaryDouble(), 0);
  239. salaryField.setValue("-1"); // Does not pass validator
  240. Assert.assertEquals(10, person.getSalaryDouble(), 0);
  241. }
  242. @Test
  243. public void convertInitialValue() {
  244. bindAgeWithValidatorConverterValidator();
  245. assertEquals("32", ageField.getValue());
  246. }
  247. @Test
  248. public void convertToModelValidAge() {
  249. bindAgeWithValidatorConverterValidator();
  250. ageField.setValue("33");
  251. assertEquals(33, p.getAge());
  252. }
  253. @Test
  254. public void convertToModelNegativeAgeFailsOnFirstValidator() {
  255. bindAgeWithValidatorConverterValidator();
  256. ageField.setValue("");
  257. assertEquals(32, p.getAge());
  258. assertValidationErrors(binder.validate(), "Value cannot be empty");
  259. }
  260. private void assertValidationErrors(
  261. List<ValidationError<?>> validationErrors,
  262. String... errorMessages) {
  263. Assert.assertEquals(errorMessages.length, validationErrors.size());
  264. for (int i = 0; i < errorMessages.length; i++) {
  265. Assert.assertEquals(errorMessages[i],
  266. validationErrors.get(i).getMessage());
  267. }
  268. }
  269. @Test
  270. public void convertToModelConversionFails() {
  271. bindAgeWithValidatorConverterValidator();
  272. ageField.setValue("abc");
  273. assertEquals(32, p.getAge());
  274. assertValidationErrors(binder.validate(), "Value must be a number");
  275. }
  276. @Test
  277. public void convertToModelNegativeAgeFailsOnIntegerValidator() {
  278. bindAgeWithValidatorConverterValidator();
  279. ageField.setValue("-5");
  280. assertEquals(32, p.getAge());
  281. assertValidationErrors(binder.validate(), "Value must be positive");
  282. }
  283. @Test
  284. public void convertDataToField() {
  285. bindAgeWithValidatorConverterValidator();
  286. binder.getBean().get().setAge(12);
  287. binder.load(binder.getBean().get());
  288. Assert.assertEquals("12", ageField.getValue());
  289. }
  290. @Test
  291. public void convertNotValidatableDataToField() {
  292. bindAgeWithValidatorConverterValidator();
  293. binder.getBean().get().setAge(-12);
  294. binder.load(binder.getBean().get());
  295. Assert.assertEquals("-12", ageField.getValue());
  296. }
  297. @Test(expected = IllegalArgumentException.class)
  298. public void convertInvalidDataToField() {
  299. TextField field = new TextField();
  300. StatusBean bean = new StatusBean();
  301. bean.setStatus("1");
  302. Binder<StatusBean> binder = new Binder<StatusBean>();
  303. Binding<StatusBean, String, String> binding = binder.forField(field)
  304. .withConverter(presentation -> {
  305. if (presentation.equals("OK")) {
  306. return "1";
  307. } else if (presentation.equals("NOTOK")) {
  308. return "2";
  309. }
  310. throw new IllegalArgumentException(
  311. "Value must be OK or NOTOK");
  312. }, model -> {
  313. if (model.equals("1")) {
  314. return "OK";
  315. } else if (model.equals("2")) {
  316. return "NOTOK";
  317. } else {
  318. throw new IllegalArgumentException(
  319. "Value in model must be 1 or 2");
  320. }
  321. });
  322. binding.bind(StatusBean::getStatus, StatusBean::setStatus);
  323. binder.bind(bean);
  324. bean.setStatus("3");
  325. binder.load(bean);
  326. }
  327. }