diff options
author | Pekka Hyvönen <pekka@vaadin.com> | 2016-11-11 09:41:43 +0200 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2016-11-29 10:18:11 +0000 |
commit | f2d8f812efa067b4baa7e27c0ea76f7596b291e6 (patch) | |
tree | 8e35e6c4eef4ffc5f8006d30989914da1deeab1b /server/src/test | |
parent | 13443562ccbd633ceb561bb87893014f65437ad1 (diff) | |
download | vaadin-framework-f2d8f812efa067b4baa7e27c0ea76f7596b291e6.tar.gz vaadin-framework-f2d8f812efa067b4baa7e27c0ea76f7596b291e6.zip |
Add MultiSelect support for Grid
Still missing following things coming in next patches:
- select all checkbox
- firing an event when data provider is changed in grid
- read only selection models for grid
Part 1 for vaadin/framework8-issues#232
Change-Id: Ib2c7c81a838f43cb7c521a56d50139c91961f54a
Diffstat (limited to 'server/src/test')
4 files changed, 911 insertions, 29 deletions
diff --git a/server/src/test/java/com/vaadin/data/GridAsMultiSelectInBinder.java b/server/src/test/java/com/vaadin/data/GridAsMultiSelectInBinder.java new file mode 100644 index 0000000000..e31b2ab188 --- /dev/null +++ b/server/src/test/java/com/vaadin/data/GridAsMultiSelectInBinder.java @@ -0,0 +1,270 @@ +package com.vaadin.data; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.util.converter.Converter; +import com.vaadin.data.util.converter.ValueContext; +import com.vaadin.tests.data.bean.BeanWithEnums; +import com.vaadin.tests.data.bean.Sex; +import com.vaadin.tests.data.bean.TestEnum; +import com.vaadin.ui.Grid; +import com.vaadin.ui.MultiSelect; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; +import com.vaadin.ui.components.grid.SingleSelectionModelImpl; + +public class GridAsMultiSelectInBinder + extends BinderTestBase<Binder<BeanWithEnums>, BeanWithEnums> { + public class TestEnumSetToStringConverter + implements Converter<Set<TestEnum>, String> { + @Override + public Result<String> convertToModel(Set<TestEnum> value, + ValueContext context) { + return Result.ok(value.stream().map(TestEnum::name) + .collect(Collectors.joining(","))); + } + + @Override + public Set<TestEnum> convertToPresentation(String value, + ValueContext context) { + return Stream.of(value.split(",")) + .filter(string -> !string.isEmpty()).map(TestEnum::valueOf) + .collect(Collectors.toSet()); + } + } + + private class CustomMultiSelectModel extends MultiSelectionModelImpl<Sex> { + + public CustomMultiSelectModel(Grid<Sex> grid) { + super(grid); + } + + @Override + public void updateSelection(Set<Sex> addedItems, Set<Sex> removedItems, + boolean userOriginated) { + super.updateSelection(addedItems, removedItems, userOriginated); + } + + } + + private Binder<AtomicReference<String>> converterBinder = new Binder<>(); + private Grid<TestEnum> grid; + private MultiSelect<TestEnum> select; + + @Before + public void setUp() { + binder = new Binder<>(); + item = new BeanWithEnums(); + grid = new Grid<>(); + grid.setItems(TestEnum.values()); + grid.setSelectionModel(new MultiSelectionModelImpl<>(grid)); + select = grid.asMultiSelect(); + + converterBinder.forField(select) + .withConverter(new TestEnumSetToStringConverter()) + .bind(AtomicReference::get, AtomicReference::set); + } + + @Test(expected = IllegalStateException.class) + public void boundGridInBinder_selectionModelChanged_throws() { + grid.setSelectionModel(new SingleSelectionModelImpl<>(grid)); + + select.select(TestEnum.ONE); + } + + @Test + public void beanBound_bindSelectByShortcut_selectionUpdated() { + item.setEnums(Collections.singleton(TestEnum.ONE)); + binder.setBean(item); + binder.bind(select, BeanWithEnums::getEnums, BeanWithEnums::setEnums); + + assertEquals(Collections.singleton(TestEnum.ONE), + select.getSelectedItems()); + } + + @Test + public void beanBound_bindSelect_selectionUpdated() { + item.setEnums(Collections.singleton(TestEnum.TWO)); + binder.setBean(item); + binder.forField(select).bind(BeanWithEnums::getEnums, + BeanWithEnums::setEnums); + + assertEquals(Collections.singleton(TestEnum.TWO), + select.getSelectedItems()); + } + + @Test + public void selectBound_bindBeanWithoutEnums_selectedItemNotPresent() { + bindEnum(); + + assertTrue(select.getSelectedItems().isEmpty()); + } + + @Test + public void selectBound_bindBean_selectionUpdated() { + item.setEnums(Collections.singleton(TestEnum.ONE)); + bindEnum(); + + assertEquals(Collections.singleton(TestEnum.ONE), + select.getSelectedItems()); + } + + @Test + public void bound_setSelection_beanValueUpdated() { + bindEnum(); + + select.select(TestEnum.TWO); + + assertEquals(Collections.singleton(TestEnum.TWO), item.getEnums()); + } + + @Test + public void bound_setSelection_beanValueIsACopy() { + bindEnum(); + + select.select(TestEnum.TWO); + + Set<TestEnum> enums = item.getEnums(); + + binder.setBean(new BeanWithEnums()); + select.select(TestEnum.ONE); + + assertEquals(Collections.singleton(TestEnum.TWO), enums); + } + + @Test + public void bound_deselect_beanValueUpdatedToNull() { + item.setEnums(Collections.singleton(TestEnum.ONE)); + bindEnum(); + + select.deselect(TestEnum.ONE); + + assertTrue(item.getEnums().isEmpty()); + } + + @Test + public void unbound_changeSelection_beanValueNotUpdated() { + item.setEnums(Collections.singleton(TestEnum.ONE)); + bindEnum(); + binder.removeBean(); + + select.select(TestEnum.TWO); + + assertEquals(Collections.singleton(TestEnum.ONE), item.getEnums()); + } + + @Test + public void withConverter_load_selectUpdated() { + converterBinder.readBean(new AtomicReference<>("TWO")); + + assertEquals(Collections.singleton(TestEnum.TWO), + select.getSelectedItems()); + } + + @Test + public void withConverter_save_referenceUpdated() { + select.select(TestEnum.ONE); + select.select(TestEnum.TWO); + + AtomicReference<String> reference = new AtomicReference<>(""); + converterBinder.writeBeanIfValid(reference); + + assertEquals("ONE,TWO", reference.get()); + } + + @Test + public void withValidator_validate_validatorUsed() { + binder.forField(select) + .withValidator(selection -> selection.size() % 2 == 1, + "Must select odd number of items") + .bind(BeanWithEnums::getEnums, BeanWithEnums::setEnums); + binder.setBean(item); + + assertFalse(binder.validate().isOk()); + + select.select(TestEnum.TWO); + + assertTrue(binder.validate().isOk()); + } + + @Test + public void addValueChangeListener_selectionUpdated_eventTriggeredForMultiSelect() { + Grid<Sex> grid = new Grid<>(); + CustomMultiSelectModel model = new CustomMultiSelectModel(grid); + grid.setSelectionModel(model); + grid.setItems(Sex.values()); + MultiSelect<Sex> select = grid.asMultiSelect(); + + List<Sex> selected = new ArrayList<>(); + List<Boolean> userOriginated = new ArrayList<>(); + select.addValueChangeListener(event -> { + selected.addAll(event.getValue()); + userOriginated.add(event.isUserOriginated()); + assertSame(grid, event.getComponent()); + // cannot compare that the event source is the select since a new + // MultiSelect wrapper object has been created for the event + + assertEquals(select.getValue(), event.getValue()); + }); + + select.select(Sex.UNKNOWN); + + assertEquals(Arrays.asList(Sex.UNKNOWN), selected); + + model.updateSelection(new LinkedHashSet<>(Arrays.asList(Sex.MALE)), + Collections.emptySet(), true); // simulate client side selection + assertEquals(Arrays.asList(Sex.UNKNOWN, Sex.UNKNOWN, Sex.MALE), + selected); + selected.clear(); + + select.select(Sex.MALE); // NOOP + assertEquals(Arrays.asList(), selected); + selected.clear(); + + model.updateSelection(Collections.emptySet(), + new LinkedHashSet<>(Arrays.asList(Sex.UNKNOWN)), true); // client + // side + // deselect + assertEquals(Arrays.asList(Sex.MALE), selected); + selected.clear(); + + select.deselect(Sex.UNKNOWN); // NOOP + assertEquals(Arrays.asList(), selected); + selected.clear(); + + select.deselect(Sex.FEMALE, Sex.MALE); // partly NOOP + assertEquals(Arrays.asList(), selected); + + model.selectItems(Sex.FEMALE, Sex.MALE); + assertEquals(Arrays.asList(Sex.FEMALE, Sex.MALE), selected); + selected.clear(); + + model.updateSelection(new LinkedHashSet<>(Arrays.asList(Sex.FEMALE)), + Collections.emptySet(), true); // client side NOOP + assertEquals(Arrays.asList(), selected); + + assertEquals(Arrays.asList(false, true, true, false, false), + userOriginated); + } + + protected void bindEnum() { + binder.forField(select).bind(BeanWithEnums::getEnums, + BeanWithEnums::setEnums); + binder.setBean(item); + } +} diff --git a/server/src/test/java/com/vaadin/data/GridAsSingleSelectInBinder.java b/server/src/test/java/com/vaadin/data/GridAsSingleSelectInBinder.java index 7b34c78f25..cf649384e0 100644 --- a/server/src/test/java/com/vaadin/data/GridAsSingleSelectInBinder.java +++ b/server/src/test/java/com/vaadin/data/GridAsSingleSelectInBinder.java @@ -16,7 +16,8 @@ import com.vaadin.tests.data.bean.Person; import com.vaadin.tests.data.bean.Sex; import com.vaadin.ui.Grid; import com.vaadin.ui.SingleSelect; -import com.vaadin.ui.components.grid.SingleSelectionModel; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; +import com.vaadin.ui.components.grid.SingleSelectionModelImpl; public class GridAsSingleSelectInBinder extends BinderTestBase<Binder<Person>, Person> { @@ -29,7 +30,7 @@ public class GridAsSingleSelectInBinder } } - private class CustomSingleSelectModel extends SingleSelectionModel<Sex> { + private class CustomSingleSelectModel extends SingleSelectionModelImpl<Sex> { public CustomSingleSelectModel(Grid<Sex> grid) { super(grid); @@ -52,6 +53,13 @@ public class GridAsSingleSelectInBinder select = grid.asSingleSelect(); } + @Test(expected = IllegalStateException.class) + public void boundGridInBinder_selectionModelChanged_throws() { + grid.setSelectionModel(new MultiSelectionModelImpl<>(grid)); + + select.setValue(Sex.MALE); + } + @Test public void personBound_bindSelectByShortcut_selectionUpdated() { item.setSex(Sex.FEMALE); @@ -117,8 +125,6 @@ public class GridAsSingleSelectInBinder @Test public void addValueChangeListener_selectionUpdated_eventTriggeredForSelect() { - binder = new Binder<>(); - item = new Person(); GridWithCustomSingleSelectionModel grid = new GridWithCustomSingleSelectionModel(); CustomSingleSelectModel model = new CustomSingleSelectModel(grid); grid.setSelectionModel(model); diff --git a/server/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionModelTest.java b/server/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionModelTest.java new file mode 100644 index 0000000000..d7a087be54 --- /dev/null +++ b/server/src/test/java/com/vaadin/tests/components/grid/GridMultiSelectionModelTest.java @@ -0,0 +1,543 @@ +package com.vaadin.tests.components.grid; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import org.easymock.Capture; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import com.vaadin.data.HasValue.ValueChangeEvent; +import com.vaadin.event.selection.SingleSelectionEvent; +import com.vaadin.event.selection.SingleSelectionListener; +import com.vaadin.server.data.provider.bov.Person; +import com.vaadin.shared.Registration; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.GridSelectionModel; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; +import com.vaadin.ui.components.grid.SingleSelectionModelImpl; + +import elemental.json.JsonObject; + +public class GridMultiSelectionModelTest { + + public static final Person PERSON_C = new Person("c", 3); + public static final Person PERSON_B = new Person("b", 2); + public static final Person PERSON_A = new Person("a", 1); + + private Grid<Person> grid; + private MultiSelectionModelImpl<Person> selectionModel; + private Capture<List<Person>> currentSelectionCapture; + private Capture<List<Person>> oldSelectionCapture; + private AtomicInteger events; + + public static class CustomMultiSelectionModel + extends MultiSelectionModelImpl<String> { + public final Map<String, Boolean> generatedData = new LinkedHashMap<>(); + + public CustomMultiSelectionModel(Grid<String> grid) { + super(grid); + } + + @Override + public void generateData(String item, JsonObject jsonObject) { + super.generateData(item, jsonObject); + // capture updated row + generatedData.put(item, isSelected(item)); + } + + } + + @Before + public void setUp() { + grid = new Grid<>(); + selectionModel = new MultiSelectionModelImpl<>(grid); + grid.setSelectionModel(selectionModel); + grid.setItems(PERSON_A, PERSON_B, PERSON_C); + + currentSelectionCapture = new Capture<>(); + oldSelectionCapture = new Capture<>(); + events = new AtomicInteger(); + + selectionModel.addSelectionListener(event -> { + currentSelectionCapture + .setValue(new ArrayList<>(event.getNewSelection())); + oldSelectionCapture + .setValue(new ArrayList<>(event.getOldSelection())); + events.incrementAndGet(); + }); + } + + @Test(expected = IllegalStateException.class) + public void selectionModelChanged_usingPreviousSelectionModel_throws() { + grid.setSelectionModel(new SingleSelectionModelImpl<>(grid)); + + selectionModel.select(PERSON_A); + } + + @Test + public void changingSelectionModel_firesSelectionEvent() { + Grid<String> customGrid = new Grid<>(); + customGrid.setSelectionModel(new MultiSelectionModelImpl<>(customGrid)); + customGrid.setItems("Foo", "Bar", "Baz"); + + List<String> selectionChanges = new ArrayList<>(); + Capture<List<String>> oldSelectionCapture = new Capture<>(); + ((MultiSelectionModelImpl<String>) customGrid.getSelectionModel()) + .addSelectionListener(e -> { + selectionChanges.addAll(e.getValue()); + oldSelectionCapture + .setValue(new ArrayList<>(e.getOldSelection())); + }); + + customGrid.getSelectionModel().select("Foo"); + assertEquals(Arrays.asList("Foo"), selectionChanges); + selectionChanges.clear(); + + customGrid.getSelectionModel().select("Bar"); + assertEquals("Foo", + customGrid.getSelectionModel().getFirstSelectedItem().get()); + assertEquals(Arrays.asList("Foo", "Bar"), selectionChanges); + selectionChanges.clear(); + + customGrid.setSelectionModel(new SingleSelectionModelImpl<>(customGrid)); + assertFalse(customGrid.getSelectionModel().getFirstSelectedItem() + .isPresent()); + assertEquals(Arrays.asList(), selectionChanges); + assertEquals(Arrays.asList("Foo", "Bar"), + oldSelectionCapture.getValue()); + } + + @Test + public void serverSideSelection_GridChangingSelectionModel_sendsUpdatedRowsToClient() { + Grid<String> customGrid = new Grid<>(); + customGrid.setItems("Foo", "Bar", "Baz"); + + CustomMultiSelectionModel customModel = new CustomMultiSelectionModel( + customGrid); + customGrid.setSelectionModel(customModel); + customGrid.getDataCommunicator().beforeClientResponse(true); + + Assert.assertFalse("Item should have been updated as selected", + customModel.generatedData.get("Foo")); + Assert.assertFalse("Item should have been updated as NOT selected", + customModel.generatedData.get("Bar")); + Assert.assertFalse("Item should have been updated as NOT selected", + customModel.generatedData.get("Baz")); + + customModel.generatedData.clear(); + + customGrid.getSelectionModel().select("Foo"); + customGrid.getDataCommunicator().beforeClientResponse(false); + + Assert.assertTrue("Item should have been updated as selected", + customModel.generatedData.get("Foo")); + Assert.assertFalse("Item should have NOT been updated", + customModel.generatedData.containsKey("Bar")); + Assert.assertFalse("Item should have NOT been updated", + customModel.generatedData.containsKey("Baz")); + + customModel.generatedData.clear(); + + customModel.updateSelection(asSet("Bar"), asSet("Foo")); + customGrid.getDataCommunicator().beforeClientResponse(false); + + Assert.assertFalse("Item should have been updated as NOT selected", + customModel.generatedData.get("Foo")); + Assert.assertTrue("Item should have been updated as selected", + customModel.generatedData.get("Bar")); + Assert.assertFalse("Item should have NOT been updated", + customModel.generatedData.containsKey("Baz")); + + // switch to single to cause event + customModel.generatedData.clear(); + customGrid.setSelectionModel(new SingleSelectionModelImpl<>(customGrid)); + customGrid.getDataCommunicator().beforeClientResponse(false); + + // changing selection model should trigger row updates, but the old + // selection model is not triggered as it has been removed + Assert.assertTrue(customModel.generatedData.isEmpty()); // not triggered + } + + @Test + public void select_gridWithStrings() { + Grid<String> gridWithStrings = new Grid<>(); + gridWithStrings + .setSelectionModel(new MultiSelectionModelImpl<>(gridWithStrings)); + gridWithStrings.setItems("Foo", "Bar", "Baz"); + + GridSelectionModel<String> model = gridWithStrings.getSelectionModel(); + Assert.assertFalse(model.isSelected("Foo")); + + model.select("Foo"); + Assert.assertTrue(model.isSelected("Foo")); + Assert.assertEquals(Optional.of("Foo"), model.getFirstSelectedItem()); + + model.select("Bar"); + Assert.assertTrue(model.isSelected("Foo")); + Assert.assertTrue(model.isSelected("Bar")); + Assert.assertEquals(Arrays.asList("Foo", "Bar"), + new ArrayList<>(model.getSelectedItems())); + + model.deselect("Bar"); + Assert.assertFalse(model.isSelected("Bar")); + Assert.assertTrue(model.getFirstSelectedItem().isPresent()); + Assert.assertEquals(Arrays.asList("Foo"), + new ArrayList<>(model.getSelectedItems())); + } + + @Test + public void select() { + selectionModel.select(PERSON_B); + + assertEquals(PERSON_B, + selectionModel.getFirstSelectedItem().orElse(null)); + assertEquals(Optional.of(PERSON_B), + selectionModel.getFirstSelectedItem()); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(PERSON_B), + currentSelectionCapture.getValue()); + + selectionModel.select(PERSON_A); + assertEquals(PERSON_B, + selectionModel.getFirstSelectedItem().orElse(null)); + + assertTrue(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(PERSON_B, PERSON_A), + currentSelectionCapture.getValue()); + assertEquals(2, events.get()); + } + + @Test + public void deselect() { + selectionModel.select(PERSON_B); + selectionModel.deselect(PERSON_B); + + assertFalse(selectionModel.getFirstSelectedItem().isPresent()); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(2, events.get()); + } + + @Test + public void selectItems() { + selectionModel.selectItems(PERSON_C, PERSON_B); + + assertEquals(PERSON_C, + selectionModel.getFirstSelectedItem().orElse(null)); + assertEquals(Optional.of(PERSON_C), + selectionModel.getFirstSelectedItem()); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(PERSON_C, PERSON_B), + currentSelectionCapture.getValue()); + + selectionModel.selectItems(PERSON_A, PERSON_C); // partly NOOP + assertTrue(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(PERSON_C, PERSON_B, PERSON_A), + currentSelectionCapture.getValue()); + assertEquals(2, events.get()); + } + + @Test + public void deselectItems() { + selectionModel.selectItems(PERSON_C, PERSON_A, PERSON_B); + + selectionModel.deselectItems(PERSON_A); + assertEquals(PERSON_C, + selectionModel.getFirstSelectedItem().orElse(null)); + assertEquals(Optional.of(PERSON_C), + selectionModel.getFirstSelectedItem()); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(PERSON_C, PERSON_B), + currentSelectionCapture.getValue()); + + selectionModel.deselectItems(PERSON_A, PERSON_B, PERSON_C); + assertNull(selectionModel.getFirstSelectedItem().orElse(null)); + assertEquals(Optional.empty(), selectionModel.getFirstSelectedItem()); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(3, events.get()); + } + + @Test + public void selectionEvent_newSelection_oldSelection() { + selectionModel.selectItems(PERSON_C, PERSON_A, PERSON_B); + + assertEquals(Arrays.asList(PERSON_C, PERSON_A, PERSON_B), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(), oldSelectionCapture.getValue()); + + selectionModel.deselect(PERSON_A); + + assertEquals(Arrays.asList(PERSON_C, PERSON_B), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_C, PERSON_A, PERSON_B), + oldSelectionCapture.getValue()); + + selectionModel.deselectItems(PERSON_A, PERSON_B, PERSON_C); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_C, PERSON_B), + oldSelectionCapture.getValue()); + + selectionModel.selectItems(PERSON_A); + assertEquals(Arrays.asList(PERSON_A), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(), oldSelectionCapture.getValue()); + + selectionModel.updateSelection( + new LinkedHashSet<>(Arrays.asList(PERSON_B, PERSON_C)), + new LinkedHashSet<>(Arrays.asList(PERSON_A))); + assertEquals(Arrays.asList(PERSON_B, PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_A), oldSelectionCapture.getValue()); + + selectionModel.deselectAll(); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_B, PERSON_C), + oldSelectionCapture.getValue()); + + selectionModel.select(PERSON_C); + assertEquals(Arrays.asList(PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(), oldSelectionCapture.getValue()); + + selectionModel.deselect(PERSON_C); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_C), oldSelectionCapture.getValue()); + } + + @Test + public void deselectAll() { + selectionModel.selectItems(PERSON_A, PERSON_C, PERSON_B); + + assertTrue(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_A, PERSON_C, PERSON_B), + currentSelectionCapture.getValue()); + assertEquals(1, events.get()); + + selectionModel.deselectAll(); + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_A, PERSON_C, PERSON_B), + oldSelectionCapture.getValue()); + assertEquals(2, events.get()); + + selectionModel.select(PERSON_C); + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(), oldSelectionCapture.getValue()); + assertEquals(3, events.get()); + + selectionModel.deselectAll(); + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_C), oldSelectionCapture.getValue()); + assertEquals(4, events.get()); + + selectionModel.deselectAll(); + assertEquals(4, events.get()); + } + + @Test + public void updateSelection() { + selectionModel.updateSelection(asSet(PERSON_A), Collections.emptySet()); + + assertTrue(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_A), + currentSelectionCapture.getValue()); + assertEquals(1, events.get()); + + selectionModel.updateSelection(asSet(PERSON_B), asSet(PERSON_A)); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_B), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_A), oldSelectionCapture.getValue()); + assertEquals(2, events.get()); + + selectionModel.updateSelection(asSet(PERSON_B), asSet(PERSON_A)); // NOOP + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_B), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_A), oldSelectionCapture.getValue()); + assertEquals(2, events.get()); + + selectionModel.updateSelection(asSet(PERSON_A, PERSON_C), + asSet(PERSON_A)); // partly NOOP + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_B, PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_B), oldSelectionCapture.getValue()); + assertEquals(3, events.get()); + + selectionModel.updateSelection(asSet(PERSON_B, PERSON_A), + asSet(PERSON_B)); // partly NOOP + + assertTrue(selectionModel.isSelected(PERSON_A)); + assertTrue(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(PERSON_B, PERSON_C, PERSON_A), + currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_B, PERSON_C), + oldSelectionCapture.getValue()); + assertEquals(4, events.get()); + + selectionModel.updateSelection(asSet(), + asSet(PERSON_B, PERSON_A, PERSON_C)); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(Arrays.asList(PERSON_B, PERSON_C, PERSON_A), + oldSelectionCapture.getValue()); + assertEquals(5, events.get()); + } + + private <T> Set<T> asSet(@SuppressWarnings("unchecked") T... people) { + return new LinkedHashSet<>(Arrays.asList(people)); + } + + @Test + public void selectTwice() { + selectionModel.select(PERSON_C); + selectionModel.select(PERSON_C); + + assertEquals(PERSON_C, + selectionModel.getFirstSelectedItem().orElse(null)); + + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertTrue(selectionModel.isSelected(PERSON_C)); + + assertEquals(Optional.of(PERSON_C), + selectionModel.getFirstSelectedItem()); + + assertEquals(Arrays.asList(PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(1, events.get()); + } + + @Test + public void deselectTwice() { + selectionModel.select(PERSON_C); + assertEquals(Arrays.asList(PERSON_C), + currentSelectionCapture.getValue()); + assertEquals(1, events.get()); + + selectionModel.deselect(PERSON_C); + + assertFalse(selectionModel.getFirstSelectedItem().isPresent()); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(2, events.get()); + + selectionModel.deselect(PERSON_C); + + assertFalse(selectionModel.getFirstSelectedItem().isPresent()); + assertFalse(selectionModel.isSelected(PERSON_A)); + assertFalse(selectionModel.isSelected(PERSON_B)); + assertFalse(selectionModel.isSelected(PERSON_C)); + assertEquals(Arrays.asList(), currentSelectionCapture.getValue()); + assertEquals(2, events.get()); + } + + @SuppressWarnings({ "serial" }) + @Test + public void addValueChangeListener() { + AtomicReference<SingleSelectionListener<String>> selectionListener = new AtomicReference<>(); + Registration registration = Mockito.mock(Registration.class); + Grid<String> grid = new Grid<>(); + grid.setItems("foo", "bar"); + String value = "foo"; + SingleSelectionModelImpl<String> select = new SingleSelectionModelImpl<String>( + grid) { + @Override + public Registration addSelectionListener( + SingleSelectionListener<String> listener) { + selectionListener.set(listener); + return registration; + } + + @Override + public Optional<String> getSelectedItem() { + return Optional.of(value); + } + }; + + AtomicReference<ValueChangeEvent<?>> event = new AtomicReference<>(); + Registration actualRegistration = select.addSelectionListener(evt -> { + Assert.assertNull(event.get()); + event.set(evt); + }); + Assert.assertSame(registration, actualRegistration); + + selectionListener.get().accept(new SingleSelectionEvent<>(grid, + select.asSingleSelect(), true)); + + Assert.assertEquals(grid, event.get().getComponent()); + Assert.assertEquals(value, event.get().getValue()); + Assert.assertTrue(event.get().isUserOriginated()); + } +} diff --git a/server/src/test/java/com/vaadin/tests/components/grid/GridSingleSelectionModelTest.java b/server/src/test/java/com/vaadin/tests/components/grid/GridSingleSelectionModelTest.java index 67b35c5208..17b409c1a6 100644 --- a/server/src/test/java/com/vaadin/tests/components/grid/GridSingleSelectionModelTest.java +++ b/server/src/test/java/com/vaadin/tests/components/grid/GridSingleSelectionModelTest.java @@ -6,7 +6,9 @@ import static org.junit.Assert.assertTrue; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; @@ -20,62 +22,124 @@ import com.vaadin.event.selection.SingleSelectionEvent; import com.vaadin.event.selection.SingleSelectionListener; import com.vaadin.server.data.provider.bov.Person; import com.vaadin.shared.Registration; -import com.vaadin.shared.data.DataCommunicatorClientRpc; import com.vaadin.ui.Grid; import com.vaadin.ui.Grid.GridSelectionModel; -import com.vaadin.ui.components.grid.SingleSelectionModel; +import com.vaadin.ui.components.grid.MultiSelectionModelImpl; +import com.vaadin.ui.components.grid.SingleSelectionModelImpl; + +import elemental.json.JsonObject; public class GridSingleSelectionModelTest { public static final Person PERSON_C = new Person("c", 3); public static final Person PERSON_B = new Person("b", 2); public static final Person PERSON_A = new Person("a", 1); - public static final String RPC_INTERFACE = DataCommunicatorClientRpc.class - .getName(); - private class CustomSelectionModelGrid extends Grid<String> { - public void switchSelectionModel() { - // just switch selection model to cause event - setSelectionModel(new SingleSelectionModel(this)); + public static class CustomSingleSelectionModel + extends SingleSelectionModelImpl<String> { + public final Map<String, Boolean> generatedData = new LinkedHashMap<>(); + + public CustomSingleSelectionModel(Grid<String> grid) { + super(grid); } + + @Override + public void generateData(String item, JsonObject jsonObject) { + super.generateData(item, jsonObject); + // capture updated row + generatedData.put(item, isSelected(item)); + } + } private List<Person> selectionChanges; private Grid<Person> grid; - private SingleSelectionModel<Person> selectionModel; + private SingleSelectionModelImpl<Person> selectionModel; @Before public void setUp() { grid = new Grid<>(); grid.setItems(PERSON_A, PERSON_B, PERSON_C); - selectionModel = (SingleSelectionModel<Person>) grid + selectionModel = (SingleSelectionModelImpl<Person>) grid .getSelectionModel(); selectionChanges = new ArrayList<>(); - selectionModel.addSelectionChangeListener( - e -> selectionChanges.add(e.getValue())); + selectionModel + .addSelectionListener(e -> selectionChanges.add(e.getValue())); + } + + @Test(expected = IllegalStateException.class) + public void selectionModelChanged_usingPreviousSelectionModel_throws() { + grid.setSelectionModel(new MultiSelectionModelImpl<>(grid)); + + selectionModel.select(PERSON_A); } @Test - public void testGridChangingSelectionModel_firesSelectionChangeEvent() { - CustomSelectionModelGrid customGrid = new CustomSelectionModelGrid(); + public void gridChangingSelectionModel_firesSelectionChangeEvent() { + Grid<String> customGrid = new Grid<>(); customGrid.setItems("Foo", "Bar", "Baz"); List<String> selectionChanges = new ArrayList<>(); - ((SingleSelectionModel<String>) customGrid.getSelectionModel()) - .addSelectionChangeListener( - e -> selectionChanges.add(e.getValue())); + ((SingleSelectionModelImpl<String>) customGrid.getSelectionModel()) + .addSelectionListener(e -> selectionChanges.add(e.getValue())); customGrid.getSelectionModel().select("Foo"); assertEquals("Foo", customGrid.getSelectionModel().getFirstSelectedItem().get()); assertEquals(Arrays.asList("Foo"), selectionChanges); - customGrid.switchSelectionModel(); + customGrid + .setSelectionModel(new CustomSingleSelectionModel(customGrid)); assertEquals(Arrays.asList("Foo", null), selectionChanges); } @Test + public void serverSideSelection_GridChangingSelectionModel_sendsUpdatedRowsToClient() { + Grid<String> customGrid = new Grid<>(); + customGrid.setItems("Foo", "Bar", "Baz"); + + CustomSingleSelectionModel customModel = new CustomSingleSelectionModel( + customGrid); + customGrid.setSelectionModel(customModel); + + customGrid.getDataCommunicator().beforeClientResponse(true); + + Assert.assertFalse("Item should have been updated as selected", + customModel.generatedData.get("Foo")); + Assert.assertFalse("Item should have been updated as NOT selected", + customModel.generatedData.get("Bar")); + Assert.assertFalse("Item should have been updated as NOT selected", + customModel.generatedData.get("Baz")); + + customModel.generatedData.clear(); + + customGrid.getSelectionModel().select("Foo"); + customGrid.getDataCommunicator().beforeClientResponse(false); + + Assert.assertTrue("Item should have been updated as selected", + customModel.generatedData.get("Foo")); + Assert.assertFalse("Item should have NOT been updated", + customModel.generatedData.containsKey("Bar")); + Assert.assertFalse("Item should have NOT been updated", + customModel.generatedData.containsKey("Baz")); + + // switch to another selection model to cause event + customModel.generatedData.clear(); + customGrid.setSelectionModel(new SingleSelectionModelImpl<>(customGrid)); + customGrid.getDataCommunicator().beforeClientResponse(false); + + // since the selection model has been removed, it is no longer a data + // generator for the data communicator, would need to verify somehow + // that row is not marked as selected anymore ? (done in UI tests) + Assert.assertTrue(customModel.generatedData.isEmpty()); // at least + // removed + // selection + // model is not + // triggered + } + + @Test public void testGridWithSingleSelection() { Grid<String> gridWithStrings = new Grid<>(); gridWithStrings.setItems("Foo", "Bar", "Baz"); @@ -210,10 +274,10 @@ public class GridSingleSelectionModelTest { Grid<String> grid = new Grid<>(); grid.setItems("foo", "bar"); String value = "foo"; - SingleSelectionModel<String> select = new SingleSelectionModel<String>( + SingleSelectionModelImpl<String> select = new SingleSelectionModelImpl<String>( grid) { @Override - public Registration addSelectionChangeListener( + public Registration addSelectionListener( SingleSelectionListener<String> listener) { selectionListener.set(listener); return registration; @@ -226,11 +290,10 @@ public class GridSingleSelectionModelTest { }; AtomicReference<ValueChangeEvent<?>> event = new AtomicReference<>(); - Registration actualRegistration = select - .addSelectionChangeListener(evt -> { - Assert.assertNull(event.get()); - event.set(evt); - }); + Registration actualRegistration = select.addSelectionListener(evt -> { + Assert.assertNull(event.get()); + event.set(evt); + }); Assert.assertSame(registration, actualRegistration); selectionListener.get().accept(new SingleSelectionEvent<>(grid, |