Skip to content

Commit

Permalink
Replaced HasLitRenderer with LitRendererTestUtils and associated refa…
Browse files Browse the repository at this point in the history
…ctorings.
  • Loading branch information
joelpop committed May 21, 2024
1 parent 4be5de1 commit 7ed37e5
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 108 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
package com.vaadin.flow.component.grid;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasLitRenderer;
import com.vaadin.flow.data.provider.SortDirection;
import com.vaadin.flow.data.provider.SortOrder;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.data.renderer.LitRenderer;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.testbench.unit.ComponentTester;
import com.vaadin.testbench.unit.LitRendererTestUtil;
import com.vaadin.testbench.unit.MetaKeys;
import com.vaadin.testbench.unit.MouseButton;
import com.vaadin.testbench.unit.Tests;
Expand All @@ -38,8 +38,7 @@
* item type
*/
@Tests(fqn = { "com.vaadin.flow.component.grid.Grid" })
public class GridTester<T extends Grid<Y>, Y> extends ComponentTester<T>
implements HasLitRenderer<Y> {
public class GridTester<T extends Grid<Y>, Y> extends ComponentTester<T> {
/**
* Wrap grid for testing.
*
Expand Down Expand Up @@ -320,7 +319,7 @@ private <V> V getLitRendererPropertyValue(int row, Grid.Column<Y> column,
ensureVisible();

if (column.getRenderer() instanceof LitRenderer<Y> litRenderer) {
return getLitRendererPropertyValue(row, propertyName, propertyClass,
return LitRendererTestUtil.getPropertyValue(row, propertyName, propertyClass,
this::getField, litRenderer, this::getRow);
} else {
throw new IllegalArgumentException(
Expand Down Expand Up @@ -378,7 +377,7 @@ private void invokeLitRendererFunction(int row, Grid.Column<Y> column, String fu
ensureVisible();

if (column.getRenderer() instanceof LitRenderer<Y> litRenderer) {
invokeLitRendererFunction(row, functionName, jsonArray,
LitRendererTestUtil.invokeFunction(row, functionName, jsonArray,
this::getField, litRenderer, this::getRow);
} else {
throw new IllegalArgumentException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
package com.vaadin.flow.component.virtuallist;

import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.HasLitRenderer;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.data.renderer.ComponentRenderer;
import com.vaadin.flow.data.renderer.LitRenderer;
import com.vaadin.flow.data.renderer.Renderer;
import com.vaadin.testbench.unit.ComponentTester;
import com.vaadin.testbench.unit.LitRendererTestUtil;
import com.vaadin.testbench.unit.Tests;
import elemental.json.Json;
import elemental.json.JsonArray;
Expand All @@ -30,8 +30,7 @@
* value type
*/
@Tests(VirtualList.class)
public class VirtualListTester<T extends VirtualList<Y>, Y> extends ComponentTester<T>
implements HasLitRenderer<Y> {
public class VirtualListTester<T extends VirtualList<Y>, Y> extends ComponentTester<T> {

// don't use a value too large
// otherwise Vaadin 19+ will calculate a negative limit
Expand Down Expand Up @@ -107,9 +106,9 @@ public String getItemText(int index) {

// use LitRenderer label if renderer is ValueProvider (i.e., has a single property "label")
if (itemRenderer instanceof LitRenderer<Y> litRenderer) {
if ((getLitRendererProperties(litRenderer, this::getField).stream()
if ((LitRendererTestUtil.getProperties(litRenderer, this::getField).stream()
.allMatch(propertyName -> propertyName.equals("label"))) &&
(getLitRendererFunctionNames(litRenderer, this::getField).isEmpty())) {
(LitRendererTestUtil.getFunctionNames(litRenderer, this::getField).isEmpty())) {
return getLitRendererPropertyValue(index, "label", String.class);
} else {
throw new UnsupportedOperationException(
Expand Down Expand Up @@ -172,7 +171,7 @@ public <V> V getLitRendererPropertyValue(int index,
ensureVisible();

if (getItemRenderer() instanceof LitRenderer<Y> litRenderer) {
return getLitRendererPropertyValue(index, propertyName, propertyClass,
return LitRendererTestUtil.getPropertyValue(index, propertyName, propertyClass,
this::getField, litRenderer, this::getItem);
} else {
throw new IllegalArgumentException(
Expand All @@ -196,7 +195,7 @@ public void invokeLitRendererFunction(int index, String functionName, JsonArray
ensureVisible();

if (getItemRenderer() instanceof LitRenderer<Y> litRenderer) {
invokeLitRendererFunction(index, functionName, jsonArray,
LitRendererTestUtil.invokeFunction(index, functionName, jsonArray,
this::getField, litRenderer, this::getItem);
} else {
throw new IllegalArgumentException(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* Copyright (C) 2000-2024 Vaadin Ltd
*
* This program is available under Vaadin Commercial License and Service Terms.
*
* See <https://vaadin.com/commercial-license-and-service-terms> for the full
* license.
*/
package com.vaadin.testbench.unit;

import com.vaadin.flow.data.renderer.LitRenderer;
import com.vaadin.flow.function.SerializableBiConsumer;
import com.vaadin.flow.function.ValueProvider;
import elemental.json.JsonArray;

import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.IntFunction;

/**
* Utility methods for unit testing properties and functions of LitRenderers.
*/
public class LitRendererTestUtil {

private LitRendererTestUtil() throws InstantiationException {
throw new InstantiationException(LitRendererTestUtil.class.getName() + " should not be instantiated");
}

/**
* Gets the property names for the supplied {@link LitRenderer} using the given field getter.
*
* @param litRenderer the LitRenderer with properties to get
* @param fieldGetter the field getter of the ComponentTester
* @return the set of property names of the LitRenderer
* @param <Y> the type being renderer by the LitRenderer
*/
public static <Y> Set<String> getProperties(LitRenderer<Y> litRenderer,
BiFunction<Class<?>, String, Field> fieldGetter) {
var valueProvidersField = fieldGetter.apply(LitRenderer.class, "valueProviders");
try {
@SuppressWarnings("unchecked")
var valueProviders = (Map<String, ValueProvider<Y, ?>>) valueProvidersField.get(litRenderer);
return valueProviders.keySet();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

private static <Y> ValueProvider<Y, ?> findProperty(LitRenderer<Y> litRenderer, String propertyName,
BiFunction<Class<?>, String, Field> fieldGetter) {
var valueProvidersField = fieldGetter.apply(LitRenderer.class, "valueProviders");
try {
@SuppressWarnings("unchecked")
var valueProviders = (Map<String, ValueProvider<Y, ?>>) valueProvidersField.get(litRenderer);
var valueProvider = valueProviders.get(propertyName);
if (valueProvider == null) {
throw new IllegalArgumentException("Property " + propertyName + " is not registered in LitRenderer.");
}
return valueProvider;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

/**
* Gets the property value for the supplied {@link LitRenderer}.
*
* @param index the index of the item rendered by the LitRenderer
* @param propertyName the name of the property of the LitRenderer
* @param propertyClass the type of the property value
* @param fieldGetter the field getter of the ComponentTester
* @param litRenderer the LitRenderer with properties to get
* @param itemGetter the getter for the item rendered by the LitRenderer
* @return the property value
* @param <Y> the type being renderer by the LitRenderer
* @param <V> the type of the property value
*/
public static <Y, V> V getPropertyValue(int index,
String propertyName, Class<V> propertyClass,
BiFunction<Class<?>, String, Field> fieldGetter,
LitRenderer<Y> litRenderer,
IntFunction<Y> itemGetter) {
var valueProvider = findProperty(litRenderer, propertyName, fieldGetter);
var untypedValue = valueProvider.apply(itemGetter.apply(index));
if (propertyClass.isInstance(untypedValue)) {
return propertyClass.cast(untypedValue);
} else {
throw new IllegalArgumentException("Type of property value does not match propertyClass - expected %s, found %s."
.formatted(propertyClass.getCanonicalName(), untypedValue.getClass().getCanonicalName()));
}
}


/**
* Gets the function names for the supplied {@link LitRenderer} using the given field getter.
*
* @param litRenderer the LitRenderer with properties to get
* @param fieldGetter the field getter of the ComponentTester
* @return the set of function names of the LitRenderer
* @param <Y> the type being renderer by the LitRenderer
*/
public static <Y> Set<String> getFunctionNames(LitRenderer<Y> litRenderer,
BiFunction<Class<?>, String, Field> fieldGetter) {
var clientCallablesField = fieldGetter.apply(LitRenderer.class, "clientCallables");
try {
@SuppressWarnings("unchecked")
var clientCallables = (Map<String, SerializableBiConsumer<Y, JsonArray>>) clientCallablesField.get(litRenderer);
return clientCallables.keySet();
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

private static <Y> SerializableBiConsumer<Y, JsonArray> findFunction(LitRenderer<Y> litRenderer, String functionName,
BiFunction<Class<?>, String, Field> fieldGetter) {
var clientCallablesField = fieldGetter.apply(LitRenderer.class, "clientCallables");
try {
@SuppressWarnings("unchecked")
var clientCallables = (Map<String, SerializableBiConsumer<Y, JsonArray>>) clientCallablesField.get(litRenderer);
var callable = clientCallables.get(functionName);
if (callable == null) {
throw new IllegalArgumentException("Function " + functionName + " is not registered in LitRenderer.");
}
return callable;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}

/**
* Invokes the function by name for the supplied {@link LitRenderer} using the given field getter.
*
* @param index the index of the item rendered by the LitRenderer
* @param functionName the name of the function of the LitRenderer
* @param jsonArray additional parameters to pass to the function
* @param fieldGetter the field getter of the ComponentTester
* @param litRenderer the LitRenderer with properties to get
* @param itemGetter the getter for the item rendered by the LitRenderer
* @param <Y> the type being renderer by the LitRenderer
*/
public static <Y> void invokeFunction(int index, String functionName, JsonArray jsonArray,
BiFunction<Class<?>, String, Field> fieldGetter,
LitRenderer<Y> litRenderer,
IntFunction<Y> itemGetter) {
var callable = findFunction(litRenderer, functionName, fieldGetter);
callable.accept(itemGetter.apply(index), jsonArray);
}

}

0 comments on commit 7ed37e5

Please sign in to comment.