/* * Copyright 2010 Google Inc. * * 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.google.gwt.editor.client; import com.google.gwt.core.client.GWT; import com.google.gwt.editor.client.adapters.EditorSource; import com.google.gwt.editor.client.adapters.ListEditor; import com.google.gwt.junit.client.GWTTestCase; import com.google.gwt.validation.client.impl.ConstraintViolationImpl; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; import javax.validation.ConstraintViolation; import javax.validation.Path; /** * Tests error propagation in generated code. */ public class EditorErrorTest extends GWTTestCase { class AddressEditorWithErrors extends AddressEditor implements ValueAwareEditor<Address> { private EditorDelegate<Address> delegate; @Override public void flush() { delegate.recordError("Hello Errors!", null, null); } @Override public void onPropertyChange(String... paths) { } @Override public void setDelegate(EditorDelegate<Address> delegate) { this.delegate = delegate; } @Override public void setValue(Address value) { } } class AddressEditorReceivesErrors extends AddressEditor implements HasEditorErrors<Address> { List<EditorError> errors; @Override public void showErrors(List<EditorError> errors) { this.errors = errors; for (EditorError error : errors) { error.setConsumed(true); } } } class PersonEditorReceivesErrors extends PersonEditor implements HasEditorErrors<Person> { List<EditorError> errors; @Override public void showErrors(List<EditorError> errors) { this.errors = errors; for (EditorError error : errors) { error.setConsumed(true); } } } class Workgroup { List<Person> people = new ArrayList<Person>(); List<Person> getPeople() { return people; } } class WorkgroupEditor implements HasEditorErrors<Workgroup> { ListEditor<Person, PersonEditor> people = ListEditor.of(new EditorSource<PersonEditor>() { @Override public PersonEditor create(int index) { PersonEditor toReturn = new PersonEditor(); toReturn.addressEditor = new AddressEditorWithErrors(); return toReturn; } }); private List<EditorError> errors; @Override public void showErrors(List<EditorError> errors) { this.errors = errors; } } interface WorkgroupEditorDriver extends SimpleBeanEditorDriver<Workgroup, WorkgroupEditor> { } class WorkgroupNestedErrorsEditor implements HasEditorErrors<Workgroup> { ListEditor<Person, PersonEditorReceivesErrors> people = ListEditor.of(new EditorSource<PersonEditorReceivesErrors>() { @Override public PersonEditorReceivesErrors create(int index) { PersonEditorReceivesErrors toReturn = new PersonEditorReceivesErrors(); toReturn.addressEditor = new AddressEditorWithErrors(); return toReturn; } }); List<EditorError> errors; @Override public void showErrors(List<EditorError> errors) { this.errors = errors; } } interface WorkgroupNestedErrorsEditorDriver extends SimpleBeanEditorDriver<Workgroup, WorkgroupNestedErrorsEditor> { } @Override public String getModuleName() { return "com.google.gwt.editor.Editor"; } public void testListOfErrors() { Address a = new Address(); Person p1 = new Person(); p1.address = a; Person p2 = new Person(); p2.address = a; Workgroup workgroup = new Workgroup(); workgroup.people.addAll(Arrays.asList(p1, p2)); WorkgroupEditorDriver driver = GWT.create(WorkgroupEditorDriver.class); WorkgroupEditor editor = new WorkgroupEditor(); driver.initialize(editor); driver.edit(workgroup); driver.flush(); List<EditorError> errors = editor.errors; assertNotNull(errors); assertEquals("Wrong number of EditorErrors", 2, errors.size()); assertEquals(Arrays.asList("people[0].address", "people[1].address"), Arrays.asList(errors.get(0).getPath(), errors.get(1).getPath())); } public void testNestedErrors() { Address a = new Address(); Person p1 = new Person(); p1.address = a; Person p2 = new Person(); p2.address = a; Workgroup workgroup = new Workgroup(); workgroup.people.addAll(Arrays.asList(p1, p2)); WorkgroupNestedErrorsEditorDriver driver = GWT.create(WorkgroupNestedErrorsEditorDriver.class); WorkgroupNestedErrorsEditor editor = new WorkgroupNestedErrorsEditor(); driver.initialize(editor); driver.edit(workgroup); driver.flush(); // Verify the nested editors received the errors List<EditorError> errors = editor.errors; assertNotNull(errors); assertEquals(0, errors.size()); List<PersonEditorReceivesErrors> editors = editor.people.getEditors(); for (int i = 0, j = editors.size(); i < j; i++) { PersonEditorReceivesErrors personEditor = editors.get(i); assertEquals(1, personEditor.errors.size()); EditorError error = personEditor.errors.get(0); assertNotNull(error); assertEquals("people[" + i + "].address", error.getAbsolutePath()); assertEquals("Hello Errors!", error.getMessage()); assertEquals("address", error.getPath()); assertNull(error.getUserData()); assertSame(a, error.getValue()); assertSame(personEditor.addressEditor, error.getEditor()); } } public void testNoErrors() { Address a = new Address(); Person p = new Person(); p.address = a; PersonEditorDriver driver = GWT.create(PersonEditorDriver.class); PersonEditorReceivesErrors editor = new PersonEditorReceivesErrors(); driver.initialize(editor); driver.edit(p); driver.flush(); assertEquals(0, editor.errors.size()); assertFalse(driver.hasErrors()); assertNotNull(driver.getErrors()); assertEquals(0, driver.getErrors().size()); assertFalse(driver.setConstraintViolations(Collections.<ConstraintViolation<?>>emptyList())); assertEquals(0, editor.errors.size()); assertFalse(driver.hasErrors()); assertNotNull(driver.getErrors()); assertEquals(0, driver.getErrors().size()); // Test no NPE is thrown; see issue 6578 assertFalse(driver.setConstraintViolations(null)); assertEquals(0, editor.errors.size()); assertFalse(driver.hasErrors()); assertNotNull(driver.getErrors()); assertEquals(0, driver.getErrors().size()); } public void testSimpleError() { PersonEditorReceivesErrors editor = new PersonEditorReceivesErrors(); editor.addressEditor = new AddressEditorWithErrors(); Address a = new Address(); Person p = new Person(); p.address = a; PersonEditorDriver driver = GWT.create(PersonEditorDriver.class); driver.initialize(editor); driver.edit(p); driver.flush(); List<EditorError> list = editor.errors; assertNotNull(list); assertEquals(1, list.size()); EditorError error = list.get(0); assertNotNull(error); assertEquals("address", error.getAbsolutePath()); assertEquals("Hello Errors!", error.getMessage()); assertEquals("address", error.getPath()); assertNull(error.getUserData()); assertSame(a, error.getValue()); assertSame(editor.addressEditor, error.getEditor()); } public void testUnmatchedConstraintViolationsHasErrors() { Address a = new Address(); Person p = new Person(); p.address = a; PersonEditorDriver driver = GWT.create(PersonEditorDriver.class); PersonEditor editor = new PersonEditor(); driver.initialize(editor); driver.edit(p); driver.flush(); ConstraintViolation<Person> e1 = createViolation("msg1", p, "address"); ConstraintViolation<Person> e2 = createViolation("msg2", p, "bogus1"); ConstraintViolation<Person> e3 = createViolation("msg3", p, "address.city"); ConstraintViolation<Person> e4 = createViolation("msg4", p, "address.bogus2"); ConstraintViolation<Person> e5 = createViolation("msg5", p, "address.city.bogus3"); ConstraintViolation<Person> e6 = createViolation("msg6", p, "address.bogusparent.boguschild"); ConstraintViolation<Person> e7 = createViolation("msg7", p, "."); ConstraintViolation<Person> e8 = createViolation("msg8", p, "address."); driver.setConstraintViolations( Arrays.<ConstraintViolation<?>>asList(e1, e2, e3, e4, e5, e6, e7, e8)); assertTrue(driver.hasErrors()); assertEquals(driver.getErrors().toString(), 8, driver.getErrors().size()); List<EditorError> list = driver.getErrors(); // All the errors w/ addressEditor are collected first EditorError error = list.get(0); assertEquals("msg1", error.getMessage()); assertEquals("address", error.getAbsolutePath()); assertEquals("address", error.getPath()); assertSame(e1, error.getUserData()); assertSame(a, error.getValue()); assertSame(editor.addressEditor, error.getEditor()); error = list.get(1); assertEquals("msg4", error.getMessage()); assertEquals("address.bogus2", error.getAbsolutePath()); assertEquals("address.bogus2", error.getPath()); assertSame(e4, error.getUserData()); assertSame(null, error.getValue()); assertSame(editor.addressEditor, error.getEditor()); error = list.get(2); assertEquals("msg6", error.getMessage()); assertEquals("address.bogusparent.boguschild", error.getAbsolutePath()); assertEquals("address.bogusparent.boguschild", error.getPath()); assertSame(e6, error.getUserData()); assertSame(null, error.getValue()); assertSame(editor.addressEditor, error.getEditor()); error = list.get(3); assertEquals("msg8", error.getMessage()); assertEquals("address.", error.getAbsolutePath()); assertEquals("address.", error.getPath()); assertSame(e8, error.getUserData()); assertSame(null, error.getValue()); assertSame(editor.addressEditor, error.getEditor()); // Then the rest of the errors. error = list.get(4); assertEquals("msg2", error.getMessage()); assertEquals("bogus1", error.getAbsolutePath()); assertEquals("bogus1", error.getPath()); assertSame(e2, error.getUserData()); assertSame(null, error.getValue()); assertSame(editor, error.getEditor()); error = list.get(5); assertEquals("msg3", error.getMessage()); assertEquals("address.city", error.getAbsolutePath()); assertEquals("address.city", error.getPath()); assertSame(e3, error.getUserData()); assertSame(a.city, error.getValue()); assertSame(editor.addressEditor.city, error.getEditor()); error = list.get(6); assertEquals("msg5", error.getMessage()); assertEquals("address.city.bogus3", error.getAbsolutePath()); assertEquals("address.city.bogus3", error.getPath()); assertSame(e5, error.getUserData()); assertSame(null, error.getValue()); assertSame(editor.addressEditor.city, error.getEditor()); error = list.get(7); assertEquals("msg7", error.getMessage()); assertEquals(".", error.getAbsolutePath()); assertEquals(".", error.getPath()); assertSame(e7, error.getUserData()); assertSame(null, error.getValue()); assertSame(editor, error.getEditor()); } /** * Test for IndexOutOfBoundsException in getPath() when called on a leaf editor error display. * * The external bug report: http://code.google.com/p/google-web-toolkit/issues/detail?id=5589 */ public void testSamePathHasError() { PersonEditorReceivesErrors editor = new PersonEditorReceivesErrors(); AddressEditorReceivesErrors addressEditor = new AddressEditorReceivesErrors(); editor.addressEditor = addressEditor; Address a = new Address(); Person p = new Person(); p.address = a; PersonEditorDriver driver = GWT.create(PersonEditorDriver.class); driver.initialize(editor); driver.edit(p); driver.flush(); driver.setConstraintViolations( Arrays.<ConstraintViolation<?>>asList(createViolation("samePathError", p, "address"))); assertEquals(0, driver.getErrors().size()); assertEquals(0, editor.errors.size()); List<EditorError> list = addressEditor.errors; assertNotNull(list); assertEquals(1, list.size()); EditorError error = list.get(0); assertNotNull(error); assertEquals("address", error.getAbsolutePath()); assertEquals("samePathError", error.getMessage()); assertEquals("", error.getPath()); assertNotNull(error.getUserData()); assertSame(a, error.getValue()); assertSame(addressEditor, error.getEditor()); } private <T> ConstraintViolation<T> createViolation( String msg, T rootBean, final String path) { return new ConstraintViolationImpl.Builder<T>() .setMessage(msg) .setRootBean(rootBean) .setPropertyPath(new Path() { @Override public Iterator<Node> iterator() { return null; } @Override public String toString() { return path; } }).build(); } }