Browse Source

Add ErrorLevel to Validators and results (#10099)

Fixes #9792
tags/8.2.0.alpha3
Teemu Suo-Anttila 6 years ago
parent
commit
d0b5741b81

+ 1
- 0
all/src/main/templates/release-notes.html View File

@@ -115,6 +115,7 @@
<li><tt>Button</tt> has a new constructor that may cause constructor calls with null as first parameter to be ambiguous.</li>
<li><tt>DataCommunicator</tt> methods <tt>getDataProviderSize</tt> and <tt>fetchItemsWithRange</tt> are now <tt>public</tt>, not <tt>protected</tt>.</li>
<li><tt>Binder</tt> method <tt>getBindings</tt> now returns a Collection, not a Set.</li>
<li><tt>Binder</tt> protected method <tt>handleError</tt> now takes a <tt>ValidationResult</tt>, not <tt>String</tt>.</li>
<li><tt>BindingBuilder</tt> now works like a proper builder. Adding a converter will not mark Binding as <tt>bound</tt> allowing chaining to the same object.</li>
<li><tt>ErrorLevel</tt> is removed from <tt>ErrorMessage</tt> and now <tt>com.vaadin.shared.ui.ErrorLevel</tt> should be used.</li>
<li>Error indicators are now <tt>&lt;span class="v-errorindicator"&gt;&lt;/span&gt;</tt> elements.</li>

+ 104
- 17
server/src/main/java/com/vaadin/data/Binder.java View File

@@ -43,12 +43,14 @@ import com.vaadin.data.HasValue.ValueChangeListener;
import com.vaadin.data.converter.StringToIntegerConverter;
import com.vaadin.data.validator.BeanValidator;
import com.vaadin.event.EventRouter;
import com.vaadin.server.AbstractErrorMessage.ContentMode;
import com.vaadin.server.ErrorMessage;
import com.vaadin.server.SerializableFunction;
import com.vaadin.server.SerializablePredicate;
import com.vaadin.server.Setter;
import com.vaadin.server.UserError;
import com.vaadin.shared.Registration;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.ui.AbstractComponent;
import com.vaadin.ui.Component;
import com.vaadin.ui.Label;
@@ -286,6 +288,7 @@ public class Binder<BEAN> implements Serializable {
* failure, the property value is not updated.
*
* @see #withValidator(Validator)
* @see #withValidator(SerializablePredicate, String, ErrorLevel)
* @see #withValidator(SerializablePredicate, ErrorMessageProvider)
* @see Validator#from(SerializablePredicate, String)
*
@@ -303,6 +306,40 @@ public class Binder<BEAN> implements Serializable {
return withValidator(Validator.from(predicate, message));
}

/**
* A convenience method to add a validator to this binding using the
* {@link Validator#from(SerializablePredicate, String, ErrorLevel)}
* factory method.
* <p>
* Validators are applied, in registration order, when the field value
* is written to the backing property. If any validator returns a
* failure, the property value is not updated.
*
* @see #withValidator(Validator)
* @see #withValidator(SerializablePredicate, String)
* @see #withValidator(SerializablePredicate, ErrorMessageProvider,
* ErrorLevel)
* @see Validator#from(SerializablePredicate, String)
*
* @param predicate
* the predicate performing validation, not null
* @param message
* the error message to report in case validation failure
* @param errorLevel
* the error level for failures from this validator, not null
* @return this binding, for chaining
* @throws IllegalStateException
* if {@code bind} has already been called
*
* @since 8.2
*/
public default BindingBuilder<BEAN, TARGET> withValidator(
SerializablePredicate<? super TARGET> predicate, String message,
ErrorLevel errorLevel) {
return withValidator(
Validator.from(predicate, message, errorLevel));
}

/**
* A convenience method to add a validator to this binding using the
* {@link Validator#from(SerializablePredicate, ErrorMessageProvider)}
@@ -314,6 +351,8 @@ public class Binder<BEAN> implements Serializable {
*
* @see #withValidator(Validator)
* @see #withValidator(SerializablePredicate, String)
* @see #withValidator(SerializablePredicate, ErrorMessageProvider,
* ErrorLevel)
* @see Validator#from(SerializablePredicate, ErrorMessageProvider)
*
* @param predicate
@@ -331,6 +370,41 @@ public class Binder<BEAN> implements Serializable {
Validator.from(predicate, errorMessageProvider));
}

/**
* A convenience method to add a validator to this binding using the
* {@link Validator#from(SerializablePredicate, ErrorMessageProvider, ErrorLevel)}
* factory method.
* <p>
* Validators are applied, in registration order, when the field value
* is written to the backing property. If any validator returns a
* failure, the property value is not updated.
*
* @see #withValidator(Validator)
* @see #withValidator(SerializablePredicate, String, ErrorLevel)
* @see #withValidator(SerializablePredicate, ErrorMessageProvider)
* @see Validator#from(SerializablePredicate, ErrorMessageProvider,
* ErrorLevel)
*
* @param predicate
* the predicate performing validation, not null
* @param errorMessageProvider
* the provider to generate error messages, not null
* @param errorLevel
* the error level for failures from this validator, not null
* @return this binding, for chaining
* @throws IllegalStateException
* if {@code bind} has already been called
*
* @since 8.2
*/
public default BindingBuilder<BEAN, TARGET> withValidator(
SerializablePredicate<? super TARGET> predicate,
ErrorMessageProvider errorMessageProvider,
ErrorLevel errorLevel) {
return withValidator(Validator.from(predicate, errorMessageProvider,
errorLevel));
}

/**
* Maps the binding to another data type using the given
* {@link Converter}.
@@ -905,10 +979,7 @@ public class Binder<BEAN> implements Serializable {

private BindingValidationStatus<TARGET> toValidationStatus(
Result<TARGET> result) {
return new BindingValidationStatus<>(this,
result.isError()
? ValidationResult.error(result.getMessage().get())
: ValidationResult.ok());
return new BindingValidationStatus<>(result, this);
}

/**
@@ -1031,11 +1102,7 @@ public class Binder<BEAN> implements Serializable {
@Override
public Result<T> convertToModel(T value, ValueContext context) {
ValidationResult validationResult = validator.apply(value, context);
if (validationResult.isError()) {
return Result.error(validationResult.getErrorMessage());
} else {
return Result.ok(value);
}
return new ValidationResultWrap<>(value, validationResult);
}

@Override
@@ -2012,14 +2079,18 @@ public class Binder<BEAN> implements Serializable {
*
* @param field
* the field with the invalid value
* @param error
* the error message to set
* @param result
* the validation error result
*
* @since 8.2
*/
protected void handleError(HasValue<?> field, String error) {
if (field instanceof AbstractComponent) {
((AbstractComponent) field).setComponentError(new UserError(error));
}

protected void handleError(HasValue<?> field, ValidationResult result) {
result.getErrorLevel().ifPresent(level -> {
if (field instanceof AbstractComponent) {
((AbstractComponent) field).setComponentError(new UserError(
result.getErrorMessage(), ContentMode.TEXT, level));
}
});
}

/**
@@ -2033,7 +2104,23 @@ public class Binder<BEAN> implements Serializable {
HasValue<?> source = status.getField();
clearError(source);
if (status.isError()) {
handleError(source, status.getMessage().get());
Optional<ValidationResult> firstError = status
.getValidationResults().stream()
.filter(ValidationResult::isError).findFirst();
if (firstError.isPresent()) {
// Failed with a Validation error
handleError(source, firstError.get());
} else {
// Conversion error
status.getResult()
.ifPresent(result -> handleError(source, result));
}
} else {
// Show first non-error ValidationResult message.
status.getValidationResults().stream()
.filter(result -> result.getErrorLevel().isPresent())
.findFirst()
.ifPresent(result -> handleError(source, result));
}
}


+ 63
- 17
server/src/main/java/com/vaadin/data/BindingValidationStatus.java View File

@@ -16,6 +16,8 @@
package com.vaadin.data;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

@@ -24,8 +26,8 @@ import com.vaadin.data.Binder.BindingBuilder;

/**
* Represents the status of field validation. Status can be {@code Status.OK},
* {@code Status.ERROR} or {@code Status.RESET}. Status OK and ERROR are always
* associated with a ValidationResult {@link #getResult}.
* {@code Status.ERROR} or {@code Status.UNRESOLVED}. Status OK and ERROR are
* always associated with a ValidationResult {@link #getResult}.
* <p>
* Use
* {@link BindingBuilder#withValidationStatusHandler(BindingValidationStatusHandler)}
@@ -70,8 +72,9 @@ public class BindingValidationStatus<TARGET> implements Serializable {
}

private final Status status;
private final ValidationResult result;
private final List<ValidationResult> results;
private final Binding<?, TARGET> binding;
private Result<TARGET> result;

/**
* Convenience method for creating a {@link Status#UNRESOLVED} validation
@@ -86,7 +89,7 @@ public class BindingValidationStatus<TARGET> implements Serializable {
*/
public static <TARGET> BindingValidationStatus<TARGET> createUnresolvedStatus(
Binding<?, TARGET> source) {
return new BindingValidationStatus<>(source, Status.UNRESOLVED, null);
return new BindingValidationStatus<TARGET>(null, source);
}

/**
@@ -98,6 +101,7 @@ public class BindingValidationStatus<TARGET> implements Serializable {
* @param result
* the result of the validation
*/
@Deprecated
public BindingValidationStatus(Binding<?, TARGET> source,
ValidationResult result) {
this(source, result.isError() ? Status.ERROR : Status.OK, result);
@@ -116,20 +120,43 @@ public class BindingValidationStatus<TARGET> implements Serializable {
* @param result
* the related result, may be {@code null}
*/
@Deprecated
public BindingValidationStatus(Binding<?, TARGET> source, Status status,
ValidationResult result) {
this(result.isError() ? Result.error(result.getErrorMessage())
: Result.ok(null), source);
}

/**
* Creates a new status change event.
* <p>
* If {@code result} is {@code null}, the {@code status} is
* {@link Status#UNRESOLVED}.
*
* @param result
* the related result object, may be {@code null}
* @param source
* field whose status has changed, not {@code null}
*
* @since 8.2
*/
public BindingValidationStatus(Result<TARGET> result,
Binding<?, TARGET> source) {
Objects.requireNonNull(source, "Event source may not be null");
Objects.requireNonNull(status, "Status may not be null");
if (Objects.equals(status, Status.OK) && result.isError()
|| Objects.equals(status, Status.ERROR) && !result.isError()
|| Objects.equals(status, Status.UNRESOLVED)
&& result != null) {
throw new IllegalStateException(
"Invalid validation status " + status + " for given result "
+ (result == null ? "null" : result.toString()));
}

binding = source;
this.status = status;
if (result != null) {
this.status = result.isError() ? Status.ERROR : Status.OK;
if (result instanceof ValidationResultWrap) {
results = ((ValidationResultWrap<TARGET>) result)
.getValidationResults();
} else {
results = Collections.emptyList();
}
} else {
this.status = Status.UNRESOLVED;
results = Collections.emptyList();
}
this.result = result;
}

@@ -159,8 +186,10 @@ public class BindingValidationStatus<TARGET> implements Serializable {
* status is not an error
*/
public Optional<String> getMessage() {
return Optional.ofNullable(result).filter(ValidationResult::isError)
.map(ValidationResult::getErrorMessage);
if (getStatus() == Status.OK || result == null) {
return Optional.empty();
}
return result.getMessage();
}

/**
@@ -171,7 +200,24 @@ public class BindingValidationStatus<TARGET> implements Serializable {
* @return the validation result
*/
public Optional<ValidationResult> getResult() {
return Optional.ofNullable(result);
if (result == null) {
return Optional.empty();
}
return Optional.of(result.isError()
? ValidationResult.error(result.getMessage().orElse(""))
: ValidationResult.ok());
}

/**
* Gets all the validation results related to this binding validation
* status.
*
* @return list of validation results
*
* @since 8.2
*/
public List<ValidationResult> getValidationResults() {
return Collections.unmodifiableList(results);
}

/**

+ 67
- 10
server/src/main/java/com/vaadin/data/ValidationResult.java View File

@@ -17,6 +17,9 @@ package com.vaadin.data;

import java.io.Serializable;
import java.util.Objects;
import java.util.Optional;

import com.vaadin.shared.ui.ErrorLevel;

/**
* Represents the result of a validation. A result may be either successful or
@@ -35,26 +38,30 @@ public interface ValidationResult extends Serializable {
class SimpleValidationResult implements ValidationResult {

private final String error;
private final ErrorLevel errorLevel;

SimpleValidationResult(String error) {
SimpleValidationResult(String error, ErrorLevel errorLevel) {
if (error != null && errorLevel == null) {
throw new IllegalStateException("ValidationResult has an "
+ "error message, but no ErrorLevel is provided.");
}
this.error = error;
this.errorLevel = errorLevel;
}

@Override
public String getErrorMessage() {
if (error == null) {
if (!getErrorLevel().isPresent()) {
throw new IllegalStateException("The result is not an error. "
+ "It cannot contain error message");
} else {
return error;
return error != null ? error : "";
}
}

@Override
public boolean isError() {
return error != null;
public Optional<ErrorLevel> getErrorLevel() {
return Optional.ofNullable(errorLevel);
}

}

/**
@@ -68,13 +75,37 @@ public interface ValidationResult extends Serializable {
*/
String getErrorMessage();

/**
* Returns optional error level for this validation result. Error level is
* not present for successful validation results.
* <p>
* <strong>Note:</strong> By default {@link ErrorLevel#INFO} and
* {@link ErrorLevel#WARNING} are not considered to be blocking the
* validation and conversion chain.
*
* @see #isError()
*
* @return optional error level; error level is present for validation
* results that have not passed validation
*
* @since 8.2
*/
Optional<ErrorLevel> getErrorLevel();

/**
* Checks if the result denotes an error.
* <p>
* <strong>Note:</strong> By default {@link ErrorLevel#INFO} and
* {@link ErrorLevel#WARNING} are not considered to be errors.
*
* @return <code>true</code> if the result denotes an error,
* <code>false</code> otherwise
*/
boolean isError();
default boolean isError() {
ErrorLevel errorLevel = getErrorLevel().orElse(null);
return errorLevel != null && errorLevel != ErrorLevel.INFO
&& errorLevel != ErrorLevel.WARNING;
}

/**
* Returns a successful result.
@@ -82,7 +113,7 @@ public interface ValidationResult extends Serializable {
* @return the successful result
*/
public static ValidationResult ok() {
return new SimpleValidationResult(null);
return new SimpleValidationResult(null, null);
}

/**
@@ -98,6 +129,32 @@ public interface ValidationResult extends Serializable {
*/
public static ValidationResult error(String errorMessage) {
Objects.requireNonNull(errorMessage);
return new SimpleValidationResult(errorMessage);
return create(errorMessage, ErrorLevel.ERROR);
}

/**
* Creates the validation result with the given {@code errorMessage} and
* {@code errorLevel}. Results with {@link ErrorLevel} of {@code INFO} or
* {@code WARNING} are not errors by default.
*
* @see #ok()
* @see #error(String)
*
* @param errorMessage
* error message, not {@code null}
* @param errorLevel
* error level, not {@code null}
* @return validation result with the given {@code errorMessage} and
* {@code errorLevel}
* @throws NullPointerException
* if {@code errorMessage} or {@code errorLevel} is {@code null}
*
* @since 8.2
*/
public static ValidationResult create(String errorMessage,
ErrorLevel errorLevel) {
Objects.requireNonNull(errorMessage);
Objects.requireNonNull(errorLevel);
return new SimpleValidationResult(errorMessage, errorLevel);
}
}

+ 102
- 0
server/src/main/java/com/vaadin/data/ValidationResultWrap.java View File

@@ -0,0 +1,102 @@
/*
* Copyright 2000-2016 Vaadin Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.vaadin.data;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import com.vaadin.server.SerializableConsumer;
import com.vaadin.server.SerializableFunction;

/**
* Internal implementation of a {@code Result} that collects all possible
* ValidationResults into one list. This class intercepts the normal chaining of
* Converters and Validators, catching and collecting results.
*
* @param <R>
* the result data type
*
* @since 8.2
*/
class ValidationResultWrap<R> implements Result<R> {

private final List<ValidationResult> resultList;
private final Result<R> wrappedResult;

ValidationResultWrap(Result<R> result, List<ValidationResult> resultList) {
this.resultList = resultList;
this.wrappedResult = result;
}

ValidationResultWrap(R value, ValidationResult result) {
if (result.isError()) {
wrappedResult = new SimpleResult<>(null, result.getErrorMessage());
} else {
wrappedResult = new SimpleResult<>(value, null);
}
this.resultList = new ArrayList<>();
this.resultList.add(result);
}

List<ValidationResult> getValidationResults() {
return Collections.unmodifiableList(resultList);
}

Result<R> getWrappedResult() {
return wrappedResult;
}

@Override
public <S> Result<S> flatMap(SerializableFunction<R, Result<S>> mapper) {
Result<S> result = wrappedResult.flatMap(mapper);
if (!(result instanceof ValidationResultWrap)) {
return new ValidationResultWrap<S>(result, resultList);
}

List<ValidationResult> currentResults = new ArrayList<>(resultList);
ValidationResultWrap<S> resultWrap = (ValidationResultWrap<S>) result;
currentResults.addAll(resultWrap.getValidationResults());

return new ValidationResultWrap<>(resultWrap.getWrappedResult(),
currentResults);
}

@Override
public void handle(SerializableConsumer<R> ifOk,
SerializableConsumer<String> ifError) {
wrappedResult.handle(ifOk, ifError);
}

@Override
public boolean isError() {
return wrappedResult.isError();
}

@Override
public Optional<String> getMessage() {
return wrappedResult.getMessage();
}

@Override
public <X extends Throwable> R getOrThrow(
SerializableFunction<String, ? extends X> exceptionProvider)
throws X {
return wrappedResult.getOrThrow(exceptionProvider);
}

}

+ 64
- 7
server/src/main/java/com/vaadin/data/Validator.java View File

@@ -21,6 +21,7 @@ import java.util.Objects;
import java.util.function.BiFunction;

import com.vaadin.server.SerializablePredicate;
import com.vaadin.shared.ui.ErrorLevel;

/**
* A functional interface for validating user input or other potentially invalid
@@ -80,8 +81,8 @@ public interface Validator<T>
* Builds a validator out of a conditional function and an error message. If
* the function returns true, the validator returns {@code Result.ok()}; if
* it returns false or throws an exception,
* {@link ValidationResult#error(String)} is returned with the given
* message.
* {@link ValidationResult#error(String)} is returned with the given message
* and error level {@link ErrorLevel#ERROR}.
* <p>
* For instance, the following validator checks if a number is between 0 and
* 10, inclusive:
@@ -101,11 +102,43 @@ public interface Validator<T>
*/
public static <T> Validator<T> from(SerializablePredicate<T> guard,
String errorMessage) {
Objects.requireNonNull(guard, "guard cannot be null");
Objects.requireNonNull(errorMessage, "errorMessage cannot be null");
return from(guard, ctx -> 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
* it returns false or throws an exception,
* {@link ValidationResult#error(String)} is returned with the given message
* and error level.
* <p>
* For instance, the following validator checks if a number is between 0 and
* 10, inclusive:
*
* <pre>
* Validator&lt;Integer&gt; v = Validator.from(num -&gt; num &gt;= 0 && num &lt;= 10,
* "number must be between 0 and 10", ErrorLevel.ERROR);
* </pre>
*
* @param <T>
* the value type
* @param guard
* the function used to validate, not null
* @param errorMessage
* the message returned if validation fails, not null
* @param errorLevel
* the error level for failures from this validator, not null
* @return the new validator using the function
*
* @since 8.2
*/
public static <T> Validator<T> from(SerializablePredicate<T> guard,
String errorMessage, ErrorLevel errorLevel) {
Objects.requireNonNull(errorMessage, "errorMessage cannot be null");
return from(guard, ctx -> errorMessage, errorLevel);
}

/**
* Builds a validator out of a conditional function and an error message
* provider. If the function returns true, the validator returns
@@ -122,20 +155,44 @@ public interface Validator<T>
*/
public static <T> Validator<T> from(SerializablePredicate<T> guard,
ErrorMessageProvider errorMessageProvider) {
return from(guard, errorMessageProvider, ErrorLevel.ERROR);
}

/**
* Builds a validator out of a conditional function and an error message
* provider. If the function returns true, the validator returns
* {@code Result.ok()}; if it returns false or throws an exception,
* {@code Result.error()} is returned with the message from the provider.
*
* @param <T>
* the value type
* @param guard
* the function used to validate, not null
* @param errorMessageProvider
* the provider to generate error messages, not null
* @param errorLevel
* the error level for failures from this validator, not null
* @return the new validator using the function
*
* @since 8.2
*/
public static <T> Validator<T> from(SerializablePredicate<T> guard,
ErrorMessageProvider errorMessageProvider, ErrorLevel errorLevel) {
Objects.requireNonNull(guard, "guard cannot be null");
Objects.requireNonNull(errorMessageProvider,
"errorMessageProvider cannot be null");
Objects.requireNonNull(errorLevel, "errorLevel cannot be null");
return (value, context) -> {
try {
if (guard.test(value)) {
return ValidationResult.ok();
} else {
return ValidationResult
.error(errorMessageProvider.apply(context));
return ValidationResult.create(
errorMessageProvider.apply(context), errorLevel);
}
} catch (Exception e) {
return ValidationResult
.error(errorMessageProvider.apply(context));
return ValidationResult.create(
errorMessageProvider.apply(context), errorLevel);
}
};
}

+ 20
- 0
server/src/test/java/com/vaadin/data/BinderTest.java View File

@@ -14,6 +14,7 @@ import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;

import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

@@ -24,6 +25,7 @@ import com.vaadin.data.validator.IntegerRangeValidator;
import com.vaadin.data.validator.NotEmptyValidator;
import com.vaadin.data.validator.StringLengthValidator;
import com.vaadin.server.ErrorMessage;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.tests.data.bean.Person;
import com.vaadin.tests.data.bean.Sex;
import com.vaadin.ui.TextField;
@@ -942,6 +944,24 @@ public class BinderTest extends BinderTestBase<Binder<Person>, Person> {
ageBeforeUnbind, String.valueOf(item.getAge()));
}

@Test
public void info_validator_not_considered_error() {
String infoMessage = "Young";
binder.forField(ageField)
.withConverter(new StringToIntegerConverter("Can't convert"))
.withValidator(i -> i > 5, infoMessage, ErrorLevel.INFO)
.bind(Person::getAge, Person::setAge);

binder.setBean(item);
ageField.setValue("3");
Assert.assertEquals(infoMessage,
ageField.getComponentError().getFormattedHtmlMessage());
Assert.assertEquals(ErrorLevel.INFO,
ageField.getComponentError().getErrorLevel());

Assert.assertEquals(3, item.getAge());
}

@Test
public void two_asRequired_fields_without_initial_values() {
binder.forField(nameField).asRequired("Empty name").bind(p -> "",

+ 1
- 1
tests/screenshots

@@ -1 +1 @@
Subproject commit 93a7b5d5a00209767b54a69395b1a7cc30ee970d
Subproject commit a20d0108ad315b1b5a4626619cd157e03cfa7b7f

+ 29
- 0
uitest/src/main/java/com/vaadin/tests/binder/BinderValidatorErrorLevel.java View File

@@ -0,0 +1,29 @@
package com.vaadin.tests.binder;

import com.vaadin.annotations.Widgetset;
import com.vaadin.data.Binder;
import com.vaadin.server.VaadinRequest;
import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.tests.components.AbstractTestUI;
import com.vaadin.ui.TextField;

@Widgetset("com.vaadin.DefaultWidgetSet")
public class BinderValidatorErrorLevel extends AbstractTestUI {

@Override
protected void setup(VaadinRequest request) {
Binder<Object> binder = new Binder<>();

for (ErrorLevel l : ErrorLevel.values()) {
TextField field = new TextField(l.name());
binder.forField(field)
.withValidator(s -> s.length() > 3,
"ErrorLevel: " + l.name(), l)
.bind(t -> "", (t, s) -> {
});
addComponent(field);
}
binder.validate();
}

}

+ 41
- 0
uitest/src/test/java/com/vaadin/tests/binder/BinderValidatorErrorLevelTest.java View File

@@ -0,0 +1,41 @@
package com.vaadin.tests.binder;

import java.io.IOException;

import org.junit.Assert;
import org.junit.Test;

import com.vaadin.shared.ui.ErrorLevel;
import com.vaadin.testbench.By;
import com.vaadin.testbench.elements.TextFieldElement;
import com.vaadin.tests.tb3.SingleBrowserTest;

public class BinderValidatorErrorLevelTest extends SingleBrowserTest {

@Test
public void testErrorLevelStyleNames() throws IOException {
openTestURL();

for (ErrorLevel l : ErrorLevel.values()) {
TextFieldElement textField = $(TextFieldElement.class)
.caption(l.name()).first();

// Screenshot the whole slot
compareScreen(textField.findElement(By.xpath("..")),
l.name().toLowerCase());

Assert.assertTrue("Error style for " + l.name() + " not present",
textField.getAttribute("class").contains(
"v-textfield-error-" + l.name().toLowerCase()));
textField.setValue("long enough text");
Assert.assertFalse("Error style for " + l.name() + " still present",
textField.getAttribute("class").contains(
"v-textfield-error-" + l.name().toLowerCase()));
textField.setValue("foo");
Assert.assertTrue(
"Error style for " + l.name() + " should be present again.",
textField.getAttribute("class").contains(
"v-textfield-error-" + l.name().toLowerCase()));
}
}
}

Loading…
Cancel
Save