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.

BinderConverterValidatorTest.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734
  1. /*
  2. * Copyright 2000-2016 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.data;
  17. import static org.junit.Assert.assertEquals;
  18. import static org.junit.Assert.assertFalse;
  19. import static org.junit.Assert.assertNotNull;
  20. import static org.junit.Assert.assertNull;
  21. import static org.junit.Assert.assertTrue;
  22. import static org.junit.Assert.fail;
  23. import java.io.Serializable;
  24. import java.util.List;
  25. import java.util.concurrent.atomic.AtomicBoolean;
  26. import org.junit.Before;
  27. import org.junit.Test;
  28. import com.vaadin.data.Binder.Binding;
  29. import com.vaadin.data.Binder.BindingBuilder;
  30. import com.vaadin.data.converter.StringToIntegerConverter;
  31. import com.vaadin.data.validator.NotEmptyValidator;
  32. import com.vaadin.server.AbstractErrorMessage;
  33. import com.vaadin.server.ErrorMessage;
  34. import com.vaadin.server.SerializablePredicate;
  35. import com.vaadin.server.UserError;
  36. import com.vaadin.tests.data.bean.Person;
  37. import com.vaadin.ui.Label;
  38. import com.vaadin.ui.TextField;
  39. public class BinderConverterValidatorTest
  40. extends BinderTestBase<Binder<Person>, Person> {
  41. private static class StatusBean implements Serializable {
  42. private String status;
  43. public String getStatus() {
  44. return status;
  45. }
  46. public void setStatus(String status) {
  47. this.status = status;
  48. }
  49. }
  50. @Before
  51. public void setUp() {
  52. binder = new Binder<>();
  53. item = new Person();
  54. item.setFirstName("Johannes");
  55. item.setAge(32);
  56. }
  57. @Test
  58. public void validate_notBound_noErrors() {
  59. BinderValidationStatus<Person> status = binder.validate();
  60. assertTrue(status.isOk());
  61. }
  62. @Test
  63. public void bound_validatorsAreOK_noErrors() {
  64. BindingBuilder<Person, String> binding = binder.forField(nameField);
  65. binding.withValidator(Validator.alwaysPass()).bind(Person::getFirstName,
  66. Person::setFirstName);
  67. nameField.setComponentError(new UserError(""));
  68. BinderValidationStatus<Person> status = binder.validate();
  69. assertTrue(status.isOk());
  70. assertNull(nameField.getComponentError());
  71. }
  72. @SuppressWarnings("serial")
  73. @Test
  74. public void bound_validatorsFail_errors() {
  75. BindingBuilder<Person, String> binding = binder.forField(nameField);
  76. binding.withValidator(Validator.alwaysPass());
  77. String msg1 = "foo";
  78. String msg2 = "bar";
  79. binding.withValidator((String value,
  80. ValueContext context) -> ValidationResult.error(msg1));
  81. binding.withValidator(value -> false, msg2);
  82. binding.bind(Person::getFirstName, Person::setFirstName);
  83. BinderValidationStatus<Person> status = binder.validate();
  84. List<BindingValidationStatus<?>> errors = status
  85. .getFieldValidationErrors();
  86. assertEquals(1, errors.size());
  87. BindingValidationStatus<?> validationStatus = errors.stream()
  88. .findFirst().get();
  89. String msg = validationStatus.getMessage().get();
  90. assertEquals(msg1, msg);
  91. HasValue<?> field = validationStatus.getField();
  92. assertEquals(nameField, field);
  93. ErrorMessage componentError = nameField.getComponentError();
  94. assertNotNull(componentError);
  95. assertEquals("foo",
  96. ((AbstractErrorMessage) componentError).getMessage());
  97. }
  98. @Test
  99. public void validatorForSuperTypeCanBeUsed() {
  100. // Validates that a validator for a super type can be used, e.g.
  101. // validator for Number can be used on a Double
  102. TextField salaryField = new TextField();
  103. Validator<Number> positiveNumberValidator = (value, context) -> {
  104. if (value.doubleValue() >= 0) {
  105. return ValidationResult.ok();
  106. }
  107. return ValidationResult.error(NEGATIVE_ERROR_MESSAGE);
  108. };
  109. binder.forField(salaryField)
  110. .withConverter(Double::valueOf, String::valueOf)
  111. .withValidator(positiveNumberValidator)
  112. .bind(Person::getSalaryDouble, Person::setSalaryDouble);
  113. Person person = new Person();
  114. binder.setBean(person);
  115. salaryField.setValue("10");
  116. assertEquals(10, person.getSalaryDouble(), 0);
  117. salaryField.setValue("-1"); // Does not pass validator
  118. assertEquals(10, person.getSalaryDouble(), 0);
  119. }
  120. @Test
  121. public void convertInitialValue() {
  122. bindAgeWithValidatorConverterValidator();
  123. assertEquals("32", ageField.getValue());
  124. }
  125. @Test
  126. public void convertToModelValidAge() {
  127. bindAgeWithValidatorConverterValidator();
  128. ageField.setValue("33");
  129. assertEquals(33, item.getAge());
  130. }
  131. @Test
  132. public void convertToModelNegativeAgeFailsOnFirstValidator() {
  133. bindAgeWithValidatorConverterValidator();
  134. ageField.setValue("");
  135. assertEquals(32, item.getAge());
  136. assertValidationErrors(binder.validate(), EMPTY_ERROR_MESSAGE);
  137. }
  138. private void assertValidationErrors(
  139. List<BindingValidationStatus<?>> validationErrors,
  140. String... errorMessages) {
  141. assertEquals(errorMessages.length, validationErrors.size());
  142. for (int i = 0; i < errorMessages.length; i++) {
  143. assertEquals(errorMessages[i],
  144. validationErrors.get(i).getMessage().get());
  145. }
  146. }
  147. private void assertValidationErrors(BinderValidationStatus<Person> status,
  148. String... errorMessages) {
  149. assertValidationErrors(status.getFieldValidationErrors(),
  150. errorMessages);
  151. }
  152. @Test
  153. public void convertToModelConversionFails() {
  154. bindAgeWithValidatorConverterValidator();
  155. ageField.setValue("abc");
  156. assertEquals(32, item.getAge());
  157. assertValidationErrors(binder.validate(), NOT_NUMBER_ERROR_MESSAGE);
  158. }
  159. @Test
  160. public void convertToModelNegativeAgeFailsOnIntegerValidator() {
  161. bindAgeWithValidatorConverterValidator();
  162. ageField.setValue("-5");
  163. assertEquals(32, item.getAge());
  164. assertValidationErrors(binder.validate(), NEGATIVE_ERROR_MESSAGE);
  165. }
  166. @Test
  167. public void convertDataToField() {
  168. bindAgeWithValidatorConverterValidator();
  169. binder.getBean().setAge(12);
  170. binder.readBean(binder.getBean());
  171. assertEquals("12", ageField.getValue());
  172. }
  173. @Test
  174. public void convertNotValidatableDataToField() {
  175. bindAgeWithValidatorConverterValidator();
  176. binder.getBean().setAge(-12);
  177. binder.readBean(binder.getBean());
  178. assertEquals("-12", ageField.getValue());
  179. }
  180. @Test(expected = IllegalArgumentException.class)
  181. public void convertInvalidDataToField() {
  182. TextField field = new TextField();
  183. StatusBean bean = new StatusBean();
  184. bean.setStatus("1");
  185. Binder<StatusBean> binder = new Binder<>();
  186. BindingBuilder<StatusBean, String> binding = binder.forField(field)
  187. .withConverter(presentation -> {
  188. if (presentation.equals("OK")) {
  189. return "1";
  190. }
  191. if (presentation.equals("NOTOK")) {
  192. return "2";
  193. }
  194. throw new IllegalArgumentException(
  195. "Value must be OK or NOTOK");
  196. }, model -> {
  197. if (model.equals("1")) {
  198. return "OK";
  199. }
  200. if (model.equals("2")) {
  201. return "NOTOK";
  202. }
  203. throw new IllegalArgumentException(
  204. "Value in model must be 1 or 2");
  205. });
  206. binding.bind(StatusBean::getStatus, StatusBean::setStatus);
  207. binder.setBean(bean);
  208. bean.setStatus("3");
  209. binder.readBean(bean);
  210. }
  211. @Test
  212. public void validate_failedBeanValidatorWithoutFieldValidators() {
  213. binder.forField(nameField).bind(Person::getFirstName,
  214. Person::setFirstName);
  215. String msg = "foo";
  216. binder.withValidator(Validator.from(bean -> false, msg));
  217. Person person = new Person();
  218. binder.setBean(person);
  219. List<BindingValidationStatus<?>> errors = binder.validate()
  220. .getFieldValidationErrors();
  221. assertEquals(0, errors.size());
  222. }
  223. @Test
  224. public void validate_failedBeanValidatorWithFieldValidator() {
  225. String msg = "foo";
  226. BindingBuilder<Person, String> binding = binder.forField(nameField)
  227. .withValidator(new NotEmptyValidator<>(msg));
  228. binding.bind(Person::getFirstName, Person::setFirstName);
  229. binder.withValidator(Validator.from(bean -> false, msg));
  230. Person person = new Person();
  231. binder.setBean(person);
  232. List<BindingValidationStatus<?>> errors = binder.validate()
  233. .getFieldValidationErrors();
  234. assertEquals(1, errors.size());
  235. BindingValidationStatus<?> error = errors.get(0);
  236. assertEquals(msg, error.getMessage().get());
  237. assertEquals(nameField, error.getField());
  238. }
  239. @Test
  240. public void validate_failedBothBeanValidatorAndFieldValidator() {
  241. String msg1 = "foo";
  242. BindingBuilder<Person, String> binding = binder.forField(nameField)
  243. .withValidator(new NotEmptyValidator<>(msg1));
  244. binding.bind(Person::getFirstName, Person::setFirstName);
  245. String msg2 = "bar";
  246. binder.withValidator(Validator.from(bean -> false, msg2));
  247. Person person = new Person();
  248. binder.setBean(person);
  249. List<BindingValidationStatus<?>> errors = binder.validate()
  250. .getFieldValidationErrors();
  251. assertEquals(1, errors.size());
  252. BindingValidationStatus<?> error = errors.get(0);
  253. assertEquals(msg1, error.getMessage().get());
  254. assertEquals(nameField, error.getField());
  255. }
  256. @Test
  257. public void validate_okBeanValidatorWithoutFieldValidators() {
  258. binder.forField(nameField).bind(Person::getFirstName,
  259. Person::setFirstName);
  260. String msg = "foo";
  261. binder.withValidator(Validator.from(bean -> true, msg));
  262. Person person = new Person();
  263. binder.setBean(person);
  264. assertFalse(binder.validate().hasErrors());
  265. assertTrue(binder.validate().isOk());
  266. }
  267. @Test
  268. public void binder_saveIfValid() {
  269. String msg1 = "foo";
  270. BindingBuilder<Person, String> binding = binder.forField(nameField)
  271. .withValidator(new NotEmptyValidator<>(msg1));
  272. binding.bind(Person::getFirstName, Person::setFirstName);
  273. String beanValidatorErrorMessage = "bar";
  274. binder.withValidator(
  275. Validator.from(bean -> false, beanValidatorErrorMessage));
  276. Person person = new Person();
  277. String firstName = "first name";
  278. person.setFirstName(firstName);
  279. binder.readBean(person);
  280. nameField.setValue("");
  281. assertFalse(binder.writeBeanIfValid(person));
  282. // check that field level-validation failed and bean is not updated
  283. assertEquals(firstName, person.getFirstName());
  284. nameField.setValue("new name");
  285. assertFalse(binder.writeBeanIfValid(person));
  286. // Bean is updated but reverted
  287. assertEquals(firstName, person.getFirstName());
  288. }
  289. @Test
  290. public void updateBoundField_bindingValdationFails_beanLevelValidationIsNotRun() {
  291. bindAgeWithValidatorConverterValidator();
  292. bindName();
  293. AtomicBoolean beanLevelValidationRun = new AtomicBoolean();
  294. binder.withValidator(Validator
  295. .from(bean -> beanLevelValidationRun.getAndSet(true), ""));
  296. ageField.setValue("not a number");
  297. assertFalse(beanLevelValidationRun.get());
  298. nameField.setValue("foo");
  299. assertFalse(beanLevelValidationRun.get());
  300. }
  301. @Test
  302. public void updateBoundField_bindingValdationSuccess_beanLevelValidationIsRun() {
  303. bindAgeWithValidatorConverterValidator();
  304. bindName();
  305. AtomicBoolean beanLevelValidationRun = new AtomicBoolean();
  306. binder.withValidator(Validator
  307. .from(bean -> beanLevelValidationRun.getAndSet(true), ""));
  308. ageField.setValue(String.valueOf(12));
  309. assertTrue(beanLevelValidationRun.get());
  310. }
  311. @Test
  312. public void binderHasChanges() throws ValidationException {
  313. binder.forField(nameField)
  314. .withValidator(Validator.from(name -> !"".equals(name),
  315. "Name can't be empty"))
  316. .bind(Person::getFirstName, Person::setFirstName);
  317. assertFalse(binder.hasChanges());
  318. binder.setBean(item);
  319. assertFalse(binder.hasChanges());
  320. // Bound binder + valid user changes: hasChanges == false
  321. nameField.setValue("foo");
  322. assertFalse(binder.hasChanges());
  323. nameField.setValue("bar");
  324. binder.writeBeanIfValid(new Person());
  325. assertFalse(binder.hasChanges());
  326. // Bound binder + invalid user changes: hasChanges() == true
  327. nameField.setValue("");
  328. binder.writeBeanIfValid(new Person());
  329. assertTrue(binder.hasChanges());
  330. // Read bean resets hasChanges
  331. binder.readBean(item);
  332. assertFalse(binder.hasChanges());
  333. // Removing a bound bean resets hasChanges
  334. nameField.setValue("");
  335. assertTrue(binder.hasChanges());
  336. binder.removeBean();
  337. assertFalse(binder.hasChanges());
  338. // Unbound binder + valid user changes: hasChanges() == true
  339. nameField.setValue("foo");
  340. assertTrue(binder.hasChanges());
  341. // successful writeBean resets hasChanges to false
  342. binder.writeBeanIfValid(new Person());
  343. assertFalse(binder.hasChanges());
  344. // Unbound binder + invalid user changes: hasChanges() == true
  345. nameField.setValue("");
  346. assertTrue(binder.hasChanges());
  347. // unsuccessful writeBean doesn't affect hasChanges
  348. nameField.setValue("");
  349. binder.writeBeanIfValid(new Person());
  350. assertTrue(binder.hasChanges());
  351. }
  352. @Test(expected = ValidationException.class)
  353. public void save_fieldValidationErrors() throws ValidationException {
  354. Binder<Person> binder = new Binder<>();
  355. String msg = "foo";
  356. binder.forField(nameField).withValidator(new NotEmptyValidator<>(msg))
  357. .bind(Person::getFirstName, Person::setFirstName);
  358. Person person = new Person();
  359. String firstName = "foo";
  360. person.setFirstName(firstName);
  361. nameField.setValue("");
  362. try {
  363. binder.writeBean(person);
  364. } finally {
  365. // Bean should not have been updated
  366. assertEquals(firstName, person.getFirstName());
  367. }
  368. }
  369. @Test(expected = ValidationException.class)
  370. public void save_beanValidationErrors() throws ValidationException {
  371. Binder<Person> binder = new Binder<>();
  372. binder.forField(nameField).withValidator(new NotEmptyValidator<>("a"))
  373. .bind(Person::getFirstName, Person::setFirstName);
  374. binder.withValidator(Validator.from(person -> false, "b"));
  375. Person person = new Person();
  376. nameField.setValue("foo");
  377. try {
  378. binder.writeBean(person);
  379. } finally {
  380. // Bean should have been updated for item validation but reverted
  381. assertNull(person.getFirstName());
  382. }
  383. }
  384. @Test
  385. public void save_fieldsAndBeanLevelValidation() throws ValidationException {
  386. binder.forField(nameField).withValidator(new NotEmptyValidator<>("a"))
  387. .bind(Person::getFirstName, Person::setFirstName);
  388. binder.withValidator(
  389. Validator.from(person -> person.getLastName() != null, "b"));
  390. Person person = new Person();
  391. person.setLastName("bar");
  392. nameField.setValue("foo");
  393. binder.writeBean(person);
  394. assertEquals(nameField.getValue(), person.getFirstName());
  395. assertEquals("bar", person.getLastName());
  396. }
  397. @Test
  398. public void saveIfValid_fieldValidationErrors() {
  399. String msg = "foo";
  400. binder.forField(nameField).withValidator(new NotEmptyValidator<>(msg))
  401. .bind(Person::getFirstName, Person::setFirstName);
  402. Person person = new Person();
  403. person.setFirstName("foo");
  404. nameField.setValue("");
  405. assertFalse(binder.writeBeanIfValid(person));
  406. assertEquals("foo", person.getFirstName());
  407. }
  408. @Test
  409. public void saveIfValid_noValidationErrors() {
  410. String msg = "foo";
  411. binder.forField(nameField).withValidator(new NotEmptyValidator<>(msg))
  412. .bind(Person::getFirstName, Person::setFirstName);
  413. Person person = new Person();
  414. person.setFirstName("foo");
  415. nameField.setValue("bar");
  416. assertTrue(binder.writeBeanIfValid(person));
  417. assertEquals("bar", person.getFirstName());
  418. }
  419. @Test
  420. public void saveIfValid_beanValidationErrors() {
  421. Binder<Person> binder = new Binder<>();
  422. binder.forField(nameField).bind(Person::getFirstName,
  423. Person::setFirstName);
  424. String msg = "foo";
  425. binder.withValidator(Validator.from(
  426. prsn -> prsn.getAddress() != null || prsn.getEmail() != null,
  427. msg));
  428. Person person = new Person();
  429. person.setFirstName("foo");
  430. nameField.setValue("");
  431. assertFalse(binder.writeBeanIfValid(person));
  432. assertEquals("foo", person.getFirstName());
  433. }
  434. @Test
  435. public void save_null_beanIsUpdated() throws ValidationException {
  436. Binder<Person> binder = new Binder<>();
  437. binder.forField(nameField).withConverter(fieldValue -> {
  438. if ("null".equals(fieldValue)) {
  439. return null;
  440. }
  441. return fieldValue;
  442. }, model -> model).bind(Person::getFirstName, Person::setFirstName);
  443. Person person = new Person();
  444. person.setFirstName("foo");
  445. nameField.setValue("null");
  446. binder.writeBean(person);
  447. assertNull(person.getFirstName());
  448. }
  449. @Test
  450. public void save_validationErrors_exceptionContainsErrors()
  451. throws ValidationException {
  452. String msg = "foo";
  453. BindingBuilder<Person, String> nameBinding = binder.forField(nameField)
  454. .withValidator(new NotEmptyValidator<>(msg));
  455. nameBinding.bind(Person::getFirstName, Person::setFirstName);
  456. BindingBuilder<Person, Integer> ageBinding = binder.forField(ageField)
  457. .withConverter(stringToInteger).withValidator(notNegative);
  458. ageBinding.bind(Person::getAge, Person::setAge);
  459. Person person = new Person();
  460. nameField.setValue("");
  461. ageField.setValue("-1");
  462. try {
  463. binder.writeBean(person);
  464. fail();
  465. } catch (ValidationException exception) {
  466. List<BindingValidationStatus<?>> validationErrors = exception
  467. .getFieldValidationErrors();
  468. assertEquals(2, validationErrors.size());
  469. BindingValidationStatus<?> error = validationErrors.get(0);
  470. assertEquals(nameField, error.getField());
  471. assertEquals(msg, error.getMessage().get());
  472. error = validationErrors.get(1);
  473. assertEquals(ageField, error.getField());
  474. assertEquals(NEGATIVE_ERROR_MESSAGE, error.getMessage().get());
  475. }
  476. }
  477. @Test
  478. public void binderBindAndLoad_clearsErrors() {
  479. BindingBuilder<Person, String> binding = binder.forField(nameField)
  480. .withValidator(notEmpty);
  481. binding.bind(Person::getFirstName, Person::setFirstName);
  482. binder.withValidator(bean -> !bean.getFirstName().contains("error"),
  483. "error");
  484. Person person = new Person();
  485. person.setFirstName("");
  486. binder.setBean(person);
  487. // initial value is invalid but no error
  488. assertNull(nameField.getComponentError());
  489. // make error show
  490. nameField.setValue("foo");
  491. nameField.setValue("");
  492. assertNotNull(nameField.getComponentError());
  493. // bind to another person to see that error is cleared
  494. person = new Person();
  495. person.setFirstName("");
  496. binder.setBean(person);
  497. // error has been cleared
  498. assertNull(nameField.getComponentError());
  499. // make show error
  500. nameField.setValue("foo");
  501. nameField.setValue("");
  502. assertNotNull(nameField.getComponentError());
  503. // load should also clear error
  504. binder.readBean(person);
  505. assertNull(nameField.getComponentError());
  506. // bind a new field that has invalid value in bean
  507. TextField lastNameField = new TextField();
  508. // The test starts with a valid value as the last name of the person,
  509. // since the binder assumes any non-changed values to be valid.
  510. person.setLastName("bar");
  511. BindingBuilder<Person, String> binding2 = binder.forField(lastNameField)
  512. .withValidator(notEmpty);
  513. binding2.bind(Person::getLastName, Person::setLastName);
  514. // should not have error shown when initialized
  515. assertNull(lastNameField.getComponentError());
  516. // Set a value that breaks the validation
  517. lastNameField.setValue("");
  518. assertNotNull(lastNameField.getComponentError());
  519. // add status label to show bean level error
  520. Label statusLabel = new Label();
  521. binder.setStatusLabel(statusLabel);
  522. nameField.setValue("error");
  523. // no error shown yet because second field validation doesn't pass
  524. assertEquals("", statusLabel.getValue());
  525. // make second field validation pass to get bean validation error
  526. lastNameField.setValue("foo");
  527. assertEquals("error", statusLabel.getValue());
  528. // reload bean to clear error
  529. binder.readBean(person);
  530. assertEquals("", statusLabel.getValue());
  531. // reset() should clear all errors and status label
  532. nameField.setValue("");
  533. lastNameField.setValue("");
  534. assertNotNull(nameField.getComponentError());
  535. assertNotNull(lastNameField.getComponentError());
  536. statusLabel.setComponentError(new UserError("ERROR"));
  537. binder.removeBean();
  538. assertNull(nameField.getComponentError());
  539. assertNull(lastNameField.getComponentError());
  540. assertEquals("", statusLabel.getValue());
  541. }
  542. @Test
  543. public void binderLoad_withCrossFieldValidation_clearsErrors() {
  544. TextField lastNameField = new TextField();
  545. final SerializablePredicate<String> lengthPredicate = v -> v
  546. .length() > 2;
  547. BindingBuilder<Person, String> firstNameBinding = binder
  548. .forField(nameField).withValidator(lengthPredicate, "length");
  549. firstNameBinding.bind(Person::getFirstName, Person::setFirstName);
  550. Binding<Person, String> lastNameBinding = binder.forField(lastNameField)
  551. .withValidator(v -> !nameField.getValue().isEmpty()
  552. || lengthPredicate.test(v), "err")
  553. .withValidator(lengthPredicate, "length")
  554. .bind(Person::getLastName, Person::setLastName);
  555. // this will be triggered as a new bean is bound with binder.bind(),
  556. // causing a validation error to be visible until reset is done
  557. nameField.addValueChangeListener(v -> lastNameBinding.validate());
  558. Person person = new Person();
  559. binder.setBean(person);
  560. assertNull(nameField.getComponentError());
  561. assertNull(lastNameField.getComponentError());
  562. nameField.setValue("x");
  563. assertNotNull(nameField.getComponentError());
  564. assertNotNull(lastNameField.getComponentError());
  565. binder.setBean(person);
  566. assertNull(nameField.getComponentError());
  567. assertNull(lastNameField.getComponentError());
  568. }
  569. protected void bindName() {
  570. binder.bind(nameField, Person::getFirstName, Person::setFirstName);
  571. binder.setBean(item);
  572. }
  573. protected void bindAgeWithValidatorConverterValidator() {
  574. binder.forField(ageField).withValidator(notEmpty)
  575. .withConverter(stringToInteger).withValidator(notNegative)
  576. .bind(Person::getAge, Person::setAge);
  577. binder.setBean(item);
  578. }
  579. @Test(expected = ValidationException.class)
  580. public void save_beanValidationErrorsWithConverter()
  581. throws ValidationException {
  582. Binder<Person> binder = new Binder<>();
  583. binder.forField(ageField)
  584. .withConverter(new StringToIntegerConverter("Can't convert"))
  585. .bind(Person::getAge, Person::setAge);
  586. binder.withValidator(Validator.from(person -> false, "b"));
  587. Person person = new Person();
  588. ageField.setValue("1");
  589. try {
  590. binder.writeBean(person);
  591. } finally {
  592. // Bean should have been updated for item validation but reverted
  593. assertEquals(0, person.getAge());
  594. }
  595. }
  596. }