Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Binder.validate() no longer fails with "bean level validators have been configured but no bean is currently set" when in buffered mode #18121

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2586,16 +2586,11 @@ public BinderValidationStatus<BEAN> validate() {
*
*/
protected BinderValidationStatus<BEAN> validate(boolean fireEvent) {
if (getBean() == null && !validators.isEmpty()) {
throw new IllegalStateException("Cannot validate binder: "
+ "bean level validators have been configured "
+ "but no bean is currently set");
}
List<BindingValidationStatus<?>> bindingStatuses = validateBindings();

BinderValidationStatus<BEAN> validationStatus;
if (validators.isEmpty() || bindingStatuses.stream()
.anyMatch(BindingValidationStatus::isError)) {
if (validators.isEmpty() || getBean() == null || bindingStatuses
.stream().anyMatch(BindingValidationStatus::isError)) {
validationStatus = new BinderValidationStatus<>(this,
bindingStatuses, Collections.emptyList());
} else {
Expand Down
12 changes: 12 additions & 0 deletions flow-data/src/main/java/com/vaadin/flow/data/binder/Validator.java
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ static <T> Validator<T> alwaysPass() {
return (value, context) -> ValidationResult.ok();
}

/**
* Returns a validator that fails on any value.
*
* @param <T>
* the value type
* @return an always-failing validator
*/
static <T> Validator<T> alwaysFail(String errorMessage) {
Objects.requireNonNull(errorMessage);
return (value, context) -> ValidationResult.error(errorMessage);
}

/**
* Builds a validator out of a conditional function and an error message. If
* the function returns true, the validator returns {@code Result.ok()}; if
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -469,22 +469,27 @@ public void save_fieldValidationErrors() throws ValidationException {
}
}

@Test(expected = ValidationException.class)
public void save_beanValidationErrors() throws ValidationException {
@Test
public void save_beanValidationErrors() {
Binder<Person> binder = new Binder<>();
binder.forField(nameField).withValidator(new NotEmptyValidator<>("a"))
.bind(Person::getFirstName, Person::setFirstName);

binder.withValidator(Validator.from(person -> false, "b"));
binder.withValidator(Validator.alwaysFail("b"));

Person person = new Person();
nameField.setValue("foo");
try {
binder.writeBean(person);
} finally {
// Bean should have been updated for item validation but reverted
assertNull(person.getFirstName());
Assert.fail("Validation should have failed but it passed instead");
} catch (ValidationException ex) {
// writeBean() should run bean validations
Assert.assertEquals(1, ex.getBeanValidationErrors().size());
// field validations pass
Assert.assertEquals(0, ex.getFieldValidationErrors().size());
}
// Bean should have been updated for item validation but reverted
assertNull(person.getFirstName());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1147,14 +1147,14 @@ public void isValidTest_unbound_binder() {
Assert.assertTrue(binder.isValid());
}

@Test(expected = IllegalStateException.class)
public void isValidTest_unbound_binder_throws_with_bean_level_validation() {
@Test
public void isValidTest_unbound_binder_passes_with_bean_level_validation() {
binder.forField(nameField).bind(Person::getFirstName,
Person::setFirstName);
binder.withValidator(Validator.from(
person -> !person.getFirstName().equals("fail bean validation"),
""));
binder.isValid();
Assert.assertTrue(binder.isValid());
}

@Test
Expand Down Expand Up @@ -1433,20 +1433,22 @@ public void beanvalidation_initially_broken_bean() {
Assert.assertFalse(binder.validate().isOk());
}

@Test(expected = IllegalStateException.class)
public void beanvalidation_isValid_throws_with_readBean() {
@Test
public void beanvalidation_isValid_passes_with_readBean() {
TestTextField lastNameField = new TestTextField();
setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
binder.withValidator(Validator.alwaysFail("fail"));

binder.readBean(item);

Assert.assertTrue(binder.isValid());
}

@Test(expected = IllegalStateException.class)
public void beanvalidation_validate_throws_with_readBean() {
@Test
public void beanvalidation_validate_passes_with_readBean() {
TestTextField lastNameField = new TestTextField();
setBeanValidationFirstNameNotEqualsLastName(nameField, lastNameField);
binder.withValidator(Validator.alwaysFail("fail"));

binder.readBean(item);

Expand Down
Loading