package com.vaadin.tests.server.component.grid;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.easymock.Capture;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import com.vaadin.data.Binder.Binding;
import com.vaadin.data.ValidationException;
import com.vaadin.data.ValueProvider;
import com.vaadin.data.provider.GridSortOrder;
import com.vaadin.data.provider.QuerySortOrder;
import com.vaadin.data.provider.bov.Person;
import com.vaadin.event.selection.SelectionEvent;
import com.vaadin.server.SerializableComparator;
import com.vaadin.shared.data.sort.SortDirection;
import com.vaadin.shared.ui.grid.HeightMode;
import com.vaadin.tests.util.MockUI;
import com.vaadin.ui.Grid;
import com.vaadin.ui.Grid.Column;
import com.vaadin.ui.Grid.SelectionMode;
import com.vaadin.ui.TextField;
import com.vaadin.ui.renderers.NumberRenderer;
import elemental.json.Json;
import elemental.json.JsonObject;
public class GridTest {
private Grid<String> grid;
private Column<String, String> fooColumn;
private Column<String, Integer> lengthColumn;
private Column<String, Object> objectColumn;
private Column<String, String> randomColumn;
@Before
public void setUp() {
grid = new Grid<>();
fooColumn = grid.addColumn(ValueProvider.identity()).setId("foo");
lengthColumn = grid.addColumn(String::length, new NumberRenderer())
.setId("length");
objectColumn = grid.addColumn(string -> new Object());
randomColumn = grid.addColumn(ValueProvider.identity())
.setId("randomColumnId");
}
@Test
public void testGridHeightModeChange() {
assertEquals("Initial height mode was not CSS", HeightMode.CSS,
grid.getHeightMode());
grid.setHeightByRows(13.24);
assertEquals("Setting height by rows did not change height mode",
HeightMode.ROW, grid.getHeightMode());
grid.setHeight("100px");
assertEquals("Setting height did not change height mode.",
HeightMode.CSS, grid.getHeightMode());
}
@Test(expected = IllegalArgumentException.class)
public void testFrozenColumnCountTooBig() {
grid.setFrozenColumnCount(5);
}
@Test(expected = IllegalArgumentException.class)
public void testFrozenColumnCountTooSmall() {
grid.setFrozenColumnCount(-2);
}
@Test()
public void testSetFrozenColumnCount() {
for (int i = -1; i < 2; ++i) {
grid.setFrozenColumnCount(i);
assertEquals("Frozen column count not updated", i,
grid.getFrozenColumnCount());
}
}
@Test
public void testGridColumnIdentifier() {
grid.getColumn("foo").setCaption("Bar");
assertEquals("Column header not updated correctly", "Bar",
grid.getHeaderRow(0).getCell("foo").getText());
}
@Test(expected = IllegalArgumentException.class)
public void testGridMultipleColumnsWithSameIdentifier() {
grid.addColumn(t -> t).setId("foo");
}
@Test
public void testAddSelectionListener_singleSelectMode() {
grid.setItems("foo", "bar", "baz");
Capture<SelectionEvent<String>> eventCapture = new Capture<>();
grid.addSelectionListener(event -> eventCapture.setValue(event));
grid.getSelectionModel().select("foo");
SelectionEvent<String> event = eventCapture.getValue();
assertNotNull(event);
assertFalse(event.isUserOriginated());
assertEquals("foo", event.getFirstSelectedItem().get());
assertEquals("foo",
event.getAllSelectedItems().stream().findFirst().get());
grid.getSelectionModel().select("bar");
event = eventCapture.getValue();
assertNotNull(event);
assertFalse(event.isUserOriginated());
assertEquals("bar", event.getFirstSelectedItem().get());
assertEquals("bar",
event.getAllSelectedItems().stream().findFirst().get());
grid.getSelectionModel().deselect("bar");
event = eventCapture.getValue();
assertNotNull(event);
assertFalse(event.isUserOriginated());
assertEquals(Optional.empty(), event.getFirstSelectedItem());
assertEquals(0, event.getAllSelectedItems().size());
}
@Test
public void testAddSelectionListener_multiSelectMode() {
grid.setItems("foo", "bar", "baz");
grid.setSelectionMode(SelectionMode.MULTI);
Capture<SelectionEvent<String>> eventCapture = new Capture<>();
grid.addSelectionListener(event -> eventCapture.setValue(event));
grid.getSelectionModel().select("foo");
SelectionEvent<String> event = eventCapture.getValue();
assertNotNull(event);
assertFalse(event.isUserOriginated());
assertEquals("foo", event.getFirstSelectedItem().get());
assertEquals("foo",
event.getAllSelectedItems().stream().findFirst().get());
grid.getSelectionModel().select("bar");
event = eventCapture.getValue();
assertNotNull(event);
assertFalse(event.isUserOriginated());
assertEquals("foo", event.getFirstSelectedItem().get());
assertEquals("foo",
event.getAllSelectedItems().stream().findFirst().get());
Assert.assertArrayEquals(new String[] { "foo", "bar" },
event.getAllSelectedItems().toArray(new String[2]));
grid.getSelectionModel().deselect("foo");
event = eventCapture.getValue();
assertNotNull(event);
assertFalse(event.isUserOriginated());
assertEquals("bar", event.getFirstSelectedItem().get());
assertEquals("bar",
event.getAllSelectedItems().stream().findFirst().get());
Assert.assertArrayEquals(new String[] { "bar" },
event.getAllSelectedItems().toArray(new String[1]));
grid.getSelectionModel().deselectAll();
event = eventCapture.getValue();
assertNotNull(event);
assertFalse(event.isUserOriginated());
assertEquals(Optional.empty(), event.getFirstSelectedItem());
assertEquals(0, event.getAllSelectedItems().size());
}
@Test(expected = UnsupportedOperationException.class)
public void testAddSelectionListener_noSelectionMode() {
grid.setSelectionMode(SelectionMode.NONE);
grid.addSelectionListener(
event -> Assert.fail("never ever happens (tm)"));
}
@Test
public void sortByColumn_sortOrderIsAscendingOneColumn() {
Column<String, ?> column = grid.getColumns().get(1);
grid.sort(column);
GridSortOrder<String> sortOrder = grid.getSortOrder().get(0);
Assert.assertEquals(column, sortOrder.getSorted());
Assert.assertEquals(SortDirection.ASCENDING, sortOrder.getDirection());
}
@Test
public void sortByColumnDesc_sortOrderIsDescendingOneColumn() {
Column<String, ?> column = grid.getColumns().get(1);
grid.sort(column, SortDirection.DESCENDING);
GridSortOrder<String> sortOrder = grid.getSortOrder().get(0);
Assert.assertEquals(column, sortOrder.getSorted());
Assert.assertEquals(SortDirection.DESCENDING, sortOrder.getDirection());
}
@Test
public void setSortOrder() {
Column<String, ?> column1 = grid.getColumns().get(1);
Column<String, ?> column2 = grid.getColumns().get(2);
List<GridSortOrder<String>> order = Arrays.asList(
new GridSortOrder<>(column2, SortDirection.DESCENDING),
new GridSortOrder<>(column1, SortDirection.ASCENDING));
grid.setSortOrder(order);
List<GridSortOrder<String>> sortOrder = grid.getSortOrder();
Assert.assertEquals(column2, sortOrder.get(0).getSorted());
Assert.assertEquals(SortDirection.DESCENDING,
sortOrder.get(0).getDirection());
Assert.assertEquals(column1, sortOrder.get(1).getSorted());
Assert.assertEquals(SortDirection.ASCENDING,
sortOrder.get(1).getDirection());
}
@Test
public void clearSortOrder() {
Column<String, ?> column = grid.getColumns().get(1);
grid.sort(column);
grid.clearSortOrder();
assertEquals(0, grid.getSortOrder().size());
}
@Test
public void sortListener_eventIsFired() {
Column<String, ?> column1 = grid.getColumns().get(1);
Column<String, ?> column2 = grid.getColumns().get(2);
List<GridSortOrder<String>> list = new ArrayList<>();
AtomicReference<Boolean> fired = new AtomicReference<>();
grid.addSortListener(event -> {
Assert.assertTrue(list.isEmpty());
fired.set(true);
list.addAll(event.getSortOrder());
});
grid.sort(column1, SortDirection.DESCENDING);
Assert.assertEquals(column1, list.get(0).getSorted());
Assert.assertEquals(SortDirection.DESCENDING,
list.get(0).getDirection());
List<GridSortOrder<String>> order = Arrays.asList(
new GridSortOrder<>(column2, SortDirection.DESCENDING),
new GridSortOrder<>(column1, SortDirection.ASCENDING));
list.clear();
grid.setSortOrder(order);
Assert.assertEquals(column2, list.get(0).getSorted());
Assert.assertEquals(SortDirection.DESCENDING,
list.get(0).getDirection());
Assert.assertEquals(column1, list.get(1).getSorted());
Assert.assertEquals(SortDirection.ASCENDING,
list.get(1).getDirection());
list.clear();
fired.set(false);
grid.clearSortOrder();
Assert.assertEquals(0, list.size());
Assert.assertTrue(fired.get());
}
@Test
public void beanGrid() {
Grid<Person> grid = new Grid<>(Person.class);
Column<Person, ?> nameColumn = grid.getColumn("name");
Column<Person, ?> bornColumn = grid.getColumn("born");
Assert.assertNotNull(nameColumn);
Assert.assertNotNull(bornColumn);
Assert.assertEquals("Name", nameColumn.getCaption());
Assert.assertEquals("Born", bornColumn.getCaption());
JsonObject json = getRowData(grid, new Person("Lorem", 2000));
Set<String> values = Stream.of(json.keys()).map(json::getString)
.collect(Collectors.toSet());
Assert.assertEquals(new HashSet<>(Arrays.asList("Lorem", "2000")),
values);
assertSingleSortProperty(nameColumn, "name");
assertSingleSortProperty(bornColumn, "born");
}
@Test
public void beanGrid_editor() throws ValidationException {
Grid<Person> grid = new Grid<>(Person.class);
Column<Person, ?> nameColumn = grid.getColumn("name");
TextField nameField = new TextField();
nameColumn.setEditorComponent(nameField);
Optional<Binding<Person, ?>> maybeBinding = grid.getEditor().getBinder()
.getBinding("name");
Assert.assertTrue(maybeBinding.isPresent());
Binding<Person, ?> binding = maybeBinding.get();
Assert.assertSame(nameField, binding.getField());
Person person = new Person("Lorem", 2000);
grid.getEditor().getBinder().setBean(person);
Assert.assertEquals("Lorem", nameField.getValue());
nameField.setValue("Ipsum");
Assert.assertEquals("Ipsum", person.getName());
}
@Test(expected = IllegalStateException.class)
public void oneArgSetEditor_nonBeanGrid() {
Grid<Person> grid = new Grid<>();
Column<Person, String> nameCol = grid.addColumn(Person::getName)
.setId("name");
nameCol.setEditorComponent(new TextField());
}
@Test(expected = IllegalStateException.class)
public void addExistingColumnById_throws() {
Grid<Person> grid = new Grid<>(Person.class);
grid.addColumn("name");
}
@Test
public void removeByColumn_readdById() {
Grid<Person> grid = new Grid<>(Person.class);
grid.removeColumn(grid.getColumn("name"));
grid.addColumn("name");
List<Column<Person, ?>> columns = grid.getColumns();
Assert.assertEquals(2, columns.size());
Assert.assertEquals("born", columns.get(0).getId());
Assert.assertEquals("name", columns.get(1).getId());
}
@Test
public void removeColumnByColumn() {
grid.removeColumn(fooColumn);
Assert.assertEquals(
Arrays.asList(lengthColumn, objectColumn, randomColumn),
grid.getColumns());
}
@Test
public void removeColumnByColumn_alreadyRemoved() {
grid.removeColumn(fooColumn);
// Questionable that this doesn't throw, but that's a separate ticket...
grid.removeColumn(fooColumn);
Assert.assertEquals(
Arrays.asList(lengthColumn, objectColumn, randomColumn),
grid.getColumns());
}
@Test(expected = IllegalStateException.class)
public void removeColumnById_alreadyRemoved() {
grid.removeColumn("foo");
grid.removeColumn("foo");
}
@Test
public void removeColumnById() {
grid.removeColumn("foo");
Assert.assertEquals(
Arrays.asList(lengthColumn, objectColumn, randomColumn),
grid.getColumns());
}
@Test
public void removeAllColumns() {
grid.removeAllColumns();
Assert.assertEquals(Collections.emptyList(), grid.getColumns());
}
@Test
public void removeAllColumnsInGridWithoutColumns() {
grid.removeAllColumns();
grid.removeAllColumns();
Assert.assertEquals(Collections.emptyList(), grid.getColumns());
}
@Test
public void removeFrozenColumn() {
grid.setFrozenColumnCount(3);
grid.removeColumn(fooColumn);
assertEquals(2, grid.getFrozenColumnCount());
}
@Test
public void removeHiddenFrozenColumn() {
lengthColumn.setHidden(true);
grid.setFrozenColumnCount(3);
grid.removeColumn(lengthColumn);
assertEquals(2, grid.getFrozenColumnCount());
}
@Test
public void removeNonFrozenColumn() {
grid.setFrozenColumnCount(3);
grid.removeColumn(randomColumn);
assertEquals(3, grid.getFrozenColumnCount());
}
@Test
public void testFrozenColumnRemoveColumn() {
assertEquals("Grid should not start with a frozen column", 0,
grid.getFrozenColumnCount());
int columnCount = grid.getColumns().size();
grid.setFrozenColumnCount(columnCount);
grid.removeColumn(grid.getColumns().get(0));
assertEquals(
"Frozen column count should be updated when removing a frozen column",
columnCount - 1, grid.getFrozenColumnCount());
}
@Test
public void setColumns_reorder() {
// Will remove other columns
grid.setColumns("length", "foo");
List<Column<String, ?>> columns = grid.getColumns();
Assert.assertEquals(2, columns.size());
Assert.assertEquals("length", columns.get(0).getId());
Assert.assertEquals("foo", columns.get(1).getId());
}
@Test(expected = IllegalStateException.class)
public void setColumns_addColumn_notBeangrid() {
// Not possible to add a column in a grid that cannot add columns based
// on a string
grid.setColumns("notHere");
}
@Test
public void setColumns_addColumns_beangrid() {
Grid<Person> grid = new Grid<>(Person.class);
// Remove so we can add it back
grid.removeColumn("name");
grid.setColumns("born", "name");
List<Column<Person, ?>> columns = grid.getColumns();
Assert.assertEquals(2, columns.size());
Assert.assertEquals("born", columns.get(0).getId());
Assert.assertEquals("name", columns.get(1).getId());
}
@Test
public void setColumnOrder_byColumn() {
grid.setColumnOrder(randomColumn, lengthColumn);
Assert.assertEquals(Arrays.asList(randomColumn, lengthColumn, fooColumn,
objectColumn), grid.getColumns());
}
@Test(expected = IllegalStateException.class)
public void setColumnOrder_byColumn_removedColumn() {
grid.removeColumn(randomColumn);
grid.setColumnOrder(randomColumn, lengthColumn);
}
@Test
public void setColumnOrder_byString() {
grid.setColumnOrder("randomColumnId", "length");
Assert.assertEquals(Arrays.asList(randomColumn, lengthColumn, fooColumn,
objectColumn), grid.getColumns());
}
@Test(expected = IllegalStateException.class)
public void setColumnOrder_byString_removedColumn() {
grid.removeColumn("randomColumnId");
grid.setColumnOrder("randomColumnId", "length");
}
@Test
public void defaultSorting_comparableTypes() {
testValueProviderSorting(1, 2, 3);
}
@Test
public void defaultSorting_strings() {
testValueProviderSorting("a", "b", "c");
}
@Test
public void defaultSorting_notComparable() {
assert !Comparable.class.isAssignableFrom(AtomicInteger.class);
testValueProviderSorting(new AtomicInteger(10), new AtomicInteger(8),
new AtomicInteger(9));
}
@Test
public void defaultSorting_differentComparables() {
testValueProviderSorting(10.1, 200, 3000.1, 4000);
}
@Test
public void defaultSorting_mutuallyComparableTypes() {
testValueProviderSorting(new Date(10), new java.sql.Date(1000000),
new Date(100000000));
}
private static void testValueProviderSorting(Object... expectedOrder) {
SerializableComparator<Object> comparator = new Grid<>()
.addColumn(ValueProvider.identity())
.getComparator(SortDirection.ASCENDING);
Assert.assertNotNull(comparator);
List<Object> values = new ArrayList<>(Arrays.asList(expectedOrder));
Collections.shuffle(values, new Random(42));
Assert.assertArrayEquals(expectedOrder,
values.stream().sorted(comparator).toArray());
}
@Test
public void addBeanColumn_validRenderer() {
Grid<Person> grid = new Grid<>(Person.class);
grid.removeColumn("born");
grid.addColumn("born", new NumberRenderer(new DecimalFormat("#,###",
DecimalFormatSymbols.getInstance(Locale.US))));
Person person = new Person("Name", 2017);
JsonObject rowData = getRowData(grid, person);
String formattedValue = Stream.of(rowData.keys())
.map(rowData::getString).filter(value -> !value.equals("Name"))
.findFirst().orElse(null);
Assert.assertEquals(formattedValue, "2,017");
}
@Test(expected = IllegalArgumentException.class)
public void addBeanColumn_invalidRenderer() {
Grid<Person> grid = new Grid<>(Person.class);
grid.removeColumn("name");
grid.addColumn("name", new NumberRenderer());
}
@Test
public void columnId_sortProperty() {
assertSingleSortProperty(lengthColumn, "length");
}
@Test
public void columnId_sortProperty_noId() {
Assert.assertEquals(0,
objectColumn.getSortOrder(SortDirection.ASCENDING).count());
}
@Test
public void sortProperty_setId_doesntOverride() {
objectColumn.setSortProperty("foo");
objectColumn.setId("bar");
assertSingleSortProperty(objectColumn, "foo");
}
private static void assertSingleSortProperty(Column<?, ?> column,
String expectedProperty) {
QuerySortOrder[] sortOrders = column
.getSortOrder(SortDirection.ASCENDING)
.toArray(QuerySortOrder[]::new);
Assert.assertEquals(1, sortOrders.length);
Assert.assertEquals(SortDirection.ASCENDING,
sortOrders[0].getDirection());
Assert.assertEquals(expectedProperty, sortOrders[0].getSorted());
}
private static <T> JsonObject getRowData(Grid<T> grid, T row) {
JsonObject json = Json.createObject();
if (grid.getColumns().isEmpty()) {
return json;
}
// generateData only works if Grid is attached
new MockUI().setContent(grid);
grid.getColumns().forEach(column -> column.generateData(row, json));
// Detach again
grid.getUI().setContent(null);
return json.getObject("d");
}
}