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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058
  1. package com.vaadin.data;
  2. import static org.junit.Assert.assertArrayEquals;
  3. import static org.junit.Assert.assertEquals;
  4. import static org.junit.Assert.assertFalse;
  5. import static org.junit.Assert.assertNotEquals;
  6. import static org.junit.Assert.assertNotNull;
  7. import static org.junit.Assert.assertNull;
  8. import static org.junit.Assert.assertSame;
  9. import static org.junit.Assert.assertTrue;
  10. import java.util.Locale;
  11. import java.util.Objects;
  12. import java.util.concurrent.atomic.AtomicInteger;
  13. import java.util.stream.Stream;
  14. import org.junit.Assert;
  15. import org.junit.Before;
  16. import org.junit.Test;
  17. import com.vaadin.data.Binder.Binding;
  18. import com.vaadin.data.Binder.BindingBuilder;
  19. import com.vaadin.data.converter.StringToIntegerConverter;
  20. import com.vaadin.data.validator.IntegerRangeValidator;
  21. import com.vaadin.data.validator.NotEmptyValidator;
  22. import com.vaadin.data.validator.StringLengthValidator;
  23. import com.vaadin.server.ErrorMessage;
  24. import com.vaadin.shared.ui.ErrorLevel;
  25. import com.vaadin.tests.data.bean.Person;
  26. import com.vaadin.tests.data.bean.Sex;
  27. import com.vaadin.ui.TextField;
  28. public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
  29. @Before
  30. public void setUp() {
  31. binder = new Binder<>();
  32. item = new Person();
  33. item.setFirstName("Johannes");
  34. item.setAge(32);
  35. }
  36. @Test
  37. public void bindNullBean_noBeanPresent() {
  38. binder.setBean(item);
  39. assertNotNull(binder.getBean());
  40. binder.setBean(null);
  41. assertNull(binder.getBean());
  42. }
  43. @Test
  44. public void bindNullBean_FieldsAreCleared() {
  45. binder.forField(nameField).bind(Person::getFirstName,
  46. Person::setFirstName);
  47. binder.forField(ageField)
  48. .withConverter(new StringToIntegerConverter(""))
  49. .bind(Person::getAge, Person::setAge);
  50. binder.setBean(item);
  51. assertEquals("No name field value", "Johannes", nameField.getValue());
  52. assertEquals("No age field value", "32", ageField.getValue());
  53. binder.setBean(null);
  54. assertEquals("Name field not empty", "", nameField.getValue());
  55. assertEquals("Age field not empty", "", ageField.getValue());
  56. }
  57. @Test
  58. public void clearForReadBean_boundFieldsAreCleared() {
  59. binder.forField(nameField).bind(Person::getFirstName,
  60. Person::setFirstName);
  61. binder.forField(ageField)
  62. .withConverter(new StringToIntegerConverter(""))
  63. .bind(Person::getAge, Person::setAge);
  64. binder.readBean(item);
  65. assertEquals("No name field value", "Johannes", nameField.getValue());
  66. assertEquals("No age field value", "32", ageField.getValue());
  67. binder.readBean(null);
  68. assertEquals("Name field not empty", "", nameField.getValue());
  69. assertEquals("Age field not empty", "", ageField.getValue());
  70. }
  71. @Test
  72. public void clearReadOnlyField_shouldClearField() {
  73. binder.forField(nameField).bind(Person::getFirstName,
  74. Person::setFirstName);
  75. // Make name field read only
  76. nameField.setReadOnly(true);
  77. binder.setBean(item);
  78. assertEquals("No name field value", "Johannes", nameField.getValue());
  79. binder.setBean(null);
  80. assertEquals("ReadOnly field not empty", "", nameField.getValue());
  81. }
  82. @Test
  83. public void clearBean_setsHasChangesToFalse() {
  84. binder.forField(nameField).bind(Person::getFirstName,
  85. Person::setFirstName);
  86. // Make name field read only
  87. nameField.setReadOnly(true);
  88. binder.readBean(item);
  89. assertEquals("No name field value", "Johannes", nameField.getValue());
  90. nameField.setValue("James");
  91. assertTrue("Binder did not have value changes", binder.hasChanges());
  92. binder.readBean(null);
  93. assertFalse("Binder has changes after clearing all fields",
  94. binder.hasChanges());
  95. }
  96. @Test
  97. public void clearReadOnlyBinder_shouldClearFields() {
  98. binder.forField(nameField).bind(Person::getFirstName,
  99. Person::setFirstName);
  100. binder.forField(ageField)
  101. .withConverter(new StringToIntegerConverter(""))
  102. .bind(Person::getAge, Person::setAge);
  103. binder.setReadOnly(true);
  104. binder.setBean(item);
  105. binder.setBean(null);
  106. assertEquals("ReadOnly name field not empty", "", nameField.getValue());
  107. assertEquals("ReadOnly age field not empty", "", ageField.getValue());
  108. }
  109. @Test(expected = NullPointerException.class)
  110. public void bindNullField_throws() {
  111. binder.forField(null);
  112. }
  113. @Test(expected = NullPointerException.class)
  114. public void bindNullGetter_throws() {
  115. binder.bind(nameField, null, Person::setFirstName);
  116. }
  117. @Test
  118. public void fieldBound_bindItem_fieldValueUpdated() {
  119. binder.forField(nameField).bind(Person::getFirstName,
  120. Person::setFirstName);
  121. binder.setBean(item);
  122. assertEquals("Johannes", nameField.getValue());
  123. }
  124. @Test
  125. public void fieldBoundWithShortcut_bindBean_fieldValueUpdated() {
  126. bindName();
  127. assertEquals("Johannes", nameField.getValue());
  128. }
  129. @Test
  130. public void beanBound_updateFieldValue_beanValueUpdated() {
  131. binder.setBean(item);
  132. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  133. assertEquals("Johannes", nameField.getValue());
  134. nameField.setValue("Artur");
  135. assertEquals("Artur", item.getFirstName());
  136. }
  137. @Test
  138. public void bound_getBean_returnsBoundBean() {
  139. assertNull(binder.getBean());
  140. binder.setBean(item);
  141. assertSame(item, binder.getBean());
  142. }
  143. @Test
  144. public void unbound_getBean_returnsNothing() {
  145. binder.setBean(item);
  146. binder.removeBean();
  147. assertNull(binder.getBean());
  148. }
  149. @Test
  150. public void bound_changeFieldValue_beanValueUpdated() {
  151. bindName();
  152. nameField.setValue("Henri");
  153. assertEquals("Henri", item.getFirstName());
  154. }
  155. @Test
  156. public void unbound_changeFieldValue_beanValueNotUpdated() {
  157. bindName();
  158. nameField.setValue("Henri");
  159. binder.removeBean();
  160. nameField.setValue("Aleksi");
  161. assertEquals("Henri", item.getFirstName());
  162. }
  163. @Test
  164. public void bindNullSetter_valueChangesIgnored() {
  165. binder.bind(nameField, Person::getFirstName, null);
  166. binder.setBean(item);
  167. nameField.setValue("Artur");
  168. assertEquals(item.getFirstName(), "Johannes");
  169. }
  170. @Test
  171. public void bound_bindToAnotherBean_stopsUpdatingOriginal() {
  172. bindName();
  173. nameField.setValue("Leif");
  174. Person p2 = new Person();
  175. p2.setFirstName("Marlon");
  176. binder.setBean(p2);
  177. assertEquals("Marlon", nameField.getValue());
  178. assertEquals("Leif", item.getFirstName());
  179. assertSame(p2, binder.getBean());
  180. nameField.setValue("Ilia");
  181. assertEquals("Ilia", p2.getFirstName());
  182. assertEquals("Leif", item.getFirstName());
  183. }
  184. @Test
  185. public void save_unbound_noChanges() throws ValidationException {
  186. Binder<Person> binder = new Binder<>();
  187. Person person = new Person();
  188. int age = 10;
  189. person.setAge(age);
  190. binder.writeBean(person);
  191. assertEquals(age, person.getAge());
  192. }
  193. @Test
  194. public void save_bound_beanIsUpdated() throws ValidationException {
  195. Binder<Person> binder = new Binder<>();
  196. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  197. Person person = new Person();
  198. String fieldValue = "bar";
  199. nameField.setValue(fieldValue);
  200. person.setFirstName("foo");
  201. binder.writeBean(person);
  202. assertEquals(fieldValue, person.getFirstName());
  203. }
  204. @Test
  205. public void load_bound_fieldValueIsUpdated() {
  206. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  207. Person person = new Person();
  208. String name = "bar";
  209. person.setFirstName(name);
  210. binder.readBean(person);
  211. assertEquals(name, nameField.getValue());
  212. }
  213. @Test
  214. public void load_unbound_noChanges() {
  215. nameField.setValue("");
  216. Person person = new Person();
  217. String name = "bar";
  218. person.setFirstName(name);
  219. binder.readBean(person);
  220. assertEquals("", nameField.getValue());
  221. }
  222. protected void bindName() {
  223. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  224. binder.setBean(item);
  225. }
  226. @Test
  227. public void binding_with_null_representation() {
  228. String nullRepresentation = "Some arbitrary text";
  229. String realName = "John";
  230. Person namelessPerson = new Person(null, "Doe", "", 25, Sex.UNKNOWN,
  231. null);
  232. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  233. .bind(Person::getFirstName, Person::setFirstName);
  234. // Bind a person with null value and check that null representation is
  235. // used
  236. binder.setBean(namelessPerson);
  237. assertEquals(
  238. "Null value from bean was not converted to explicit null representation",
  239. nullRepresentation, nameField.getValue());
  240. // Verify that changes are applied to bean
  241. nameField.setValue(realName);
  242. assertEquals(
  243. "Bean was not correctly updated from a change in the field",
  244. realName, namelessPerson.getFirstName());
  245. // Verify conversion back to null
  246. nameField.setValue(nullRepresentation);
  247. assertEquals(
  248. "Two-way null representation did not change value back to null",
  249. null, namelessPerson.getFirstName());
  250. }
  251. @Test
  252. public void binding_with_default_null_representation() {
  253. TextField nullTextField = new TextField() {
  254. @Override
  255. public String getEmptyValue() {
  256. return "null";
  257. }
  258. };
  259. Person namelessPerson = new Person(null, "Doe", "", 25, Sex.UNKNOWN,
  260. null);
  261. binder.bind(nullTextField, Person::getFirstName, Person::setFirstName);
  262. binder.setBean(namelessPerson);
  263. assertTrue(nullTextField.isEmpty());
  264. assertEquals(null, namelessPerson.getFirstName());
  265. // Change value, see that textfield is not empty and bean is updated.
  266. nullTextField.setValue("");
  267. assertFalse(nullTextField.isEmpty());
  268. assertEquals("First name of person was not properly updated", "",
  269. namelessPerson.getFirstName());
  270. // Verify that default null representation does not map back to null
  271. nullTextField.setValue("null");
  272. assertTrue(nullTextField.isEmpty());
  273. assertEquals("Default one-way null representation failed.", "null",
  274. namelessPerson.getFirstName());
  275. }
  276. @Test
  277. public void binding_with_null_representation_value_not_null() {
  278. String nullRepresentation = "Some arbitrary text";
  279. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  280. .bind(Person::getFirstName, Person::setFirstName);
  281. assertFalse("First name in item should not be null",
  282. Objects.isNull(item.getFirstName()));
  283. binder.setBean(item);
  284. assertEquals("Field value was not set correctly", item.getFirstName(),
  285. nameField.getValue());
  286. }
  287. @Test
  288. public void withConverter_disablesDefaulNullRepresentation() {
  289. Integer customNullConverter = 0;
  290. binder.forField(ageField).withNullRepresentation("foo")
  291. .withConverter(new StringToIntegerConverter(""))
  292. .withConverter(age -> age,
  293. age -> age == null ? customNullConverter : age)
  294. .bind(Person::getSalary, Person::setSalary);
  295. binder.setBean(item);
  296. assertEquals(customNullConverter.toString(), ageField.getValue());
  297. Integer salary = 11;
  298. ageField.setValue(salary.toString());
  299. assertEquals(11, salary.intValue());
  300. }
  301. @Test
  302. public void beanBinder_nullRepresentationIsNotDisabled() {
  303. Binder<Person> binder = new Binder<>(Person.class);
  304. binder.forField(nameField).bind("firstName");
  305. Person person = new Person();
  306. binder.setBean(person);
  307. assertEquals("", nameField.getValue());
  308. }
  309. @Test
  310. public void beanBinder_withConverter_nullRepresentationIsNotDisabled() {
  311. String customNullPointerRepresentation = "foo";
  312. Binder<Person> binder = new Binder<>(Person.class);
  313. binder.forField(nameField)
  314. .withConverter(value -> value,
  315. value -> value == null ? customNullPointerRepresentation
  316. : value)
  317. .bind("firstName");
  318. Person person = new Person();
  319. binder.setBean(person);
  320. assertEquals(customNullPointerRepresentation, nameField.getValue());
  321. }
  322. @Test
  323. public void withValidator_doesNotDisablesDefaulNullRepresentation() {
  324. String nullRepresentation = "foo";
  325. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  326. .withValidator(new NotEmptyValidator<>(""))
  327. .bind(Person::getFirstName, Person::setFirstName);
  328. item.setFirstName(null);
  329. binder.setBean(item);
  330. assertEquals(nullRepresentation, nameField.getValue());
  331. String newValue = "bar";
  332. nameField.setValue(newValue);
  333. assertEquals(newValue, item.getFirstName());
  334. }
  335. @Test
  336. public void setRequired_withErrorMessage_fieldGetsRequiredIndicatorAndValidator() {
  337. TextField textField = new TextField();
  338. assertFalse(textField.isRequiredIndicatorVisible());
  339. BindingBuilder<Person, String> binding = binder.forField(textField);
  340. assertFalse(textField.isRequiredIndicatorVisible());
  341. binding.asRequired("foobar");
  342. assertTrue(textField.isRequiredIndicatorVisible());
  343. binding.bind(Person::getFirstName, Person::setFirstName);
  344. binder.setBean(item);
  345. assertNull(textField.getErrorMessage());
  346. textField.setValue(textField.getEmptyValue());
  347. ErrorMessage errorMessage = textField.getErrorMessage();
  348. assertNotNull(errorMessage);
  349. assertEquals("foobar", errorMessage.getFormattedHtmlMessage());
  350. textField.setValue("value");
  351. assertNull(textField.getErrorMessage());
  352. assertTrue(textField.isRequiredIndicatorVisible());
  353. }
  354. @Test
  355. public void readNullBeanRemovesError() {
  356. TextField textField = new TextField();
  357. binder.forField(textField).asRequired("foobar")
  358. .bind(Person::getFirstName, Person::setFirstName);
  359. assertTrue(textField.isRequiredIndicatorVisible());
  360. assertNull(textField.getErrorMessage());
  361. binder.readBean(item);
  362. assertNull(textField.getErrorMessage());
  363. textField.setValue(textField.getEmptyValue());
  364. assertTrue(textField.isRequiredIndicatorVisible());
  365. assertNotNull(textField.getErrorMessage());
  366. binder.readBean(null);
  367. assertTrue(textField.isRequiredIndicatorVisible());
  368. assertNull(textField.getErrorMessage());
  369. }
  370. @Test
  371. public void setRequired_withErrorMessageProvider_fieldGetsRequiredIndicatorAndValidator() {
  372. TextField textField = new TextField();
  373. textField.setLocale(Locale.CANADA);
  374. assertFalse(textField.isRequiredIndicatorVisible());
  375. BindingBuilder<Person, String> binding = binder.forField(textField);
  376. assertFalse(textField.isRequiredIndicatorVisible());
  377. AtomicInteger invokes = new AtomicInteger();
  378. binding.asRequired(context -> {
  379. invokes.incrementAndGet();
  380. assertSame(Locale.CANADA, context.getLocale().get());
  381. return "foobar";
  382. });
  383. assertTrue(textField.isRequiredIndicatorVisible());
  384. binding.bind(Person::getFirstName, Person::setFirstName);
  385. binder.setBean(item);
  386. assertNull(textField.getErrorMessage());
  387. assertEquals(0, invokes.get());
  388. textField.setValue(textField.getEmptyValue());
  389. ErrorMessage errorMessage = textField.getErrorMessage();
  390. assertNotNull(errorMessage);
  391. assertEquals("foobar", errorMessage.getFormattedHtmlMessage());
  392. // validation is done for all changed bindings once.
  393. assertEquals(1, invokes.get());
  394. textField.setValue("value");
  395. assertNull(textField.getErrorMessage());
  396. assertTrue(textField.isRequiredIndicatorVisible());
  397. }
  398. @Test
  399. public void validationStatusHandler_onlyRunForChangedField() {
  400. TextField firstNameField = new TextField();
  401. TextField lastNameField = new TextField();
  402. AtomicInteger invokes = new AtomicInteger();
  403. binder.forField(firstNameField)
  404. .withValidator(new NotEmptyValidator<>(""))
  405. .withValidationStatusHandler(
  406. validationStatus -> invokes.addAndGet(1))
  407. .bind(Person::getFirstName, Person::setFirstName);
  408. binder.forField(lastNameField)
  409. .withValidator(new NotEmptyValidator<>(""))
  410. .bind(Person::getLastName, Person::setLastName);
  411. binder.setBean(item);
  412. // setting the bean causes 2:
  413. assertEquals(2, invokes.get());
  414. lastNameField.setValue("");
  415. assertEquals(2, invokes.get());
  416. firstNameField.setValue("");
  417. assertEquals(3, invokes.get());
  418. binder.removeBean();
  419. Person person = new Person();
  420. person.setFirstName("a");
  421. person.setLastName("a");
  422. binder.readBean(person);
  423. // reading from a bean causes 2:
  424. assertEquals(5, invokes.get());
  425. lastNameField.setValue("");
  426. assertEquals(5, invokes.get());
  427. firstNameField.setValue("");
  428. assertEquals(6, invokes.get());
  429. }
  430. @Test(expected = IllegalStateException.class)
  431. public void noArgsConstructor_stringBind_throws() {
  432. binder.bind(new TextField(), "firstName");
  433. }
  434. @Test
  435. public void setReadOnly_unboundBinder() {
  436. binder.forField(nameField).bind(Person::getFirstName,
  437. Person::setFirstName);
  438. binder.forField(ageField);
  439. binder.setReadOnly(true);
  440. assertTrue(nameField.isReadOnly());
  441. assertFalse(ageField.isReadOnly());
  442. binder.setReadOnly(false);
  443. assertFalse(nameField.isReadOnly());
  444. assertFalse(ageField.isReadOnly());
  445. }
  446. @Test
  447. public void setReadOnly_boundBinder() {
  448. binder.forField(nameField).bind(Person::getFirstName,
  449. Person::setFirstName);
  450. binder.forField(ageField)
  451. .withConverter(new StringToIntegerConverter(""))
  452. .bind(Person::getAge, Person::setAge);
  453. binder.setBean(new Person());
  454. binder.setReadOnly(true);
  455. assertTrue(nameField.isReadOnly());
  456. assertTrue(ageField.isReadOnly());
  457. binder.setReadOnly(false);
  458. assertFalse(nameField.isReadOnly());
  459. assertFalse(ageField.isReadOnly());
  460. }
  461. @Test
  462. public void setReadOnly_binderLoadedByReadBean() {
  463. binder.forField(nameField).bind(Person::getFirstName,
  464. Person::setFirstName);
  465. binder.forField(ageField)
  466. .withConverter(new StringToIntegerConverter(""))
  467. .bind(Person::getAge, Person::setAge);
  468. binder.readBean(new Person());
  469. binder.setReadOnly(true);
  470. assertTrue(nameField.isReadOnly());
  471. assertTrue(ageField.isReadOnly());
  472. binder.setReadOnly(false);
  473. assertFalse(nameField.isReadOnly());
  474. assertFalse(ageField.isReadOnly());
  475. }
  476. @Test
  477. public void setReadonlyShouldIgnoreBindingsWithNullSetter() {
  478. binder.bind(nameField, Person::getFirstName, null);
  479. binder.forField(ageField)
  480. .withConverter(new StringToIntegerConverter(""))
  481. .bind(Person::getAge, Person::setAge);
  482. binder.setReadOnly(true);
  483. assertTrue("Name field should be ignored but should be readonly", nameField.isReadOnly());
  484. assertTrue("Age field should be readonly", ageField.isReadOnly());
  485. binder.setReadOnly(false);
  486. assertTrue("Name field should be ignored and should remain readonly", nameField.isReadOnly());
  487. assertFalse("Age field should not be readonly", ageField.isReadOnly());
  488. nameField.setReadOnly(false);
  489. binder.setReadOnly(false);
  490. assertFalse("Name field should be ignored and remain not readonly", nameField.isReadOnly());
  491. assertFalse("Age field should not be readonly", ageField.isReadOnly());
  492. binder.setReadOnly(true);
  493. assertFalse("Name field should be ignored and remain not readonly", nameField.isReadOnly());
  494. assertTrue("Age field should be readonly", ageField.isReadOnly());
  495. }
  496. @Test
  497. public void isValidTest_bound_binder() {
  498. binder.forField(nameField)
  499. .withValidator(Validator.from(
  500. name -> !name.equals("fail field validation"), ""))
  501. .bind(Person::getFirstName, Person::setFirstName);
  502. binder.withValidator(Validator.from(
  503. person -> !person.getFirstName().equals("fail bean validation"),
  504. ""));
  505. binder.setBean(item);
  506. assertTrue(binder.isValid());
  507. nameField.setValue("fail field validation");
  508. assertFalse(binder.isValid());
  509. nameField.setValue("");
  510. assertTrue(binder.isValid());
  511. nameField.setValue("fail bean validation");
  512. assertFalse(binder.isValid());
  513. }
  514. @Test
  515. public void isValidTest_unbound_binder() {
  516. binder.forField(nameField)
  517. .withValidator(Validator.from(
  518. name -> !name.equals("fail field validation"), ""))
  519. .bind(Person::getFirstName, Person::setFirstName);
  520. assertTrue(binder.isValid());
  521. nameField.setValue("fail field validation");
  522. assertFalse(binder.isValid());
  523. nameField.setValue("");
  524. assertTrue(binder.isValid());
  525. }
  526. @Test(expected = IllegalStateException.class)
  527. public void isValidTest_unbound_binder_throws_with_bean_level_validation() {
  528. binder.forField(nameField).bind(Person::getFirstName,
  529. Person::setFirstName);
  530. binder.withValidator(Validator.from(
  531. person -> !person.getFirstName().equals("fail bean validation"),
  532. ""));
  533. binder.isValid();
  534. }
  535. @Test
  536. public void getFields_returnsFields() {
  537. assertEquals(0, binder.getFields().count());
  538. binder.forField(nameField).bind(Person::getFirstName,
  539. Person::setFirstName);
  540. assertStreamEquals(Stream.of(nameField), binder.getFields());
  541. binder.forField(ageField)
  542. .withConverter(new StringToIntegerConverter(""))
  543. .bind(Person::getAge, Person::setAge);
  544. assertStreamEquals(Stream.of(nameField, ageField), binder.getFields());
  545. }
  546. private void assertStreamEquals(Stream<?> s1, Stream<?> s2) {
  547. assertArrayEquals(s1.toArray(), s2.toArray());
  548. }
  549. @Test
  550. public void multiple_calls_to_same_binding_builder() {
  551. String stringLength = "String length failure";
  552. String conversion = "Conversion failed";
  553. String ageLimit = "Age not in valid range";
  554. BindingValidationStatus validation;
  555. binder = new Binder<>(Person.class);
  556. BindingBuilder builder = binder.forField(ageField);
  557. builder.withValidator(new StringLengthValidator(stringLength, 0, 3));
  558. builder.withConverter(new StringToIntegerConverter(conversion));
  559. builder.withValidator(new IntegerRangeValidator(ageLimit, 3, 150));
  560. Binding<Person, ?> bind = builder.bind("age");
  561. binder.setBean(item);
  562. ageField.setValue("123123");
  563. validation = bind.validate();
  564. assertTrue(validation.isError());
  565. assertEquals(stringLength, validation.getMessage().get());
  566. ageField.setValue("age");
  567. validation = bind.validate();
  568. assertTrue(validation.isError());
  569. assertEquals(conversion, validation.getMessage().get());
  570. ageField.setValue("256");
  571. validation = bind.validate();
  572. assertTrue(validation.isError());
  573. assertEquals(ageLimit, validation.getMessage().get());
  574. ageField.setValue("30");
  575. validation = bind.validate();
  576. assertFalse(validation.isError());
  577. assertEquals(30, item.getAge());
  578. }
  579. @Test
  580. public void remove_field_binding() {
  581. binder.forField(ageField)
  582. .withConverter(new StringToIntegerConverter("Can't convert"))
  583. .bind(Person::getAge, Person::setAge);
  584. // Test that the binding does work
  585. assertTrue("Field not initially empty", ageField.isEmpty());
  586. binder.setBean(item);
  587. assertEquals("Binding did not work", String.valueOf(item.getAge()),
  588. ageField.getValue());
  589. binder.setBean(null);
  590. assertTrue("Field not cleared", ageField.isEmpty());
  591. // Remove the binding
  592. binder.removeBinding(ageField);
  593. // Test that it does not work anymore
  594. binder.setBean(item);
  595. assertNotEquals("Binding was not removed",
  596. String.valueOf(item.getAge()), ageField.getValue());
  597. }
  598. @Test
  599. public void remove_propertyname_binding() {
  600. // Use a bean aware binder
  601. Binder<Person> binder = new Binder<>(Person.class);
  602. binder.bind(nameField, "firstName");
  603. // Test that the binding does work
  604. assertTrue("Field not initially empty", nameField.isEmpty());
  605. binder.setBean(item);
  606. assertEquals("Binding did not work", item.getFirstName(),
  607. nameField.getValue());
  608. binder.setBean(null);
  609. assertTrue("Field not cleared", nameField.isEmpty());
  610. // Remove the binding
  611. binder.removeBinding("firstName");
  612. // Test that it does not work anymore
  613. binder.setBean(item);
  614. assertNotEquals("Binding was not removed", item.getFirstName(),
  615. nameField.getValue());
  616. }
  617. @Test
  618. public void remove_binding() {
  619. Binding<Person, Integer> binding = binder.forField(ageField)
  620. .withConverter(new StringToIntegerConverter("Can't convert"))
  621. .bind(Person::getAge, Person::setAge);
  622. // Test that the binding does work
  623. assertTrue("Field not initially empty", ageField.isEmpty());
  624. binder.setBean(item);
  625. assertEquals("Binding did not work", String.valueOf(item.getAge()),
  626. ageField.getValue());
  627. binder.setBean(null);
  628. assertTrue("Field not cleared", ageField.isEmpty());
  629. // Remove the binding
  630. binder.removeBinding(binding);
  631. // Test that it does not work anymore
  632. binder.setBean(item);
  633. assertNotEquals("Binding was not removed",
  634. String.valueOf(item.getAge()), ageField.getValue());
  635. }
  636. @Test
  637. public void beanvalidation_two_fields_not_equal() {
  638. TextField lastNameField = new TextField();
  639. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  640. item.setLastName("Valid");
  641. binder.setBean(item);
  642. assertFalse("Should not have changes initially", binder.hasChanges());
  643. assertTrue("Should be ok initially", binder.validate().isOk());
  644. assertNotEquals("First name and last name are not same initially",
  645. item.getFirstName(), item.getLastName());
  646. nameField.setValue("Invalid");
  647. assertFalse("First name change not handled", binder.hasChanges());
  648. assertTrue(
  649. "Changing first name to something else than last name should be ok",
  650. binder.validate().isOk());
  651. lastNameField.setValue("Invalid");
  652. assertTrue("Last name should not be saved yet", binder.hasChanges());
  653. assertFalse("Binder validation should fail with pending illegal value",
  654. binder.validate().isOk());
  655. assertNotEquals("Illegal last name should not be stored to bean",
  656. item.getFirstName(), item.getLastName());
  657. nameField.setValue("Valid");
  658. assertFalse("With new first name both changes should be saved",
  659. binder.hasChanges());
  660. assertTrue("Everything should be ok for 'Valid Invalid'",
  661. binder.validate().isOk());
  662. assertNotEquals("First name and last name should never match.",
  663. item.getFirstName(), item.getLastName());
  664. }
  665. @Test
  666. public void beanvalidation_initially_broken_bean() {
  667. TextField lastNameField = new TextField();
  668. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  669. item.setLastName(item.getFirstName());
  670. binder.setBean(item);
  671. assertFalse(binder.isValid());
  672. assertFalse(binder.validate().isOk());
  673. }
  674. @Test(expected = IllegalStateException.class)
  675. public void beanvalidation_isValid_throws_with_readBean() {
  676. TextField lastNameField = new TextField();
  677. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  678. binder.readBean(item);
  679. assertTrue(binder.isValid());
  680. }
  681. @Test(expected = IllegalStateException.class)
  682. public void beanvalidation_validate_throws_with_readBean() {
  683. TextField lastNameField = new TextField();
  684. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  685. binder.readBean(item);
  686. assertTrue(binder.validate().isOk());
  687. }
  688. protected void setBeanValidationFirstNameNotEqualsLastName(
  689. TextField firstNameField, TextField lastNameField) {
  690. binder.bind(firstNameField, Person::getFirstName, Person::setFirstName);
  691. binder.forField(lastNameField)
  692. .withValidator(t -> !"foo".equals(t),
  693. "Last name cannot be 'foo'")
  694. .bind(Person::getLastName, Person::setLastName);
  695. binder.withValidator(p -> !p.getFirstName().equals(p.getLastName()),
  696. "First name and last name can't be the same");
  697. }
  698. static class MyBindingHandler implements BindingValidationStatusHandler {
  699. boolean expectingError = false;
  700. int callCount = 0;
  701. @Override
  702. public void statusChange(BindingValidationStatus<?> statusChange) {
  703. ++callCount;
  704. if (expectingError) {
  705. assertTrue("Expecting error", statusChange.isError());
  706. } else {
  707. assertFalse("Unexpected error", statusChange.isError());
  708. }
  709. }
  710. }
  711. @Test
  712. public void execute_binding_status_handler_from_binder_status_handler() {
  713. MyBindingHandler bindingHandler = new MyBindingHandler();
  714. binder.forField(nameField)
  715. .withValidator(t -> !t.isEmpty(), "No empty values.")
  716. .withValidationStatusHandler(bindingHandler)
  717. .bind(Person::getFirstName, Person::setFirstName);
  718. String ageError = "CONVERSIONERROR";
  719. binder.forField(ageField)
  720. .withConverter(new StringToIntegerConverter(ageError))
  721. .bind(Person::getAge, Person::setAge);
  722. binder.setValidationStatusHandler(
  723. status -> status.notifyBindingValidationStatusHandlers());
  724. String initialName = item.getFirstName();
  725. int initialAge = item.getAge();
  726. binder.setBean(item);
  727. // Test specific error handling.
  728. bindingHandler.expectingError = true;
  729. nameField.setValue("");
  730. // Test default error handling.
  731. ageField.setValue("foo");
  732. assertTrue("Component error does not contain error message",
  733. ageField.getComponentError().getFormattedHtmlMessage()
  734. .contains(ageError));
  735. // Restore values and test no errors.
  736. ageField.setValue(String.valueOf(initialAge));
  737. assertNull("There should be no component error",
  738. ageField.getComponentError());
  739. bindingHandler.expectingError = false;
  740. nameField.setValue(initialName);
  741. // Assert that the handler was called.
  742. assertEquals(
  743. "Unexpected callCount to binding validation status handler", 6,
  744. bindingHandler.callCount);
  745. }
  746. @Test
  747. public void removed_binding_not_updates_value() {
  748. Binding<Person, Integer> binding = binder.forField(ageField)
  749. .withConverter(new StringToIntegerConverter("Can't convert"))
  750. .bind(Person::getAge, Person::setAge);
  751. binder.setBean(item);
  752. String modifiedAge = String.valueOf(item.getAge() + 10);
  753. String ageBeforeUnbind = String.valueOf(item.getAge());
  754. binder.removeBinding(binding);
  755. ageField.setValue(modifiedAge);
  756. assertEquals("Binding still affects bean even after unbind",
  757. ageBeforeUnbind, String.valueOf(item.getAge()));
  758. }
  759. @Test
  760. public void info_validator_not_considered_error() {
  761. String infoMessage = "Young";
  762. binder.forField(ageField)
  763. .withConverter(new StringToIntegerConverter("Can't convert"))
  764. .withValidator(i -> i > 5, infoMessage, ErrorLevel.INFO)
  765. .bind(Person::getAge, Person::setAge);
  766. binder.setBean(item);
  767. ageField.setValue("3");
  768. Assert.assertEquals(infoMessage,
  769. ageField.getComponentError().getFormattedHtmlMessage());
  770. Assert.assertEquals(ErrorLevel.INFO,
  771. ageField.getComponentError().getErrorLevel());
  772. Assert.assertEquals(3, item.getAge());
  773. }
  774. @Test
  775. public void two_asRequired_fields_without_initial_values() {
  776. binder.forField(nameField).asRequired("Empty name").bind(p -> "",
  777. (p, s) -> {
  778. });
  779. binder.forField(ageField).asRequired("Empty age").bind(p -> "",
  780. (p, s) -> {
  781. });
  782. binder.setBean(item);
  783. assertNull("Initially there should be no errors",
  784. nameField.getComponentError());
  785. assertNull("Initially there should be no errors",
  786. ageField.getComponentError());
  787. nameField.setValue("Foo");
  788. assertNull("Name with a value should not be an error",
  789. nameField.getComponentError());
  790. assertNull(
  791. "Age field should not be in error, since it has not been modified.",
  792. ageField.getComponentError());
  793. nameField.setValue("");
  794. assertNotNull("Empty name should now be in error.",
  795. nameField.getComponentError());
  796. assertNull("Age field should still be ok.",
  797. ageField.getComponentError());
  798. }
  799. @Test
  800. public void refreshValueFromBean() {
  801. Binding<Person, String> binding = binder.bind(nameField,
  802. Person::getFirstName, Person::setFirstName);
  803. binder.readBean(item);
  804. assertEquals("Name should be read from the item", item.getFirstName(),
  805. nameField.getValue());
  806. nameField.setValue("foo");
  807. assertNotEquals("Name should be different from the item",
  808. item.getFirstName(), nameField.getValue());
  809. binding.read(item);
  810. assertEquals("Name should be read again from the item",
  811. item.getFirstName(), nameField.getValue());
  812. }
  813. @Test(expected = IllegalArgumentException.class)
  814. public void remove_binding_from_different_binder() {
  815. Binder<Person> anotherBinder = new Binder<>();
  816. Binding<Person, String> binding = anotherBinder.bind(nameField,
  817. Person::getFirstName, Person::setFirstName);
  818. binder.removeBinding(binding);
  819. }
  820. @Test
  821. public void bindWithNullSetterShouldMarkFieldAsReadonly() {
  822. binder.bind(nameField, Person::getFirstName, null);
  823. binder.forField(ageField)
  824. .withConverter(new StringToIntegerConverter(""))
  825. .bind(Person::getAge, Person::setAge);
  826. assertTrue("Name field should be readonly", nameField.isReadOnly());
  827. assertFalse("Name field should be readonly", ageField.isReadOnly());
  828. }
  829. }