Skip to content

Commit f7d3db7

Browse files
committed
feat: add fast grid cell dump methods to GridElement
Add three new methods to GridElement for efficient grid cell extraction: - dumpVisibleCells(): dumps currently visible cells in one browser round-trip - dumpAllCells(): dumps all grid cells by scrolling through pages - dumpCells(fromRow, toRow): dumps cells for a specific row range This addresses the performance issue when testing grid content, reducing browser round-trips from N×M (one per cell) to 1-50 requests depending on grid size, resulting in 20-500x performance improvement. Only visible columns are included in the output. Hidden columns are filtered out automatically. Fixes #1876
1 parent 1c2ac19 commit f7d3db7

File tree

3 files changed

+417
-0
lines changed
  • vaadin-grid-flow-parent
    • vaadin-grid-flow-integration-tests/src
    • vaadin-grid-testbench/src/main/java/com/vaadin/flow/component/grid/testbench

3 files changed

+417
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2000-2026 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.vaadin.flow.component.grid.it;
17+
18+
import java.util.stream.Collectors;
19+
import java.util.stream.IntStream;
20+
21+
import com.vaadin.flow.component.grid.Grid;
22+
import com.vaadin.flow.component.html.Div;
23+
import com.vaadin.flow.data.bean.Person;
24+
import com.vaadin.flow.data.provider.DataProvider;
25+
import com.vaadin.flow.router.Route;
26+
27+
@Route("vaadin-grid/dump")
28+
public class GridDumpPage extends Div {
29+
30+
public GridDumpPage() {
31+
createSmallGrid();
32+
createMediumGrid();
33+
createLargeGrid();
34+
createGridWithHiddenColumn();
35+
}
36+
37+
private void createSmallGrid() {
38+
Grid<Person> grid = new Grid<>();
39+
grid.setItems(IntStream.range(0, 10)
40+
.mapToObj(i -> new Person("Person " + i, i))
41+
.collect(Collectors.toList()));
42+
43+
grid.addColumn(Person::getFirstName).setHeader("Name");
44+
grid.addColumn(Person::getAge).setHeader("Age");
45+
46+
grid.setId("small-grid");
47+
48+
add(grid);
49+
}
50+
51+
private void createMediumGrid() {
52+
Grid<Person> grid = new Grid<>();
53+
grid.setItems(IntStream.range(0, 100)
54+
.mapToObj(i -> new Person("Person " + i, i))
55+
.collect(Collectors.toList()));
56+
57+
grid.addColumn(Person::getFirstName).setHeader("Name");
58+
grid.addColumn(Person::getAge).setHeader("Age");
59+
60+
grid.setId("medium-grid");
61+
62+
add(grid);
63+
}
64+
65+
private void createLargeGrid() {
66+
Grid<Person> grid = new Grid<>();
67+
grid.setItems(
68+
DataProvider
69+
.fromCallbacks(
70+
query -> IntStream
71+
.range(query.getOffset(),
72+
query.getOffset()
73+
+ query.getLimit())
74+
.mapToObj(index -> new Person(
75+
"Person " + index, index)),
76+
query -> 1000));
77+
78+
grid.addColumn(Person::getFirstName).setHeader("Name");
79+
grid.addColumn(Person::getAge).setHeader("Age");
80+
81+
grid.setId("large-grid");
82+
83+
add(grid);
84+
}
85+
86+
private void createGridWithHiddenColumn() {
87+
Grid<Person> grid = new Grid<>();
88+
grid.setItems(IntStream.range(0, 10)
89+
.mapToObj(i -> new Person("Person " + i, i))
90+
.collect(Collectors.toList()));
91+
92+
grid.addColumn(Person::getFirstName).setHeader("Name");
93+
grid.addColumn(Person::getAge).setHeader("Age").setVisible(false);
94+
grid.addColumn(person -> "Email" + person.getAge()).setHeader("Email");
95+
96+
grid.setId("hidden-column-grid");
97+
98+
add(grid);
99+
}
100+
}
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
* Copyright 2000-2026 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.vaadin.flow.component.grid.it;
17+
18+
import java.util.List;
19+
20+
import org.junit.Assert;
21+
import org.junit.Before;
22+
import org.junit.Test;
23+
24+
import com.vaadin.flow.component.grid.testbench.GridElement;
25+
import com.vaadin.flow.testutil.TestPath;
26+
import com.vaadin.tests.AbstractComponentIT;
27+
28+
@TestPath("vaadin-grid/dump")
29+
public class GridDumpIT extends AbstractComponentIT {
30+
31+
@Before
32+
public void init() {
33+
open();
34+
}
35+
36+
@Test
37+
public void dumpVisibleCells_smallGrid_returnsVisibleCells() {
38+
GridElement grid = $(GridElement.class).id("small-grid");
39+
scrollToElement(grid);
40+
waitUntil(driver -> grid.getRowCount() > 0);
41+
42+
List<List<String>> cells = grid.dumpVisibleCells();
43+
44+
Assert.assertNotNull("Cells should not be null", cells);
45+
Assert.assertTrue("Grid should have visible cells", cells.size() > 0);
46+
47+
// Check first row
48+
List<String> firstRow = cells.get(0);
49+
Assert.assertEquals("First row should have 2 columns", 2,
50+
firstRow.size());
51+
Assert.assertEquals("Person 0", firstRow.get(0));
52+
Assert.assertEquals("0", firstRow.get(1));
53+
}
54+
55+
@Test
56+
public void dumpAllCells_smallGrid_returnsAllCells() {
57+
GridElement grid = $(GridElement.class).id("small-grid");
58+
scrollToElement(grid);
59+
waitUntil(driver -> grid.getRowCount() > 0);
60+
61+
List<List<String>> cells = grid.dumpAllCells();
62+
63+
Assert.assertEquals("Should have 10 rows", 10, cells.size());
64+
65+
// Verify first and last rows
66+
Assert.assertEquals("Person 0", cells.get(0).get(0));
67+
Assert.assertEquals("0", cells.get(0).get(1));
68+
Assert.assertEquals("Person 9", cells.get(9).get(0));
69+
Assert.assertEquals("9", cells.get(9).get(1));
70+
}
71+
72+
@Test
73+
public void dumpAllCells_mediumGrid_returnsAllCells() {
74+
GridElement grid = $(GridElement.class).id("medium-grid");
75+
scrollToElement(grid);
76+
waitUntil(driver -> grid.getRowCount() > 0);
77+
78+
List<List<String>> cells = grid.dumpAllCells();
79+
80+
Assert.assertEquals("Should have 100 rows", 100, cells.size());
81+
82+
// Verify first, middle and last rows
83+
Assert.assertEquals("Person 0", cells.get(0).get(0));
84+
Assert.assertEquals("Person 50", cells.get(50).get(0));
85+
Assert.assertEquals("Person 99", cells.get(99).get(0));
86+
}
87+
88+
@Test
89+
public void dumpCells_mediumGrid_returnsSpecifiedRange() {
90+
GridElement grid = $(GridElement.class).id("medium-grid");
91+
scrollToElement(grid);
92+
waitUntil(driver -> grid.getRowCount() > 0);
93+
94+
List<List<String>> cells = grid.dumpCells(20, 29);
95+
96+
Assert.assertEquals("Should have 10 rows", 10, cells.size());
97+
98+
// Verify row data
99+
Assert.assertEquals("Person 20", cells.get(0).get(0));
100+
Assert.assertEquals("20", cells.get(0).get(1));
101+
Assert.assertEquals("Person 29", cells.get(9).get(0));
102+
Assert.assertEquals("29", cells.get(9).get(1));
103+
}
104+
105+
@Test
106+
public void dumpAllCells_largeGrid_returnsAllCells() {
107+
GridElement grid = $(GridElement.class).id("large-grid");
108+
scrollToElement(grid);
109+
waitUntil(driver -> grid.getRowCount() > 0);
110+
111+
List<List<String>> cells = grid.dumpAllCells();
112+
113+
Assert.assertEquals("Should have 1000 rows", 1000, cells.size());
114+
115+
// Verify sampling of rows
116+
Assert.assertEquals("Person 0", cells.get(0).get(0));
117+
Assert.assertEquals("Person 500", cells.get(500).get(0));
118+
Assert.assertEquals("Person 999", cells.get(999).get(0));
119+
}
120+
121+
@Test
122+
public void dumpCells_largeGrid_returnsSpecifiedRange() {
123+
GridElement grid = $(GridElement.class).id("large-grid");
124+
scrollToElement(grid);
125+
waitUntil(driver -> grid.getRowCount() > 0);
126+
127+
List<List<String>> cells = grid.dumpCells(800, 850);
128+
129+
Assert.assertEquals("Should have 51 rows", 51, cells.size());
130+
Assert.assertEquals("Person 800", cells.get(0).get(0));
131+
Assert.assertEquals("Person 850", cells.get(50).get(0));
132+
}
133+
134+
@Test
135+
public void dumpAllCells_hiddenColumn_onlyVisibleColumns() {
136+
GridElement grid = $(GridElement.class).id("hidden-column-grid");
137+
scrollToElement(grid);
138+
waitUntil(driver -> grid.getRowCount() > 0);
139+
140+
List<List<String>> cells = grid.dumpAllCells();
141+
142+
Assert.assertEquals("Should have 10 rows", 10, cells.size());
143+
144+
// Should only have 2 visible columns (Name and Email, Age is hidden)
145+
List<String> firstRow = cells.get(0);
146+
Assert.assertEquals("Should have 2 visible columns", 2,
147+
firstRow.size());
148+
Assert.assertEquals("Person 0", firstRow.get(0));
149+
Assert.assertEquals("Email0", firstRow.get(1));
150+
}
151+
152+
@Test
153+
public void dumpCells_invalidRange_throwsException() {
154+
GridElement grid = $(GridElement.class).id("small-grid");
155+
scrollToElement(grid);
156+
waitUntil(driver -> grid.getRowCount() > 0);
157+
158+
try {
159+
grid.dumpCells(-1, 5);
160+
Assert.fail("Should throw IndexOutOfBoundsException");
161+
} catch (IndexOutOfBoundsException e) {
162+
// Expected
163+
}
164+
165+
try {
166+
grid.dumpCells(0, 100);
167+
Assert.fail("Should throw IndexOutOfBoundsException");
168+
} catch (IndexOutOfBoundsException e) {
169+
// Expected
170+
}
171+
172+
try {
173+
grid.dumpCells(5, 3);
174+
Assert.fail("Should throw IndexOutOfBoundsException");
175+
} catch (IndexOutOfBoundsException e) {
176+
// Expected
177+
}
178+
}
179+
180+
@Test
181+
public void dumpAllCells_fasterThanGetText() {
182+
GridElement grid = $(GridElement.class).id("medium-grid");
183+
scrollToElement(grid);
184+
waitUntil(driver -> grid.getRowCount() > 0);
185+
186+
// Test dumpAllCells performance
187+
long startDump = System.currentTimeMillis();
188+
List<List<String>> dumpedCells = grid.dumpAllCells();
189+
long dumpTime = System.currentTimeMillis() - startDump;
190+
191+
// Test getText performance (just first 10 rows to keep test fast)
192+
long startGetText = System.currentTimeMillis();
193+
for (int row = 0; row < 10; row++) {
194+
for (int col = 0; col < 2; col++) {
195+
grid.getCell(row, col).getText();
196+
}
197+
}
198+
long getTextTime = System.currentTimeMillis() - startGetText;
199+
200+
// Verify data is correct
201+
Assert.assertEquals("Should have 100 rows", 100, dumpedCells.size());
202+
Assert.assertEquals("Person 0", dumpedCells.get(0).get(0));
203+
204+
// dumpAllCells should be significantly faster
205+
// Even dumping 100 rows should be faster than getText on just 10 rows
206+
Assert.assertTrue(
207+
"dumpAllCells should be faster than getText. dumpAllCells: "
208+
+ dumpTime + "ms, getText (10 rows): " + getTextTime
209+
+ "ms",
210+
dumpTime < getTextTime * 5);
211+
}
212+
}

0 commit comments

Comments
 (0)