diff options
7 files changed, 538 insertions, 29 deletions
diff --git a/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java b/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java index 8e32585a47..b6bf97e68e 100644 --- a/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java +++ b/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java @@ -129,10 +129,6 @@ public class DefaultFieldGroupFieldFactory implements FieldGroupFieldFactory { return (T) field; } - private boolean anyField(Class<?> fieldType) { - return fieldType == Field.class || fieldType == AbstractField.class; - } - protected AbstractSelect createCompatibleSelect( Class<? extends AbstractSelect> fieldType) { AbstractSelect select; @@ -157,7 +153,23 @@ public class DefaultFieldGroupFieldFactory implements FieldGroupFieldFactory { return select; } - private boolean anySelect(Class<? extends Field> fieldType) { + /** + * @since 7.4 + * @param fieldType + * the type of the field + * @return true if any AbstractField can be assigned to the field + */ + protected boolean anyField(Class<?> fieldType) { + return fieldType == Field.class || fieldType == AbstractField.class; + } + + /** + * @since 7.4 + * @param fieldType + * the type of the field + * @return true if any AbstractSelect can be assigned to the field + */ + protected boolean anySelect(Class<? extends Field> fieldType) { return anyField(fieldType) || fieldType == AbstractSelect.class; } diff --git a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java index 069cb2e153..8f1bf8b40a 100644 --- a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java +++ b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import com.vaadin.data.Item; import com.vaadin.data.Property; @@ -465,46 +466,74 @@ public class FieldGroup implements Serializable { try { firePreCommitEvent(); - List<InvalidValueException> invalidValueExceptions = commitFields(); + Map<Field<?>, InvalidValueException> invalidValueExceptions = commitFields(); if (invalidValueExceptions.isEmpty()) { firePostCommitEvent(); commitTransactions(); } else { - throwInvalidValueException(invalidValueExceptions); + throw new FieldGroupInvalidValueException( + invalidValueExceptions); } } catch (Exception e) { rollbackTransactions(); - - throw new CommitException("Commit failed", e); + throw new CommitException("Commit failed", this, e); } } - private List<InvalidValueException> commitFields() { - List<InvalidValueException> invalidValueExceptions = new ArrayList<InvalidValueException>(); + /** + * Tries to commit all bound fields one by one and gathers any validation + * exceptions in a map, which is returned to the caller + * + * @return a propertyId to validation exception map which is empty if all + * commits succeeded + */ + private Map<Field<?>, InvalidValueException> commitFields() { + Map<Field<?>, InvalidValueException> invalidValueExceptions = new HashMap<Field<?>, InvalidValueException>(); for (Field<?> f : fieldToPropertyId.keySet()) { try { f.commit(); } catch (InvalidValueException e) { - invalidValueExceptions.add(e); + invalidValueExceptions.put(f, e); } } return invalidValueExceptions; } - private void throwInvalidValueException( - List<InvalidValueException> invalidValueExceptions) { - if (invalidValueExceptions.size() == 1) { - throw invalidValueExceptions.get(0); - } else { - InvalidValueException[] causes = invalidValueExceptions - .toArray(new InvalidValueException[invalidValueExceptions - .size()]); + /** + * Exception which wraps InvalidValueExceptions from all invalid fields in a + * FieldGroup + * + * @since 7.4 + */ + public static class FieldGroupInvalidValueException extends + InvalidValueException { + private Map<Field<?>, InvalidValueException> invalidValueExceptions; - throw new InvalidValueException(null, causes); + /** + * Constructs a new exception with the specified validation exceptions. + * + * @param invalidValueExceptions + * a property id to exception map + */ + public FieldGroupInvalidValueException( + Map<Field<?>, InvalidValueException> invalidValueExceptions) { + super(null, invalidValueExceptions.values().toArray( + new InvalidValueException[invalidValueExceptions.size()])); + this.invalidValueExceptions = invalidValueExceptions; + } + + /** + * Returns a map containing fields which failed validation and the + * exceptions the corresponding validators threw. + * + * @return a map with all the invalid value exceptions + */ + public Map<Field<?>, InvalidValueException> getInvalidFields() { + return invalidValueExceptions; } } @@ -1006,26 +1035,64 @@ public class FieldGroup implements Serializable { return fieldName.toLowerCase().replace("_", ""); } + /** + * Exception thrown by a FieldGroup when the commit operation fails. + * + * Provides information about validation errors through + * {@link #getInvalidFields()} if the cause of the failure is that all bound + * fields did not pass validation + * + */ public static class CommitException extends Exception { + private FieldGroup fieldGroup; + public CommitException() { super(); - // TODO Auto-generated constructor stub + } + + public CommitException(String message, FieldGroup fieldGroup, + Throwable cause) { + super(message, cause); + this.fieldGroup = fieldGroup; } public CommitException(String message, Throwable cause) { super(message, cause); - // TODO Auto-generated constructor stub } public CommitException(String message) { super(message); - // TODO Auto-generated constructor stub } public CommitException(Throwable cause) { super(cause); - // TODO Auto-generated constructor stub + } + + /** + * Returns a map containing the fields which failed validation and the + * exceptions the corresponding validators threw. + * + * @since 7.4 + * @return a map with all the invalid value exceptions. Can be empty but + * not null + */ + public Map<Field<?>, InvalidValueException> getInvalidFields() { + if (getCause() instanceof FieldGroupInvalidValueException) { + return ((FieldGroupInvalidValueException) getCause()) + .getInvalidFields(); + } + return new HashMap<Field<?>, InvalidValueException>(); + } + + /** + * Returns the field group where the exception occurred + * + * @since 7.4 + * @return the field group + */ + public FieldGroup getFieldGroup() { + return fieldGroup; } } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index 8f35001447..458522f58e 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -47,6 +48,8 @@ import com.vaadin.data.Item; import com.vaadin.data.Property; import com.vaadin.data.RpcDataProviderExtension; import com.vaadin.data.RpcDataProviderExtension.DataProviderKeyMapper; +import com.vaadin.data.Validator.InvalidValueException; +import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory; import com.vaadin.data.fieldgroup.FieldGroup; import com.vaadin.data.fieldgroup.FieldGroup.CommitException; import com.vaadin.data.fieldgroup.FieldGroupFieldFactory; @@ -87,6 +90,7 @@ import com.vaadin.shared.ui.grid.GridStaticSectionState.RowState; import com.vaadin.shared.ui.grid.HeightMode; import com.vaadin.shared.ui.grid.ScrollDestination; import com.vaadin.shared.util.SharedUtil; +import com.vaadin.ui.Notification.Type; import com.vaadin.ui.renderer.ObjectRenderer; import com.vaadin.ui.renderer.Renderer; import com.vaadin.util.ReflectTools; @@ -166,6 +170,11 @@ public class Grid extends AbstractComponent implements SelectionNotifier, * been bound. */ private final class CustomFieldGroup extends FieldGroup { + + public CustomFieldGroup() { + setFieldFactory(EditorFieldFactory.get()); + } + @Override protected Class<?> getPropertyType(Object propertyId) throws BindException { @@ -178,6 +187,155 @@ public class Grid extends AbstractComponent implements SelectionNotifier, } /** + * Field factory used by default in the editor. + * + * Aims to fields of suitable type and with suitable size for use in the + * editor row. + */ + public static class EditorFieldFactory extends + DefaultFieldGroupFieldFactory { + private static final EditorFieldFactory INSTANCE = new EditorFieldFactory(); + + protected EditorFieldFactory() { + } + + /** + * Returns the singleton instance + * + * @return the singleton instance + */ + public static EditorFieldFactory get() { + return INSTANCE; + } + + @Override + public <T extends Field> T createField(Class<?> type, Class<T> fieldType) { + T f = super.createField(type, fieldType); + if (f != null) { + f.setWidth("100%"); + } + return f; + } + + @Override + protected AbstractSelect createCompatibleSelect( + Class<? extends AbstractSelect> fieldType) { + if (anySelect(fieldType)) { + return super.createCompatibleSelect(ComboBox.class); + } + return super.createCompatibleSelect(fieldType); + } + + @Override + protected void populateWithEnumData(AbstractSelect select, + Class<? extends Enum> enumClass) { + // Use enums directly and the EnumToStringConverter to be consistent + // with what is shown in the Grid + @SuppressWarnings("unchecked") + EnumSet<?> enumSet = EnumSet.allOf(enumClass); + for (Object r : enumSet) { + select.addItem(r); + } + } + } + + /** + * Error handler for the editor + */ + public interface EditorErrorHandler extends Serializable { + + /** + * Called when an exception occurs while the editor row is being saved + * + * @param event + * An event providing more information about the error + */ + void commitError(CommitErrorEvent event); + } + + /** + * An event which is fired when saving the editor fails + */ + public static class CommitErrorEvent extends Component.Event { + + private CommitException cause; + + public CommitErrorEvent(Grid grid, CommitException cause) { + super(grid); + this.cause = cause; + } + + /** + * Retrieves the cause of the failure + * + * @return the cause of the failure + */ + public CommitException getCause() { + return cause; + } + + @Override + public Grid getComponent() { + return (Grid) super.getComponent(); + } + + /** + * Checks if validation exceptions caused this error + * + * @return true if the problem was caused by a validation error + */ + public boolean isValidationFailure() { + return cause.getCause() instanceof InvalidValueException; + } + + } + + /** + * Default error handler for the editor + * + */ + public class DefaultEditorErrorHandler implements EditorErrorHandler { + + @Override + public void commitError(CommitErrorEvent event) { + Map<Field<?>, InvalidValueException> invalidFields = event + .getCause().getInvalidFields(); + + if (!invalidFields.isEmpty()) { + // Validation error, show first failure as + // "<Column header>: <message>" + FieldGroup fieldGroup = event.getCause().getFieldGroup(); + Object propertyId = getFirstPropertyId(fieldGroup, + invalidFields.keySet()); + Field<?> field = fieldGroup.getField(propertyId); + String caption = getColumn(propertyId).getHeaderCaption(); + // TODO This should be shown in the editor component once + // there is a place for that. Optionally, all errors should be + // shown + Notification.show(caption + ": " + + invalidFields.get(field).getLocalizedMessage(), + Type.ERROR_MESSAGE); + + } else { + com.vaadin.server.ErrorEvent.findErrorHandler(Grid.this).error( + new ConnectorErrorEvent(Grid.this, event.getCause())); + } + } + + private Object getFirstPropertyId(FieldGroup fieldGroup, + Set<Field<?>> keySet) { + for (Column c : getColumns()) { + Object propertyId = c.getPropertyId(); + Field<?> f = fieldGroup.getField(propertyId); + if (keySet.contains(f)) { + return propertyId; + } + } + return null; + } + } + + /** * Selection modes representing built-in {@link SelectionModel * SelectionModels} that come bundled with {@link Grid}. * <p> @@ -2561,6 +2719,8 @@ public class Grid extends AbstractComponent implements SelectionNotifier, */ private boolean defaultContainer = true; + private EditorErrorHandler editorErrorHandler = new DefaultEditorErrorHandler(); + private static final Method SELECTION_CHANGE_METHOD = ReflectTools .findMethod(SelectionListener.class, "select", SelectionEvent.class); @@ -2800,6 +2960,15 @@ public class Grid extends AbstractComponent implements SelectionNotifier, try { saveEditor(); success = true; + } catch (CommitException e) { + try { + getEditorErrorHandler().commitError( + new CommitErrorEvent(Grid.this, e)); + } catch (Exception ee) { + // A badly written error handler can throw an exception, + // which would lock up the Grid + handleError(ee); + } } catch (Exception e) { handleError(e); } @@ -4653,6 +4822,35 @@ public class Grid extends AbstractComponent implements SelectionNotifier, } /** + * Sets the error handler for the editor. + * + * The error handler is called whenever there is an exception in the editor. + * + * @param editorErrorHandler + * The editor error handler to use + * @throws IllegalArgumentException + * if the error handler is null + */ + public void setEditorErrorHandler(EditorErrorHandler editorErrorHandler) + throws IllegalArgumentException { + if (editorErrorHandler == null) { + throw new IllegalArgumentException( + "The error handler cannot be null"); + } + this.editorErrorHandler = editorErrorHandler; + } + + /** + * Gets the error handler used for the editor + * + * @see #setErrorHandler(com.vaadin.server.ErrorHandler) + * @return the editor error handler, never null + */ + public EditorErrorHandler getEditorErrorHandler() { + return editorErrorHandler; + } + + /** * Gets the field factory for the {@link FieldGroup}. The field factory is * only used when {@link FieldGroup} creates a new field. * <p> diff --git a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java index da86c83771..d77a2e190b 100644 --- a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java +++ b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/FieldGroupTest.java @@ -15,10 +15,23 @@ */ package com.vaadin.tests.server.component.fieldgroup; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + import org.junit.Assert; import org.junit.Test; +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.data.util.AbstractProperty; +import com.vaadin.ui.Field; import com.vaadin.ui.TextField; /** @@ -52,4 +65,85 @@ public class FieldGroupTest { Assert.assertFalse("Field is not writable", field.isReadOnly()); } + + @Test + public void commit_validationFailed_allValidationFailuresAvailable() + throws CommitException { + FieldGroup fieldGroup = new FieldGroup(); + + fieldGroup.setItemDataSource(new TestItem()); + + TextField field1 = new TextField(); + field1.setRequired(true); + fieldGroup.bind(field1, "prop1"); + + TextField field2 = new TextField(); + field2.setRequired(true); + fieldGroup.bind(field2, "prop2"); + + Set<TextField> set = new HashSet<TextField>(Arrays.asList(field1, + field2)); + + try { + fieldGroup.commit(); + Assert.fail("No commit exception is thrown"); + } catch (CommitException exception) { + Map<Field<?>, ? extends InvalidValueException> invalidFields = exception + .getInvalidFields(); + for (Entry<Field<?>, ? extends InvalidValueException> entry : invalidFields + .entrySet()) { + set.remove(entry.getKey()); + } + Assert.assertEquals( + "Some fields are not found in the invalid fields map", 0, + set.size()); + Assert.assertEquals( + "Invalid value exception should be thrown for each field", + 2, invalidFields.size()); + } + } + + private static class TestItem implements Item { + + @Override + public Property<String> getItemProperty(Object id) { + return new StringProperty(); + } + + @Override + public Collection<?> getItemPropertyIds() { + return Arrays.asList("prop1", "prop2"); + } + + @Override + public boolean addItemProperty(Object id, Property property) + throws UnsupportedOperationException { + return false; + } + + @Override + public boolean removeItemProperty(Object id) + throws UnsupportedOperationException { + return false; + } + + } + + private static class StringProperty extends AbstractProperty<String> { + + @Override + public String getValue() { + return null; + } + + @Override + public void setValue(String newValue) throws Property.ReadOnlyException { + } + + @Override + public Class<? extends String> getType() { + return String.class; + } + } + } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java index 0218fffe61..c6eb8d042b 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridEditorTest.java @@ -187,9 +187,10 @@ public class GridEditorTest extends GridBasicFeaturesTest { intField.clear(); intField.sendKeys("banana phone"); editor.save(); - assertTrue( - "No exception on invalid value.", - logContainsText("Exception occured, com.vaadin.data.fieldgroup.FieldGroup$CommitException: Commit failed")); + WebElement n = $(NotificationElement.class).first(); + assertEquals("Column 7: Could not convert value to Integer", + n.getText()); + n.click(); editor.cancel(); selectMenuPath(EDIT_ITEM_100); diff --git a/uitest/src/com/vaadin/tests/fieldgroup/AbstractBasicCrud.java b/uitest/src/com/vaadin/tests/fieldgroup/AbstractBasicCrud.java index 30cc5676bb..c88047e414 100644 --- a/uitest/src/com/vaadin/tests/fieldgroup/AbstractBasicCrud.java +++ b/uitest/src/com/vaadin/tests/fieldgroup/AbstractBasicCrud.java @@ -16,16 +16,20 @@ package com.vaadin.tests.fieldgroup; import java.util.Iterator; +import java.util.Map; import com.vaadin.annotations.Theme; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.fieldgroup.BeanFieldGroup; import com.vaadin.data.fieldgroup.FieldGroup.CommitException; import com.vaadin.data.fieldgroup.PropertyId; import com.vaadin.data.util.BeanItem; import com.vaadin.data.util.BeanItemContainer; +import com.vaadin.data.validator.IntegerRangeValidator; import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.shared.util.SharedUtil; import com.vaadin.tests.components.AbstractTestUIWithLog; import com.vaadin.ui.Alignment; @@ -34,10 +38,15 @@ import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.CheckBox; import com.vaadin.ui.ComboBox; +import com.vaadin.ui.Component; import com.vaadin.ui.Field; import com.vaadin.ui.GridLayout; import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.Notification; +import com.vaadin.ui.Notification.Type; import com.vaadin.ui.TextField; +import com.vaadin.ui.themes.ValoTheme; @Theme("valo") public abstract class AbstractBasicCrud extends AbstractTestUIWithLog { @@ -100,6 +109,7 @@ public abstract class AbstractBasicCrud extends AbstractTestUIWithLog { private TextField birthDate = new TextField("Birth date"); private TextField age = new TextField("Age"); private CheckBox alive = new CheckBox("Alive"); + private Label errorLabel = new Label((String) null, ContentMode.HTML); @PropertyId("address.streetAddress") private TextField address_streetAddress = new TextField( @@ -120,17 +130,51 @@ public abstract class AbstractBasicCrud extends AbstractTestUIWithLog { address_country.setNullRepresentation(""); birthDate.setNullRepresentation(""); + age.addValidator(new IntegerRangeValidator( + "Must be between 0 and 100", 0, 100)); + setDefaultComponentAlignment(Alignment.MIDDLE_LEFT); addComponents(firstName, lastName, gender, birthDate, age, alive, address_streetAddress, address_postalCode, address_city, address_country); + errorLabel.addStyleName(ValoTheme.LABEL_COLORED); + setRows(3); + addComponent(errorLabel, 0, 2, getColumns() - 1, 2); + HorizontalLayout hl = new HorizontalLayout(save, cancel); hl.setSpacing(true); addComponent(hl); } + @Override + protected void handleCommitException(CommitException e) { + String message = ""; + // Produce error message in the order in which the fields are in the + // layout + for (Component c : this) { + if (!(c instanceof Field)) { + continue; + } + Field<?> f = (Field<?>) c; + Map<Field<?>, InvalidValueException> exceptions = e + .getInvalidFields(); + if (exceptions.containsKey(f)) { + message += f.getCaption() + ": " + + exceptions.get(f).getLocalizedMessage() + + "<br/>\n"; + } + } + + errorLabel.setValue(message); + } + + @Override + protected void discard() { + super.discard(); + errorLabel.setValue(null); + } } protected abstract void deselectAll(); @@ -153,6 +197,7 @@ public abstract class AbstractBasicCrud extends AbstractTestUIWithLog { fieldGroup.commit(); log("Saved " + fieldGroup.getItemDataSource()); } catch (CommitException e) { + handleCommitException(e); log("Commit failed: " + e.getMessage()); } } @@ -161,11 +206,28 @@ public abstract class AbstractBasicCrud extends AbstractTestUIWithLog { @Override public void buttonClick(ClickEvent event) { log("Discarded " + fieldGroup.getItemDataSource()); - deselectAll(); + discard(); } }); } + protected void discard() { + deselectAll(); + } + + protected void handleCommitException(CommitException e) { + String message = ""; + for (Object propertyId : e.getInvalidFields().keySet()) { + Field<?> f = e.getFieldGroup().getField(propertyId); + message += f.getCaption() + ": " + + e.getInvalidFields().get(propertyId); + } + + if (!message.isEmpty()) { + Notification.show(message, Type.ERROR_MESSAGE); + } + } + public void edit(BeanItem<ComplexPerson> item) { fieldGroup.setItemDataSource(item); } diff --git a/uitest/src/com/vaadin/tests/fieldgroup/BasicCrudGridEditorRowTest.java b/uitest/src/com/vaadin/tests/fieldgroup/BasicCrudGridEditorRowTest.java new file mode 100644 index 0000000000..bdf8603c48 --- /dev/null +++ b/uitest/src/com/vaadin/tests/fieldgroup/BasicCrudGridEditorRowTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2014 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.fieldgroup; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.elements.DateFieldElement; +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.elements.GridElement.GridCellElement; +import com.vaadin.testbench.elements.GridElement.GridEditorElement; +import com.vaadin.tests.annotations.TestCategory; +import com.vaadin.tests.tb3.MultiBrowserTest; + +@TestCategory("grid") +public class BasicCrudGridEditorRowTest extends MultiBrowserTest { + private GridElement grid; + + @Before + public void openTest() { + openTestURL(); + grid = $(GridElement.class).first(); + + } + + @Test + public void lookAndFeel() throws Exception { + GridCellElement ritaBirthdate = grid.getCell(2, 3); + compareScreen("grid"); + + // Open editor row + new Actions(getDriver()).doubleClick(ritaBirthdate).perform(); + compareScreen("editorrow"); + } + + @Test + public void editorRowOneInvalidValue() throws Exception { + GridCellElement ritaBirthdate = grid.getCell(2, 3); + // Open editor row + new Actions(getDriver()).doubleClick(ritaBirthdate).perform(); + + GridEditorElement editor = grid.getEditor(); + DateFieldElement dateField = editor.$(DateFieldElement.class).first(); + WebElement input = dateField.findElement(By.xpath("input")); + // input.click(); + input.sendKeys("Invalid", Keys.TAB); + editor.save(); + + Assert.assertTrue("Editor wasn't displayed.", editor.isDisplayed()); + Assert.assertTrue("DateField wasn't displayed.", + dateField.isDisplayed()); + + Assert.assertTrue("DateField didn't have 'v-invalid' css class.", + hasCssClass(dateField, "v-datefield-error")); + } + +} |