|
8 | 8 | import static org.junit.Assert.assertNull;
|
9 | 9 | import static org.junit.Assert.assertSame;
|
10 | 10 | import static org.junit.Assert.assertTrue;
|
| 11 | +import static org.junit.Assert.fail; |
11 | 12 |
|
12 | 13 | import java.util.Locale;
|
13 | 14 | import java.util.Objects;
|
14 | 15 | import java.util.concurrent.atomic.AtomicBoolean;
|
15 | 16 | import java.util.concurrent.atomic.AtomicInteger;
|
| 17 | +import java.util.concurrent.atomic.AtomicReference; |
16 | 18 | import java.util.stream.Stream;
|
17 | 19 |
|
18 | 20 | import org.junit.Assert;
|
19 | 21 | import org.junit.Before;
|
20 | 22 | import org.junit.Test;
|
| 23 | +import org.junit.Rule; |
| 24 | +import org.junit.rules.ExpectedException; |
21 | 25 |
|
22 | 26 | import com.vaadin.data.Binder.Binding;
|
23 | 27 | import com.vaadin.data.Binder.BindingBuilder;
|
|
31 | 35 | import com.vaadin.tests.data.bean.Sex;
|
32 | 36 | import com.vaadin.ui.TextField;
|
33 | 37 | import org.apache.commons.lang.StringUtils;
|
| 38 | +import org.hamcrest.CoreMatchers; |
34 | 39 |
|
35 | 40 | public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
|
36 | 41 |
|
| 42 | + @Rule |
| 43 | + /* |
| 44 | + * transient to avoid interfering with serialization tests that capture a |
| 45 | + * test instance in a closure |
| 46 | + */ |
| 47 | + public transient ExpectedException exceptionRule = ExpectedException.none(); |
| 48 | + |
37 | 49 | @Before
|
38 | 50 | public void setUp() {
|
39 | 51 | binder = new Binder<>();
|
@@ -333,7 +345,7 @@ public String getEmptyValue() {
|
333 | 345 | binder.setBean(namelessPerson);
|
334 | 346 |
|
335 | 347 | assertTrue(nullTextField.isEmpty());
|
336 |
| - assertEquals(null, namelessPerson.getFirstName()); |
| 348 | + assertEquals("null", namelessPerson.getFirstName()); |
337 | 349 |
|
338 | 350 | // Change value, see that textfield is not empty and bean is updated.
|
339 | 351 | nullTextField.setValue("");
|
@@ -525,15 +537,15 @@ public void setRequired_withCustomValidator_fieldGetsRequiredIndicatorAndValidat
|
525 | 537 | binding.bind(Person::getFirstName, Person::setFirstName);
|
526 | 538 | binder.setBean(item);
|
527 | 539 | assertNull(textField.getErrorMessage());
|
528 |
| - assertEquals(0, invokes.get()); |
| 540 | + assertEquals(1, invokes.get()); |
529 | 541 |
|
530 | 542 | textField.setValue(" ");
|
531 | 543 | ErrorMessage errorMessage = textField.getErrorMessage();
|
532 | 544 | assertNotNull(errorMessage);
|
533 | 545 | assertEquals("Input is required.",
|
534 | 546 | errorMessage.getFormattedHtmlMessage());
|
535 | 547 | // validation is done for all changed bindings once.
|
536 |
| - assertEquals(1, invokes.get()); |
| 548 | + assertEquals(2, invokes.get()); |
537 | 549 |
|
538 | 550 | textField.setValue("value");
|
539 | 551 | assertNull(textField.getErrorMessage());
|
@@ -582,15 +594,15 @@ public String convertToPresentation(String value,
|
582 | 594 |
|
583 | 595 | binder.setBean(item);
|
584 | 596 | assertNull(textField.getErrorMessage());
|
585 |
| - assertEquals(0, invokes.get()); |
| 597 | + assertEquals(1, invokes.get()); |
586 | 598 |
|
587 | 599 | textField.setValue(" ");
|
588 | 600 | ErrorMessage errorMessage = textField.getErrorMessage();
|
589 | 601 | assertNotNull(errorMessage);
|
590 | 602 | assertEquals("Input required.",
|
591 | 603 | errorMessage.getFormattedHtmlMessage());
|
592 | 604 | // validation is done for all changed bindings once.
|
593 |
| - assertEquals(1, invokes.get()); |
| 605 | + assertEquals(2, invokes.get()); |
594 | 606 |
|
595 | 607 | textField.setValue("value");
|
596 | 608 | assertNull(textField.getErrorMessage());
|
@@ -1099,12 +1111,12 @@ public void info_validator_not_considered_error() {
|
1099 | 1111 |
|
1100 | 1112 | binder.setBean(item);
|
1101 | 1113 | ageField.setValue("3");
|
1102 |
| - Assert.assertEquals(infoMessage, |
| 1114 | + assertEquals(infoMessage, |
1103 | 1115 | ageField.getComponentError().getFormattedHtmlMessage());
|
1104 |
| - Assert.assertEquals(ErrorLevel.INFO, |
| 1116 | + assertEquals(ErrorLevel.INFO, |
1105 | 1117 | ageField.getComponentError().getErrorLevel());
|
1106 | 1118 |
|
1107 |
| - Assert.assertEquals(3, item.getAge()); |
| 1119 | + assertEquals(3, item.getAge()); |
1108 | 1120 | }
|
1109 | 1121 |
|
1110 | 1122 | @Test
|
@@ -1246,4 +1258,118 @@ public void valueChangeListenerOrder() {
|
1246 | 1258 |
|
1247 | 1259 | nameField.setValue("Foo");
|
1248 | 1260 | }
|
| 1261 | + |
| 1262 | + @Test |
| 1263 | + public void nonSymetricValue_setBean_writtenToBean() { |
| 1264 | + binder.bind(nameField, Person::getLastName, Person::setLastName); |
| 1265 | + |
| 1266 | + assertNull(item.getLastName()); |
| 1267 | + |
| 1268 | + binder.setBean(item); |
| 1269 | + |
| 1270 | + assertEquals("", item.getLastName()); |
| 1271 | + } |
| 1272 | + |
| 1273 | + @Test |
| 1274 | + public void nonSymmetricValue_readBean_beanNotTouched() { |
| 1275 | + binder.bind(nameField, Person::getLastName, Person::setLastName); |
| 1276 | + binder.addValueChangeListener( |
| 1277 | + event -> fail("No value change event should be fired")); |
| 1278 | + |
| 1279 | + assertNull(item.getLastName()); |
| 1280 | + |
| 1281 | + binder.readBean(item); |
| 1282 | + |
| 1283 | + assertNull(item.getLastName()); |
| 1284 | + } |
| 1285 | + |
| 1286 | + @Test |
| 1287 | + public void symetricValue_setBean_beanNotUpdated() { |
| 1288 | + binder.bind(nameField, Person::getFirstName, Person::setFirstName); |
| 1289 | + |
| 1290 | + binder.setBean(new Person() { |
| 1291 | + @Override |
| 1292 | + public String getFirstName() { |
| 1293 | + return "First"; |
| 1294 | + } |
| 1295 | + |
| 1296 | + @Override |
| 1297 | + public void setFirstName(String firstName) { |
| 1298 | + fail("Setter should not be called"); |
| 1299 | + } |
| 1300 | + }); |
| 1301 | + } |
| 1302 | + |
| 1303 | + @Test |
| 1304 | + public void nullRejetingField_nullValue_wrappedExceptionMentionsNullRepresentation() { |
| 1305 | + TextField field = createNullAnd42RejectingFieldWithEmptyValue(""); |
| 1306 | + |
| 1307 | + Binder<AtomicReference<Integer>> binder = createIntegerConverterBinder( |
| 1308 | + field); |
| 1309 | + |
| 1310 | + exceptionRule.expect(IllegalStateException.class); |
| 1311 | + exceptionRule.expectMessage("null representation"); |
| 1312 | + exceptionRule.expectCause(CoreMatchers.isA(NullPointerException.class)); |
| 1313 | + |
| 1314 | + binder.readBean(new AtomicReference<>()); |
| 1315 | + } |
| 1316 | + |
| 1317 | + |
| 1318 | + @Test |
| 1319 | + public void nullRejetingField_otherRejectedValue_originalExceptionIsThrown() { |
| 1320 | + TextField field = createNullAnd42RejectingFieldWithEmptyValue(""); |
| 1321 | + |
| 1322 | + Binder<AtomicReference<Integer>> binder = createIntegerConverterBinder( |
| 1323 | + field); |
| 1324 | + |
| 1325 | + exceptionRule.expect(IllegalArgumentException.class); |
| 1326 | + exceptionRule.expectMessage("42"); |
| 1327 | + |
| 1328 | + binder.readBean(new AtomicReference<>(Integer.valueOf(42))); |
| 1329 | + } |
| 1330 | + |
| 1331 | + @Test(expected = NullPointerException.class) |
| 1332 | + public void nullAcceptingField_nullValue_originalExceptionIsThrown() { |
| 1333 | + /* |
| 1334 | + * Edge case with a field that throws for null but has null as the empty |
| 1335 | + * value. This is most likely the case if the field doesn't explicitly |
| 1336 | + * reject null values but is instead somehow broken so that any value is |
| 1337 | + * rejected. |
| 1338 | + */ |
| 1339 | + TextField field = createNullAnd42RejectingFieldWithEmptyValue(null); |
| 1340 | + |
| 1341 | + Binder<AtomicReference<Integer>> binder = createIntegerConverterBinder( |
| 1342 | + field); |
| 1343 | + |
| 1344 | + binder.readBean(new AtomicReference<>(null)); |
| 1345 | + } |
| 1346 | + |
| 1347 | + private TextField createNullAnd42RejectingFieldWithEmptyValue( |
| 1348 | + String emptyValue) { |
| 1349 | + return new TextField() { |
| 1350 | + @Override |
| 1351 | + public void setValue(String value) { |
| 1352 | + if (value == null) { |
| 1353 | + throw new NullPointerException("Null value"); |
| 1354 | + } else if ("42".equals(value)) { |
| 1355 | + throw new IllegalArgumentException("42 is not allowed"); |
| 1356 | + } |
| 1357 | + super.setValue(value); |
| 1358 | + } |
| 1359 | + |
| 1360 | + @Override |
| 1361 | + public String getEmptyValue() { |
| 1362 | + return emptyValue; |
| 1363 | + } |
| 1364 | + }; |
| 1365 | + } |
| 1366 | + |
| 1367 | + private Binder<AtomicReference<Integer>> createIntegerConverterBinder( |
| 1368 | + TextField field) { |
| 1369 | + Binder<AtomicReference<Integer>> binder = new Binder<>(); |
| 1370 | + binder.forField(field) |
| 1371 | + .withConverter(new StringToIntegerConverter("Must have number")) |
| 1372 | + .bind(AtomicReference::get, AtomicReference::set); |
| 1373 | + return binder; |
| 1374 | + } |
1249 | 1375 | }
|
0 commit comments