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.

BeanBinderTest.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. package com.vaadin.data;
  2. import static org.junit.Assert.assertEquals;
  3. import static org.junit.Assert.assertFalse;
  4. import static org.junit.Assert.assertSame;
  5. import static org.junit.Assert.assertTrue;
  6. import java.io.Serializable;
  7. import java.time.LocalDate;
  8. import java.util.List;
  9. import java.util.Set;
  10. import javax.validation.constraints.Digits;
  11. import javax.validation.constraints.Max;
  12. import javax.validation.constraints.NotNull;
  13. import javax.validation.constraints.Size;
  14. import org.hibernate.validator.constraints.NotEmpty;
  15. import org.junit.Before;
  16. import org.junit.Test;
  17. import com.vaadin.data.BeanBinderTest.RequiredConstraints.SubConstraint;
  18. import com.vaadin.data.BeanBinderTest.RequiredConstraints.SubSubConstraint;
  19. import com.vaadin.data.BinderInstanceFieldTest.IntegerTextField;
  20. import com.vaadin.data.converter.StringToIntegerConverter;
  21. import com.vaadin.tests.data.bean.BeanToValidate;
  22. import com.vaadin.ui.CheckBoxGroup;
  23. import com.vaadin.ui.TextField;
  24. @SuppressWarnings("unused")
  25. public class BeanBinderTest
  26. extends BinderTestBase<Binder<BeanToValidate>, BeanToValidate> {
  27. private enum TestEnum {
  28. }
  29. private class TestClass {
  30. private CheckBoxGroup<TestEnum> enums;
  31. private TextField number = new TextField();
  32. }
  33. private class TestClassWithoutFields {
  34. }
  35. private static class TestBean implements Serializable {
  36. private Set<TestEnum> enums;
  37. private int number;
  38. public Set<TestEnum> getEnums() {
  39. return enums;
  40. }
  41. public void setEnums(Set<TestEnum> enums) {
  42. this.enums = enums;
  43. }
  44. public int getNumber() {
  45. return number;
  46. }
  47. public void setNumber(int number) {
  48. this.number = number;
  49. }
  50. }
  51. public static class RequiredConstraints implements Serializable {
  52. @NotNull
  53. @Max(10)
  54. private String firstname;
  55. @Size(min = 3, max = 16)
  56. @Digits(integer = 3, fraction = 2)
  57. private String age;
  58. @NotEmpty
  59. private String lastname;
  60. private SubConstraint subfield;
  61. public String getFirstname() {
  62. return firstname;
  63. }
  64. public void setFirstname(String firstname) {
  65. this.firstname = firstname;
  66. }
  67. public String getAge() {
  68. return age;
  69. }
  70. public void setAge(String age) {
  71. this.age = age;
  72. }
  73. public String getLastname() {
  74. return lastname;
  75. }
  76. public void setLastname(String lastname) {
  77. this.lastname = lastname;
  78. }
  79. public SubConstraint getSubfield() {
  80. return subfield;
  81. }
  82. public void setSubfield(SubConstraint subfield) {
  83. this.subfield = subfield;
  84. }
  85. public static class SubConstraint implements Serializable {
  86. @NotNull
  87. @NotEmpty
  88. @Size(min = 5)
  89. private String name;
  90. private SubSubConstraint subsub;
  91. public String getName() {
  92. return name;
  93. }
  94. public void setName(String name) {
  95. this.name = name;
  96. }
  97. public SubSubConstraint getSubsub() {
  98. return subsub;
  99. }
  100. public void setSubsub(SubSubConstraint subsub) {
  101. this.subsub = subsub;
  102. }
  103. }
  104. public static class SubSubConstraint implements Serializable {
  105. @Size(min = 10)
  106. private String value;
  107. public String getValue() {
  108. return value;
  109. }
  110. public void setValue(String value) {
  111. this.value = value;
  112. }
  113. }
  114. }
  115. public static class Person {
  116. LocalDate mydate;
  117. public LocalDate getMydate() {
  118. return mydate;
  119. }
  120. public void setMydate(LocalDate mydate) {
  121. this.mydate = mydate;
  122. }
  123. }
  124. public static class PersonForm {
  125. private TextField mydate = new TextField();
  126. }
  127. public interface TestInterface {
  128. int getProperty();
  129. }
  130. public interface TestInterfaceWithOverwrittenMethod extends TestInterface {
  131. @Override
  132. int getProperty();
  133. }
  134. @Before
  135. public void setUp() {
  136. binder = new BeanValidationBinder<>(BeanToValidate.class);
  137. item = new BeanToValidate();
  138. item.setFirstname("Johannes");
  139. item.setAge(32);
  140. }
  141. @Test
  142. public void bindInstanceFields_parameters_type_erased() {
  143. Binder<TestBean> otherBinder = new Binder<>(TestBean.class);
  144. TestClass testClass = new TestClass();
  145. otherBinder.forField(testClass.number)
  146. .withConverter(new StringToIntegerConverter("")).bind("number");
  147. // Should correctly bind the enum field without throwing
  148. otherBinder.bindInstanceFields(testClass);
  149. testSerialization(otherBinder);
  150. }
  151. @Test
  152. public void bindInstanceFields_automatically_binds_incomplete_forMemberField_bindings() {
  153. Binder<TestBean> otherBinder = new Binder<>(TestBean.class);
  154. TestClass testClass = new TestClass();
  155. otherBinder.forMemberField(testClass.number)
  156. .withConverter(new StringToIntegerConverter(""));
  157. otherBinder.bindInstanceFields(testClass);
  158. TestBean bean = new TestBean();
  159. otherBinder.setBean(bean);
  160. testClass.number.setValue("50");
  161. assertEquals(50, bean.number);
  162. testSerialization(otherBinder);
  163. }
  164. @Test(expected = IllegalStateException.class)
  165. public void bindInstanceFields_does_not_automatically_bind_incomplete_forField_bindings() {
  166. Binder<TestBean> otherBinder = new Binder<>(TestBean.class);
  167. TestClass testClass = new TestClass();
  168. otherBinder.forField(testClass.number)
  169. .withConverter(new StringToIntegerConverter(""));
  170. // Should throw an IllegalStateException since the binding for number is
  171. // not completed with bind
  172. otherBinder.bindInstanceFields(testClass);
  173. }
  174. @Test(expected = IllegalStateException.class)
  175. public void bindInstanceFields_throw_if_no_fields_bound() {
  176. Binder<TestBean> otherBinder = new Binder<>(TestBean.class);
  177. TestClassWithoutFields testClass = new TestClassWithoutFields();
  178. // Should throw an IllegalStateException no fields are bound
  179. otherBinder.bindInstanceFields(testClass);
  180. }
  181. @Test
  182. public void bindInstanceFields_does_not_throw_if_fields_are_bound_manually() {
  183. PersonForm form = new PersonForm();
  184. Binder<Person> binder = new Binder<>(Person.class);
  185. binder.forMemberField(form.mydate)
  186. .withConverter(str -> LocalDate.now(), date -> "Hello")
  187. .bind("mydate");
  188. binder.bindInstanceFields(form);
  189. }
  190. @Test
  191. public void bindInstanceFields_does_not_throw_if_there_are_incomplete_bindings() {
  192. PersonForm form = new PersonForm();
  193. Binder<Person> binder = new Binder<>(Person.class);
  194. binder.forMemberField(form.mydate).withConverter(str -> LocalDate.now(),
  195. date -> "Hello");
  196. binder.bindInstanceFields(form);
  197. }
  198. @Test(expected = IllegalStateException.class)
  199. public void incomplete_forMemberField_bindings() {
  200. Binder<TestBean> otherBinder = new Binder<>(TestBean.class);
  201. TestClass testClass = new TestClass();
  202. otherBinder.forMemberField(testClass.number)
  203. .withConverter(new StringToIntegerConverter(""));
  204. // Should throw an IllegalStateException since the forMemberField
  205. // binding has not been completed
  206. otherBinder.setBean(new TestBean());
  207. }
  208. @Test
  209. public void fieldBound_bindBean_fieldValueUpdated() {
  210. binder.bind(nameField, "firstname");
  211. binder.setBean(item);
  212. assertEquals("Johannes", nameField.getValue());
  213. }
  214. @Test
  215. public void beanBound_bindField_fieldValueUpdated() {
  216. binder.setBean(item);
  217. binder.bind(nameField, "firstname");
  218. assertEquals("Johannes", nameField.getValue());
  219. }
  220. @Test(expected = IllegalArgumentException.class)
  221. public void bindInvalidPropertyName_throws() {
  222. binder.bind(nameField, "firstnaem");
  223. }
  224. @Test(expected = NullPointerException.class)
  225. public void bindNullPropertyName_throws() {
  226. binder.bind(nameField, null);
  227. }
  228. @Test(expected = IllegalArgumentException.class)
  229. public void bindNonReadableProperty_throws() {
  230. binder.bind(nameField, "writeOnlyProperty");
  231. }
  232. @Test
  233. public void beanBound_setValidFieldValue_propertyValueChanged() {
  234. binder.setBean(item);
  235. binder.bind(nameField, "firstname");
  236. nameField.setValue("Henri");
  237. assertEquals("Henri", item.getFirstname());
  238. }
  239. @Test
  240. public void readOnlyPropertyBound_setFieldValue_ignored() {
  241. binder.bind(nameField, "readOnlyProperty");
  242. binder.setBean(item);
  243. String propertyValue = item.getReadOnlyProperty();
  244. nameField.setValue("Foo");
  245. assertEquals(propertyValue, item.getReadOnlyProperty());
  246. }
  247. @Test
  248. public void bindReadOnlyPropertyShouldMarkFieldAsReadonly() {
  249. binder.bind(nameField, "readOnlyProperty");
  250. assertTrue("Name field should be readonly", nameField.isReadOnly());
  251. }
  252. @Test
  253. public void setReadonlyShouldIgnoreBindingsForReadOnlyProperties() {
  254. binder.bind(nameField, "readOnlyProperty");
  255. binder.setReadOnly(true);
  256. assertTrue("Name field should be ignored and be readonly",
  257. nameField.isReadOnly());
  258. binder.setReadOnly(false);
  259. assertTrue("Name field should be ignored and be readonly",
  260. nameField.isReadOnly());
  261. nameField.setReadOnly(false);
  262. binder.setReadOnly(true);
  263. assertFalse("Name field should be ignored and not be readonly",
  264. nameField.isReadOnly());
  265. binder.setReadOnly(false);
  266. assertFalse("Name field should be ignored and not be readonly",
  267. nameField.isReadOnly());
  268. }
  269. @Test
  270. public void beanBound_setInvalidFieldValue_validationError() {
  271. binder.setBean(item);
  272. binder.bind(nameField, "firstname");
  273. nameField.setValue("H"); // too short
  274. assertEquals("Johannes", item.getFirstname());
  275. assertInvalid(nameField, "size must be between 3 and 16");
  276. }
  277. @Test
  278. public void beanNotBound_setInvalidFieldValue_validationError() {
  279. binder.bind(nameField, "firstname");
  280. nameField.setValue("H"); // too short
  281. assertInvalid(nameField, "size must be between 3 and 16");
  282. }
  283. @Test
  284. public void explicitValidatorAdded_setInvalidFieldValue_explicitValidatorRunFirst() {
  285. binder.forField(nameField).withValidator(name -> name.startsWith("J"),
  286. "name must start with J").bind("firstname");
  287. nameField.setValue("A");
  288. assertInvalid(nameField, "name must start with J");
  289. }
  290. @Test
  291. public void explicitValidatorAdded_setInvalidFieldValue_beanValidatorRun() {
  292. binder.forField(nameField).withValidator(name -> name.startsWith("J"),
  293. "name must start with J").bind("firstname");
  294. nameField.setValue("J");
  295. assertInvalid(nameField, "size must be between 3 and 16");
  296. }
  297. @Test(expected = ClassCastException.class)
  298. public void fieldWithIncompatibleTypeBound_bindBean_throws() {
  299. binder.bind(ageField, "age");
  300. binder.setBean(item);
  301. }
  302. @Test(expected = ClassCastException.class)
  303. public void fieldWithIncompatibleTypeBound_loadBean_throws() {
  304. binder.bind(ageField, "age");
  305. binder.readBean(item);
  306. }
  307. @Test(expected = ClassCastException.class)
  308. public void fieldWithIncompatibleTypeBound_saveBean_throws()
  309. throws Throwable {
  310. try {
  311. binder.bind(ageField, "age");
  312. binder.writeBean(item);
  313. } catch (RuntimeException e) {
  314. throw e.getCause();
  315. }
  316. }
  317. @Test
  318. public void fieldWithConverterBound_bindBean_fieldValueUpdated() {
  319. binder.forField(ageField)
  320. .withConverter(Integer::valueOf, String::valueOf).bind("age");
  321. binder.setBean(item);
  322. assertEquals("32", ageField.getValue());
  323. }
  324. @Test(expected = ClassCastException.class)
  325. public void fieldWithInvalidConverterBound_bindBean_fieldValueUpdated() {
  326. binder.forField(ageField).withConverter(Float::valueOf, String::valueOf)
  327. .bind("age");
  328. binder.setBean(item);
  329. assertEquals("32", ageField.getValue());
  330. }
  331. @Test
  332. public void beanBinderWithBoxedType() {
  333. binder.forField(ageField)
  334. .withConverter(Integer::valueOf, String::valueOf).bind("age");
  335. binder.setBean(item);
  336. ageField.setValue(String.valueOf(20));
  337. assertEquals(20, item.getAge());
  338. }
  339. @Test
  340. public void firstName_isNotNullConstraint_fieldIsRequired() {
  341. BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
  342. RequiredConstraints.class);
  343. RequiredConstraints bean = new RequiredConstraints();
  344. TextField field = new TextField();
  345. binder.bind(field, "firstname");
  346. binder.setBean(bean);
  347. assertTrue(field.isRequiredIndicatorVisible());
  348. testSerialization(binder);
  349. }
  350. @Test
  351. public void age_minSizeConstraint_fieldIsRequired() {
  352. BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
  353. RequiredConstraints.class);
  354. RequiredConstraints bean = new RequiredConstraints();
  355. TextField field = new TextField();
  356. binder.bind(field, "age");
  357. binder.setBean(bean);
  358. assertTrue(field.isRequiredIndicatorVisible());
  359. testSerialization(binder);
  360. }
  361. @Test
  362. public void lastName_minSizeConstraint_fieldIsRequired() {
  363. BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
  364. RequiredConstraints.class);
  365. RequiredConstraints bean = new RequiredConstraints();
  366. TextField field = new TextField();
  367. binder.bind(field, "lastname");
  368. binder.setBean(bean);
  369. assertTrue(field.isRequiredIndicatorVisible());
  370. testSerialization(binder);
  371. }
  372. @Test
  373. public void subfield_name_fieldIsRequired() {
  374. BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
  375. RequiredConstraints.class);
  376. RequiredConstraints bean = new RequiredConstraints();
  377. bean.setSubfield(new RequiredConstraints.SubConstraint());
  378. TextField field = new TextField();
  379. binder.bind(field, "subfield.name");
  380. binder.setBean(bean);
  381. assertTrue(field.isRequiredIndicatorVisible());
  382. testSerialization(binder);
  383. }
  384. @Test
  385. public void subsubfield_name_fieldIsRequired() {
  386. BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
  387. RequiredConstraints.class);
  388. RequiredConstraints bean = new RequiredConstraints();
  389. RequiredConstraints.SubConstraint subfield = new RequiredConstraints.SubConstraint();
  390. subfield.setSubsub(new SubSubConstraint());
  391. bean.setSubfield(subfield);
  392. TextField field = new TextField();
  393. binder.bind(field, "subfield.subsub.value");
  394. binder.setBean(bean);
  395. assertTrue(field.isRequiredIndicatorVisible());
  396. testSerialization(binder);
  397. }
  398. @Test
  399. public void subfield_name_valueCanBeValidated() {
  400. BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
  401. RequiredConstraints.class);
  402. TextField field = new TextField();
  403. binder.bind(field, "subfield.name");
  404. RequiredConstraints bean = new RequiredConstraints();
  405. bean.setSubfield(new SubConstraint());
  406. binder.setBean(bean);
  407. assertFalse(binder.validate().isOk());
  408. field.setValue("overfive");
  409. assertTrue(binder.validate().isOk());
  410. }
  411. @Test
  412. public void subSubfield_name_valueCanBeValidated() {
  413. BeanValidationBinder<RequiredConstraints> binder = new BeanValidationBinder<>(
  414. RequiredConstraints.class);
  415. TextField field = new TextField();
  416. binder.bind(field, "subfield.subsub.value");
  417. RequiredConstraints bean = new RequiredConstraints();
  418. SubConstraint subfield = new SubConstraint();
  419. bean.setSubfield(subfield);
  420. subfield.setSubsub(new SubSubConstraint());
  421. binder.setBean(bean);
  422. assertFalse(binder.validate().isOk());
  423. field.setValue("overtencharacters");
  424. assertTrue(binder.validate().isOk());
  425. }
  426. @Test
  427. public void interface_extension_with_overwritten_property() {
  428. Binder<TestInterfaceWithOverwrittenMethod> binder = new Binder<>(
  429. TestInterfaceWithOverwrittenMethod.class);
  430. IntegerTextField field = new IntegerTextField();
  431. binder.bind(field, "property");
  432. TestInterfaceWithOverwrittenMethod bean = new TestInterfaceWithOverwrittenMethod() {
  433. @Override
  434. public int getProperty() {
  435. return 5;
  436. }
  437. };
  438. binder.setBean(bean);
  439. assertEquals(Integer.valueOf(5), field.getValue());
  440. }
  441. private void assertInvalid(HasValue<?> field, String message) {
  442. BinderValidationStatus<?> status = binder.validate();
  443. List<BindingValidationStatus<?>> errors = status
  444. .getFieldValidationErrors();
  445. assertEquals(1, errors.size());
  446. assertSame(field, errors.get(0).getField());
  447. assertEquals(message, errors.get(0).getMessage().get());
  448. }
  449. }