/*
* Copyright (C) 2011 Red Hat, Inc. and/or its affiliates.
*
* 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 org.jboss.errai.databinding.client.test;
import static org.jboss.errai.common.client.util.EventTestingUtil.invokeEventListeners;
import static org.jboss.errai.common.client.util.EventTestingUtil.setupAddEventListenerInterceptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jboss.errai.databinding.client.BindableProxy;
import org.jboss.errai.databinding.client.BindableProxyFactory;
import org.jboss.errai.databinding.client.ComponentAlreadyBoundException;
import org.jboss.errai.databinding.client.DeclarativeBindingModule;
import org.jboss.errai.databinding.client.DeclarativeBindingModuleUsingBinder;
import org.jboss.errai.databinding.client.DeclarativeBindingModuleUsingModel;
import org.jboss.errai.databinding.client.DeclarativeBindingModuleUsingParams;
import org.jboss.errai.databinding.client.DeclarativeBindingModuleWithKeyUpEvent;
import org.jboss.errai.databinding.client.HasBoundIsElement;
import org.jboss.errai.databinding.client.InjectedDataBinderModuleBoundOnKeyUp;
import org.jboss.errai.databinding.client.ListOfStringWidget;
import org.jboss.errai.databinding.client.ModuleWithInjectedBindable;
import org.jboss.errai.databinding.client.ModuleWithInjectedDataBinder;
import org.jboss.errai.databinding.client.NonExistingPropertyException;
import org.jboss.errai.databinding.client.SimpleTextInputPresenter;
import org.jboss.errai.databinding.client.SingletonBindable;
import org.jboss.errai.databinding.client.TakesValueCheckInputPresenter;
import org.jboss.errai.databinding.client.TestModel;
import org.jboss.errai.databinding.client.TestModelTakesValueWidget;
import org.jboss.errai.databinding.client.TestModelWidget;
import org.jboss.errai.databinding.client.TestModelWithList;
import org.jboss.errai.databinding.client.TestModelWithNestedConfiguredBindable;
import org.jboss.errai.databinding.client.TestModelWithoutBindableAnnotation;
import org.jboss.errai.databinding.client.ViewWithInput;
import org.jboss.errai.databinding.client.ViewWithOnKeyUpInput;
import org.jboss.errai.databinding.client.api.Convert;
import org.jboss.errai.databinding.client.api.DataBinder;
import org.jboss.errai.databinding.client.api.StateSync;
import org.jboss.errai.databinding.client.api.handler.property.PropertyChangeEvent;
import org.jboss.errai.databinding.client.api.handler.property.PropertyChangeHandler;
import org.jboss.errai.databinding.client.scan.TestModelBindable;
import org.jboss.errai.databinding.client.scan.TestModelWithoutBindableA;
import org.jboss.errai.databinding.client.scan.TestModelWithoutBindableB;
import org.jboss.errai.databinding.client.scan.TestModelWithoutBindableC;
import org.jboss.errai.ioc.client.container.Factory;
import org.jboss.errai.ioc.client.container.IOC;
import org.jboss.errai.ioc.client.test.AbstractErraiIOCTest;
import org.jboss.errai.marshalling.client.Marshalling;
import org.jboss.errai.marshalling.client.api.MarshallerFramework;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.IntegerBox;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
/**
* Tests functionality provided by the {@link DataBinder} API.
*
* @author Christian Sadilek <csadilek@redhat.com>
*/
public class DataBindingIntegrationTest extends AbstractErraiIOCTest {
@Override
public String getModuleName() {
return "org.jboss.errai.databinding.DataBindingTestModule";
}
@Override
protected void gwtSetUp() throws Exception {
setupAddEventListenerInterceptor();
super.gwtSetUp();
final Logger logger = LoggerFactory.getLogger(getClass());
GWT.setUncaughtExceptionHandler(e -> logger.error("Uncaught error.", e));
Convert.deregisterDefaultConverters();
MarshallerFramework.initializeDefaultSessionProvider();
}
@Test
public void testBasicBinding() {
final TextBox textBox = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "value").getModel();
textBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getValue());
model.setValue("model change");
assertEquals("Widget not properly updated", "model change", textBox.getText());
}
@Test
public void testElementBinding() {
final InputElement textInput = Document.get().createTextInputElement();
final TestModel model = DataBinder.forType(TestModel.class).bind(textInput, "value").getModel();
textInput.setValue("UI change");
invokeEventListeners(textInput, "change");
assertEquals("Model not properly updated", "UI change", model.getValue());
model.setValue("model change");
assertEquals("Widget not properly updated", "model change", textInput.getValue());
}
@Test
public void testBindingWithHasTextAndHasValue() {
final IntegerBox integerBox = new IntegerBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(integerBox, "age").getModel();
integerBox.setValue(5, true);
assertEquals("Model not properly updated", (Integer) 5, model.getAge());
model.setAge(3);
assertEquals("Widget not properly updated", "3", integerBox.getText());
integerBox.setValue(null, true);
assertEquals("Model not properly updated", (Integer) null, model.getAge());
model.setAge(null);
assertEquals("Widget not properly updated", "", integerBox.getText());
}
@Test
public void testBasicBindingWithInjection() {
final ModuleWithInjectedDataBinder module =
IOC.getBeanManager().lookupBean(ModuleWithInjectedDataBinder.class).getInstance();
final TestModel model = module.getModel();
final TextBox nameTextBox = module.getNameTextBox();
model.setName("model change");
assertEquals("Widget not properly updated", "model change", nameTextBox.getText());
nameTextBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getName());
}
@Test
public void testBasicBindingOfNonAnnotatedType() {
final TextBox textBox = new TextBox();
final TestModelWithoutBindableAnnotation model =
DataBinder.forType(TestModelWithoutBindableAnnotation.class).bind(textBox, "value").getModel();
textBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getValue());
model.setValue("model change");
assertEquals("Widget not properly updated", "model change", textBox.getText());
}
@Test
public void testScanBindingOfNonAnnotatedTypes() {
////////////// A /////////////////
{
final TextBox textBoxA = new TextBox();
final TestModelWithoutBindableA modelA =
DataBinder.forType(TestModelWithoutBindableA.class).bind(textBoxA, "value").getModel();
textBoxA.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", modelA.getValue());
modelA.setValue("model change");
assertEquals("Widget not properly updated", "model change", textBoxA.getText());
}
////////////// B /////////////////
{
final TextBox textBoxB = new TextBox();
final TestModelWithoutBindableB modelB =
DataBinder.forType(TestModelWithoutBindableB.class).bind(textBoxB, "value").getModel();
textBoxB.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", modelB.getValue());
modelB.setValue("model change");
assertEquals("Widget not properly updated", "model change", textBoxB.getText());
}
////////////// C /////////////////
{
final TextBox textBoxC = new TextBox();
final TestModelWithoutBindableC modelC =
DataBinder.forType(TestModelWithoutBindableC.class).bind(textBoxC, "value").getModel();
textBoxC.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", modelC.getValue());
modelC.setValue("model change");
assertEquals("Widget not properly updated", "model change", textBoxC.getText());
}
////////////// D /////////////////
{
final TextBox textBoxD = new TextBox();
final TestModelBindable modelD =
DataBinder.forType(TestModelBindable.class).bind(textBoxD, "value").getModel();
textBoxD.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", modelD.getValue());
modelD.setValue("model change");
assertEquals("Widget not properly updated", "model change", textBoxD.getText());
}
}
@Test
public void testNestedBindingOfNonAnnotatedType() {
final TextBox textBox = new TextBox();
final TestModelWithoutBindableAnnotation model =
DataBinder.forType(TestModelWithoutBindableAnnotation.class).bind(textBox, "child.value").getModel();
textBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getChild().getValue());
model.getChild().setValue("model change");
assertEquals("Widget not properly updated", "model change", textBox.getText());
}
@Test
public void testNestedBindingWithModelChange() {
final TextBox textBox = new TextBox();
TestModel model = new TestModel();
final DataBinder<TestModel> binder = DataBinder.forModel(model);
model = binder.bind(textBox, "child.value", null, StateSync.FROM_MODEL).getModel();
textBox.setValue("old string", true);
assertEquals("Model not properly updated", "old string", model.getChild().getValue());
final TestModel newChild = new TestModel();
newChild.value = "new string";
model.setChild(newChild);
assertEquals("Widget not properly updated", "new string", textBox.getText());
}
@Test
public void testBindingOfReadOnlyField() {
final Label label = new Label();
final TestModel model = DataBinder.forType(TestModel.class).bind(label, "id").getModel();
model.setId(1701);
assertEquals("Widget not properly updated", "1701", label.getText());
}
@Test
public void testBindingWithDefaultConversion() {
final TextBox textBox = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "age").getModel();
model.setAge(25);
assertEquals("Widget not properly updated", model.getAge().toString(), textBox.getText());
textBox.setValue("52", true);
assertEquals("Model not properly updated", Integer.valueOf(52), model.getAge());
}
@Test
public void testBindingOfNonExistingPropertyThrowsException() {
try {
DataBinder.forType(TestModel.class).bind(new TextBox(), "non-existing");
fail("Expected NonExistingPropertyException!");
}
catch (final NonExistingPropertyException ex) {
assertTrue("Exception message does not contain correct property name: " + ex.getMessage(), ex.getMessage().contains("non-existing"));
assertTrue("Exception message does not contain correct type name: " + ex.getMessage(), ex.getMessage().contains("TestModel"));
}
}
@Test
public void testBindingOfSingleWidgetToMultiplePropertiesThrowsException() {
final TextBox textBox = new TextBox();
try {
DataBinder.forType(TestModel.class).bind(textBox, "value").bind(textBox, "name");
fail("Binding a widget to multiple properties should fail with an exception!");
}
catch (final ComponentAlreadyBoundException e) {
// expected
assertTrue("Exception message does not contain property name", e.getMessage().contains("value"));
}
}
@Test
public void testBindingOfSinglePropertyToMultipleWidgets() {
final TextBox textBox1 = new TextBox();
final TextBox textBox2 = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class)
.bind(textBox1, "value")
.bind(textBox2, "value").getModel();
textBox1.setValue("UI change 1", true);
assertEquals("Model not properly updated", "UI change 1", model.getValue());
assertEquals("Widget not properly updated", "UI change 1", textBox2.getValue());
textBox2.setValue("UI change 2", true);
assertEquals("Model not properly updated", "UI change 2", model.getValue());
assertEquals("Widget not properly updated", "UI change 2", textBox1.getValue());
model.setValue("model change");
assertEquals("Widget not properly updated", "model change", textBox1.getText());
assertEquals("Widget not properly updated", "model change", textBox2.getText());
}
@Test
public void testUnbindingSpecificProperty() {
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class);
final TextBox textBox = new TextBox();
final TestModel model = binder.bind(textBox, "value").getModel();
binder.unbind("value");
model.setValue("model change");
assertEquals("Widget should not have been updated because unbind was called", "", textBox.getText());
textBox.setValue("UI change", true);
assertEquals("Model should not have been updated because unbind was called", "model change", model.getValue());
}
@Test
public void testUnbindingAllProperties() {
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class);
final TextBox textBox = new TextBox();
final TestModel model = binder.bind(textBox, "value").getModel();
binder.unbind();
model.setValue("model change");
assertEquals("Widget should not have been updated because unbind was called", "", textBox.getText());
textBox.setValue("UI change", true);
assertEquals("Model should not have been updated because unbind was called", "model change", model.getValue());
}
@Test
public void testBindingOfMultipleProperties() {
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class);
final TextBox valueTextBox = new TextBox();
binder.bind(valueTextBox, "value");
final TextBox nameTextBox = new TextBox();
binder.bind(nameTextBox, "name");
final TestModel model = binder.getModel();
nameTextBox.setValue("ui.name", true);
assertEquals("Name not properly updated", "ui.name", model.getName());
assertNull("Value should not have been updated", model.getValue());
model.setName("model.name");
assertEquals("Widget for name not properly updated", "model.name", nameTextBox.getText());
assertEquals("Widget for value should not have been updated", "", valueTextBox.getText());
nameTextBox.setValue("ui.name", true);
valueTextBox.setValue("ui.value", true);
assertEquals("Name not properly updated", "ui.name", model.getName());
assertEquals("Value not properly updated", "ui.value", model.getValue());
model.setName("model.name");
model.setValue("model.value");
assertEquals("Widget for name not properly updated", "model.name", nameTextBox.getText());
assertEquals("Widget for value not properly updated", "model.value", valueTextBox.getText());
binder.unbind("name");
nameTextBox.setValue("ui.name", true);
valueTextBox.setValue("ui.value", true);
assertEquals("Name should not have been updated", "model.name", model.getName());
assertEquals("Value not properly updated", "ui.value", model.getValue());
model.setName("model.name");
model.setValue("model.value");
assertEquals("Widget for name should not have been updated", "ui.name", nameTextBox.getText());
assertEquals("Widget for value not properly updated", "model.value", valueTextBox.getText());
}
@Test
public void testBindingWithModelInstanceChange() {
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class);
final TextBox textBox = new TextBox();
binder.bind(textBox, "name");
TestModel model = new TestModel();
model.setName("initial name");
binder.setModel(model, StateSync.FROM_MODEL);
assertEquals("Widget not updated after model change", "initial name", textBox.getText());
model = new TestModel();
textBox.setText("changed name");
binder.setModel(model, StateSync.FROM_UI);
assertEquals("Model not updated after model change", "changed name", model.getName());
}
@Test
public void testBindingWithInitialStateSync() {
final TextBox textBox = new TextBox();
textBox.setValue("initial ui value");
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class).bind(textBox, "name", null, StateSync.FROM_UI);
assertEquals("Model not initialized based on widget's state", "initial ui value", binder.getModel().getName());
final TestModel model = new TestModel();
model.setName("initial model value");
DataBinder.forModel(model).bind(textBox, "name", null, StateSync.FROM_MODEL);
assertEquals("Model not initialized based on widget's state", "initial model value", textBox.getValue());
}
@Test
public void testBindingToCustomHasValueType() {
final TestModelWidget widget = new TestModelWidget();
final TestModel childModel = new TestModel();
childModel.setName("child");
final TestModel model = new TestModel();
model.setChild(childModel);
final DataBinder<TestModel> binder = DataBinder.forModel(model).bind(widget, "child", null, StateSync.FROM_MODEL);
assertEquals("Widget not updated based on model's state", childModel, binder.getModel().getChild());
}
@Test
public void testBindingToCustomTakesValueType() {
final TestModelTakesValueWidget widget = new TestModelTakesValueWidget();
final TestModel childModel = new TestModel();
childModel.setName("child");
final TestModel model = new TestModel();
model.setChild(childModel);
final DataBinder<TestModel> binder = DataBinder.forModel(model).bind(widget, "child", null, StateSync.FROM_MODEL);
assertEquals("Widget not updated based on model's state", childModel, binder.getModel().getChild());
}
@Test
public void testBindablePropertyChain() {
final TextBox textBox = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "child.value").getModel();
textBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getChild().getValue());
model.getChild().setValue("model change");
assertEquals("Widget not properly updated", "model change", textBox.getText());
}
@Test
public void testBindablePropertyChainTwoLevelsDeep() {
final TextBox textBox = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "child.child.name").getModel();
textBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getChild().getChild().getName());
model.getChild().getChild().setName("model change");
assertEquals("Widget not properly updated", "model change", textBox.getText());
}
@Test
public void testBindablePropertyChainWithRootInstanceChange() {
final TextBox textBox = new TextBox();
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class).bind(textBox, "child.child.value");
TestModel model = new TestModel();
final TestModel childModel = new TestModel();
final TestModel grandChildModel = new TestModel("value1");
childModel.setChild(grandChildModel);
model.setChild(childModel);
binder.setModel(model);
assertEquals("Widget not properly updated", "value1", textBox.getText());
final TestModel newGrandChildModel = new TestModel("value2");
childModel.setChild(newGrandChildModel);
model = binder.setModel(model);
assertEquals("Widget not properly updated", "value2", textBox.getText());
textBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getChild().getChild().getValue());
model.getChild().getChild().setValue("model change");
assertEquals("Widget not properly updated", "model change", textBox.getText());
}
@Test
public void testBindablePropertyChainWithNestedInstanceChange() {
final TextBox textBox = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "child.value").getModel();
model.setChild(new TestModel("value"));
assertEquals("Widget not properly updated", "value", textBox.getText());
textBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getChild().getValue());
model.setChild(new TestModel("newValue"));
assertEquals("Widget not properly updated", "newValue", textBox.getText());
model.getChild().setValue("model change");
assertEquals("Widget not properly updated", "model change", textBox.getText());
}
@Test
public void testBindablePropertyChainWithNestedConfiguredInstanceChange() {
final TextBox textBox = new TextBox();
final TestModelWithNestedConfiguredBindable model = DataBinder.forType(TestModelWithNestedConfiguredBindable.class)
.bind(textBox, "nested.value").getModel();
model.setNested(new TestModelWithoutBindableAnnotation("value"));
assertEquals("Widget not properly updated", "value", textBox.getText());
textBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getNested().getValue());
model.setNested(new TestModelWithoutBindableAnnotation("newValue"));
assertEquals("Widget not properly updated", "newValue", textBox.getText());
model.getNested().setValue("model change");
assertEquals("Widget not properly updated", "model change", textBox.getText());
}
@Test
public void testBindablePropertyChainWithNestedInstanceChangeInNonAccessorMethod() {
final TextBox textBox = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "child.child.value").getModel();
// changing the nested bindable using a non accessor method
model.resetChildren();
textBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getChild().getChild().getValue());
// changing the nested bindable using a non accessor method
model.resetChildren();
model.getChild().getChild().setValue("model change");
assertEquals("Widget not properly updated", "model change", textBox.getText());
}
@Test
public void testBindablePropertyChainWithInitialStateSync() {
final TextBox textBox = new TextBox();
textBox.setValue("initial ui value");
DataBinder<TestModel> binder =
DataBinder.forType(TestModel.class).bind(textBox, "child.name", null, StateSync.FROM_UI);
assertEquals("Model not initialized based on widget's state", "initial ui value",
binder.getModel().getChild().getName());
TestModel childModel = new TestModel();
childModel.setName("initial model value");
TestModel model = new TestModel();
model.setChild(childModel);
binder = DataBinder.forModel(model).bind(textBox, "child.name", null, StateSync.FROM_MODEL);
assertEquals("Model not initialized based on widget's state", "initial model value", textBox.getValue());
childModel = new TestModel();
childModel.setName("updated model value");
model = new TestModel();
model.setChild(childModel);
binder.setModel(model, StateSync.FROM_MODEL);
assertEquals("Model not initialized based on widget's state", "updated model value", textBox.getValue());
}
@Test
public void testBindablePropertyChainWithUnbinding() {
final TextBox valueTextBox = new TextBox();
final TextBox nameTextBox = new TextBox();
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class)
.bind(valueTextBox, "child.value")
.bind(nameTextBox, "child.name");
final TestModel model = binder.getModel();
// unbind specific nested property
binder.unbind("child.name");
assertEquals("Only one bound property should be left", 1, binder.getBoundProperties().size());
model.getChild().setName("model change");
assertEquals("Widget should not have been updated because unbind was called for this property", "",
nameTextBox.getText());
nameTextBox.setValue("UI change", true);
assertEquals("Model should not have been updated because unbind was called for this property", "model change",
model.getChild().getName());
model.getChild().setValue("model change");
assertEquals("Widget not properly updated", "model change", valueTextBox.getText());
valueTextBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getChild().getValue());
model.getChild().setValue("");
// unbind all properties
binder.unbind();
assertEquals("No property should be bound", 0, binder.getBoundProperties().size());
model.getChild().setValue("model change");
assertEquals("Widget should not have been updated because unbind was called", "", valueTextBox.getText());
valueTextBox.setValue("UI change", true);
assertEquals("Model should not have been updated because unbind was called", "model change",
model.getChild().getValue());
}
@Test
public void testBindingUsingNonAccessorMethod() {
final TextBox textBox = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "active").getModel();
// change the property 'active' using a non accessor method.
model.activate();
assertTrue("Model not properly updated", model.isActive());
assertEquals("Widget not properly updated", "true", textBox.getText());
}
@Test
public void testBindingUsingNonAccessorMethodCalledSet() {
final TextBox textBox = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "active").getModel();
// change the property 'active' using a non accessor method.
model.setActivateStatus(true);
assertTrue("Model not properly updated", model.isActive());
assertTrue("Model not properly updated", model.getActivateStatus("ignore"));
assertEquals("Widget not properly updated", "true", textBox.getText());
}
@Test
public void testBindablePropertyChainsUsingNonAccessorMethod() {
final TextBox textBox = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "child.active").getModel();
// change the property 'active' on the model and all children using a non accessor method.
model.activate();
assertTrue("Model not properly updated", model.getChild().isActive());
assertEquals("Widget not properly updated", "true", textBox.getText());
}
@Test
public void testBindablePropertyChainsUsingNonAccesssorMethodOnChild() {
final TextBox textBox = new TextBox();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "child.active").getModel();
// change the property 'active' using a non accessor method.
model.getChild().activate();
assertTrue("Model not properly updated", model.getChild().isActive());
assertEquals("Widget not properly updated", "true", textBox.getText());
}
@Test
public void testBindableProxyMarshalling() {
final TestModel model = DataBinder.forType(TestModel.class).bind(new TextBox(), "value").getModel();
model.setName("test");
final String marshalledModel = Marshalling.toJSON(model);
assertEquals(model, Marshalling.fromJSON(marshalledModel, TestModel.class));
}
@Test
public void testBindableProxyListMarshalling() {
final TestModel model = DataBinder.forType(TestModel.class).bind(new TextBox(), "value").getModel();
model.setName("test");
final List<TestModel> modelList = new ArrayList<>();
modelList.add(model);
final String marshalledModelList = Marshalling.toJSON(modelList);
assertEquals(modelList, Marshalling.fromJSON(marshalledModelList, List.class));
}
@Test
public void testBindableProxyMapMarshalling() {
final TestModel model = DataBinder.forType(TestModel.class).bind(new TextBox(), "value").getModel();
model.setName("test");
final Map<TestModel, TestModel> modelMap = new HashMap<>();
modelMap.put(model, model);
final String marshalledModelMap = Marshalling.toJSON(modelMap);
assertEquals(modelMap, Marshalling.fromJSON(marshalledModelMap, Map.class));
}
@Test
public void testBindableProxyToString() {
final TestModel model = new TestModel();
model.setName("test");
final DataBinder<TestModel> binder = DataBinder.forModel(model);
assertEquals(model.toString(), binder.getModel().toString());
}
@Test
public void testGetWidgets() {
final TextBox textBox1 = new TextBox();
final TextBox textBox2 = new TextBox();
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class).bind(textBox1, "value").bind(textBox2, "value");
assertEquals("Bound widget not found", textBox1, binder.getComponents("value").get(0));
assertEquals("Bound widget not found", textBox2, binder.getComponents("value").get(1));
assertEquals("Should have exactly 2 bound widgets", 2, binder.getComponents("value").size());
}
@Test
public void testGetBoundProperties() {
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class)
.bind(new TextBox(), "value")
.bind(new TextBox(), "child.child.value");
final Set<String> boundProperties = binder.getBoundProperties();
assertNotNull("Bound properties set should not be null", boundProperties);
assertEquals("There should be exactly two bound properties", 2, boundProperties.size());
assertTrue("value should be a bound property", boundProperties.contains("value"));
assertTrue("child.child.value should be a bound property", boundProperties.contains("child.child.value"));
}
@Test
public void testUpdateWidgets() {
final List<String> changedProperties = new ArrayList<>();
final TestModel model = new TestModel();
final TextBox textBox = new TextBox();
final DataBinder<TestModel> binder = DataBinder.forModel(model).bind(textBox, "value");
binder.addPropertyChangeHandler(new PropertyChangeHandler<Long>() {
@Override
public void onPropertyChange(final PropertyChangeEvent<Long> event) {
changedProperties.add(event.getPropertyName());
}
});
// using direct field access on the target object so the bindable proxy has no chance of seeing
// the change
model.value = "model change";
assertEquals("TextBox should be empty", "", textBox.getText());
// This call is used by Errai JPA, to update the widgets after an entity was changed
// using direct field access (e.g. the id was set).
((BindableProxy<?>) binder.getModel()).updateWidgets();
assertEquals("TextBox should have been updated", "model change", textBox.getText());
assertEquals("Unexpected property change events received", Arrays.asList("value"), changedProperties);
}
@Test
@SuppressWarnings("unchecked")
public void testDeepUnwrap() {
final TestModel parent = new TestModel("v0");
parent.setName("parent");
final TestModel child = new TestModel("v1");
child.setName("child");
final TestModel grandChild = new TestModel("v2");
grandChild.setName("grandChild");
child.setChild(grandChild);
parent.setChild(child);
final DataBinder<TestModel> binder = DataBinder.forModel(parent)
.bind(new TextBox(), "child.value")
.bind(new TextBox(), "child.child.value");
final TestModel unwrapped = ((BindableProxy<TestModel>) binder.getModel()).deepUnwrap();
assertNotNull(unwrapped);
assertNotNull(unwrapped.getChild());
assertNotNull(unwrapped.getChild().getChild());
assertNotSame(unwrapped, parent);
assertNotSame(unwrapped.getChild(), parent.getChild());
assertNotSame(unwrapped.getChild().getChild(), parent.getChild().getChild());
assertFalse(unwrapped instanceof BindableProxy);
assertFalse(unwrapped.getChild() instanceof BindableProxy);
assertFalse(unwrapped.getChild().getChild() instanceof BindableProxy);
assertEquals(unwrapped, parent);
}
@Test
@SuppressWarnings("unchecked")
public void testDeepUnwrapClonesUnproxiedProperties() {
final TestModel parent = new TestModel("v0");
parent.setName("parent");
final TestModel child = new TestModel("v1");
child.setName("child");
final TestModel grandChild = new TestModel("v2");
grandChild.setName("grandChild");
child.setChild(grandChild);
parent.setChild(child);
final DataBinder<TestModel> binder = DataBinder.forModel(parent);
final TestModel unwrapped = ((BindableProxy<TestModel>) binder.getModel()).deepUnwrap();
assertNotNull(unwrapped);
assertNotNull(unwrapped.getChild());
assertNotNull(unwrapped.getChild().getChild());
assertNotSame(unwrapped, parent);
assertNotSame(unwrapped.getChild(), parent.getChild());
assertNotSame(unwrapped.getChild().getChild(), parent.getChild().getChild());
assertFalse(unwrapped instanceof BindableProxy);
assertFalse(unwrapped.getChild() instanceof BindableProxy);
assertFalse(unwrapped.getChild().getChild() instanceof BindableProxy);
assertEquals(unwrapped, parent);
}
@Test
public void testUpdateWidgetsWithBindablePropertyChain() {
final TestModel grandChildModel = new TestModel();
final TestModel childModel = new TestModel();
childModel.setChild(grandChildModel);
final TestModel model = new TestModel();
model.setChild(childModel);
final TextBox textBox = new TextBox();
final DataBinder<TestModel> binder = DataBinder.forModel(model).bind(textBox, "child.child.value");
// using direct field access on the target object so the bindable proxy has no chance of seeing
// the change
model.child = new TestModel();
model.child.child = new TestModel();
model.child.child.value = "model change";
assertEquals("TextBox should be empty", "", textBox.getText());
// This call is used by Errai JPA, to update the widgets after an entity was changed
// using direct field access (e.g. the id was set).
((BindableProxy<?>) binder.getModel()).updateWidgets();
assertEquals("TextBox should have been updated", "model change", textBox.getText());
}
@Test
public void testDeclarativeBindingUsingBinder() {
final DeclarativeBindingModule module =
IOC.getBeanManager().lookupBean(DeclarativeBindingModuleUsingBinder.class).getInstance();
testDeclarativeBinding(module);
}
@Test
public void testDeclarativeBindingUsingModel() {
final DeclarativeBindingModule module =
IOC.getBeanManager().lookupBean(DeclarativeBindingModuleUsingModel.class).getInstance();
testDeclarativeBinding(module);
}
@Test
public void testDeclarativeBindingUsingModelSetter() {
final DeclarativeBindingModuleUsingModel module =
IOC.getBeanManager().lookupBean(DeclarativeBindingModuleUsingModel.class).getInstance();
final TestModel model = new TestModel();
model.setId(123);
model.setName("custom name");
module.setModel(model);
// ensure the model is proxied, caused by @ModelSetter
assertTrue(module.getModel() instanceof BindableProxy);
assertTrue(module.getModel().getName().equals(model.getName()));
assertEquals("123", module.getLabel().getText());
module.getLabel().setText("");
module.getDateTextBox().setText("");
testDeclarativeBinding(module);
}
@Test
public void testDeclarativeBindingUsingParams() {
final DeclarativeBindingModule module =
IOC.getBeanManager().lookupBean(DeclarativeBindingModuleUsingParams.class).getInstance();
testDeclarativeBinding(module);
}
public void testDeclarativeBinding(final DeclarativeBindingModule module) {
final Label idLabel = module.getLabel();
assertNotNull(idLabel);
assertEquals("", idLabel.getText());
final TextBox nameTextBox = module.getNameTextBox();
assertNotNull(nameTextBox);
assertEquals("", nameTextBox.getValue());
final TextBox dateTextBox = module.getDateTextBox();
assertNotNull(dateTextBox);
assertEquals("", dateTextBox.getValue());
final TextBox age = module.getAge();
assertNotNull(age);
assertEquals("", age.getValue());
final TestModel model = module.getModel();
model.setId(1711);
model.getChild().setName("errai");
model.setLastChanged(new Date());
model.setAge(47);
assertEquals("Label (id) was not updated!", Integer.valueOf(model.getId()).toString(), idLabel.getText());
assertEquals("TextBox (name) was not updated!", model.getChild().getName(), nameTextBox.getValue());
assertEquals("TextBox (date) was not updated using custom converter!", "testdate", dateTextBox.getValue());
assertEquals("TextBox (age) was not updated", model.getAge().toString(), age.getValue());
nameTextBox.setValue("updated", true);
dateTextBox.setValue("updated", true);
age.setValue("0", true);
assertEquals("Model (name) was not updated!", nameTextBox.getValue(), model.getChild().getName());
assertEquals("Model (lastUpdate) was not updated using custom converter!",
DeclarativeBindingModuleUsingBinder.TEST_DATE, model.getLastChanged());
assertEquals("Model (phoneNumber) was not updated!", age.getValue(), model.getAge().toString());
}
@Test
public void testInjectedBindableTypeIsNotBound() {
final ModuleWithInjectedBindable module =
IOC.getBeanManager().lookupBean(ModuleWithInjectedBindable.class).getInstance();
// injecting a bindable type without @Model qualification shouldn't result in a bindable proxy
assertEquals(SingletonBindable.class, Factory.maybeUnwrapProxy(module.getUnboundModel()).getClass());
// verify that the injection scope is correct
assertSame(Factory.maybeUnwrapProxy(module.getUnboundModel2()), Factory.maybeUnwrapProxy(module.getUnboundModel()));
}
@Test
public void testBindingWithSharedModel() {
final TextBox textBox1 = new TextBox();
final TextBox textBox2 = new TextBox();
final TestModel model = new TestModel();
final DataBinder<TestModel> binder1 = DataBinder.forModel(model).bind(textBox1, "value");
final DataBinder<TestModel> binder2 = DataBinder.forModel(model).bind(textBox2, "value");
// Ensure we got the same proxy instance for our model instance
assertSame(binder1.getModel(), binder2.getModel());
textBox1.setValue("UI change 1", true);
assertEquals("Model not properly updated", "UI change 1", model.getValue());
assertEquals("Widget not properly updated", "UI change 1", textBox2.getText());
textBox2.setValue("UI change 2", true);
assertEquals("Model not properly updated", "UI change 2", model.getValue());
assertEquals("Widget not properly updated", "UI change 2", textBox1.getText());
binder1.getModel().setValue("model change 1");
assertEquals("Widget not properly updated", "model change 1", textBox1.getText());
assertEquals("Widget not properly updated", "model change 1", textBox2.getText());
binder2.getModel().setValue("model change 2");
assertEquals("Widget not properly updated", "model change 2", textBox1.getText());
assertEquals("Widget not properly updated", "model change 2", textBox2.getText());
}
@Test
public void testBindingWithSharedModelProxy() {
final TextBox textBox1 = new TextBox();
final TextBox textBox2 = new TextBox();
final TestModel model = new TestModel();
final DataBinder<TestModel> binder1 = DataBinder.forModel(model).bind(textBox1, "value");
final DataBinder<TestModel> binder2 = DataBinder.forModel(binder1.getModel()).bind(textBox2, "value");
// Ensure we got the same proxy instance for our model instance
assertSame(binder1.getModel(), binder2.getModel());
textBox1.setValue("UI change 1", true);
assertEquals("Model not properly updated", "UI change 1", model.getValue());
assertEquals("Widget not properly updated", "UI change 1", textBox2.getText());
textBox2.setValue("UI change 2", true);
assertEquals("Model not properly updated", "UI change 2", model.getValue());
assertEquals("Widget not properly updated", "UI change 2", textBox1.getText());
binder1.getModel().setValue("model change 1");
assertEquals("Widget not properly updated", "model change 1", textBox1.getText());
assertEquals("Widget not properly updated", "model change 1", textBox2.getText());
binder2.getModel().setValue("model change 2");
assertEquals("Widget not properly updated", "model change 2", textBox1.getText());
assertEquals("Widget not properly updated", "model change 2", textBox2.getText());
}
@Test
public void testUnbindingWithSharedModel() {
final TextBox textBox1 = new TextBox();
final TextBox textBox2 = new TextBox();
final TestModel model = new TestModel();
final DataBinder<TestModel> binder1 = DataBinder.forModel(model).bind(textBox1, "value");
final DataBinder<TestModel> binder2 = DataBinder.forModel(model).bind(textBox2, "value");
assertSame(binder1.getModel(), binder2.getModel());
binder1.unbind();
textBox2.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.getValue());
assertEquals("Widget should not have been updated", "", textBox1.getText());
binder2.getModel().setValue("model change 1");
assertEquals("Widget should not have been updated", "", textBox1.getText());
assertEquals("Widget not properly updated", "model change 1", textBox2.getText());
binder1.getModel().setValue("model change 2");
assertEquals("Widget should not have been updated", "", textBox1.getText());
assertEquals("Widget not properly updated", "model change 2", textBox2.getText());
}
@Test
public void testSetModelWithSharedProxies() {
final TextBox textBox1 = new TextBox();
final TextBox textBox2 = new TextBox();
final TestModel model = new TestModel();
final DataBinder<TestModel> binder1 = DataBinder.forModel(model).bind(textBox1, "value");
final DataBinder<TestModel> binder2 = DataBinder.forModel(model).bind(textBox2, "name");
final TestModel modelProxy1 = binder1.getModel();
final TestModel modelProxy2 = binder2.getModel();
assertSame(modelProxy1, modelProxy2);
assertEquals("", textBox1.getText());
assertEquals("", textBox2.getText());
TestModel model2 = new TestModel();
model2.setValue("value");
binder1.setModel(model2);
assertEquals("value", textBox1.getText());
assertTrue(binder1.getBoundProperties().contains("value"));
model2 = new TestModel();
model2.setName("name");
binder2.setModel(model2);
assertEquals("name", textBox2.getText());
assertTrue(binder2.getBoundProperties().contains("name"));
}
@Test
public void testSetModelWithSharedProxiesAndPropertyChain() {
final TextBox textBox1 = new TextBox();
final TextBox textBox2 = new TextBox();
final TestModel model = new TestModel();
final DataBinder<TestModel> binder1 = DataBinder.forModel(model).bind(textBox1, "child.value");
final DataBinder<TestModel> binder2 = DataBinder.forModel(model).bind(textBox2, "child.value");
final TestModel modelProxy1 = binder1.getModel();
final TestModel modelProxy2 = binder2.getModel();
assertSame(modelProxy1, modelProxy2);
assertEquals("", textBox1.getText());
assertEquals("", textBox2.getText());
final TestModel model2 = new TestModel();
model2.setChild(new TestModel("value1"));
binder1.setModel(model2);
assertEquals("value1", textBox1.getText());
assertTrue(binder1.getBoundProperties().contains("child.value"));
model2.setChild(new TestModel("value2"));
binder2.setModel(model2);
assertEquals("value2", textBox2.getText());
assertTrue(binder2.getBoundProperties().contains("child.value"));
binder2.setModel(binder1.getModel());
textBox2.setValue("value3", true);
assertEquals("value3", binder2.getModel().getChild().getValue());
assertEquals("value3", textBox1.getText());
assertEquals("value3", binder1.getModel().getChild().getValue());
}
@Test
public void testSharedProxyCleanup() {
final TextBox textBox1 = new TextBox();
final TextBox textBox2 = new TextBox();
final TestModel model = new TestModel();
final DataBinder<TestModel> binder1 = DataBinder.forModel(model).bind(textBox1, "value");
final DataBinder<TestModel> binder2 = DataBinder.forModel(model).bind(textBox2, "value");
final TestModel modelProxy1 = binder1.getModel();
final TestModel modelProxy2 = binder2.getModel();
assertSame(modelProxy1, modelProxy2);
// Unbinding all binders for a given proxy should clear it from the cache we keep to ensure that
// we always use the same proxy for the same model instance
binder1.unbind();
TestModel modelProxy3 = DataBinder.forModel(model).getModel();
assertSame(modelProxy1, modelProxy3);
assertSame(modelProxy2, modelProxy3);
binder2.unbind();
modelProxy3 = DataBinder.forModel(model).getModel();
assertNotSame(modelProxy1, modelProxy3);
assertNotSame(modelProxy2, modelProxy3);
}
@Test
public void testListChangesTriggerWidgetUpdates() {
final ListOfStringWidget widget = new ListOfStringWidget();
final TestModelWithList model = DataBinder.forType(TestModelWithList.class).bind(widget, "list").getModel();
model.getList().add("1");
assertEquals("Widget not properly updated", 1, widget.getValue().size());
assertTrue("Widget not properly updated", widget.getValue().contains("1"));
model.getList().remove("1");
assertEquals("Widget not properly updated", 0, widget.getValue().size());
}
@Test
public void testDeclarativeBindingWithKeyUpBindingEvent() {
final DeclarativeBindingModule module =
IOC.getBeanManager().lookupBean(DeclarativeBindingModuleWithKeyUpEvent.class).getInstance();
final TestModel model = module.getModel();
model.setAge(1);
assertEquals("Widget not properly updated", model.getAge(), new Integer(module.getAge().getValue()));
module.getAge().setValue("6");
// fire key event manually
DomEvent.fireNativeEvent(Document.get().createKeyUpEvent(false, false, false, false,
KeyCodes.KEY_NUM_SIX), module.getAge());
assertEquals("Model not properly updated", module.getAge().getValue(), model.getAge().toString());
}
@Test
public void testDeclarativeBindingWithKeyUpEventAndChainedProperty () {
final DeclarativeBindingModule module =
IOC.getBeanManager().lookupBean(DeclarativeBindingModuleWithKeyUpEvent.class).getInstance();
final TestModel model = module.getModel();
model.getChild().setName("model change");
assertEquals("Widget not properly updated", model.getChild().getName(), module.getNameTextBox().getValue());
module.getNameTextBox().setValue("widget name");
// fire key event manually
DomEvent.fireNativeEvent(Document.get().createKeyUpEvent(false, false, false, false, KeyCodes.KEY_E), module
.getNameTextBox());
assertEquals("Model and widget do not match", module.getNameTextBox().getValue(), model.getChild().getName());
}
@Test
public void testInjectedDataBinderWithKeyUpEvent () {
final InjectedDataBinderModuleBoundOnKeyUp module =
IOC.getBeanManager().lookupBean(InjectedDataBinderModuleBoundOnKeyUp.class).getInstance();
final TestModel model = module.getModel();
final TextBox nameTextBox = module.getNameTextBox();
model.setName("model change");
assertEquals("Widget not properly updated", "model change", nameTextBox.getText());
nameTextBox.setValue("UI change", true);
// fire key event manually
DomEvent.fireNativeEvent(Document.get().createKeyUpEvent(false, false, false, false, KeyCodes.KEY_E),
nameTextBox);
assertEquals("Model not properly updated", "UI change", model.getName());
}
@Test
public void testUnbindingWithKeyUpEvent() {
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class);
final TextBox textBox = new TextBox();
final TestModel model = binder.bind(textBox, "value", null, StateSync.FROM_MODEL, true).getModel();
textBox.setValue("UI change");
DomEvent.fireNativeEvent(Document.get().createKeyUpEvent(false, false, false, false, KeyCodes.KEY_E), textBox);
assertEquals("Model not properly updated", textBox.getValue(), model.getValue());
binder.unbind("value");
model.setValue("model change");
// textBox value should be same as before
assertEquals("Widget should not have been updated because unbind was called", "UI change", textBox.getText());
textBox.setValue("Another UI change", true);
// model value should be same as before
assertEquals("Model should not have been updated because unbind was called", "model change", model.getValue());
}
@Test
public void testSetModelWithKeyUpEvent() throws Exception {
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class);
final TextBox textBox = new TextBox();
final TestModel model = binder.bind(textBox, "value", null, StateSync.FROM_MODEL, true).getModel();
textBox.setValue("UI change");
DomEvent.fireNativeEvent(Document.get().createKeyUpEvent(false, false, false, false, KeyCodes.KEY_E), textBox);
assertEquals("Model not properly updated", textBox.getValue(), model.getValue());
binder.setModel(new TestModel());
final String newValue = "new value";
textBox.setValue(newValue, false);
assertFalse("Model should not have updated, no event should have been fired.", newValue.equals(binder.getModel().getValue()));
DomEvent.fireNativeEvent(Document.get().createKeyUpEvent(false, false, false, false, KeyCodes.KEY_A), textBox);
assertEquals("Model should have updated after key up event fired.", newValue, binder.getModel().getValue());
}
@Test
public void testBindingNonTextWidgetOnUnhandledEvent() {
final CheckBox checkBox = new CheckBox();
try {
// bind non-ValueBoxBase widget on KeyUpEvents
DataBinder.forType(TestModel.class).bind(checkBox, "active", null, StateSync.FROM_MODEL, true);
fail("Widgets that do not extend ValueBoxBase should not bind on KeyUpEvents.");
} catch (final Exception e) {
// this is the expected behavior
}
}
@Test
public void testKeyUpWithMultipleWidgetsBoundToChainedProperty() {
final TextBox textBox = new TextBox();
final Label label = new Label();
final TestModel model = DataBinder.forType(TestModel.class).bind(textBox, "child.name", null, StateSync.FROM_MODEL, true)
.bind(label, "child.name").getModel();
textBox.setValue("new value");
DomEvent.fireNativeEvent(Document.get().createKeyUpEvent(false, false, false, false, KeyCodes.KEY_E), textBox);
assertEquals("Model not updated", textBox.getValue(), model.getChild().getName());
assertEquals("Second widget not updated", textBox.getValue(), label.getText());
}
@Test
public void testBidirectionalChainedKeyUpEventBinding() {
final TextBox tb1 = new TextBox();
final TextBox tb2 = new TextBox();
DataBinder.forType(TestModel.class).bind(tb1, "child.name", null, StateSync.FROM_MODEL, true)
.bind(tb2, "child.name", null, StateSync.FROM_MODEL, true);
tb1.setValue("change in tb1");
DomEvent.fireNativeEvent(Document.get().createKeyUpEvent(false, false, false, false, KeyCodes.KEY_NUM_ONE), tb1);
assertEquals("Second widget not updated", tb1.getValue(), tb2.getValue());
tb2.setValue("change in tb2");
DomEvent.fireNativeEvent(Document.get().createKeyUpEvent(false, false, false, false, KeyCodes.KEY_NUM_TWO), tb2);
assertEquals("First widget not updated", tb2.getValue(), tb1.getValue());
}
@Test
public void testPauseResumeFromUI() {
final TextBox parentTextBox = new TextBox();
final TextBox childTextBox = new TextBox();
final DataBinder<TestModel> binder = DataBinder
.forType(TestModel.class)
.bind(parentTextBox, "value")
.bind(childTextBox, "child.value");
final TestModel model = binder.getModel();
binder.pause();
assertSame("Pause should not change model instance", model, binder.getModel());
parentTextBox.setValue("UI change paused", true);
childTextBox.setValue("UI change paused", true);
assertEquals("Parent model should not have been updated while paused", null, model.getValue());
assertEquals("Child model should not have been updated while paused", null, model.getChild().getValue());
model.setValue("model change while paused");
model.getChild().setValue("model change while paused");
assertEquals("Parent bound widget should not have been updated while paused", "UI change paused", parentTextBox.getText());
assertEquals("Child bound widget should not have been updated while paused", "UI change paused", childTextBox.getText());
binder.resume(StateSync.FROM_UI);
assertEquals("Parent model not properly updated", "UI change paused", model.getValue());
assertEquals("Child model not properly updated", "UI change paused", model.getChild().getValue());
assertEquals("Parent bound widget should not have been updated", "UI change paused", parentTextBox.getText());
assertEquals("Child bound widget should not have been updated", "UI change paused", childTextBox.getText());
parentTextBox.setValue("UI change resumed", true);
childTextBox.setValue("UI change resumed", true);
assertEquals("Parent model not properly updated", "UI change resumed", model.getValue());
assertEquals("Child model not properly updated", "UI change resumed", model.getChild().getValue());
model.setValue("model change resumed");
model.getChild().setValue("model change resumed");
assertEquals("Parent bound widget not properly updated", "model change resumed", parentTextBox.getText());
assertEquals("Child bound widget not properly updated", "model change resumed", childTextBox.getText());
}
@Test
public void testPauseResumeFromModel() {
final TextBox parentTextBox = new TextBox();
final TextBox childTextBox = new TextBox();
final DataBinder<TestModel> binder = DataBinder
.forType(TestModel.class)
.bind(parentTextBox, "value")
.bind(childTextBox, "child.value");
final TestModel model = binder.getModel();
binder.pause();
assertSame("Pause should not change model instance", model, binder.getModel());
parentTextBox.setValue("UI change paused", true);
childTextBox.setValue("UI change paused", true);
assertEquals("Parent model should not have been updated", null, model.getValue());
assertEquals("Child model should not have been updated", null, model.getChild().getValue());
model.setValue("model change paused");
model.getChild().setValue("model change paused");
assertEquals("Parent bound widget should not have been updated", "UI change paused", parentTextBox.getText());
assertEquals("Child bound widget should not have been updated", "UI change paused", childTextBox.getText());
binder.resume(StateSync.FROM_MODEL);
assertEquals("Parent bound widget not properly updated", "model change paused", parentTextBox.getText());
assertEquals("Child bound widget not properly updated", "model change paused", childTextBox.getText());
assertEquals("Parent model should not have been updated", "model change paused", model.getValue());
assertEquals("Child model should not have been updated", "model change paused", model.getChild().getValue());
parentTextBox.setValue("UI change resumed", true);
childTextBox.setValue("UI change resumed", true);
assertEquals("Parent model not properly updated", "UI change resumed", model.getValue());
assertEquals("Child model not properly updated", "UI change resumed", model.getChild().getValue());
model.setValue("model change resumed");
model.getChild().setValue("model change resumed");
assertEquals("Parent bound widget not properly updated", "model change resumed", parentTextBox.getText());
assertEquals("Child bound widget not properly updated", "model change resumed", childTextBox.getText());
}
@Test
public void testPauseResumeWithSetModel() {
final TextBox parentTextBox = new TextBox();
final TextBox childTextBox = new TextBox();
final DataBinder<TestModel> binder = DataBinder
.forType(TestModel.class)
.bind(parentTextBox, "value")
.bind(childTextBox, "child.value");
final TestModel model = binder.getModel();
binder.pause();
assertSame("Pause should not change model instance", model, binder.getModel());
parentTextBox.setValue("UI change paused", true);
childTextBox.setValue("UI change paused", true);
assertEquals("Parent model should not have been updated", null, model.getValue());
assertEquals("Child model should not have been updated", null, model.getChild().getValue());
model.setValue("model change paused");
model.getChild().setValue("model change paused");
assertEquals("Parent bound widget should not have been updated", "UI change paused", parentTextBox.getText());
assertEquals("Child bound widget should not have been updated", "UI change paused", childTextBox.getText());
final TestModel unproxiedModel = new TestModel("model update");
unproxiedModel.setChild(new TestModel("model update"));
// Resume using setModel
final TestModel model2 = binder.setModel(unproxiedModel);
assertEquals("Parent bound widget not properly updated", "model update", parentTextBox.getText());
assertEquals("Child bound widget not properly updated", "model update", childTextBox.getText());
assertEquals("Parent model should not have been updated", "model update", model2.getValue());
assertEquals("Child model should not have been updated", "model update", model2.getChild().getValue());
parentTextBox.setValue("UI change resumed", true);
childTextBox.setValue("UI change resumed", true);
assertEquals("Parent model not properly updated", "UI change resumed", model2.getValue());
assertEquals("Child model not properly updated", "UI change resumed", model2.getChild().getValue());
model2.setValue("model change resumed");
model2.getChild().setValue("model change resumed");
assertEquals("Parent bound widget not properly updated", "model change resumed", parentTextBox.getText());
assertEquals("Child bound widget not properly updated", "model change resumed", childTextBox.getText());
// Explicit resume should have no effect after resuming with setModel
binder.resume(StateSync.FROM_MODEL);
parentTextBox.setValue("UI change resumed", true);
childTextBox.setValue("UI change resumed", true);
assertEquals("Parent model not properly updated", "UI change resumed", model2.getValue());
assertEquals("Child model not properly updated", "UI change resumed", model2.getChild().getValue());
model2.setValue("model change resumed");
model2.getChild().setValue("model change resumed");
assertEquals("Parent bound widget not properly updated", "model change resumed", parentTextBox.getText());
assertEquals("Child bound widget not properly updated", "model change resumed", childTextBox.getText());
}
@Test
public void testResumeWithoutPauseDoesntThrowException() {
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class).bind(new TextBox(), "value");
binder.resume(StateSync.FROM_MODEL);
}
@Test
public void testPausingMultipeTimes() {
final TextBox textBox = new TextBox();
final DataBinder<TestModel> binder = DataBinder.forType(TestModel.class).bind(textBox, "value");
final TestModel model = binder.getModel();
binder.pause();
binder.pause();
assertSame("Pause should not change model instance", model, binder.getModel());
}
@Test
public void testBindingUsingFqcn() {
final TextBox textBox = new TextBox();
final String fqcn = TestModel.class.getName();
final BindableProxy<?> model = BindableProxyFactory.getBindableProxy(fqcn);
DataBinder.forModel(model).bind(textBox, "value");
textBox.setValue("UI change", true);
assertEquals("Model not properly updated", "UI change", model.get("value"));
model.set("value", "model change");
model.updateWidgets();
assertEquals("Widget not properly updated", "model change", textBox.getText());
}
@Test
public void testBindingToIsElement() throws Exception {
final SimpleTextInputPresenter presenter = new SimpleTextInputPresenter();
final TestModel model = DataBinder.forType(TestModel.class).bind(presenter, "value").getModel();
presenter.setValue("UI change");
invokeEventListeners(presenter.getElement(), "change");
assertEquals("Model not properly updated", "UI change", model.getValue());
model.setValue("model change");
assertEquals("Component not properly updated", "model change", presenter.getValue());
}
@Test
public void testBindingToIsElementWithTakesValue() throws Exception {
final TakesValueCheckInputPresenter presenter = new TakesValueCheckInputPresenter();
final TestModel model = DataBinder.forType(TestModel.class).bind(presenter, "active", Convert.identityConverter(Boolean.class)).getModel();
assertFalse("Expected model.isActive() to start as false.", model.isActive());
presenter.setValue(true);
invokeEventListeners(presenter.getElement(), "change");
assertTrue("Model not properly updated", model.isActive());
model.setActive(false);
assertFalse("Component not properly updated", presenter.getValue());
}
@Test
public void testDeclarativeBindingToIsElement() throws Exception {
final HasBoundIsElement instance = IOC.getBeanManager().lookupBean(HasBoundIsElement.class).getInstance();
final SimpleTextInputPresenter presenter = instance.getTextPresenter();
final TestModel model = instance.getBinder().getModel();
presenter.setValue("UI change");
invokeEventListeners(presenter.getElement(), "change");
assertEquals("Model not properly updated", "UI change", model.getValue());
model.setValue("model change");
assertEquals("Component not properly updated", "model change", presenter.getValue());
}
@Test
public void testDeclarativeBindingToIsElementWithTakesValue() throws Exception {
final HasBoundIsElement instance = IOC.getBeanManager().lookupBean(HasBoundIsElement.class).getInstance();
final TakesValueCheckInputPresenter presenter = instance.getCheckPresenter();
final TestModel model = instance.getBinder().getModel();
assertFalse("Expected model.isActive() to start as false.", model.isActive());
presenter.setValue(true);
invokeEventListeners(presenter.getElement(), "change");
assertTrue("Model not properly updated", model.isActive());
model.setActive(false);
assertFalse("Component not properly updated", presenter.getValue());
}
/**
* Regression tests for ERRAI-945.
*/
@Test
public void testDeclarativeBindingToErraiTextInput() throws Exception {
final ViewWithInput view = IOC.getBeanManager().lookupBean(ViewWithInput.class).getInstance();
final TestModel model = view.binder.getModel();
// Preconditions
assertNull(model.getValue());
assertTrue(view.value.getValue().isEmpty());
// Test
model.setValue("foo");
assertEquals(model.getValue(), view.value.getValue());
view.value.setValue("bar");
invokeEventListeners(view.value, "change");
assertEquals(view.value.getValue(), model.getValue());
}
@Test
public void testDeclarativeBindingWithOnKeyUpForTextInput() throws Exception {
final ViewWithOnKeyUpInput view = IOC.getBeanManager().lookupBean(ViewWithOnKeyUpInput.class).getInstance();
final TestModel model = view.binder.getModel();
// Preconditions
assertNull(model.getValue());
assertTrue(view.value.getValue().isEmpty());
// Test
model.setValue("foo");
assertEquals(model.getValue(), view.value.getValue());
view.value.setValue("bar");
invokeEventListeners(view.value, "keyup");
assertEquals(view.value.getValue(), model.getValue());
}
}