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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486
  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 static org.junit.Assert.fail;
  11. import java.util.Locale;
  12. import java.util.Objects;
  13. import java.util.concurrent.atomic.AtomicBoolean;
  14. import java.util.concurrent.atomic.AtomicInteger;
  15. import java.util.concurrent.atomic.AtomicReference;
  16. import java.util.stream.Stream;
  17. import org.junit.Assert;
  18. import org.junit.Before;
  19. import org.junit.Test;
  20. import org.junit.Rule;
  21. import org.junit.rules.ExpectedException;
  22. import com.vaadin.data.Binder.Binding;
  23. import com.vaadin.data.Binder.BindingBuilder;
  24. import com.vaadin.data.converter.StringToIntegerConverter;
  25. import com.vaadin.data.validator.IntegerRangeValidator;
  26. import com.vaadin.data.validator.NotEmptyValidator;
  27. import com.vaadin.data.validator.StringLengthValidator;
  28. import com.vaadin.server.ErrorMessage;
  29. import com.vaadin.shared.ui.ErrorLevel;
  30. import com.vaadin.tests.data.bean.Person;
  31. import com.vaadin.tests.data.bean.Sex;
  32. import com.vaadin.ui.TextField;
  33. import org.apache.commons.lang.StringUtils;
  34. import org.hamcrest.CoreMatchers;
  35. public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
  36. @Rule
  37. /*
  38. * transient to avoid interfering with serialization tests that capture a
  39. * test instance in a closure
  40. */
  41. public transient ExpectedException exceptionRule = ExpectedException.none();
  42. @Before
  43. public void setUp() {
  44. binder = new Binder<>();
  45. item = new Person();
  46. item.setFirstName("Johannes");
  47. item.setAge(32);
  48. }
  49. @Test
  50. public void bindNullBean_noBeanPresent() {
  51. binder.setBean(item);
  52. assertNotNull(binder.getBean());
  53. binder.setBean(null);
  54. assertNull(binder.getBean());
  55. }
  56. @Test
  57. public void bindNullBean_FieldsAreCleared() {
  58. binder.forField(nameField).bind(Person::getFirstName,
  59. Person::setFirstName);
  60. binder.forField(ageField)
  61. .withConverter(new StringToIntegerConverter(""))
  62. .bind(Person::getAge, Person::setAge);
  63. binder.setBean(item);
  64. assertEquals("No name field value", "Johannes", nameField.getValue());
  65. assertEquals("No age field value", "32", ageField.getValue());
  66. binder.setBean(null);
  67. assertEquals("Name field not empty", "", nameField.getValue());
  68. assertEquals("Age field not empty", "", ageField.getValue());
  69. }
  70. @Test
  71. public void clearForReadBean_boundFieldsAreCleared() {
  72. binder.forField(nameField).bind(Person::getFirstName,
  73. Person::setFirstName);
  74. binder.forField(ageField)
  75. .withConverter(new StringToIntegerConverter(""))
  76. .bind(Person::getAge, Person::setAge);
  77. binder.readBean(item);
  78. assertEquals("No name field value", "Johannes", nameField.getValue());
  79. assertEquals("No age field value", "32", ageField.getValue());
  80. binder.readBean(null);
  81. assertEquals("Name field not empty", "", nameField.getValue());
  82. assertEquals("Age field not empty", "", ageField.getValue());
  83. }
  84. @Test
  85. public void clearReadOnlyField_shouldClearField() {
  86. binder.forField(nameField).bind(Person::getFirstName,
  87. Person::setFirstName);
  88. // Make name field read only
  89. nameField.setReadOnly(true);
  90. binder.setBean(item);
  91. assertEquals("No name field value", "Johannes", nameField.getValue());
  92. binder.setBean(null);
  93. assertEquals("ReadOnly field not empty", "", nameField.getValue());
  94. }
  95. @Test
  96. public void clearBean_setsHasChangesToFalse() {
  97. binder.forField(nameField).bind(Person::getFirstName,
  98. Person::setFirstName);
  99. // Make name field read only
  100. nameField.setReadOnly(true);
  101. binder.readBean(item);
  102. assertEquals("No name field value", "Johannes", nameField.getValue());
  103. nameField.setValue("James");
  104. assertTrue("Binder did not have value changes", binder.hasChanges());
  105. binder.readBean(null);
  106. assertFalse("Binder has changes after clearing all fields",
  107. binder.hasChanges());
  108. }
  109. @Test
  110. public void clearReadOnlyBinder_shouldClearFields() {
  111. binder.forField(nameField).bind(Person::getFirstName,
  112. Person::setFirstName);
  113. binder.forField(ageField)
  114. .withConverter(new StringToIntegerConverter(""))
  115. .bind(Person::getAge, Person::setAge);
  116. binder.setReadOnly(true);
  117. binder.setBean(item);
  118. binder.setBean(null);
  119. assertEquals("ReadOnly name field not empty", "", nameField.getValue());
  120. assertEquals("ReadOnly age field not empty", "", ageField.getValue());
  121. }
  122. @Test(expected = NullPointerException.class)
  123. public void bindNullField_throws() {
  124. binder.forField(null);
  125. }
  126. @Test(expected = NullPointerException.class)
  127. public void bindNullGetter_throws() {
  128. binder.bind(nameField, null, Person::setFirstName);
  129. }
  130. @Test
  131. public void fieldBound_bindItem_fieldValueUpdated() {
  132. binder.forField(nameField).bind(Person::getFirstName,
  133. Person::setFirstName);
  134. binder.setBean(item);
  135. assertEquals("Johannes", nameField.getValue());
  136. }
  137. @Test
  138. public void fieldBoundWithShortcut_bindBean_fieldValueUpdated() {
  139. bindName();
  140. assertEquals("Johannes", nameField.getValue());
  141. }
  142. @Test
  143. public void beanBound_updateFieldValue_beanValueUpdated() {
  144. binder.setBean(item);
  145. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  146. assertEquals("Johannes", nameField.getValue());
  147. nameField.setValue("Artur");
  148. assertEquals("Artur", item.getFirstName());
  149. }
  150. @Test
  151. public void bound_getBean_returnsBoundBean() {
  152. assertNull(binder.getBean());
  153. binder.setBean(item);
  154. assertSame(item, binder.getBean());
  155. }
  156. @Test
  157. public void unbound_getBean_returnsNothing() {
  158. binder.setBean(item);
  159. binder.removeBean();
  160. assertNull(binder.getBean());
  161. }
  162. @Test
  163. public void bound_changeFieldValue_beanValueUpdated() {
  164. bindName();
  165. nameField.setValue("Henri");
  166. assertEquals("Henri", item.getFirstName());
  167. }
  168. @Test
  169. public void unbound_changeFieldValue_beanValueNotUpdated() {
  170. bindName();
  171. nameField.setValue("Henri");
  172. binder.removeBean();
  173. nameField.setValue("Aleksi");
  174. assertEquals("Henri", item.getFirstName());
  175. }
  176. @Test
  177. public void bindNullSetter_valueChangesIgnored() {
  178. binder.bind(nameField, Person::getFirstName, null);
  179. binder.setBean(item);
  180. nameField.setValue("Artur");
  181. assertEquals(item.getFirstName(), "Johannes");
  182. }
  183. @Test
  184. public void bound_bindToAnotherBean_stopsUpdatingOriginal() {
  185. bindName();
  186. nameField.setValue("Leif");
  187. Person p2 = new Person();
  188. p2.setFirstName("Marlon");
  189. binder.setBean(p2);
  190. assertEquals("Marlon", nameField.getValue());
  191. assertEquals("Leif", item.getFirstName());
  192. assertSame(p2, binder.getBean());
  193. nameField.setValue("Ilia");
  194. assertEquals("Ilia", p2.getFirstName());
  195. assertEquals("Leif", item.getFirstName());
  196. }
  197. @Test
  198. public void save_unbound_noChanges() throws ValidationException {
  199. Binder<Person> binder = new Binder<>();
  200. Person person = new Person();
  201. int age = 10;
  202. person.setAge(age);
  203. binder.writeBean(person);
  204. assertEquals(age, person.getAge());
  205. }
  206. @Test
  207. public void save_bound_beanIsUpdated() throws ValidationException {
  208. Binder<Person> binder = new Binder<>();
  209. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  210. Person person = new Person();
  211. String fieldValue = "bar";
  212. nameField.setValue(fieldValue);
  213. person.setFirstName("foo");
  214. binder.writeBean(person);
  215. assertEquals(fieldValue, person.getFirstName());
  216. }
  217. @Test
  218. public void save_bound_beanAsDraft() {
  219. Binder<Person> binder = new Binder<>();
  220. binder.forField(nameField)
  221. .withValidator((value,context) -> {
  222. if (value.equals("Mike")) {
  223. return ValidationResult.ok();
  224. } else {
  225. return ValidationResult.error("value must be Mike");
  226. }
  227. })
  228. .bind(Person::getFirstName, Person::setFirstName);
  229. binder.forField(ageField)
  230. .withConverter(new StringToIntegerConverter(""))
  231. .bind(Person::getAge, Person::setAge);
  232. Person person = new Person();
  233. String fieldValue = "John";
  234. nameField.setValue(fieldValue);
  235. int age = 10;
  236. ageField.setValue("10");
  237. person.setFirstName("Mark");
  238. binder.writeBeanAsDraft(person);
  239. // name is not written to draft as validation / conversion
  240. // does not pass
  241. assertNotEquals(fieldValue, person.getFirstName());
  242. // age is written to draft even if firstname validation
  243. // fails
  244. assertEquals(age, person.getAge());
  245. binder.writeBeanAsDraft(person,true);
  246. // name is now written despite validation as write was forced
  247. assertEquals(fieldValue, person.getFirstName());
  248. }
  249. @Test
  250. public void save_bound_bean_disable_validation_binding() throws ValidationException {
  251. Binder<Person> binder = new Binder<>();
  252. Binding<Person, String> nameBinding = binder.forField(nameField)
  253. .withValidator((value,context) -> {
  254. if (value.equals("Mike")) {
  255. return ValidationResult.ok();
  256. } else {
  257. return ValidationResult.error("value must be Mike");
  258. }
  259. })
  260. .bind(Person::getFirstName, Person::setFirstName);
  261. binder.forField(ageField)
  262. .withConverter(new StringToIntegerConverter(""))
  263. .bind(Person::getAge, Person::setAge);
  264. Person person = new Person();
  265. String fieldValue = "John";
  266. nameField.setValue(fieldValue);
  267. int age = 10;
  268. ageField.setValue("10");
  269. person.setFirstName("Mark");
  270. nameBinding.setValidatorsDisabled(true);
  271. binder.writeBean(person);
  272. // name is now written as validation was disabled
  273. assertEquals(fieldValue, person.getFirstName());
  274. assertEquals(age, person.getAge());
  275. }
  276. @Test
  277. public void save_bound_bean_disable_validation_binder() throws ValidationException {
  278. Binder<Person> binder = new Binder<>();
  279. binder.forField(nameField)
  280. .withValidator((value,context) -> {
  281. if (value.equals("Mike")) {
  282. return ValidationResult.ok();
  283. } else {
  284. return ValidationResult.error("value must be Mike");
  285. }
  286. })
  287. .bind(Person::getFirstName, Person::setFirstName);
  288. binder.forField(ageField)
  289. .withConverter(new StringToIntegerConverter(""))
  290. .bind(Person::getAge, Person::setAge);
  291. Person person = new Person();
  292. String fieldValue = "John";
  293. nameField.setValue(fieldValue);
  294. int age = 10;
  295. ageField.setValue("10");
  296. person.setFirstName("Mark");
  297. binder.setValidatorsDisabled(true);
  298. binder.writeBean(person);
  299. // name is now written as validation was disabled
  300. assertEquals(fieldValue, person.getFirstName());
  301. assertEquals(age, person.getAge());
  302. }
  303. @Test
  304. public void load_bound_fieldValueIsUpdated() {
  305. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  306. Person person = new Person();
  307. String name = "bar";
  308. person.setFirstName(name);
  309. binder.readBean(person);
  310. assertEquals(name, nameField.getValue());
  311. }
  312. @Test
  313. public void load_unbound_noChanges() {
  314. nameField.setValue("");
  315. Person person = new Person();
  316. String name = "bar";
  317. person.setFirstName(name);
  318. binder.readBean(person);
  319. assertEquals("", nameField.getValue());
  320. }
  321. protected void bindName() {
  322. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  323. binder.setBean(item);
  324. }
  325. @Test
  326. public void binding_with_null_representation() {
  327. String nullRepresentation = "Some arbitrary text";
  328. String realName = "John";
  329. Person namelessPerson = new Person(null, "Doe", "", 25, Sex.UNKNOWN,
  330. null);
  331. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  332. .bind(Person::getFirstName, Person::setFirstName);
  333. // Bind a person with null value and check that null representation is
  334. // used
  335. binder.setBean(namelessPerson);
  336. assertEquals(
  337. "Null value from bean was not converted to explicit null representation",
  338. nullRepresentation, nameField.getValue());
  339. // Verify that changes are applied to bean
  340. nameField.setValue(realName);
  341. assertEquals(
  342. "Bean was not correctly updated from a change in the field",
  343. realName, namelessPerson.getFirstName());
  344. // Verify conversion back to null
  345. nameField.setValue(nullRepresentation);
  346. assertEquals(
  347. "Two-way null representation did not change value back to null",
  348. null, namelessPerson.getFirstName());
  349. }
  350. @Test
  351. public void binding_with_default_null_representation() {
  352. TextField nullTextField = new TextField() {
  353. @Override
  354. public String getEmptyValue() {
  355. return "null";
  356. }
  357. };
  358. Person namelessPerson = new Person(null, "Doe", "", 25, Sex.UNKNOWN,
  359. null);
  360. binder.bind(nullTextField, Person::getFirstName, Person::setFirstName);
  361. binder.setBean(namelessPerson);
  362. assertTrue(nullTextField.isEmpty());
  363. assertEquals("null", namelessPerson.getFirstName());
  364. // Change value, see that textfield is not empty and bean is updated.
  365. nullTextField.setValue("");
  366. assertFalse(nullTextField.isEmpty());
  367. assertEquals("First name of person was not properly updated", "",
  368. namelessPerson.getFirstName());
  369. // Verify that default null representation does not map back to null
  370. nullTextField.setValue("null");
  371. assertTrue(nullTextField.isEmpty());
  372. assertEquals("Default one-way null representation failed.", "null",
  373. namelessPerson.getFirstName());
  374. }
  375. @Test
  376. public void binding_with_null_representation_value_not_null() {
  377. String nullRepresentation = "Some arbitrary text";
  378. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  379. .bind(Person::getFirstName, Person::setFirstName);
  380. assertFalse("First name in item should not be null",
  381. Objects.isNull(item.getFirstName()));
  382. binder.setBean(item);
  383. assertEquals("Field value was not set correctly", item.getFirstName(),
  384. nameField.getValue());
  385. }
  386. @Test
  387. public void withConverter_disablesDefaulNullRepresentation() {
  388. Integer customNullConverter = 0;
  389. binder.forField(ageField).withNullRepresentation("foo")
  390. .withConverter(new StringToIntegerConverter(""))
  391. .withConverter(age -> age,
  392. age -> age == null ? customNullConverter : age)
  393. .bind(Person::getSalary, Person::setSalary);
  394. binder.setBean(item);
  395. assertEquals(customNullConverter.toString(), ageField.getValue());
  396. Integer salary = 11;
  397. ageField.setValue(salary.toString());
  398. assertEquals(11, salary.intValue());
  399. }
  400. @Test
  401. public void beanBinder_nullRepresentationIsNotDisabled() {
  402. Binder<Person> binder = new Binder<>(Person.class);
  403. binder.forField(nameField).bind("firstName");
  404. Person person = new Person();
  405. binder.setBean(person);
  406. assertEquals("", nameField.getValue());
  407. }
  408. @Test
  409. public void beanBinder_withConverter_nullRepresentationIsNotDisabled() {
  410. String customNullPointerRepresentation = "foo";
  411. Binder<Person> binder = new Binder<>(Person.class);
  412. binder.forField(nameField)
  413. .withConverter(value -> value,
  414. value -> value == null ? customNullPointerRepresentation
  415. : value)
  416. .bind("firstName");
  417. Person person = new Person();
  418. binder.setBean(person);
  419. assertEquals(customNullPointerRepresentation, nameField.getValue());
  420. }
  421. @Test
  422. public void withValidator_doesNotDisablesDefaulNullRepresentation() {
  423. String nullRepresentation = "foo";
  424. binder.forField(nameField).withNullRepresentation(nullRepresentation)
  425. .withValidator(new NotEmptyValidator<>(""))
  426. .bind(Person::getFirstName, Person::setFirstName);
  427. item.setFirstName(null);
  428. binder.setBean(item);
  429. assertEquals(nullRepresentation, nameField.getValue());
  430. String newValue = "bar";
  431. nameField.setValue(newValue);
  432. assertEquals(newValue, item.getFirstName());
  433. }
  434. @Test
  435. public void setRequired_withErrorMessage_fieldGetsRequiredIndicatorAndValidator() {
  436. TextField textField = new TextField();
  437. assertFalse(textField.isRequiredIndicatorVisible());
  438. BindingBuilder<Person, String> bindingBuilder = binder.forField(textField);
  439. assertFalse(textField.isRequiredIndicatorVisible());
  440. bindingBuilder.asRequired("foobar");
  441. assertTrue(textField.isRequiredIndicatorVisible());
  442. Binding<Person, String> binding = bindingBuilder.bind(Person::getFirstName, Person::setFirstName);
  443. binder.setBean(item);
  444. assertNull(textField.getErrorMessage());
  445. textField.setValue(textField.getEmptyValue());
  446. ErrorMessage errorMessage = textField.getErrorMessage();
  447. assertNotNull(errorMessage);
  448. assertEquals("foobar", errorMessage.getFormattedHtmlMessage());
  449. textField.setValue("value");
  450. assertNull(textField.getErrorMessage());
  451. assertTrue(textField.isRequiredIndicatorVisible());
  452. binding.setAsRequiredEnabled(false);
  453. assertFalse(textField.isRequiredIndicatorVisible());
  454. }
  455. @Test
  456. public void readNullBeanRemovesError() {
  457. TextField textField = new TextField();
  458. binder.forField(textField).asRequired("foobar")
  459. .bind(Person::getFirstName, Person::setFirstName);
  460. assertTrue(textField.isRequiredIndicatorVisible());
  461. assertNull(textField.getErrorMessage());
  462. binder.readBean(item);
  463. assertNull(textField.getErrorMessage());
  464. textField.setValue(textField.getEmptyValue());
  465. assertTrue(textField.isRequiredIndicatorVisible());
  466. assertNotNull(textField.getErrorMessage());
  467. binder.readBean(null);
  468. assertTrue(textField.isRequiredIndicatorVisible());
  469. assertNull(textField.getErrorMessage());
  470. }
  471. @Test
  472. public void setRequired_withErrorMessageProvider_fieldGetsRequiredIndicatorAndValidator() {
  473. TextField textField = new TextField();
  474. textField.setLocale(Locale.CANADA);
  475. assertFalse(textField.isRequiredIndicatorVisible());
  476. BindingBuilder<Person, String> binding = binder.forField(textField);
  477. assertFalse(textField.isRequiredIndicatorVisible());
  478. AtomicInteger invokes = new AtomicInteger();
  479. binding.asRequired(context -> {
  480. invokes.incrementAndGet();
  481. assertSame(Locale.CANADA, context.getLocale().get());
  482. return "foobar";
  483. });
  484. assertTrue(textField.isRequiredIndicatorVisible());
  485. binding.bind(Person::getFirstName, Person::setFirstName);
  486. binder.setBean(item);
  487. assertNull(textField.getErrorMessage());
  488. assertEquals(0, invokes.get());
  489. textField.setValue(textField.getEmptyValue());
  490. ErrorMessage errorMessage = textField.getErrorMessage();
  491. assertNotNull(errorMessage);
  492. assertEquals("foobar", errorMessage.getFormattedHtmlMessage());
  493. // validation is done for all changed bindings once.
  494. assertEquals(1, invokes.get());
  495. textField.setValue("value");
  496. assertNull(textField.getErrorMessage());
  497. assertTrue(textField.isRequiredIndicatorVisible());
  498. }
  499. @Test
  500. public void setRequired_withCustomValidator_fieldGetsRequiredIndicatorAndValidator() {
  501. TextField textField = new TextField();
  502. textField.setLocale(Locale.CANADA);
  503. assertFalse(textField.isRequiredIndicatorVisible());
  504. BindingBuilder<Person, String> binding = binder.forField(textField);
  505. assertFalse(textField.isRequiredIndicatorVisible());
  506. AtomicInteger invokes = new AtomicInteger();
  507. Validator<String> customRequiredValidator = (value, context) -> {
  508. invokes.incrementAndGet();
  509. if (StringUtils.isBlank(value)) {
  510. return ValidationResult.error("Input is required.");
  511. }
  512. return ValidationResult.ok();
  513. };
  514. binding.asRequired(customRequiredValidator);
  515. assertTrue(textField.isRequiredIndicatorVisible());
  516. binding.bind(Person::getFirstName, Person::setFirstName);
  517. binder.setBean(item);
  518. assertNull(textField.getErrorMessage());
  519. assertEquals(1, invokes.get());
  520. textField.setValue(" ");
  521. ErrorMessage errorMessage = textField.getErrorMessage();
  522. assertNotNull(errorMessage);
  523. assertEquals("Input&#32;is&#32;required&#46;",
  524. errorMessage.getFormattedHtmlMessage());
  525. // validation is done for all changed bindings once.
  526. assertEquals(2, invokes.get());
  527. textField.setValue("value");
  528. assertNull(textField.getErrorMessage());
  529. assertTrue(textField.isRequiredIndicatorVisible());
  530. }
  531. @Test
  532. public void setRequired_withCustomValidator_modelConverterBeforeValidator() {
  533. TextField textField = new TextField();
  534. textField.setLocale(Locale.CANADA);
  535. assertFalse(textField.isRequiredIndicatorVisible());
  536. Converter<String, String> stringBasicPreProcessingConverter = new Converter<String, String>() {
  537. @Override
  538. public Result<String> convertToModel(String value,
  539. ValueContext context) {
  540. if (StringUtils.isBlank(value)) {
  541. return Result.ok(null);
  542. }
  543. return Result.ok(StringUtils.trim(value));
  544. }
  545. @Override
  546. public String convertToPresentation(String value,
  547. ValueContext context) {
  548. if (value == null) {
  549. return "";
  550. }
  551. return value;
  552. }
  553. };
  554. AtomicInteger invokes = new AtomicInteger();
  555. Validator<String> customRequiredValidator = (value, context) -> {
  556. invokes.incrementAndGet();
  557. if (value == null) {
  558. return ValidationResult.error("Input required.");
  559. }
  560. return ValidationResult.ok();
  561. };
  562. binder.forField(textField)
  563. .withConverter(stringBasicPreProcessingConverter)
  564. .asRequired(customRequiredValidator)
  565. .bind(Person::getFirstName, Person::setFirstName);
  566. binder.setBean(item);
  567. assertNull(textField.getErrorMessage());
  568. assertEquals(1, invokes.get());
  569. textField.setValue(" ");
  570. ErrorMessage errorMessage = textField.getErrorMessage();
  571. assertNotNull(errorMessage);
  572. assertEquals("Input&#32;required&#46;",
  573. errorMessage.getFormattedHtmlMessage());
  574. // validation is done for all changed bindings once.
  575. assertEquals(2, invokes.get());
  576. textField.setValue("value");
  577. assertNull(textField.getErrorMessage());
  578. assertTrue(textField.isRequiredIndicatorVisible());
  579. }
  580. @Test
  581. public void validationStatusHandler_onlyRunForChangedField() {
  582. TextField firstNameField = new TextField();
  583. TextField lastNameField = new TextField();
  584. AtomicInteger invokes = new AtomicInteger();
  585. binder.forField(firstNameField)
  586. .withValidator(new NotEmptyValidator<>(""))
  587. .withValidationStatusHandler(
  588. validationStatus -> invokes.addAndGet(1))
  589. .bind(Person::getFirstName, Person::setFirstName);
  590. binder.forField(lastNameField)
  591. .withValidator(new NotEmptyValidator<>(""))
  592. .bind(Person::getLastName, Person::setLastName);
  593. binder.setBean(item);
  594. // setting the bean causes 2:
  595. assertEquals(2, invokes.get());
  596. lastNameField.setValue("");
  597. assertEquals(2, invokes.get());
  598. firstNameField.setValue("");
  599. assertEquals(3, invokes.get());
  600. binder.removeBean();
  601. Person person = new Person();
  602. person.setFirstName("a");
  603. person.setLastName("a");
  604. binder.readBean(person);
  605. // reading from a bean causes 2:
  606. assertEquals(5, invokes.get());
  607. lastNameField.setValue("");
  608. assertEquals(5, invokes.get());
  609. firstNameField.setValue("");
  610. assertEquals(6, invokes.get());
  611. }
  612. @Test(expected = IllegalStateException.class)
  613. public void noArgsConstructor_stringBind_throws() {
  614. binder.bind(new TextField(), "firstName");
  615. }
  616. @Test
  617. public void setReadOnly_unboundBinder() {
  618. binder.forField(nameField).bind(Person::getFirstName,
  619. Person::setFirstName);
  620. binder.forField(ageField);
  621. binder.setReadOnly(true);
  622. assertTrue(nameField.isReadOnly());
  623. assertFalse(ageField.isReadOnly());
  624. binder.setReadOnly(false);
  625. assertFalse(nameField.isReadOnly());
  626. assertFalse(ageField.isReadOnly());
  627. }
  628. @Test
  629. public void setReadOnly_boundBinder() {
  630. binder.forField(nameField).bind(Person::getFirstName,
  631. Person::setFirstName);
  632. binder.forField(ageField)
  633. .withConverter(new StringToIntegerConverter(""))
  634. .bind(Person::getAge, Person::setAge);
  635. binder.setBean(new Person());
  636. binder.setReadOnly(true);
  637. assertTrue(nameField.isReadOnly());
  638. assertTrue(ageField.isReadOnly());
  639. binder.setReadOnly(false);
  640. assertFalse(nameField.isReadOnly());
  641. assertFalse(ageField.isReadOnly());
  642. }
  643. @Test
  644. public void setReadOnly_binderLoadedByReadBean() {
  645. binder.forField(nameField).bind(Person::getFirstName,
  646. Person::setFirstName);
  647. binder.forField(ageField)
  648. .withConverter(new StringToIntegerConverter(""))
  649. .bind(Person::getAge, Person::setAge);
  650. binder.readBean(new Person());
  651. binder.setReadOnly(true);
  652. assertTrue(nameField.isReadOnly());
  653. assertTrue(ageField.isReadOnly());
  654. binder.setReadOnly(false);
  655. assertFalse(nameField.isReadOnly());
  656. assertFalse(ageField.isReadOnly());
  657. }
  658. @Test
  659. public void setReadonlyShouldIgnoreBindingsWithNullSetter() {
  660. binder.bind(nameField, Person::getFirstName, null);
  661. binder.forField(ageField)
  662. .withConverter(new StringToIntegerConverter(""))
  663. .bind(Person::getAge, Person::setAge);
  664. binder.setReadOnly(true);
  665. assertTrue("Name field should be ignored but should be readonly",
  666. nameField.isReadOnly());
  667. assertTrue("Age field should be readonly", ageField.isReadOnly());
  668. binder.setReadOnly(false);
  669. assertTrue("Name field should be ignored and should remain readonly",
  670. nameField.isReadOnly());
  671. assertFalse("Age field should not be readonly", ageField.isReadOnly());
  672. nameField.setReadOnly(false);
  673. binder.setReadOnly(false);
  674. assertFalse("Name field should be ignored and remain not readonly",
  675. nameField.isReadOnly());
  676. assertFalse("Age field should not be readonly", ageField.isReadOnly());
  677. binder.setReadOnly(true);
  678. assertFalse("Name field should be ignored and remain not readonly",
  679. nameField.isReadOnly());
  680. assertTrue("Age field should be readonly", ageField.isReadOnly());
  681. }
  682. @Test
  683. public void isValidTest_bound_binder() {
  684. binder.forField(nameField)
  685. .withValidator(Validator.from(
  686. name -> !name.equals("fail field validation"), ""))
  687. .bind(Person::getFirstName, Person::setFirstName);
  688. binder.withValidator(Validator.from(
  689. person -> !person.getFirstName().equals("fail bean validation"),
  690. ""));
  691. binder.setBean(item);
  692. assertTrue(binder.isValid());
  693. nameField.setValue("fail field validation");
  694. assertFalse(binder.isValid());
  695. nameField.setValue("");
  696. assertTrue(binder.isValid());
  697. nameField.setValue("fail bean validation");
  698. assertFalse(binder.isValid());
  699. }
  700. @Test
  701. public void isValidTest_unbound_binder() {
  702. binder.forField(nameField)
  703. .withValidator(Validator.from(
  704. name -> !name.equals("fail field validation"), ""))
  705. .bind(Person::getFirstName, Person::setFirstName);
  706. assertTrue(binder.isValid());
  707. nameField.setValue("fail field validation");
  708. assertFalse(binder.isValid());
  709. nameField.setValue("");
  710. assertTrue(binder.isValid());
  711. }
  712. @Test(expected = IllegalStateException.class)
  713. public void isValidTest_unbound_binder_throws_with_bean_level_validation() {
  714. binder.forField(nameField).bind(Person::getFirstName,
  715. Person::setFirstName);
  716. binder.withValidator(Validator.from(
  717. person -> !person.getFirstName().equals("fail bean validation"),
  718. ""));
  719. binder.isValid();
  720. }
  721. @Test
  722. public void getFields_returnsFields() {
  723. assertEquals(0, binder.getFields().count());
  724. binder.forField(nameField).bind(Person::getFirstName,
  725. Person::setFirstName);
  726. assertStreamEquals(Stream.of(nameField), binder.getFields());
  727. binder.forField(ageField)
  728. .withConverter(new StringToIntegerConverter(""))
  729. .bind(Person::getAge, Person::setAge);
  730. assertStreamEquals(Stream.of(nameField, ageField), binder.getFields());
  731. }
  732. private void assertStreamEquals(Stream<?> s1, Stream<?> s2) {
  733. assertArrayEquals(s1.toArray(), s2.toArray());
  734. }
  735. @Test
  736. public void multiple_calls_to_same_binding_builder() {
  737. String stringLength = "String length failure";
  738. String conversion = "Conversion failed";
  739. String ageLimit = "Age not in valid range";
  740. BindingValidationStatus validation;
  741. binder = new Binder<>(Person.class);
  742. BindingBuilder builder = binder.forField(ageField);
  743. builder.withValidator(new StringLengthValidator(stringLength, 0, 3));
  744. builder.withConverter(new StringToIntegerConverter(conversion));
  745. builder.withValidator(new IntegerRangeValidator(ageLimit, 3, 150));
  746. Binding<Person, ?> bind = builder.bind("age");
  747. binder.setBean(item);
  748. ageField.setValue("123123");
  749. validation = bind.validate();
  750. assertTrue(validation.isError());
  751. assertEquals(stringLength, validation.getMessage().get());
  752. ageField.setValue("age");
  753. validation = bind.validate();
  754. assertTrue(validation.isError());
  755. assertEquals(conversion, validation.getMessage().get());
  756. ageField.setValue("256");
  757. validation = bind.validate();
  758. assertTrue(validation.isError());
  759. assertEquals(ageLimit, validation.getMessage().get());
  760. ageField.setValue("30");
  761. validation = bind.validate();
  762. assertFalse(validation.isError());
  763. assertEquals(30, item.getAge());
  764. }
  765. @Test
  766. public void remove_field_binding() {
  767. binder.forField(ageField)
  768. .withConverter(new StringToIntegerConverter("Can't convert"))
  769. .bind(Person::getAge, Person::setAge);
  770. // Test that the binding does work
  771. assertTrue("Field not initially empty", ageField.isEmpty());
  772. binder.setBean(item);
  773. assertEquals("Binding did not work", String.valueOf(item.getAge()),
  774. ageField.getValue());
  775. binder.setBean(null);
  776. assertTrue("Field not cleared", ageField.isEmpty());
  777. // Remove the binding
  778. binder.removeBinding(ageField);
  779. // Test that it does not work anymore
  780. binder.setBean(item);
  781. assertNotEquals("Binding was not removed",
  782. String.valueOf(item.getAge()), ageField.getValue());
  783. }
  784. @Test
  785. public void remove_propertyname_binding() {
  786. // Use a bean aware binder
  787. Binder<Person> binder = new Binder<>(Person.class);
  788. binder.bind(nameField, "firstName");
  789. // Test that the binding does work
  790. assertTrue("Field not initially empty", nameField.isEmpty());
  791. binder.setBean(item);
  792. assertEquals("Binding did not work", item.getFirstName(),
  793. nameField.getValue());
  794. binder.setBean(null);
  795. assertTrue("Field not cleared", nameField.isEmpty());
  796. // Remove the binding
  797. binder.removeBinding("firstName");
  798. // Test that it does not work anymore
  799. binder.setBean(item);
  800. assertNotEquals("Binding was not removed", item.getFirstName(),
  801. nameField.getValue());
  802. }
  803. @Test
  804. public void remove_binding() {
  805. Binding<Person, Integer> binding = binder.forField(ageField)
  806. .withConverter(new StringToIntegerConverter("Can't convert"))
  807. .bind(Person::getAge, Person::setAge);
  808. // Test that the binding does work
  809. assertTrue("Field not initially empty", ageField.isEmpty());
  810. binder.setBean(item);
  811. assertEquals("Binding did not work", String.valueOf(item.getAge()),
  812. ageField.getValue());
  813. binder.setBean(null);
  814. assertTrue("Field not cleared", ageField.isEmpty());
  815. // Remove the binding
  816. binder.removeBinding(binding);
  817. // Test that it does not work anymore
  818. binder.setBean(item);
  819. assertNotEquals("Binding was not removed",
  820. String.valueOf(item.getAge()), ageField.getValue());
  821. }
  822. @Test
  823. public void remove_binding_fromFieldValueChangeListener() {
  824. // Add listener before bind to make sure it will be executed first.
  825. nameField.addValueChangeListener(e -> {
  826. if (e.getValue() == "REMOVE") {
  827. binder.removeBinding(nameField);
  828. }
  829. });
  830. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  831. binder.setBean(item);
  832. nameField.setValue("REMOVE");
  833. // Removed binding should not update bean.
  834. assertNotEquals("REMOVE", item.getFirstName());
  835. }
  836. @Test
  837. public void beanvalidation_two_fields_not_equal() {
  838. TextField lastNameField = new TextField();
  839. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  840. item.setLastName("Valid");
  841. binder.setBean(item);
  842. assertFalse("Should not have changes initially", binder.hasChanges());
  843. assertTrue("Should be ok initially", binder.validate().isOk());
  844. assertNotEquals("First name and last name are not same initially",
  845. item.getFirstName(), item.getLastName());
  846. nameField.setValue("Invalid");
  847. assertFalse("First name change not handled", binder.hasChanges());
  848. assertTrue(
  849. "Changing first name to something else than last name should be ok",
  850. binder.validate().isOk());
  851. lastNameField.setValue("Invalid");
  852. assertTrue("Last name should not be saved yet", binder.hasChanges());
  853. assertFalse("Binder validation should fail with pending illegal value",
  854. binder.validate().isOk());
  855. assertNotEquals("Illegal last name should not be stored to bean",
  856. item.getFirstName(), item.getLastName());
  857. nameField.setValue("Valid");
  858. assertFalse("With new first name both changes should be saved",
  859. binder.hasChanges());
  860. assertTrue("Everything should be ok for 'Valid Invalid'",
  861. binder.validate().isOk());
  862. assertNotEquals("First name and last name should never match.",
  863. item.getFirstName(), item.getLastName());
  864. }
  865. @Test
  866. public void beanvalidation_initially_broken_bean() {
  867. TextField lastNameField = new TextField();
  868. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  869. item.setLastName(item.getFirstName());
  870. binder.setBean(item);
  871. assertFalse(binder.isValid());
  872. assertFalse(binder.validate().isOk());
  873. }
  874. @Test(expected = IllegalStateException.class)
  875. public void beanvalidation_isValid_throws_with_readBean() {
  876. TextField lastNameField = new TextField();
  877. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  878. binder.readBean(item);
  879. assertTrue(binder.isValid());
  880. }
  881. @Test(expected = IllegalStateException.class)
  882. public void beanvalidation_validate_throws_with_readBean() {
  883. TextField lastNameField = new TextField();
  884. setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
  885. binder.readBean(item);
  886. assertTrue(binder.validate().isOk());
  887. }
  888. protected void setBeanValidationFirstNameNotEqualsLastName(
  889. TextField firstNameField, TextField lastNameField) {
  890. binder.bind(firstNameField, Person::getFirstName, Person::setFirstName);
  891. binder.forField(lastNameField)
  892. .withValidator(t -> !"foo".equals(t),
  893. "Last name cannot be 'foo'")
  894. .bind(Person::getLastName, Person::setLastName);
  895. binder.withValidator(p -> !p.getFirstName().equals(p.getLastName()),
  896. "First name and last name can't be the same");
  897. }
  898. static class MyBindingHandler implements BindingValidationStatusHandler {
  899. boolean expectingError = false;
  900. int callCount = 0;
  901. @Override
  902. public void statusChange(BindingValidationStatus<?> statusChange) {
  903. ++callCount;
  904. if (expectingError) {
  905. assertTrue("Expecting error", statusChange.isError());
  906. } else {
  907. assertFalse("Unexpected error", statusChange.isError());
  908. }
  909. }
  910. }
  911. @Test
  912. public void execute_binding_status_handler_from_binder_status_handler() {
  913. MyBindingHandler bindingHandler = new MyBindingHandler();
  914. binder.forField(nameField)
  915. .withValidator(t -> !t.isEmpty(), "No empty values.")
  916. .withValidationStatusHandler(bindingHandler)
  917. .bind(Person::getFirstName, Person::setFirstName);
  918. String ageError = "CONVERSIONERROR";
  919. binder.forField(ageField)
  920. .withConverter(new StringToIntegerConverter(ageError))
  921. .bind(Person::getAge, Person::setAge);
  922. binder.setValidationStatusHandler(
  923. status -> status.notifyBindingValidationStatusHandlers());
  924. String initialName = item.getFirstName();
  925. int initialAge = item.getAge();
  926. binder.setBean(item);
  927. // Test specific error handling.
  928. bindingHandler.expectingError = true;
  929. nameField.setValue("");
  930. // Test default error handling.
  931. ageField.setValue("foo");
  932. assertTrue("Component error does not contain error message",
  933. ageField.getComponentError().getFormattedHtmlMessage()
  934. .contains(ageError));
  935. // Restore values and test no errors.
  936. ageField.setValue(String.valueOf(initialAge));
  937. assertNull("There should be no component error",
  938. ageField.getComponentError());
  939. bindingHandler.expectingError = false;
  940. nameField.setValue(initialName);
  941. // Assert that the handler was called.
  942. assertEquals(
  943. "Unexpected callCount to binding validation status handler", 6,
  944. bindingHandler.callCount);
  945. }
  946. @Test
  947. public void removed_binding_not_updates_value() {
  948. Binding<Person, Integer> binding = binder.forField(ageField)
  949. .withConverter(new StringToIntegerConverter("Can't convert"))
  950. .bind(Person::getAge, Person::setAge);
  951. binder.setBean(item);
  952. String modifiedAge = String.valueOf(item.getAge() + 10);
  953. String ageBeforeUnbind = String.valueOf(item.getAge());
  954. binder.removeBinding(binding);
  955. ageField.setValue(modifiedAge);
  956. assertEquals("Binding still affects bean even after unbind",
  957. ageBeforeUnbind, String.valueOf(item.getAge()));
  958. }
  959. @Test
  960. public void info_validator_not_considered_error() {
  961. String infoMessage = "Young";
  962. binder.forField(ageField)
  963. .withConverter(new StringToIntegerConverter("Can't convert"))
  964. .withValidator(i -> i > 5, infoMessage, ErrorLevel.INFO)
  965. .bind(Person::getAge, Person::setAge);
  966. binder.setBean(item);
  967. ageField.setValue("3");
  968. assertEquals(infoMessage,
  969. ageField.getComponentError().getFormattedHtmlMessage());
  970. assertEquals(ErrorLevel.INFO,
  971. ageField.getComponentError().getErrorLevel());
  972. assertEquals(3, item.getAge());
  973. }
  974. @Test
  975. public void two_asRequired_fields_without_initial_values() {
  976. binder.forField(nameField).asRequired("Empty name").bind(p -> "",
  977. (p, s) -> {
  978. });
  979. binder.forField(ageField).asRequired("Empty age").bind(p -> "",
  980. (p, s) -> {
  981. });
  982. binder.setBean(item);
  983. assertNull("Initially there should be no errors",
  984. nameField.getComponentError());
  985. assertNull("Initially there should be no errors",
  986. ageField.getComponentError());
  987. nameField.setValue("Foo");
  988. assertNull("Name with a value should not be an error",
  989. nameField.getComponentError());
  990. assertNull(
  991. "Age field should not be in error, since it has not been modified.",
  992. ageField.getComponentError());
  993. nameField.setValue("");
  994. assertNotNull("Empty name should now be in error.",
  995. nameField.getComponentError());
  996. assertNull("Age field should still be ok.",
  997. ageField.getComponentError());
  998. }
  999. @Test
  1000. public void refreshValueFromBean() {
  1001. Binding<Person, String> binding = binder.bind(nameField,
  1002. Person::getFirstName, Person::setFirstName);
  1003. binder.readBean(item);
  1004. assertEquals("Name should be read from the item", item.getFirstName(),
  1005. nameField.getValue());
  1006. nameField.setValue("foo");
  1007. assertNotEquals("Name should be different from the item",
  1008. item.getFirstName(), nameField.getValue());
  1009. binding.read(item);
  1010. assertEquals("Name should be read again from the item",
  1011. item.getFirstName(), nameField.getValue());
  1012. }
  1013. @Test(expected = IllegalArgumentException.class)
  1014. public void remove_binding_from_different_binder() {
  1015. Binder<Person> anotherBinder = new Binder<>();
  1016. Binding<Person, String> binding = anotherBinder.bind(nameField,
  1017. Person::getFirstName, Person::setFirstName);
  1018. binder.removeBinding(binding);
  1019. }
  1020. @Test(expected = IllegalStateException.class)
  1021. public void bindWithNullSetterSetReadWrite() {
  1022. Binding<Person, String> binding = binder.bind(nameField,
  1023. Person::getFirstName, null);
  1024. binding.setReadOnly(false);
  1025. }
  1026. @Test
  1027. public void bindWithNullSetterShouldMarkFieldAsReadonly() {
  1028. Binding<Person, String> nameBinding = binder.bind(nameField,
  1029. Person::getFirstName, null);
  1030. binder.forField(ageField)
  1031. .withConverter(new StringToIntegerConverter(""))
  1032. .bind(Person::getAge, Person::setAge);
  1033. assertTrue("Name field should be readonly", nameField.isReadOnly());
  1034. assertFalse("Age field should not be readonly", ageField.isReadOnly());
  1035. assertTrue("Binding should be marked readonly",
  1036. nameBinding.isReadOnly());
  1037. }
  1038. @Test
  1039. public void setReadOnly_binding() {
  1040. Binding<Person, String> binding = binder.bind(nameField,
  1041. Person::getFirstName, Person::setFirstName);
  1042. assertFalse("Binding should not be readonly", binding.isReadOnly());
  1043. assertFalse("Name field should not be readonly",
  1044. nameField.isReadOnly());
  1045. binding.setReadOnly(true);
  1046. assertTrue("Binding should be readonly", binding.isReadOnly());
  1047. assertTrue("Name field should be readonly", nameField.isReadOnly());
  1048. }
  1049. @Test
  1050. public void conversionWithLocaleBasedErrorMessage() {
  1051. String fiError = "VIRHE";
  1052. String otherError = "ERROR";
  1053. binder.forField(ageField).withConverter(new StringToIntegerConverter(
  1054. context -> context.getLocale().map(Locale::getLanguage)
  1055. .orElse("en").equals("fi") ? fiError : otherError))
  1056. .bind(Person::getAge, Person::setAge);
  1057. binder.setBean(item);
  1058. ageField.setValue("not a number");
  1059. assertEquals(otherError,
  1060. ageField.getErrorMessage().getFormattedHtmlMessage());
  1061. ageField.setLocale(new Locale("fi"));
  1062. // Re-validate to get the error message with correct locale
  1063. binder.validate();
  1064. assertEquals(fiError,
  1065. ageField.getErrorMessage().getFormattedHtmlMessage());
  1066. }
  1067. @Test
  1068. public void valueChangeListenerOrder() {
  1069. AtomicBoolean beanSet = new AtomicBoolean();
  1070. nameField.addValueChangeListener(e -> {
  1071. if (!beanSet.get()) {
  1072. assertEquals("Value in bean updated earlier than expected",
  1073. e.getOldValue(), item.getFirstName());
  1074. }
  1075. });
  1076. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  1077. nameField.addValueChangeListener(e -> {
  1078. if (!beanSet.get()) {
  1079. assertEquals("Value in bean not updated when expected",
  1080. e.getValue(), item.getFirstName());
  1081. }
  1082. });
  1083. beanSet.set(true);
  1084. binder.setBean(item);
  1085. beanSet.set(false);
  1086. nameField.setValue("Foo");
  1087. }
  1088. @Test
  1089. public void nonSymetricValue_setBean_writtenToBean() {
  1090. binder.bind(nameField, Person::getLastName, Person::setLastName);
  1091. assertNull(item.getLastName());
  1092. binder.setBean(item);
  1093. assertEquals("", item.getLastName());
  1094. }
  1095. @Test
  1096. public void nonSymmetricValue_readBean_beanNotTouched() {
  1097. binder.bind(nameField, Person::getLastName, Person::setLastName);
  1098. binder.addValueChangeListener(
  1099. event -> fail("No value change event should be fired"));
  1100. assertNull(item.getLastName());
  1101. binder.readBean(item);
  1102. assertNull(item.getLastName());
  1103. }
  1104. @Test
  1105. public void symetricValue_setBean_beanNotUpdated() {
  1106. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  1107. binder.setBean(new Person() {
  1108. @Override
  1109. public String getFirstName() {
  1110. return "First";
  1111. }
  1112. @Override
  1113. public void setFirstName(String firstName) {
  1114. fail("Setter should not be called");
  1115. }
  1116. });
  1117. }
  1118. @Test
  1119. public void nullRejetingField_nullValue_wrappedExceptionMentionsNullRepresentation() {
  1120. TextField field = createNullAnd42RejectingFieldWithEmptyValue("");
  1121. Binder<AtomicReference<Integer>> binder = createIntegerConverterBinder(
  1122. field);
  1123. exceptionRule.expect(IllegalStateException.class);
  1124. exceptionRule.expectMessage("null representation");
  1125. exceptionRule.expectCause(CoreMatchers.isA(NullPointerException.class));
  1126. binder.readBean(new AtomicReference<>());
  1127. }
  1128. @Test
  1129. public void nullRejetingField_otherRejectedValue_originalExceptionIsThrown() {
  1130. TextField field = createNullAnd42RejectingFieldWithEmptyValue("");
  1131. Binder<AtomicReference<Integer>> binder = createIntegerConverterBinder(
  1132. field);
  1133. exceptionRule.expect(IllegalArgumentException.class);
  1134. exceptionRule.expectMessage("42");
  1135. binder.readBean(new AtomicReference<>(Integer.valueOf(42)));
  1136. }
  1137. @Test(expected = NullPointerException.class)
  1138. public void nullAcceptingField_nullValue_originalExceptionIsThrown() {
  1139. /*
  1140. * Edge case with a field that throws for null but has null as the empty
  1141. * value. This is most likely the case if the field doesn't explicitly
  1142. * reject null values but is instead somehow broken so that any value is
  1143. * rejected.
  1144. */
  1145. TextField field = createNullAnd42RejectingFieldWithEmptyValue(null);
  1146. Binder<AtomicReference<Integer>> binder = createIntegerConverterBinder(
  1147. field);
  1148. binder.readBean(new AtomicReference<>(null));
  1149. }
  1150. private TextField createNullAnd42RejectingFieldWithEmptyValue(
  1151. String emptyValue) {
  1152. return new TextField() {
  1153. @Override
  1154. public void setValue(String value) {
  1155. if (value == null) {
  1156. throw new NullPointerException("Null value");
  1157. } else if ("42".equals(value)) {
  1158. throw new IllegalArgumentException("42 is not allowed");
  1159. }
  1160. super.setValue(value);
  1161. }
  1162. @Override
  1163. public String getEmptyValue() {
  1164. return emptyValue;
  1165. }
  1166. };
  1167. }
  1168. private Binder<AtomicReference<Integer>> createIntegerConverterBinder(
  1169. TextField field) {
  1170. Binder<AtomicReference<Integer>> binder = new Binder<>();
  1171. binder.forField(field)
  1172. .withConverter(new StringToIntegerConverter("Must have number"))
  1173. .bind(AtomicReference::get, AtomicReference::set);
  1174. return binder;
  1175. }
  1176. }