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 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  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.assertNotNull;
  5. import static org.junit.Assert.assertNull;
  6. import static org.junit.Assert.assertSame;
  7. import static org.junit.Assert.assertTrue;
  8. import java.util.Locale;
  9. import java.util.Objects;
  10. import java.util.concurrent.atomic.AtomicInteger;
  11. import org.junit.Assert;
  12. import org.junit.Before;
  13. import org.junit.Test;
  14. import com.vaadin.data.Binder.BindingBuilder;
  15. import com.vaadin.data.converter.StringToIntegerConverter;
  16. import com.vaadin.data.validator.NotEmptyValidator;
  17. import com.vaadin.server.ErrorMessage;
  18. import com.vaadin.tests.data.bean.Person;
  19. import com.vaadin.tests.data.bean.Sex;
  20. import com.vaadin.ui.TextField;
  21. public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
  22. @Before
  23. public void setUp() {
  24. binder = new Binder<>();
  25. item = new Person();
  26. item.setFirstName("Johannes");
  27. item.setAge(32);
  28. }
  29. @Test
  30. public void bindNullBean_noBeanPresent() {
  31. binder.setBean(item);
  32. assertNotNull(binder.getBean());
  33. binder.setBean(null);
  34. assertNull(binder.getBean());
  35. }
  36. @Test
  37. public void bindNullBean_FieldsAreCleared() {
  38. binder.forField(nameField).bind(Person::getFirstName,
  39. Person::setFirstName);
  40. binder.forField(ageField)
  41. .withConverter(new StringToIntegerConverter(""))
  42. .bind(Person::getAge, Person::setAge);
  43. binder.setBean(item);
  44. assertEquals("No name field value","Johannes", nameField.getValue());
  45. assertEquals("No age field value", "32", ageField.getValue());
  46. binder.setBean(null);
  47. assertEquals("Name field not empty", "", nameField.getValue());
  48. assertEquals("Age field not empty", "", ageField.getValue());
  49. }
  50. @Test
  51. public void clearForReadBean_boundFieldsAreCleared() {
  52. binder.forField(nameField).bind(Person::getFirstName,
  53. Person::setFirstName);
  54. binder.forField(ageField)
  55. .withConverter(new StringToIntegerConverter(""))
  56. .bind(Person::getAge, Person::setAge);
  57. binder.readBean(item);
  58. assertEquals("No name field value","Johannes", nameField.getValue());
  59. assertEquals("No age field value", "32", ageField.getValue());
  60. binder.readBean(null);
  61. assertEquals("Name field not empty", "", nameField.getValue());
  62. assertEquals("Age field not empty", "", ageField.getValue());
  63. }
  64. @Test
  65. public void clearReadOnlyField_shouldClearField() {
  66. binder.forField(nameField).bind(Person::getFirstName,
  67. Person::setFirstName);
  68. // Make name field read only
  69. nameField.setReadOnly(true);
  70. binder.setBean(item);
  71. assertEquals("No name field value", "Johannes", nameField.getValue());
  72. binder.setBean(null);
  73. assertEquals("ReadOnly field not empty","", nameField.getValue());
  74. }
  75. @Test
  76. public void clearBean_setsHasChangesToFalse() {
  77. binder.forField(nameField).bind(Person::getFirstName,
  78. Person::setFirstName);
  79. // Make name field read only
  80. nameField.setReadOnly(true);
  81. binder.readBean(item);
  82. assertEquals("No name field value", "Johannes", nameField.getValue());
  83. nameField.setValue("James");
  84. assertTrue("Binder did not have value changes", binder.hasChanges());
  85. binder.readBean(null);
  86. assertFalse("Binder has changes after clearing all fields",
  87. binder.hasChanges());
  88. }
  89. @Test
  90. public void clearReadOnlyBinder_shouldClearFields() {
  91. binder.forField(nameField).bind(Person::getFirstName,
  92. Person::setFirstName);
  93. binder.forField(ageField)
  94. .withConverter(new StringToIntegerConverter(""))
  95. .bind(Person::getAge, Person::setAge);
  96. binder.setReadOnly(true);
  97. binder.setBean(item);
  98. binder.setBean(null);
  99. assertEquals("ReadOnly name field not empty", "", nameField.getValue());
  100. assertEquals("ReadOnly age field not empty", "", ageField.getValue());
  101. }
  102. @Test(expected = NullPointerException.class)
  103. public void bindNullField_throws() {
  104. binder.forField(null);
  105. }
  106. @Test(expected = NullPointerException.class)
  107. public void bindNullGetter_throws() {
  108. binder.bind(nameField, null, Person::setFirstName);
  109. }
  110. @Test
  111. public void fieldBound_bindItem_fieldValueUpdated() {
  112. binder.forField(nameField).bind(Person::getFirstName,
  113. Person::setFirstName);
  114. binder.setBean(item);
  115. assertEquals("Johannes", nameField.getValue());
  116. }
  117. @Test
  118. public void fieldBoundWithShortcut_bindBean_fieldValueUpdated() {
  119. bindName();
  120. assertEquals("Johannes", nameField.getValue());
  121. }
  122. @Test
  123. public void beanBound_updateFieldValue_beanValueUpdated() {
  124. binder.setBean(item);
  125. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  126. assertEquals("Johannes", nameField.getValue());
  127. nameField.setValue("Artur");
  128. assertEquals("Artur", item.getFirstName());
  129. }
  130. @Test
  131. public void bound_getBean_returnsBoundBean() {
  132. assertNull(binder.getBean());
  133. binder.setBean(item);
  134. assertSame(item, binder.getBean());
  135. }
  136. @Test
  137. public void unbound_getBean_returnsNothing() {
  138. binder.setBean(item);
  139. binder.removeBean();
  140. assertNull(binder.getBean());
  141. }
  142. @Test
  143. public void bound_changeFieldValue_beanValueUpdated() {
  144. bindName();
  145. nameField.setValue("Henri");
  146. assertEquals("Henri", item.getFirstName());
  147. }
  148. @Test
  149. public void unbound_changeFieldValue_beanValueNotUpdated() {
  150. bindName();
  151. nameField.setValue("Henri");
  152. binder.removeBean();
  153. nameField.setValue("Aleksi");
  154. assertEquals("Henri", item.getFirstName());
  155. }
  156. @Test
  157. public void bindNullSetter_valueChangesIgnored() {
  158. binder.bind(nameField, Person::getFirstName, null);
  159. binder.setBean(item);
  160. nameField.setValue("Artur");
  161. assertEquals(item.getFirstName(), "Johannes");
  162. }
  163. @Test
  164. public void bound_bindToAnotherBean_stopsUpdatingOriginal() {
  165. bindName();
  166. nameField.setValue("Leif");
  167. Person p2 = new Person();
  168. p2.setFirstName("Marlon");
  169. binder.setBean(p2);
  170. assertEquals("Marlon", nameField.getValue());
  171. assertEquals("Leif", item.getFirstName());
  172. assertSame(p2, binder.getBean());
  173. nameField.setValue("Ilia");
  174. assertEquals("Ilia", p2.getFirstName());
  175. assertEquals("Leif", item.getFirstName());
  176. }
  177. @Test
  178. public void save_unbound_noChanges() throws ValidationException {
  179. Binder<Person> binder = new Binder<>();
  180. Person person = new Person();
  181. int age = 10;
  182. person.setAge(age);
  183. binder.writeBean(person);
  184. Assert.assertEquals(age, person.getAge());
  185. }
  186. @Test
  187. public void save_bound_beanIsUpdated() throws ValidationException {
  188. Binder<Person> binder = new Binder<>();
  189. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  190. Person person = new Person();
  191. String fieldValue = "bar";
  192. nameField.setValue(fieldValue);
  193. person.setFirstName("foo");
  194. binder.writeBean(person);
  195. Assert.assertEquals(fieldValue, person.getFirstName());
  196. }
  197. @Test
  198. public void load_bound_fieldValueIsUpdated() {
  199. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  200. Person person = new Person();
  201. String name = "bar";
  202. person.setFirstName(name);
  203. binder.readBean(person);
  204. Assert.assertEquals(name, nameField.getValue());
  205. }
  206. @Test
  207. public void load_unbound_noChanges() {
  208. nameField.setValue("");
  209. Person person = new Person();
  210. String name = "bar";
  211. person.setFirstName(name);
  212. binder.readBean(person);
  213. Assert.assertEquals("", nameField.getValue());
  214. }
  215. protected void bindName() {
  216. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  217. binder.setBean(item);
  218. }
  219. @Test
  220. public void binding_with_null_representation() {
  221. String nullRepresentation = "Some arbitrary text";
  222. String realName = "John";
  223. Person namelessPerson = new Person(null, "Doe", "", 25, Sex.UNKNOWN,
  224. null);
  225. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  226. .bind(Person::getFirstName, Person::setFirstName);
  227. // Bind a person with null value and check that null representation is
  228. // used
  229. binder.setBean(namelessPerson);
  230. Assert.assertEquals(
  231. "Null value from bean was not converted to explicit null representation",
  232. nullRepresentation, nameField.getValue());
  233. // Verify that changes are applied to bean
  234. nameField.setValue(realName);
  235. Assert.assertEquals(
  236. "Bean was not correctly updated from a change in the field",
  237. realName, namelessPerson.getFirstName());
  238. // Verify conversion back to null
  239. nameField.setValue(nullRepresentation);
  240. Assert.assertEquals(
  241. "Two-way null representation did not change value back to null",
  242. null, namelessPerson.getFirstName());
  243. }
  244. @Test
  245. public void binding_with_default_null_representation() {
  246. TextField nullTextField = new TextField() {
  247. @Override
  248. public String getEmptyValue() {
  249. return "null";
  250. }
  251. };
  252. Person namelessPerson = new Person(null, "Doe", "", 25, Sex.UNKNOWN,
  253. null);
  254. binder.bind(nullTextField, Person::getFirstName, Person::setFirstName);
  255. binder.setBean(namelessPerson);
  256. assertTrue(nullTextField.isEmpty());
  257. Assert.assertEquals(null, namelessPerson.getFirstName());
  258. // Change value, see that textfield is not empty and bean is updated.
  259. nullTextField.setValue("");
  260. assertFalse(nullTextField.isEmpty());
  261. Assert.assertEquals("First name of person was not properly updated", "",
  262. namelessPerson.getFirstName());
  263. // Verify that default null representation does not map back to null
  264. nullTextField.setValue("null");
  265. assertTrue(nullTextField.isEmpty());
  266. Assert.assertEquals("Default one-way null representation failed.",
  267. "null", namelessPerson.getFirstName());
  268. }
  269. @Test
  270. public void binding_with_null_representation_value_not_null() {
  271. String nullRepresentation = "Some arbitrary text";
  272. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  273. .bind(Person::getFirstName, Person::setFirstName);
  274. assertFalse("First name in item should not be null",
  275. Objects.isNull(item.getFirstName()));
  276. binder.setBean(item);
  277. Assert.assertEquals("Field value was not set correctly",
  278. item.getFirstName(), nameField.getValue());
  279. }
  280. @Test
  281. public void withConverter_disablesDefaulNullRepresentation() {
  282. Integer customNullConverter = 0;
  283. binder.forField(ageField).withNullRepresentation("foo")
  284. .withConverter(new StringToIntegerConverter(""))
  285. .withConverter(age -> age,
  286. age -> age == null ? customNullConverter : age)
  287. .bind(Person::getSalary, Person::setSalary);
  288. binder.setBean(item);
  289. Assert.assertEquals(customNullConverter.toString(),
  290. ageField.getValue());
  291. Integer salary = 11;
  292. ageField.setValue(salary.toString());
  293. Assert.assertEquals(11, salary.intValue());
  294. }
  295. @Test
  296. public void beanBinder_nullRepresentationIsNotDisabled() {
  297. Binder<Person> binder = new Binder<>(Person.class);
  298. binder.forField(nameField).bind("firstName");
  299. Person person = new Person();
  300. binder.setBean(person);
  301. Assert.assertEquals("", nameField.getValue());
  302. }
  303. @Test
  304. public void beanBinder_withConverter_nullRepresentationIsNotDisabled() {
  305. String customNullPointerRepresentation = "foo";
  306. Binder<Person> binder = new Binder<>(Person.class);
  307. binder.forField(nameField)
  308. .withConverter(value -> value, value -> value == null
  309. ? customNullPointerRepresentation : value)
  310. .bind("firstName");
  311. Person person = new Person();
  312. binder.setBean(person);
  313. Assert.assertEquals(customNullPointerRepresentation,
  314. nameField.getValue());
  315. }
  316. @Test
  317. public void withValidator_doesNotDisablesDefaulNullRepresentation() {
  318. String nullRepresentation = "foo";
  319. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  320. .withValidator(new NotEmptyValidator<>(""))
  321. .bind(Person::getFirstName, Person::setFirstName);
  322. item.setFirstName(null);
  323. binder.setBean(item);
  324. Assert.assertEquals(nullRepresentation, nameField.getValue());
  325. String newValue = "bar";
  326. nameField.setValue(newValue);
  327. Assert.assertEquals(newValue, item.getFirstName());
  328. }
  329. @Test
  330. public void setRequired_withErrorMessage_fieldGetsRequiredIndicatorAndValidator() {
  331. TextField textField = new TextField();
  332. assertFalse(textField.isRequiredIndicatorVisible());
  333. BindingBuilder<Person, String> binding = binder.forField(textField);
  334. assertFalse(textField.isRequiredIndicatorVisible());
  335. binding.asRequired("foobar");
  336. assertTrue(textField.isRequiredIndicatorVisible());
  337. binding.bind(Person::getFirstName, Person::setFirstName);
  338. binder.setBean(item);
  339. Assert.assertNull(textField.getErrorMessage());
  340. textField.setValue(textField.getEmptyValue());
  341. ErrorMessage errorMessage = textField.getErrorMessage();
  342. Assert.assertNotNull(errorMessage);
  343. Assert.assertEquals("foobar", errorMessage.getFormattedHtmlMessage());
  344. textField.setValue("value");
  345. Assert.assertNull(textField.getErrorMessage());
  346. assertTrue(textField.isRequiredIndicatorVisible());
  347. }
  348. @Test
  349. public void setRequired_withErrorMessageProvider_fieldGetsRequiredIndicatorAndValidator() {
  350. TextField textField = new TextField();
  351. textField.setLocale(Locale.CANADA);
  352. assertFalse(textField.isRequiredIndicatorVisible());
  353. BindingBuilder<Person, String> binding = binder.forField(textField);
  354. assertFalse(textField.isRequiredIndicatorVisible());
  355. AtomicInteger invokes = new AtomicInteger();
  356. binding.asRequired(context -> {
  357. invokes.incrementAndGet();
  358. Assert.assertSame(Locale.CANADA, context.getLocale().get());
  359. return "foobar";
  360. });
  361. assertTrue(textField.isRequiredIndicatorVisible());
  362. binding.bind(Person::getFirstName, Person::setFirstName);
  363. binder.setBean(item);
  364. Assert.assertNull(textField.getErrorMessage());
  365. Assert.assertEquals(0, invokes.get());
  366. textField.setValue(textField.getEmptyValue());
  367. ErrorMessage errorMessage = textField.getErrorMessage();
  368. Assert.assertNotNull(errorMessage);
  369. Assert.assertEquals("foobar", errorMessage.getFormattedHtmlMessage());
  370. // validation is run twice, once for the field, then for all the fields
  371. // for cross field validation...
  372. Assert.assertEquals(2, invokes.get());
  373. textField.setValue("value");
  374. Assert.assertNull(textField.getErrorMessage());
  375. assertTrue(textField.isRequiredIndicatorVisible());
  376. }
  377. @Test
  378. public void validationStatusHandler_onlyRunForChangedField() {
  379. TextField firstNameField = new TextField();
  380. TextField lastNameField = new TextField();
  381. AtomicInteger invokes = new AtomicInteger();
  382. binder.forField(firstNameField)
  383. .withValidator(new NotEmptyValidator<>(""))
  384. .withValidationStatusHandler(
  385. validationStatus -> invokes.addAndGet(1))
  386. .bind(Person::getFirstName, Person::setFirstName);
  387. binder.forField(lastNameField)
  388. .withValidator(new NotEmptyValidator<>(""))
  389. .bind(Person::getLastName, Person::setLastName);
  390. binder.setBean(item);
  391. // setting the bean causes 2:
  392. Assert.assertEquals(2, invokes.get());
  393. lastNameField.setValue("");
  394. Assert.assertEquals(2, invokes.get());
  395. firstNameField.setValue("");
  396. Assert.assertEquals(3, invokes.get());
  397. binder.removeBean();
  398. Person person = new Person();
  399. person.setFirstName("a");
  400. person.setLastName("a");
  401. binder.readBean(person);
  402. // reading from a bean causes 2:
  403. Assert.assertEquals(5, invokes.get());
  404. lastNameField.setValue("");
  405. Assert.assertEquals(5, invokes.get());
  406. firstNameField.setValue("");
  407. Assert.assertEquals(6, invokes.get());
  408. }
  409. @Test(expected = IllegalStateException.class)
  410. public void noArgsConstructor_stringBind_throws() {
  411. binder.bind(new TextField(), "firstName");
  412. }
  413. @Test
  414. public void setReadOnly_unboundBinder() {
  415. binder.forField(nameField).bind(Person::getFirstName,
  416. Person::setFirstName);
  417. binder.forField(ageField);
  418. binder.setReadOnly(true);
  419. assertTrue(nameField.isReadOnly());
  420. assertFalse(ageField.isReadOnly());
  421. binder.setReadOnly(false);
  422. assertFalse(nameField.isReadOnly());
  423. assertFalse(ageField.isReadOnly());
  424. }
  425. @Test
  426. public void setReadOnly_boundBinder() {
  427. binder.forField(nameField).bind(Person::getFirstName,
  428. Person::setFirstName);
  429. binder.forField(ageField)
  430. .withConverter(new StringToIntegerConverter(""))
  431. .bind(Person::getAge, Person::setAge);
  432. binder.setBean(new Person());
  433. binder.setReadOnly(true);
  434. assertTrue(nameField.isReadOnly());
  435. assertTrue(ageField.isReadOnly());
  436. binder.setReadOnly(false);
  437. assertFalse(nameField.isReadOnly());
  438. assertFalse(ageField.isReadOnly());
  439. }
  440. @Test
  441. public void setReadOnly_binderLoadedByReadBean() {
  442. binder.forField(nameField).bind(Person::getFirstName,
  443. Person::setFirstName);
  444. binder.forField(ageField)
  445. .withConverter(new StringToIntegerConverter(""))
  446. .bind(Person::getAge, Person::setAge);
  447. binder.readBean(new Person());
  448. binder.setReadOnly(true);
  449. assertTrue(nameField.isReadOnly());
  450. assertTrue(ageField.isReadOnly());
  451. binder.setReadOnly(false);
  452. assertFalse(nameField.isReadOnly());
  453. assertFalse(ageField.isReadOnly());
  454. }
  455. @Test
  456. public void isValidTest_bound_binder() {
  457. binder.forField(nameField)
  458. .withValidator(
  459. Validator.from(
  460. name -> !name.equals("fail field validation"),
  461. ""))
  462. .bind(Person::getFirstName, Person::setFirstName);
  463. binder.withValidator(
  464. Validator.from(person -> !person.getFirstName()
  465. .equals("fail bean validation"), ""));
  466. binder.setBean(item);
  467. Assert.assertTrue(binder.isValid());
  468. nameField.setValue("fail field validation");
  469. Assert.assertFalse(binder.isValid());
  470. nameField.setValue("");
  471. Assert.assertTrue(binder.isValid());
  472. nameField.setValue("fail bean validation");
  473. Assert.assertFalse(binder.isValid());
  474. }
  475. @Test
  476. public void isValidTest_unbound_binder() {
  477. binder.forField(nameField)
  478. .withValidator(Validator.from(
  479. name -> !name.equals("fail field validation"), ""))
  480. .bind(Person::getFirstName, Person::setFirstName);
  481. Assert.assertTrue(binder.isValid());
  482. nameField.setValue("fail field validation");
  483. Assert.assertFalse(binder.isValid());
  484. nameField.setValue("");
  485. Assert.assertTrue(binder.isValid());
  486. }
  487. @Test(expected = IllegalStateException.class)
  488. public void isValidTest_unbound_binder_throws_with_bean_level_validation() {
  489. binder.forField(nameField).bind(Person::getFirstName,
  490. Person::setFirstName);
  491. binder.withValidator(Validator.from(
  492. person -> !person.getFirstName().equals("fail bean validation"),
  493. ""));
  494. binder.isValid();
  495. }
  496. }