diff --git a/vaadin-testbench-unit-junit5/pom.xml b/vaadin-testbench-unit-junit5/pom.xml index 7485f4710..2fd0ae395 100644 --- a/vaadin-testbench-unit-junit5/pom.xml +++ b/vaadin-testbench-unit-junit5/pom.xml @@ -30,8 +30,8 @@ - 1.6.21 - 1.6.21 + 1.9.20 + 1.9.20 5.9.1 diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/CallbackLitRendererVirtualListTesterTest.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/CallbackLitRendererVirtualListTesterTest.java new file mode 100644 index 000000000..c209f3795 --- /dev/null +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/CallbackLitRendererVirtualListTesterTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2000-2024 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.virtuallist; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@ViewPackages +class CallbackLitRendererVirtualListTesterTest extends UIUnitTest { + + private VirtualListTester, User> $virtualList; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(CallbackLitRendererVirtualListView.class); + + var view = navigate(CallbackLitRendererVirtualListView.class); + $virtualList = test(view.callbackLitRendererVirtualList); + } + + @Test + void virtualList_initTester() { + Assertions.assertNotNull($virtualList, + "Tester for callback lit renderer VirtualList not initialized."); + } + + @Test + void getLitRendererPropertyValue_propertyValuesEqual() { + var index = UserData.getAnyValidIndex(); + var user = UserData.get(index); + + var firstName = $virtualList.getLitRendererPropertyValue(index, + "firstName", String.class); + Assertions.assertEquals(user.getFirstName(), firstName); + + var lastName = $virtualList.getLitRendererPropertyValue(index, + "lastName", String.class); + Assertions.assertEquals(user.getLastName(), lastName); + + var active = $virtualList.getLitRendererPropertyValue(index, + "active", Boolean.class); + Assertions.assertEquals(user.isActive(), active); + } + + @Test + void getLitRendererPropertyValue_nonexistentPropertyFails() { + var index = UserData.getAnyValidIndex(); + Assertions.assertThrows(IllegalArgumentException.class, + () -> $virtualList.getLitRendererPropertyValue(index, + "nonexistent", String.class), + "Nonexistent property request should throw an exception"); + } + + @Test + void getLitRendererPropertyValue_outOfBoundsIndexFails() { + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> $virtualList.getLitRendererPropertyValue( -1, + "firstName", String.class), + "VirtualList index out of bounds (low)"); + + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> $virtualList.getLitRendererPropertyValue(UserData.USER_COUNT, + "firstName", String.class), + "VirtualList index out of bounds (high)"); + } + + @Test + void getLitRendererPropertyValue_hiddenFails() { + $virtualList.getComponent().setVisible(false); + + var index = UserData.getAnyValidIndex(); + Assertions.assertThrows(IllegalStateException.class, + () -> $virtualList.getLitRendererPropertyValue(index, + "firstName", String.class), + "Tester should not be accessible for hidden virtual list"); + } + + @Test + void invokeLitRendererFunction_actionSucceeds() { + var index = UserData.getAnyValidIndex(); + var user = UserData.get(index); + + var originalActive = user.isActive(); + + var beforeActive = $virtualList.getLitRendererPropertyValue(index, + "active", Boolean.class); + Assertions.assertEquals(user.isActive(), beforeActive); + + $virtualList.invokeLitRendererFunction(index, "onActiveToggleClick"); + + var afterActive = $virtualList.getLitRendererPropertyValue(index, + "active", Boolean.class); + Assertions.assertEquals(user.isActive(), afterActive); + + Assertions.assertEquals(!originalActive, user.isActive()); + } + + @Test + void invokeLitRendererFunction_nonexistentFunctionFails() { + var index = UserData.getAnyValidIndex(); + Assertions.assertThrows(IllegalArgumentException.class, + () -> $virtualList.invokeLitRendererFunction(index, + "nonexistent"), + "Nonexistent function invocation should throw an exception"); + } + + @Test + void invokeLitRendererFunction_outOfBoundsIndexFails() { + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> $virtualList.invokeLitRendererFunction( -1, + "onActiveToggleClick"), + "VirtualList index out of bounds (low)"); + + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> $virtualList.invokeLitRendererFunction(UserData.USER_COUNT, + "onActiveToggleClick"), + "VirtualList index out of bounds (high)"); + } + + @Test + void invokeLitRendererFunction_hiddenFails() { + $virtualList.getComponent().setVisible(false); + + var index = UserData.getAnyValidIndex(); + Assertions.assertThrows(IllegalStateException.class, + () -> $virtualList.invokeLitRendererFunction(index, + "firstName"), + "Tester should not be accessible for hidden virtual list"); + } + +} diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/CallbackLitRendererVirtualListView.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/CallbackLitRendererVirtualListView.java new file mode 100644 index 000000000..9baeb9e40 --- /dev/null +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/CallbackLitRendererVirtualListView.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2000-2024 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.virtuallist; + +import com.vaadin.flow.component.Composite; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.data.provider.DataProvider; +import com.vaadin.flow.data.provider.Query; +import com.vaadin.flow.data.renderer.LitRenderer; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.theme.lumo.LumoUtility; + +import java.util.List; +import java.util.stream.Stream; + +@Tag("div") +@Route(value = "callback-lit-renderer-virtual-list", registerAtStartup = false) +public class CallbackLitRendererVirtualListView extends Composite { + + final VirtualList callbackLitRendererVirtualList; + + private final List users; + + public CallbackLitRendererVirtualListView() { + // virtual list using callback lit renderer + callbackLitRendererVirtualList = new VirtualList<>(); + callbackLitRendererVirtualList.setRenderer(userLitRenderer()); + + var title = new Div("Callback Lit Renderer Virtual List"); + title.addClassNames(LumoUtility.FontSize.LARGE, LumoUtility.FontWeight.BOLD); + + callbackLitRendererVirtualList.addClassNames(LumoUtility.Border.ALL); + + var block = new VerticalLayout(); + block.setSizeFull(); + block.addClassNames(LumoUtility.Background.CONTRAST_5, LumoUtility.BorderRadius.LARGE); + block.add(title); + block.add(callbackLitRendererVirtualList); + + var content = getContent(); + content.setPadding(true); + content.setSizeFull(); + content.add(block); + + users = UserData.all(); + callbackLitRendererVirtualList.setDataProvider(DataProvider.fromCallbacks(this::fetchCallback, this::countCallback)); + } + + private LitRenderer userLitRenderer() { + return LitRenderer.of(""" +
+ + Name: + ${item.firstName} + ${item.lastName} + + ; + + Active: + ${item.active ? 'Yes' : 'No'} + + +
+ """) + .withProperty("firstName", User::getFirstName) + .withProperty("lastName", User::getLastName) + .withProperty("active", User::isActive) + .withFunction("onActiveToggleClick", + user -> toggleActive(callbackLitRendererVirtualList, user)); + } + + private void toggleActive(VirtualList virtualList, User user) { + user.setActive(!user.isActive()); + virtualList.getDataProvider().refreshAll(); + } + + private Stream fetchCallback(Query userVoidQuery) { + return users.stream() + .skip(userVoidQuery.getOffset()) + .limit(userVoidQuery.getLimit()); + } + + private int countCallback(Query userVoidQuery) { + return (int) fetchCallback(userVoidQuery).count(); + } + +} diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ComponentRendererVirtualListTesterTest.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ComponentRendererVirtualListTesterTest.java new file mode 100644 index 000000000..95c6a8453 --- /dev/null +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ComponentRendererVirtualListTesterTest.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2000-2024 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.virtuallist; + +import com.vaadin.flow.component.ClickEvent; +import com.vaadin.flow.component.ComponentUtil; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.NativeButton; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@ViewPackages +class ComponentRendererVirtualListTesterTest extends UIUnitTest { + + private VirtualListTester, User> $virtualList; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(ComponentRendererVirtualListView.class); + + var view = navigate(ComponentRendererVirtualListView.class); + $virtualList = test(view.componentRendererVirtualList); + } + + @Test + void virtualList_initTester() { + Assertions.assertNotNull($virtualList, + "Tester for component renderer VirtualList not initialized."); + } + + @Test + void getItemText_existsAndEquals() { + var firstUser = UserData.first(); + Assertions.assertEquals(expectedRendererText(firstUser), + $virtualList.getItemText(0)); + + var index = UserData.getAnyValidIndex(); + var anyUser = UserData.get(index); + Assertions.assertEquals(expectedRendererText(anyUser), + $virtualList.getItemText(index)); + + var lastUser = UserData.last(); + Assertions.assertEquals(expectedRendererText(lastUser), + $virtualList.getItemText(UserData.USER_COUNT - 1)); + } + + @Test + void getItemComponent_exists() { + var $itemComponent = test((Div) $virtualList.getItemComponent(UserData.getAnyValidIndex())); + Assertions.assertNotNull($itemComponent, "Item component should not be null"); + } + + @Test + void getItemComponent_hasExpectedChildrenAndValues() { + var index = UserData.getAnyValidIndex(); + var user = UserData.get(index); + + var $itemComponent = test((Div) $virtualList.getItemComponent(index)); + + var $firstNameSpan = test($itemComponent.find(Span.class) + .withId("first-name") + .single()); + Assertions.assertThrows(IllegalStateException.class, + $firstNameSpan::getText, + "Component is a copy and not usable via a ComponentTester"); + var firstNameSpan = $firstNameSpan.getComponent(); + Assertions.assertEquals(user.getFirstName(), firstNameSpan.getText()); + + var $lastNameSpan = test($itemComponent.find(Span.class) + .withId("last-name") + .single()); + Assertions.assertThrows(IllegalStateException.class, + $lastNameSpan::getText, + "Component is a copy and not usable via a ComponentTester"); + var lastNameSpan = $lastNameSpan.getComponent(); + Assertions.assertEquals(user.getLastName(), lastNameSpan.getText()); + + var $activeSpan = test($itemComponent.find(Span.class) + .withId("active") + .single()); + Assertions.assertThrows(IllegalStateException.class, + $activeSpan::getText, + "Component is a copy and not usable via a ComponentTester"); + var activeSpan = $activeSpan.getComponent(); + Assertions.assertEquals(user.isActive() ? "Yes" : "No", activeSpan.getText()); + } + + // this is a more complicated test + // because it tests that the button toggles the state of the active indicator + @Test + void getItemComponent_buttonActionsFire() { + var index = UserData.getAnyValidIndex(); + var user = UserData.get(index); + + // BEFORE + var $beforeItemComponent = test((Div) $virtualList.getItemComponent(index)); + + var beforeActive = user.isActive(); + var beforeActiveSpan = $beforeItemComponent.find(Span.class) + .withId("active") + .single(); + Assertions.assertEquals(beforeActive ? "Yes" : "No", beforeActiveSpan.getText()); + + // TOGGLE + var $toggleButton = test($beforeItemComponent.find(NativeButton.class) + .withText("Toggle") + .single()); + Assertions.assertThrows(IllegalStateException.class, + $toggleButton::click, + "Component is a copy and not usable via a ComponentTester"); + var toggleButton = $toggleButton.getComponent(); + ComponentUtil.fireEvent(toggleButton, + new ClickEvent<>(toggleButton, true, + 0, 0, 0, 0, + 0, 0, + false, false, false, false)); + + // AFTER + // re-obtain the item component as the item has changed + var $afterItemComponent = test((Div) $virtualList.getItemComponent(index)); + + var afterActive = user.isActive(); + var afterActiveSpan = $afterItemComponent.find(Span.class) + .withId("active") + .single(); + Assertions.assertEquals(afterActive ? "Yes" : "No", afterActiveSpan.getText()); + Assertions.assertEquals(!beforeActive, afterActive); + } + + @Test + void getItemComponent_outOfBoundsIndexFails() { + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> $virtualList.getItemComponent( -1), + "VirtualList index out of bounds (low)"); + + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> $virtualList.getItemComponent(UserData.USER_COUNT), + "VirtualList index out of bounds (high)"); + } + + @Test + void getItemComponent_hiddenFails() { + $virtualList.getComponent().setVisible(false); + + var index = UserData.getAnyValidIndex(); + Assertions.assertThrows(IllegalStateException.class, + () -> $virtualList.getItemComponent(index), + "Tester should not be accessible for hidden virtual list"); + } + + private static String expectedRendererText(User user) { + return String.join("", + "Name:", user.getFirstName(), user.getLastName(), + ";", + "Active:", user.isActive() ? "Yes" : "No", + "Toggle"); + } + +} diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ComponentRendererVirtualListView.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ComponentRendererVirtualListView.java new file mode 100644 index 000000000..f5828670a --- /dev/null +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ComponentRendererVirtualListView.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2000-2024 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.virtuallist; + +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Composite; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.NativeButton; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.data.renderer.ComponentRenderer; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.theme.lumo.LumoUtility; + +@Tag("div") +@Route(value = "component-renderer-virtual-list", registerAtStartup = false) +public class ComponentRendererVirtualListView extends Composite { + + final VirtualList componentRendererVirtualList; + + public ComponentRendererVirtualListView() { + // virtual list using component renderer + componentRendererVirtualList = new VirtualList<>(); + componentRendererVirtualList.setRenderer(new ComponentRenderer<>(this::userComponent)); + + var title = new Div("Component Renderer Virtual List"); + title.addClassNames(LumoUtility.FontSize.LARGE, LumoUtility.FontWeight.BOLD); + + componentRendererVirtualList.addClassNames(LumoUtility.Border.ALL); + + var block = new VerticalLayout(); + block.setSizeFull(); + block.addClassNames(LumoUtility.Background.CONTRAST_5, LumoUtility.BorderRadius.LARGE); + block.add(title); + block.add(componentRendererVirtualList); + + var content = getContent(); + content.setPadding(true); + content.setSizeFull(); + content.add(block); + + componentRendererVirtualList.setItems(UserData.all()); + } + + private Component userComponent(User user) { + var firstNameSpan = new Span(user.getFirstName()); + firstNameSpan.setId("first-name"); + + var lastNameSpan = new Span(user.getLastName()); + lastNameSpan.setId("last-name"); + + var nameSpan = new Span(); + nameSpan.addClassNames(LumoUtility.Display.INLINE_FLEX, LumoUtility.Gap.XSMALL); + nameSpan.add("Name:"); + nameSpan.add(firstNameSpan); + nameSpan.add(lastNameSpan); + + var activeSpan = new Span(user.isActive() ? "Yes" : "No"); + activeSpan.setId("active"); + + var activeToggleButton = new NativeButton("Toggle", + event -> toggleActive(componentRendererVirtualList, user)); + + var activeToggleSpan = new Span(); + activeToggleSpan.addClassNames(LumoUtility.Display.INLINE_FLEX, LumoUtility.Gap.XSMALL); + activeToggleSpan.add("Active:"); + activeToggleSpan.add(activeSpan); + activeToggleSpan.add(activeToggleButton); + + var userDiv = new Div(); + userDiv.addClassNames(LumoUtility.Display.INLINE_FLEX, LumoUtility.Gap.XSMALL); + userDiv.add(nameSpan); + userDiv.add(";"); + userDiv.add(activeToggleSpan); + + return userDiv; + } + + private void toggleActive(VirtualList virtualList, User user) { + user.setActive(!user.isActive()); + virtualList.getDataProvider().refreshAll(); + } + +} diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/User.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/User.java new file mode 100644 index 000000000..854cbd7f8 --- /dev/null +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/User.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2000-2024 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.virtuallist; + +import java.io.Serializable; +import java.util.Objects; + +public class User implements Serializable { + private Long key; + private String firstName; + private String lastName; + private boolean active; + + public User(Long key, String firstName, String lastName, boolean active) { + this.key = key; + this.firstName = firstName; + this.lastName = lastName; + this.active = active; + } + + public User(Long key, String firstName, String lastName) { + this(key, firstName, lastName, true); + } + + public User(Long key) { + this(key, null, null); + } + + public User(String firstName, String lastName, boolean active) { + this(null, firstName, lastName, active); + } + + public User(String firstName, String lastName) { + this(firstName, lastName, true); + } + + public User() { + this(null, null); + } + + public Long getKey() { + return key; + } + + public void setKey(Long key) { + this.key = key; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public boolean isActive() { + return active; + } + + public void setActive(boolean active) { + this.active = active; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof User user)) return false; + + if (!Objects.equals(key, user.key)) return false; + if (!Objects.equals(firstName, user.firstName)) return false; + if (!Objects.equals(lastName, user.lastName)) return false; + return (active == user.active); + } + + @Override + public int hashCode() { + int result = key != null ? key.hashCode() : 0; + result = 31 * result + (firstName != null ? firstName.hashCode() : 0); + result = 31 * result + (lastName != null ? lastName.hashCode() : 0); + result = 31 * result + (active ? 1 : 0); + return result; + } +} diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/UserData.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/UserData.java new file mode 100644 index 000000000..abe5aa17b --- /dev/null +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/UserData.java @@ -0,0 +1,61 @@ +package com.vaadin.flow.component.virtuallist; + +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.LongStream; + +public final class UserData { + public static final int USER_COUNT = 10000; + + private final List users; + + // private constructor so can only be instantiated by getInstance + private UserData() { + users = LongStream.range(0, USER_COUNT) + .mapToObj(UserData::createUser) + .toList(); + } + + private static User createUser(long idx) { + var user = new User(); + user.setKey(idx); + user.setFirstName("First-" + idx); + user.setLastName("Last-" + idx); + user.setActive(idx % 5 != 0); + return user; + } + + // inner class to provide instance of class + private static final class SingletonHelper { + private static final UserData INSTANCE = new UserData(); + } + + private static UserData getInstance() { + return SingletonHelper.INSTANCE; + } + + public static List all() { + return getInstance().users; + } + + public static User get(int index) { + return all().get(index); + } + + public static User first() { + return get(0); + } + + public static User last() { + return get(all().size() - 1); + } + + public static User any() { + return get(getAnyValidIndex()); + } + + public static int getAnyValidIndex() { + return ThreadLocalRandom.current().nextInt(0, all().size()); + } + +} diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ValueProviderVirtualListTesterTest.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ValueProviderVirtualListTesterTest.java new file mode 100644 index 000000000..c34a4b5a8 --- /dev/null +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ValueProviderVirtualListTesterTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2000-2024 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.virtuallist; + +import com.vaadin.flow.router.RouteConfiguration; +import com.vaadin.testbench.unit.UIUnitTest; +import com.vaadin.testbench.unit.ViewPackages; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@ViewPackages +class ValueProviderVirtualListTesterTest extends UIUnitTest { + + private VirtualListTester, User> $virtualList; + + @BeforeEach + void init() { + RouteConfiguration.forApplicationScope() + .setAnnotatedRoute(ValueProviderVirtualListView.class); + + var view = navigate(ValueProviderVirtualListView.class); + $virtualList = test(view.valueProviderVirtualList); + } + + @Test + void virtualList_initTester() { + Assertions.assertNotNull($virtualList, + "Tester for value provider VirtualList not initialized."); + } + + @Test + void size_equalsItemCount() { + Assertions.assertEquals(UserData.USER_COUNT, $virtualList.size()); + } + + @Test + void size_hiddenFails() { + $virtualList.getComponent().setVisible(false); + + Assertions.assertThrows(IllegalStateException.class, + () -> $virtualList.size(), + "Tester should not be accessible for hidden virtual list"); + } + + @Test + void getItem_existsAndEquals() { + var firstUser = UserData.first(); + Assertions.assertEquals(firstUser, $virtualList.getItem(0)); + + var index = UserData.getAnyValidIndex(); + var anyUser = UserData.get(index); + Assertions.assertEquals(anyUser, $virtualList.getItem(index)); + + var lastUser = UserData.last(); + Assertions.assertEquals(lastUser, $virtualList.getItem(UserData.USER_COUNT - 1)); + } + + @Test + void getItem_outOfBoundsIndexFails() { + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> $virtualList.getItem( -1), + "VirtualList index out of bounds (low)"); + + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> $virtualList.getItem(UserData.USER_COUNT), + "VirtualList index out of bounds (high)"); + } + + @Test + void getItem_hiddenFails() { + $virtualList.getComponent().setVisible(false); + + var index = UserData.getAnyValidIndex(); + Assertions.assertThrows(IllegalStateException.class, + () -> $virtualList.getItem(index), + "Tester should not be accessible for hidden virtual list"); + } + + @Test + void getItemText_existsAndEquals() { + var firstUser = UserData.first(); + Assertions.assertEquals(expectedValueProviderText(firstUser), + $virtualList.getItemText(0)); + + var index = UserData.getAnyValidIndex(); + var anyUser = UserData.get(index); + Assertions.assertEquals(expectedValueProviderText(anyUser), + $virtualList.getItemText(index)); + + var lastUser = UserData.last(); + Assertions.assertEquals(expectedValueProviderText(lastUser), + $virtualList.getItemText(UserData.USER_COUNT - 1)); + } + + @Test + void getItemText_outOfBoundsIndexFails() { + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> $virtualList.getItemText( -1), + "VirtualList index out of bounds (low)"); + + Assertions.assertThrows(IndexOutOfBoundsException.class, + () -> $virtualList.getItemText(UserData.USER_COUNT), + "VirtualList index out of bounds (high)"); + } + + @Test + void getItemText_hiddenFails() { + $virtualList.getComponent().setVisible(false); + + var index = UserData.getAnyValidIndex(); + Assertions.assertThrows(IllegalStateException.class, + () -> $virtualList.getItemText(index), + "Tester should not be accessible for hidden virtual list"); + } + + @Test + void getItemComponent_verifyNotComponentRenderer() { + var index = UserData.getAnyValidIndex(); + Assertions.assertThrows(IllegalArgumentException.class, + () -> $virtualList.getItemComponent(index), + "valueProviderVirtualList should not have a ComponentRenderer"); + } + + private static String expectedValueProviderText(User user) { + return String.join(" ", + "Name:", user.getFirstName(), user.getLastName(), + ";", + "Active:", user.isActive() ? "Yes" : "No"); + } + +} diff --git a/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ValueProviderVirtualListView.java b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ValueProviderVirtualListView.java new file mode 100644 index 000000000..5174457dd --- /dev/null +++ b/vaadin-testbench-unit-junit5/src/test/java/com/vaadin/flow/component/virtuallist/ValueProviderVirtualListView.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2000-2024 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.virtuallist; + +import com.vaadin.flow.component.Composite; +import com.vaadin.flow.component.Tag; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.theme.lumo.LumoUtility; + +@Tag("div") +@Route(value = "value-provider-virtual-list", registerAtStartup = false) +public class ValueProviderVirtualListView extends Composite { + + final VirtualList valueProviderVirtualList; + + public ValueProviderVirtualListView() { + // virtual list using value provider + valueProviderVirtualList = new VirtualList<>(); + valueProviderVirtualList.setRenderer(ValueProviderVirtualListView::userValueProvider); + + var title = new Div("Value Provider Virtual List"); + title.addClassNames(LumoUtility.FontSize.LARGE, LumoUtility.FontWeight.BOLD); + + valueProviderVirtualList.addClassNames(LumoUtility.Border.ALL); + + var block = new VerticalLayout(); + block.setSizeFull(); + block.addClassNames(LumoUtility.Background.CONTRAST_5, LumoUtility.BorderRadius.LARGE); + block.add(title); + block.add(valueProviderVirtualList); + + var content = getContent(); + content.setPadding(true); + content.setSizeFull(); + content.add(block); + + valueProviderVirtualList.setItems(UserData.all()); + } + + private static String userValueProvider(User user) { + return String.join(" ", + "Name:", user.getFirstName(), user.getLastName(), + ";", "Active:", user.isActive() ? "Yes" : "No"); + } + +} diff --git a/vaadin-testbench-unit-shared/pom.xml b/vaadin-testbench-unit-shared/pom.xml index f8b67e195..c945f2a00 100644 --- a/vaadin-testbench-unit-shared/pom.xml +++ b/vaadin-testbench-unit-shared/pom.xml @@ -31,8 +31,8 @@ - 1.6.21 - 1.6.21 + 1.9.20 + 1.9.20 diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/flow/component/grid/GridTester.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/flow/component/grid/GridTester.java index 6d344509e..2cf937708 100644 --- a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/flow/component/grid/GridTester.java +++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/flow/component/grid/GridTester.java @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2000-2022 Vaadin Ltd +/* + * Copyright (C) 2000-2024 Vaadin Ltd * * This program is available under Vaadin Commercial License and Service Terms. * @@ -13,9 +13,9 @@ 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.SerializableBiConsumer; 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; @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Map; import java.util.Objects; /** @@ -315,6 +314,19 @@ private Component getRendererItem(int row, Grid.Column yColumn) { "Target column doesn't have a ComponentRenderer."); } + private V getLitRendererPropertyValue(int row, Grid.Column column, + String propertyName, Class propertyClass) { + ensureVisible(); + + if (column.getRenderer() instanceof LitRenderer litRenderer) { + return LitRendererTestUtil.getPropertyValue(litRenderer, this::getField, this::getRow, row, propertyName, propertyClass + ); + } else { + throw new IllegalArgumentException( + "Target column doesn't have a LitRenderer."); + } + } + /** * Get property value for item's LitRenderer in column. * @@ -361,37 +373,18 @@ public V getLitRendererPropertyValue(int row, int column, String propertyNam return getLitRendererPropertyValue(row, getColumns().get(column), propertyName, propertyClass); } - private V getLitRendererPropertyValue(int row, Grid.Column column, - String propertyName, Class propertyClass) { + private void invokeLitRendererFunction(int row, Grid.Column column, String functionName, JsonArray jsonArray) { ensureVisible(); + if (column.getRenderer() instanceof LitRenderer litRenderer) { - var valueProvider = findLitRendererProperty(litRenderer, propertyName); - var untypedValue = valueProvider.apply(getRow(row)); - if (propertyClass.isInstance(untypedValue)) { - return propertyClass.cast(untypedValue); - } else { - throw new IllegalArgumentException("Type of target column property value does not match propertyClass - expected %s, found %s." - .formatted(propertyClass.getCanonicalName(), untypedValue.getClass().getCanonicalName())); - } + LitRendererTestUtil.invokeFunction(litRenderer, this::getField, this::getRow, row, functionName, jsonArray + ); } else { - throw new IllegalArgumentException("Target column doesn't have a LitRenderer."); + throw new IllegalArgumentException( + "Target column doesn't have a LitRenderer."); } } - /** - * Invoke named function for item's LitRenderer in column. - * - * @param row - * item row - * @param columnName - * key/property of column - * @param functionName - * the name of the LitRenderer function to invoke - */ - public void invokeLitRendererFunction(int row, String columnName, String functionName) { - invokeLitRendererFunction(row, columnName, functionName, Json.createArray()); - } - /** * Invoke named function for item's LitRenderer in column using the supplied JSON arguments. * @@ -413,13 +406,13 @@ public void invokeLitRendererFunction(int row, String columnName, String functio * * @param row * item row - * @param column - * column to get + * @param columnName + * key/property of column * @param functionName * the name of the LitRenderer function to invoke */ - public void invokeLitRendererFunction(int row, int column, String functionName) { - invokeLitRendererFunction(row, column, functionName, Json.createArray()); + public void invokeLitRendererFunction(int row, String columnName, String functionName) { + invokeLitRendererFunction(row, columnName, functionName, Json.createArray()); } /** @@ -438,44 +431,18 @@ public void invokeLitRendererFunction(int row, int column, String functionName, invokeLitRendererFunction(row, getColumns().get(column), functionName, jsonArray); } - private void invokeLitRendererFunction(int row, Grid.Column column, String functionName, JsonArray jsonArray) { - ensureVisible(); - if (column.getRenderer() instanceof LitRenderer litRenderer) { - var callable = findLitRendererFunction(litRenderer, functionName); - callable.accept(getRow(row), jsonArray); - } else { - throw new IllegalArgumentException("Target column doesn't have a LitRenderer."); - } - } - - private ValueProvider findLitRendererProperty(LitRenderer renderer, String propertyName) { - var valueProvidersField = getField(LitRenderer.class, "valueProviders"); - try { - @SuppressWarnings("unchecked") - var valueProviders = (Map>) valueProvidersField.get(renderer); - 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); - } - } - - private SerializableBiConsumer findLitRendererFunction(LitRenderer renderer, String functionName) { - var clientCallablesField = getField(LitRenderer.class, "clientCallables"); - try { - @SuppressWarnings("unchecked") - var clientCallables = (Map>) clientCallablesField.get(renderer); - 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); - } + /** + * Invoke named function for item's LitRenderer in column. + * + * @param row + * item row + * @param column + * column to get + * @param functionName + * the name of the LitRenderer function to invoke + */ + public void invokeLitRendererFunction(int row, int column, String functionName) { + invokeLitRendererFunction(row, column, functionName, Json.createArray()); } /** diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/flow/component/virtuallist/VirtualListTester.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/flow/component/virtuallist/VirtualListTester.java new file mode 100644 index 000000000..149c30939 --- /dev/null +++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/flow/component/virtuallist/VirtualListTester.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2000-2024 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See for the full + * license. + */ +package com.vaadin.flow.component.virtuallist; + +import com.vaadin.flow.component.Component; +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; + +import java.util.Collections; + +/** + * Tester for VirtualList components. + * + * @param + * component type + * @param + * value type + */ +@Tests(VirtualList.class) +public class VirtualListTester, Y> + extends ComponentTester { + + // don't use a value too large + // otherwise Vaadin 19+ will calculate a negative limit + // and will pass it to SizeVerifier + private static final int SANE_FETCH_LIMIT = Integer.MAX_VALUE / 1000; + + /** + * Wrap given component for testing. + * + * @param component + * target component + */ + public VirtualListTester(T component) { + super(component); + } + + /** + * Get the amount of items in the virtual list. + * + * @return number of items in the virtual list + */ + public int size() { + ensureVisible(); + + var dataCommunicator = getComponent().getDataCommunicator(); + return dataCommunicator.isDefinedSize() + ? dataCommunicator.getDataProviderSize() + : dataCommunicator.getDataProvider().fetch(saneQuery()).toList() + .size(); + } + + /** + * Get the item at the given index. + * + * @param index + * the zero-based index of the item to get + * @return virtual list item at index + */ + public Y getItem(int index) { + ensureVisible(); + + return getComponent().getDataCommunicator().getItem(index); + } + + /** + * Get the text that is shown on the client for the item at index. + *

+ * The index is zero-based. + *

+ * For the default renderer ColumnPathRenderer the result is the sent text + * for defined object path. + *

+ * For a ComponentRenderer the result is the rendered component as + * prettyString. + *

+ * More to be added as we find other renderers that need handling. + * + * @param index + * the zero-based index of the item + * @return item content that is sent to the client + */ + public String getItemText(int index) { + ensureVisible(); + + // use element text of Component if renderer is ComponentRenderer + var itemRenderer = getItemRenderer(); + if (itemRenderer instanceof ComponentRenderer) { + var component = getItemComponent(index); + if (component == null) { + return null; + } + return component.getElement().getTextRecursively(); + } + + // use LitRenderer label if renderer is ValueProvider (i.e., has a + // single property "label") + if (itemRenderer instanceof LitRenderer litRenderer) { + if ((LitRendererTestUtil.getProperties(litRenderer, this::getField) + .stream() + .allMatch(propertyName -> propertyName.equals("label"))) + && (LitRendererTestUtil + .getFunctionNames(litRenderer, this::getField) + .isEmpty())) { + return getLitRendererPropertyValue(index, "label", + String.class); + } else { + throw new UnsupportedOperationException( + "VirtualListTester is unable to get item text when VirtualList uses a LitRenderer."); + } + } + + throw new UnsupportedOperationException( + "VirtualListTester is unable to get item text for this VirtualList's renderer."); + } + + /** + * Get an initialized copy of the component for the item. + *

+ * Note, this is not the actual component. + * + * @param index + * the zero-based index of the item + * @return initialized component for the target item + * @throws IllegalArgumentException + * when the VirtualList is not using a ComponentRenderer + */ + public Component getItemComponent(int index) { + ensureVisible(); + + if (getItemRenderer() instanceof ComponentRenderer componentRenderer) { + var item = getItem(index); + return componentRenderer.createComponent(item); + } + throw new IllegalArgumentException( + "VirtualList doesn't use a ComponentRenderer."); + } + + @SuppressWarnings("unchecked") + private Renderer getItemRenderer() { + var rendererField = getField("renderer"); + try { + return (Renderer) rendererField.get(getComponent()); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + /** + * Get property value for item's LitRenderer. + * + * @param index + * the zero-based index of the item + * @param propertyName + * the name of the LitRenderer property + * @param propertyClass + * the class of the value of the LitRenderer property + * @param + * the type of the LitRenderer property + * @return value of the LitRenderer property + * @throws IllegalArgumentException + * when the VirtualList is not using a LitRenderer or when the + * given type of the property does not match the actual property + * type + */ + public V getLitRendererPropertyValue(int index, String propertyName, + Class propertyClass) { + ensureVisible(); + + if (getItemRenderer() instanceof LitRenderer litRenderer) { + return LitRendererTestUtil.getPropertyValue(litRenderer, + this::getField, this::getItem, index, propertyName, + propertyClass); + } else { + throw new IllegalArgumentException( + "This VirtualList doesn't use a LitRenderer."); + } + } + + /** + * Invoke named function for item's LitRenderer using the supplied JSON + * arguments. + * + * @param index + * the zero-based index of the item + * @param functionName + * the name of the LitRenderer function to invoke + * @param jsonArray + * the arguments to pass to the function + * + * @see #invokeLitRendererFunction(int, String) + * @throws IllegalArgumentException + * when the VirtualList is not using a LitRenderer + */ + public void invokeLitRendererFunction(int index, String functionName, + JsonArray jsonArray) { + ensureVisible(); + + if (getItemRenderer() instanceof LitRenderer litRenderer) { + LitRendererTestUtil.invokeFunction(litRenderer, this::getField, + this::getItem, index, functionName, jsonArray); + } else { + throw new IllegalArgumentException( + "This VirtualList doesn't use a LitRenderer."); + } + } + + /** + * Invoke named function for item's LitRenderer. + * + * @param index + * the zero-based index of the item + * @param functionName + * the name of the LitRenderer function to invoke + * + * @see #invokeLitRendererFunction(int, String, JsonArray) + * @throws IllegalArgumentException + * when the VirtualList is not using a LitRenderer + */ + public void invokeLitRendererFunction(int index, String functionName) { + invokeLitRendererFunction(index, functionName, Json.createArray()); + } + + private Query saneQuery() { + return new Query<>(0, SANE_FETCH_LIMIT, Collections.emptyList(), null, + null); + } + +} diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/LitRendererTestUtil.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/LitRendererTestUtil.java new file mode 100644 index 000000000..a1131e37b --- /dev/null +++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/LitRendererTestUtil.java @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2000-2024 Vaadin Ltd + * + * This program is available under Vaadin Commercial License and Service Terms. + * + * See 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 + * the type being renderer by the LitRenderer + */ + public static Set getProperties(LitRenderer litRenderer, + BiFunction, String, Field> fieldGetter) { + var valueProvidersField = fieldGetter.apply(LitRenderer.class, + "valueProviders"); + try { + @SuppressWarnings("unchecked") + var valueProviders = (Map>) valueProvidersField + .get(litRenderer); + return valueProviders.keySet(); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static ValueProvider findProperty( + LitRenderer litRenderer, + BiFunction, String, Field> fieldGetter, + String propertyName) { + var valueProvidersField = fieldGetter.apply(LitRenderer.class, + "valueProviders"); + try { + @SuppressWarnings("unchecked") + var valueProviders = (Map>) 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 litRenderer + * the LitRenderer with properties to get + * @param fieldGetter + * the field getter of the ComponentTester + * @param itemGetter + * the getter for the item rendered by the 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 + * @return the property value + * @param + * the type being renderer by the LitRenderer + * @param + * the type of the property value + * @throws IllegalArgumentException + * when the type of property value does not match propertyClass + */ + public static V getPropertyValue(LitRenderer litRenderer, + BiFunction, String, Field> fieldGetter, + IntFunction itemGetter, int index, String propertyName, + Class propertyClass) { + var valueProvider = findProperty(litRenderer, fieldGetter, + propertyName); + 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 + * the type being renderer by the LitRenderer + */ + public static Set getFunctionNames(LitRenderer litRenderer, + BiFunction, String, Field> fieldGetter) { + var clientCallablesField = fieldGetter.apply(LitRenderer.class, + "clientCallables"); + try { + @SuppressWarnings("unchecked") + var clientCallables = (Map>) clientCallablesField + .get(litRenderer); + return clientCallables.keySet(); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + private static SerializableBiConsumer findFunction( + LitRenderer litRenderer, + BiFunction, String, Field> fieldGetter, + String functionName) { + var clientCallablesField = fieldGetter.apply(LitRenderer.class, + "clientCallables"); + try { + @SuppressWarnings("unchecked") + var clientCallables = (Map>) 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 litRenderer + * the LitRenderer with properties to get + * @param fieldGetter + * the field getter of the ComponentTester + * @param itemGetter + * the getter for the item rendered by the LitRenderer + * @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 + * the type being renderer by the LitRenderer + * @throws IllegalArgumentException + * when the function is not registered in LitRenderer + */ + public static void invokeFunction(LitRenderer litRenderer, + BiFunction, String, Field> fieldGetter, + IntFunction itemGetter, int index, String functionName, + JsonArray jsonArray) { + var callable = findFunction(litRenderer, fieldGetter, functionName); + callable.accept(itemGetter.apply(index), jsonArray); + } + +} diff --git a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java index 22381d21f..6566b7444 100644 --- a/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java +++ b/vaadin-testbench-unit-shared/src/main/java/com/vaadin/testbench/unit/TesterWrappers.java @@ -1,5 +1,5 @@ -/** - * Copyright (C) 2000-2022 Vaadin Ltd +/* + * Copyright (C) 2000-2024 Vaadin Ltd * * This program is available under Vaadin Commercial License and Service Terms. * @@ -8,8 +8,6 @@ */ package com.vaadin.testbench.unit; -import java.math.BigDecimal; - import com.vaadin.flow.component.accordion.Accordion; import com.vaadin.flow.component.accordion.AccordionTester; import com.vaadin.flow.component.button.Button; @@ -124,6 +122,10 @@ import com.vaadin.flow.component.timepicker.TimePickerTester; import com.vaadin.flow.component.upload.Upload; import com.vaadin.flow.component.upload.UploadTester; +import com.vaadin.flow.component.virtuallist.VirtualList; +import com.vaadin.flow.component.virtuallist.VirtualListTester; + +import java.math.BigDecimal; @SuppressWarnings("unchecked") public interface TesterWrappers { @@ -433,4 +435,12 @@ default ChartTester test(Chart chart) { default SideNavTester test(SideNav sideNav) { return BaseUIUnitTest.internalWrap(SideNavTester.class, sideNav); } + + default VirtualListTester, V> test(VirtualList virtualList) { + return BaseUIUnitTest.internalWrap(VirtualListTester.class, virtualList); + } + + default VirtualListTester, V> test(VirtualList virtualList, Class itemType) { + return BaseUIUnitTest.internalWrap(VirtualListTester.class, virtualList); + } } diff --git a/vaadin-testbench-unit/pom.xml b/vaadin-testbench-unit/pom.xml index 96dcfe12e..dd40829ac 100644 --- a/vaadin-testbench-unit/pom.xml +++ b/vaadin-testbench-unit/pom.xml @@ -30,8 +30,8 @@ - 1.6.21 - 1.6.21 + 1.9.20 + 1.9.20 4.13.2