diff options
14 files changed, 715 insertions, 22 deletions
diff --git a/server/src/main/java/com/vaadin/server/data/AbstractDataSource.java b/server/src/main/java/com/vaadin/server/data/AbstractDataSource.java new file mode 100644 index 0000000000..9dba0bd35e --- /dev/null +++ b/server/src/main/java/com/vaadin/server/data/AbstractDataSource.java @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.data; + +import java.lang.reflect.Method; +import java.util.EventObject; +import java.util.Objects; + +import com.vaadin.event.EventRouter; +import com.vaadin.shared.Registration; + +/** + * Abstract data source implementation which takes care of refreshing data from + * the underlying data provider. + * + * @author Vaadin Ltd + * @since 8.0 + * + */ +public abstract class AbstractDataSource<T> implements DataSource<T> { + + private EventRouter eventRouter; + + @Override + public Registration addDataSourceListener(DataSourceListener listener) { + Objects.requireNonNull(listener, "listener cannot be null"); + addListener(DataChangeEvent.class, listener, + DataSourceListener.class.getMethods()[0]); + return () -> removeListener(DataChangeEvent.class, listener); + } + + @Override + public void refreshAll() { + fireEvent(new DataChangeEvent(this)); + } + + /** + * Registers a new listener with the specified activation method to listen + * events generated by this component. If the activation method does not + * have any arguments the event object will not be passed to it when it's + * called. + * + * @param eventType + * the type of the listened event. Events of this type or its + * subclasses activate the listener. + * @param listener + * the object instance who owns the activation method. + * @param method + * the activation method. + * + */ + protected void addListener(Class<?> eventType, DataSourceListener listener, + Method method) { + if (eventRouter == null) { + eventRouter = new EventRouter(); + } + eventRouter.addListener(eventType, listener, method); + } + + /** + * Removes all registered listeners matching the given parameters. Since + * this method receives the event type and the listener object as + * parameters, it will unregister all <code>object</code>'s methods that are + * registered to listen to events of type <code>eventType</code> generated + * by this component. + * + * @param eventType + * the exact event type the <code>object</code> listens to. + * @param listener + * the target object that has registered to listen to events of + * type <code>eventType</code> with one or more methods. + */ + protected void removeListener(Class<?> eventType, + DataSourceListener listener) { + if (eventRouter != null) { + eventRouter.removeListener(eventType, listener); + } + } + + /** + * Sends the event to all listeners. + * + * @param event + * the Event to be sent to all listeners. + */ + protected void fireEvent(EventObject event) { + if (eventRouter != null) { + eventRouter.fireEvent(event); + } + } +} diff --git a/server/src/main/java/com/vaadin/server/data/BackEndDataSource.java b/server/src/main/java/com/vaadin/server/data/BackEndDataSource.java index 34379deb60..e0cfc8f48b 100644 --- a/server/src/main/java/com/vaadin/server/data/BackEndDataSource.java +++ b/server/src/main/java/com/vaadin/server/data/BackEndDataSource.java @@ -27,7 +27,7 @@ import java.util.stream.Stream; * @param <T> * data source data type */ -public class BackEndDataSource<T> implements DataSource<T> { +public class BackEndDataSource<T> extends AbstractDataSource<T> { private Function<Query, Stream<T>> request; private Function<Query, Integer> sizeCallback; diff --git a/server/src/main/java/com/vaadin/server/data/DataChangeEvent.java b/server/src/main/java/com/vaadin/server/data/DataChangeEvent.java new file mode 100644 index 0000000000..cf2e9621de --- /dev/null +++ b/server/src/main/java/com/vaadin/server/data/DataChangeEvent.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.data; + +import java.util.EventObject; + +/** + * An event fired when the data of a {@code DataSource} changes. + * + * + * @see DataSourceListener + * + * @author Vaadin Ltd + * @since 8.0 + * + */ +public class DataChangeEvent extends EventObject { + + /** + * Creates a new {@code DataChangeEvent} event originating from the given + * data source. + * + * @param source + * the data source, not null + */ + public DataChangeEvent(DataSource<?> source) { + super(source); + } + + @Override + public DataSource<?> getSource() { + return (DataSource<?>) super.getSource(); + } + +} diff --git a/server/src/main/java/com/vaadin/server/data/DataCommunicator.java b/server/src/main/java/com/vaadin/server/data/DataCommunicator.java index b0ade744de..5fc11bb15d 100644 --- a/server/src/main/java/com/vaadin/server/data/DataCommunicator.java +++ b/server/src/main/java/com/vaadin/server/data/DataCommunicator.java @@ -32,6 +32,7 @@ import java.util.stream.Stream; import com.vaadin.server.AbstractExtension; import com.vaadin.server.KeyMapper; import com.vaadin.shared.Range; +import com.vaadin.shared.Registration; import com.vaadin.shared.data.DataCommunicatorClientRpc; import com.vaadin.shared.data.DataCommunicatorConstants; import com.vaadin.shared.data.DataRequestRpc; @@ -50,6 +51,8 @@ import elemental.json.JsonObject; */ public class DataCommunicator<T> extends AbstractExtension { + private Registration dataSourceUpdateRegistration; + /** * Simple implementation of collection data provider communication. All data * is sent by server automatically and no data is requested by client. @@ -195,6 +198,18 @@ public class DataCommunicator<T> extends AbstractExtension { keyMapper = createKeyMapper(); } + @Override + public void attach() { + super.attach(); + attachDataSourceListener(); + } + + @Override + public void detach() { + super.detach(); + detachDataSourceListener(); + } + /** * Initially and in the case of a reset all data should be pushed to the * client. @@ -459,6 +474,22 @@ public class DataCommunicator<T> extends AbstractExtension { public void setDataSource(DataSource<T> dataSource) { Objects.requireNonNull(dataSource, "data source cannot be null"); this.dataSource = dataSource; + detachDataSourceListener(); + if (isAttached()) { + attachDataSourceListener(); + } reset(); } + + private void attachDataSourceListener() { + dataSourceUpdateRegistration = getDataSource() + .addDataSourceListener(event -> reset()); + } + + private void detachDataSourceListener() { + if (dataSourceUpdateRegistration != null) { + dataSourceUpdateRegistration.remove(); + dataSourceUpdateRegistration = null; + } + } } diff --git a/server/src/main/java/com/vaadin/server/data/DataSource.java b/server/src/main/java/com/vaadin/server/data/DataSource.java index 25679a3c89..f767f60cf0 100644 --- a/server/src/main/java/com/vaadin/server/data/DataSource.java +++ b/server/src/main/java/com/vaadin/server/data/DataSource.java @@ -21,6 +21,8 @@ import java.util.Collection; import java.util.function.Function; import java.util.stream.Stream; +import com.vaadin.shared.Registration; + /** * Minimal DataSource API for communication between the DataProvider and a back * end service. @@ -56,6 +58,27 @@ public interface DataSource<T> int size(Query t); /** + * Refreshes all data based on currently available data in the underlying + * provider. + */ + void refreshAll(); + + /** + * Adds a data source listener. The listener is called when some piece of + * data is updated. + * <p> + * The {@link #refreshAll()} method fires {@link DataChangeEvent} each time + * when it's called. It allows to update UI components when user changes + * something in the underlying data. + * + * @see #refreshAll() + * @param listener + * the data change listener, not null + * @return a registration for the listener + */ + Registration addDataSourceListener(DataSourceListener listener); + + /** * This method creates a new {@link ListDataSource} from a given Collection. * The ListDataSource creates a protective List copy of all the contents in * the Collection. diff --git a/server/src/main/java/com/vaadin/server/data/DataSourceListener.java b/server/src/main/java/com/vaadin/server/data/DataSourceListener.java new file mode 100644 index 0000000000..c9c486cfd1 --- /dev/null +++ b/server/src/main/java/com/vaadin/server/data/DataSourceListener.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.data; + +import java.io.Serializable; + +/** + * Interface for listening for a data change events fired by a + * {@link DataSource}. + * + * @author Vaadin Ltd + * @since 8.0 + */ +public interface DataSourceListener extends Serializable { + + /** + * Invoked when this listener receives a data change event from a data + * source to which it has been added. + * <p> + * This event is fired when something has changed in the underlying data. It + * doesn't allow to distinguish different kind of events + * (add/remove/update). It means that the method implementation normally + * just reloads the whole data to refresh. + * + * @param event + * the received event, not null + */ + void onDataChange(DataChangeEvent event); +} diff --git a/server/src/main/java/com/vaadin/server/data/ListDataSource.java b/server/src/main/java/com/vaadin/server/data/ListDataSource.java index ecac378182..64b7c03cb1 100644 --- a/server/src/main/java/com/vaadin/server/data/ListDataSource.java +++ b/server/src/main/java/com/vaadin/server/data/ListDataSource.java @@ -15,10 +15,8 @@ */ package com.vaadin.server.data; -import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; -import java.util.List; import java.util.Objects; import java.util.function.Function; import java.util.stream.Stream; @@ -30,10 +28,10 @@ import java.util.stream.Stream; * @param <T> * data type */ -public class ListDataSource<T> implements DataSource<T> { +public class ListDataSource<T> extends AbstractDataSource<T> { - private Function<Query, Stream<T>> request; - private int size; + private Comparator<T> sortOrder; + private final Collection<T> backend; /** * Constructs a new ListDataSource. This method makes a protective copy of @@ -44,26 +42,33 @@ public class ListDataSource<T> implements DataSource<T> { */ public ListDataSource(Collection<T> items) { Objects.requireNonNull(items, "items cannot be null"); - final List<T> backend = new ArrayList<>(items); - request = query -> backend.stream(); - size = backend.size(); + backend = items; + sortOrder = null; } /** * Chaining constructor for making modified {@link ListDataSource}s. This * Constructor is used internally for making sorted and filtered variants of * a base data source with actual data. + * + * @param items + * the backend data from the original list data source + * @param sortOrder + * a {@link Comparator} providing the needed sorting order * - * @param request - * request for the new data source */ - protected ListDataSource(Function<Query, Stream<T>> request) { - this.request = request; + protected ListDataSource(Collection<T> items, Comparator<T> sortOrder) { + this(items); + this.sortOrder = sortOrder; } @Override public Stream<T> apply(Query query) { - return request.apply(query); + Stream<T> stream = backend.stream(); + if (sortOrder != null) { + stream = stream.sorted(sortOrder); + } + return stream; } /** @@ -77,7 +82,7 @@ public class ListDataSource<T> implements DataSource<T> { * @return new data source with modified sorting */ public ListDataSource<T> sortingBy(Comparator<T> sortOrder) { - return new ListDataSource<>(q -> request.apply(q).sorted(sortOrder)); + return new ListDataSource<>(backend, sortOrder); } /** @@ -113,6 +118,7 @@ public class ListDataSource<T> implements DataSource<T> { */ @Override public int size(Query t) { - return size; + return backend.size(); } + } diff --git a/server/src/test/java/com/vaadin/server/data/AbstractDataSourceTest.java b/server/src/test/java/com/vaadin/server/data/AbstractDataSourceTest.java new file mode 100644 index 0000000000..4898869743 --- /dev/null +++ b/server/src/test/java/com/vaadin/server/data/AbstractDataSourceTest.java @@ -0,0 +1,72 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.data; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Stream; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.shared.Registration; + +/** + * @author Vaadin Ltd + * + */ +public class AbstractDataSourceTest { + + private static class TestDataSource extends AbstractDataSource<Object> { + @Override + public Stream<Object> apply(Query t) { + return null; + } + + @Override + public int size(Query t) { + return 0; + } + + @Override + public boolean isInMemory() { + return false; + } + } + + @Test + public void refreshAll_notifyListeners() { + AbstractDataSource<Object> dataSource = new TestDataSource(); + AtomicReference<DataChangeEvent> event = new AtomicReference<>(); + dataSource.addDataSourceListener(ev -> { + Assert.assertNull(event.get()); + event.set(ev); + }); + dataSource.refreshAll(); + Assert.assertNotNull(event.get()); + Assert.assertEquals(dataSource, event.get().getSource()); + } + + @Test + public void removeListener_listenerIsNotNotified() { + AbstractDataSource<Object> dataSource = new TestDataSource(); + AtomicReference<DataChangeEvent> event = new AtomicReference<>(); + Registration registration = dataSource + .addDataSourceListener(ev -> event.set(ev)); + registration.remove(); + dataSource.refreshAll(); + Assert.assertNull(event.get()); + } +} diff --git a/server/src/test/java/com/vaadin/server/data/DataCommunicatorTest.java b/server/src/test/java/com/vaadin/server/data/DataCommunicatorTest.java new file mode 100644 index 0000000000..b82d571f9b --- /dev/null +++ b/server/src/test/java/com/vaadin/server/data/DataCommunicatorTest.java @@ -0,0 +1,129 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.data; + +import java.util.Collections; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import com.vaadin.server.MockVaadinSession; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinSession; +import com.vaadin.shared.Registration; +import com.vaadin.ui.UI; + +/** + * @author Vaadin Ltd + * + */ +public class DataCommunicatorTest { + + private static class TestUI extends UI { + + private final VaadinSession session; + + TestUI(VaadinSession session) { + this.session = session; + } + + @Override + protected void init(VaadinRequest request) { + } + + @Override + public VaadinSession getSession() { + return session; + } + } + + private static class TestDataSource extends ListDataSource<Object> + implements Registration { + + private Registration registration; + + public TestDataSource() { + super(Collections.singleton(new Object())); + } + + @Override + public Registration addDataSourceListener(DataSourceListener listener) { + registration = super.addDataSourceListener(listener); + return this; + } + + @Override + public void remove() { + registration.remove(); + registration = null; + } + + public boolean isListenerAdded() { + return registration != null; + } + + } + + private static class TestDataCommunicator extends DataCommunicator<Object> { + protected void extend(UI ui) { + super.extend(ui); + } + } + + private MockVaadinSession session = new MockVaadinSession( + Mockito.mock(VaadinService.class)); + + @Test + public void attach_dataSourceListenerIsNotAddedBeforeAttachAndAddedAfter() { + session.lock(); + + UI ui = new TestUI(session); + + TestDataCommunicator communicator = new TestDataCommunicator(); + + TestDataSource dataSource = new TestDataSource(); + communicator.setDataSource(dataSource); + + Assert.assertFalse(dataSource.isListenerAdded()); + + communicator.extend(ui); + + Assert.assertTrue(dataSource.isListenerAdded()); + } + + @Test + public void detach_dataSourceListenerIsRemovedAfterDetach() { + session.lock(); + + UI ui = new TestUI(session); + + TestDataCommunicator communicator = new TestDataCommunicator(); + + TestDataSource dataSource = new TestDataSource(); + communicator.setDataSource(dataSource); + + communicator.extend(ui); + + Assert.assertTrue(dataSource.isListenerAdded()); + + communicator.detach(); + + Assert.assertFalse(dataSource.isListenerAdded()); + } + +} diff --git a/server/src/test/java/com/vaadin/server/data/datasource/ListDataSourceTest.java b/server/src/test/java/com/vaadin/server/data/datasource/ListDataSourceTest.java index 15ee2d5f2d..a6dcc77474 100644 --- a/server/src/test/java/com/vaadin/server/data/datasource/ListDataSourceTest.java +++ b/server/src/test/java/com/vaadin/server/data/datasource/ListDataSourceTest.java @@ -3,6 +3,7 @@ package com.vaadin.server.data.datasource; import static org.junit.Assert.assertTrue; import java.util.Comparator; +import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -27,12 +28,13 @@ public class ListDataSourceTest { @Test public void testListContainsAllData() { + List<StrBean> list = new LinkedList<>(data); dataSource.apply(new Query()) .forEach(str -> assertTrue( "Data source contained values not in original data", - data.remove(str))); + list.remove(str))); assertTrue("Not all values from original data were in data source", - data.isEmpty()); + list.isEmpty()); } @Test @@ -102,4 +104,92 @@ public class ListDataSourceTest { Assert.assertTrue(prev.getValue().compareTo(cur.getValue()) <= 0); } } + + @Test + public void refreshAll_changeBeanInstance() { + StrBean bean = new StrBean("foo", -1, hashCode()); + Query query = new Query(); + int size = dataSource.size(query); + + data.set(0, bean); + dataSource.refreshAll(); + + List<StrBean> list = dataSource.apply(query) + .collect(Collectors.toList()); + StrBean first = list.get(0); + Assert.assertEquals(bean.getValue(), first.getValue()); + Assert.assertEquals(bean.getRandomNumber(), first.getRandomNumber()); + Assert.assertEquals(bean.getId(), first.getId()); + + Assert.assertEquals(size, dataSource.size(query)); + } + + @Test + public void refreshAll_updateBean() { + Query query = new Query(); + int size = dataSource.size(query); + + StrBean bean = data.get(0); + bean.setValue("foo"); + dataSource.refreshAll(); + + List<StrBean> list = dataSource.apply(query) + .collect(Collectors.toList()); + StrBean first = list.get(0); + Assert.assertEquals("foo", first.getValue()); + + Assert.assertEquals(size, dataSource.size(query)); + } + + @Test + public void refreshAll_sortingBy_changeBeanInstance() { + StrBean bean = new StrBean("foo", -1, hashCode()); + Query query = new Query(); + int size = dataSource.size(query); + + data.set(0, bean); + + ListDataSource<StrBean> dSource = dataSource + .sortingBy(Comparator.comparing(StrBean::getId)); + dSource.refreshAll(); + + List<StrBean> list = dSource.apply(query).collect(Collectors.toList()); + StrBean first = list.get(0); + Assert.assertEquals(bean.getValue(), first.getValue()); + Assert.assertEquals(bean.getRandomNumber(), first.getRandomNumber()); + Assert.assertEquals(bean.getId(), first.getId()); + + Assert.assertEquals(size, dataSource.size(query)); + } + + @Test + public void refreshAll_addBeanInstance() { + StrBean bean = new StrBean("foo", -1, hashCode()); + + Query query = new Query(); + int size = dataSource.size(query); + + data.add(0, bean); + dataSource.refreshAll(); + + List<StrBean> list = dataSource.apply(query) + .collect(Collectors.toList()); + StrBean first = list.get(0); + Assert.assertEquals(bean.getValue(), first.getValue()); + Assert.assertEquals(bean.getRandomNumber(), first.getRandomNumber()); + Assert.assertEquals(bean.getId(), first.getId()); + + Assert.assertEquals(size + 1, dataSource.size(query)); + } + + @Test + public void refreshAll_removeBeanInstance() { + Query query = new Query(); + int size = dataSource.size(query); + + data.remove(0); + dataSource.refreshAll(); + + Assert.assertEquals(size - 1, dataSource.size(query)); + } } diff --git a/server/src/test/java/com/vaadin/server/data/datasource/StrBean.java b/server/src/test/java/com/vaadin/server/data/datasource/StrBean.java index 52d58be310..24f478ad47 100644 --- a/server/src/test/java/com/vaadin/server/data/datasource/StrBean.java +++ b/server/src/test/java/com/vaadin/server/data/datasource/StrBean.java @@ -31,6 +31,10 @@ class StrBean implements Serializable { return randomNumber; } + public void setValue(String value) { + this.value = value; + } + public static List<StrBean> generateRandomBeans(int max) { List<StrBean> data = new ArrayList<>(); Random r = new Random(13337); diff --git a/server/src/test/java/com/vaadin/ui/AbstractListingTest.java b/server/src/test/java/com/vaadin/ui/AbstractListingTest.java index 6367a44585..bbe0fd01aa 100644 --- a/server/src/test/java/com/vaadin/ui/AbstractListingTest.java +++ b/server/src/test/java/com/vaadin/ui/AbstractListingTest.java @@ -2,6 +2,7 @@ package com.vaadin.ui; import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedList; import java.util.List; import java.util.stream.Stream; @@ -19,8 +20,7 @@ import elemental.json.JsonObject; public class AbstractListingTest { - private final class TestListing extends - AbstractSingleSelect<String> { + private final class TestListing extends AbstractSingleSelect<String> { protected TestListing() { setSelectionModel(new SimpleSingleSelection()); @@ -69,11 +69,12 @@ public class AbstractListingTest { @Test public void testSetItemsWithCollection() { listing.setItems(items); + List<String> list = new LinkedList<>(items); listing.getDataSource().apply(new Query()).forEach( str -> Assert.assertTrue("Unexpected item in data source", - items.remove(str))); + list.remove(str))); Assert.assertTrue("Not all items from list were in data source", - items.isEmpty()); + list.isEmpty()); } @Test diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/basics/RefreshDataSource.java b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/RefreshDataSource.java new file mode 100644 index 0000000000..2623d61e28 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/basics/RefreshDataSource.java @@ -0,0 +1,79 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.grid.basics; + +import java.util.List; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.data.DataSource; +import com.vaadin.server.data.ListDataSource; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Grid; + +/** + * @author Vaadin Ltd + * + */ +public class RefreshDataSource extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Grid<DataObject> grid = new Grid<>(); + List<DataObject> data = DataObject.generateObjects(); + + ListDataSource<DataObject> dataSource = DataSource.create(data); + grid.setDataSource(dataSource); + + grid.setDataSource(dataSource); + grid.addColumn("Coordinates", DataObject::getCoordinates); + addComponent(grid); + + Button update = new Button("Update data", + event -> updateData(dataSource, data)); + update.setId("update"); + addComponent(update); + + Button add = new Button("Add data", event -> addData(dataSource, data)); + add.setId("add"); + addComponent(add); + + Button remove = new Button("Remove data", + event -> removeData(dataSource, data)); + remove.setId("remove"); + addComponent(remove); + } + + private void updateData(DataSource<DataObject> dataSource, + List<DataObject> data) { + data.get(0).setCoordinates("Updated coordinates"); + dataSource.refreshAll(); + } + + private void addData(DataSource<DataObject> dataSource, + List<DataObject> data) { + DataObject dataObject = new DataObject(); + dataObject.setCoordinates("Added"); + data.add(0, dataObject); + dataSource.refreshAll(); + } + + private void removeData(DataSource<DataObject> dataSource, + List<DataObject> data) { + data.remove(0); + dataSource.refreshAll(); + } +} diff --git a/uitest/src/test/java/com/vaadin/tests/components/grid/basics/RefreshDataSourceTest.java b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/RefreshDataSourceTest.java new file mode 100644 index 0000000000..e73bbd602c --- /dev/null +++ b/uitest/src/test/java/com/vaadin/tests/components/grid/basics/RefreshDataSourceTest.java @@ -0,0 +1,64 @@ +/* + * Copyright 2000-2016 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.grid.basics; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * @author Vaadin Ltd + * + */ +public class RefreshDataSourceTest extends MultiBrowserTest { + + @Test + public void updateFirstRow() { + openTestURL(); + + findElement(By.id("update")).click(); + WebElement first = findElement(By.tagName("td")); + Assert.assertEquals( + "UI component is not refreshed after update in data", + "Updated coordinates", first.getText()); + } + + @Test + public void addFirstRow() { + openTestURL(); + + findElement(By.id("add")).click(); + WebElement first = findElement(By.tagName("td")); + + Assert.assertEquals("UI component is not refreshed after add new data", + "Added", first.getText()); + } + + @Test + public void removeFirstRow() { + openTestURL(); + + WebElement first = findElement(By.tagName("td")); + String old = first.getText(); + first = findElement(By.id("remove")); + Assert.assertNotEquals("UI component is not refreshed after removal", + old, first.getText()); + } + +}
\ No newline at end of file |