Open DennisSuffel opened 4 months ago
True, the problem is here
The tester should call with introspection setModelValue(value, true) instead.
I checked this further ... It looks like it is problem with all the fields. The trouble is that not all of them are as easy to fix as the fields directly using AbstractField.setValue(..)
. Some other fields like DatePicker
, ComboBox
, Select
... are overriding this method and for those fields using the setModelValue(value, true)
is not enough as it is missing necessary things.
I first thought that I can simply write utility method that is used by all field Testers to set value, like below:
public class Utils {
public static <V> void setValueAsUser(AbstractField<?, V> component, V value) {
component.setValue(value);
Class<?> clazz = component.getClass();
while (!clazz.equals(AbstractField.class)) {
clazz = clazz.getSuperclass();
}
try {
Method setValueMethod = clazz.getDeclaredMethod("setModelValue",
Object.class, Boolean.TYPE);
setValueMethod.setAccessible(true);
setValueMethod.invoke(component, value, true);
} catch (NoSuchMethodException | SecurityException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException e) {
e.printStackTrace();
}
}
}
Which would allow me to rewrite TextFieldTester
as
public void setValue(V value) {
ensureComponentIsUsable();
if (value == null && getComponent().getEmptyValue() != null) {
throw new IllegalArgumentException(
"Field doesn't allow null values");
}
if (hasValidation() && value != null
&& getValidationSupport().isInvalid(value.toString())) {
if (getComponent().isPreventInvalidInputBoolean()) {
throw new IllegalArgumentException(
"Given value doesn't pass field value validation. Check validation settings for field.");
}
LoggerFactory.getLogger(TextFieldTester.class).warn(
"Gave invalid input, but value set as invalid input is not prevented.");
}
Utils.setValueAsUser(getComponent(), value);
}
This works for TextFieldTester
, NumberFieldTester
and TextAreaTester
... But I think not for the others.
I've used even wilder reflection which works for all AbstractFields:
public class FlowUtils {
@NotNull
private static <V> AbstractFieldSupport<?, V> getFieldSupport(@NotNull HasValue<?, V> component) {
try {
final Field javaField = AbstractField.class.getDeclaredField("fieldSupport");
javaField.setAccessible(true);
return (AbstractFieldSupport<?, V>) javaField.get(component);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
/**
* Sets the value to given component. Supports pretending that the value came from the browser.
* @param component the component to set the value to, not null.
* @param value the new value, may be null.
* @param isFromClient if true, we'll pretend that the value came from the browser. This causes the value change event to be fired
* with `isFromClient` set to true.
* @param <V> the value type
*/
public static <V> void setValue(@NotNull HasValue<?, V> component, @Nullable V value, boolean isFromClient) {
if (!isFromClient) {
component.setValue(value);
return;
}
if (component instanceof AbstractField) {
final AbstractFieldSupport<?, V> fs = getFieldSupport(component);
try {
final Method m = AbstractFieldSupport.class.getDeclaredMethod("setValue", Object.class, boolean.class, boolean.class);
m.setAccessible(true);
m.invoke(fs, value, false, isFromClient);
} catch (NoSuchMethodException | IllegalAccessException |
InvocationTargetException e) {
throw new RuntimeException(e);
}
return;
}
throw new IllegalArgumentException("Parameter component: invalid value " + component + ": unsupported type of HasValue: " + component.getClass());
}
}
I expect
ComponentEvent.isFromClient()
to return true when usingTestWrappers.test()
in aSpringUIUnitTest
to execute the action, that triggers theComponentEvent
. Instead it returns false.Steps to reproduce
MainView.java
MainViewTest.java
Expected Result
event.isFromClient()
should return true, whenTestWrappers.test()
is used to set the value of aTextField
. ThenMainViewTest#whenSetTextThenSetLabel
would complete successfuly.Actual Result
MainViewTest#whenSetTextThenSetLabel
fails with thisAssertionError
:Versions
Vaadin 24.4.6 Java 17 macOS 14.5