Skip to content

Commit

Permalink
feat: add VirtualListTester (#1797)
Browse files Browse the repository at this point in the history
Adds VirtualListTester to enable VirtualLists to be unit tested with TestBench.

As this tester and GridTester both support LitRenderers, the common code from GridTester was extracted to a utility class so it could be shared by both testers.

Fixes #1721
  • Loading branch information
joelpop authored May 21, 2024
1 parent f04e585 commit 0ad819b
Show file tree
Hide file tree
Showing 15 changed files with 1,349 additions and 81 deletions.
4 changes: 2 additions & 2 deletions vaadin-testbench-unit-junit5/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
</licenses>

<properties>
<kotlin.version>1.6.21</kotlin.version>
<dokka.version>1.6.21</dokka.version>
<kotlin.version>1.9.20</kotlin.version>
<dokka.version>1.9.20</dokka.version>
<junit5.version>5.9.1</junit5.version>
</properties>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* 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.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<VirtualList<User>, 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");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* 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.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<HorizontalLayout> {

final VirtualList<User> callbackLitRendererVirtualList;

private final List<User> 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<User> userLitRenderer() {
return LitRenderer.<User>of("""
<div>
<span>
Name:
<span>${item.firstName}</span>
<span>${item.lastName}</span>
</span>
;
<span>
Active:
<span>${item.active ? 'Yes' : 'No'}</span>
<button @click=${onActiveToggleClick}>Toggle</button>
</span>
</div>
""")
.withProperty("firstName", User::getFirstName)
.withProperty("lastName", User::getLastName)
.withProperty("active", User::isActive)
.withFunction("onActiveToggleClick",
user -> toggleActive(callbackLitRendererVirtualList, user));
}

private void toggleActive(VirtualList<User> virtualList, User user) {
user.setActive(!user.isActive());
virtualList.getDataProvider().refreshAll();
}

private Stream<User> fetchCallback(Query<User, Void> userVoidQuery) {
return users.stream()
.skip(userVoidQuery.getOffset())
.limit(userVoidQuery.getLimit());
}

private int countCallback(Query<User, Void> userVoidQuery) {
return (int) fetchCallback(userVoidQuery).count();
}

}
Loading

0 comments on commit 0ad819b

Please sign in to comment.