From a43fd9003a77f253a78b807d4ecadcd828b936a4 Mon Sep 17 00:00:00 2001 From: Denis Date: Tue, 13 Dec 2016 15:46:29 +0200 Subject: Provide declarative support for Grid. (#7961) Fixes vaadin/framework8-issues#390 --- .../tests/design/DeclarativeTestBaseBase.java | 4 +- .../AbstractComponentDeclarativeTestBase.java | 23 +- .../server/component/grid/GridDeclarativeTest.java | 731 +++++++++++++++++++++ 3 files changed, 748 insertions(+), 10 deletions(-) create mode 100644 server/src/test/java/com/vaadin/tests/server/component/grid/GridDeclarativeTest.java (limited to 'server/src/test') diff --git a/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java b/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java index 1bab760530..041df2deee 100644 --- a/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java +++ b/server/src/test/java/com/vaadin/tests/design/DeclarativeTestBaseBase.java @@ -221,9 +221,9 @@ public abstract class DeclarativeTestBaseBase { return context; } - public void testWrite(String design, T expected) { + public void testWrite(String expected, T component) { TestLogHandler l = new TestLogHandler(); - testWrite(design, expected, false); + testWrite(expected, component, false); Assert.assertEquals("", l.getMessages()); } diff --git a/server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java b/server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java index 7733855e95..ea7e9c77a3 100644 --- a/server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java +++ b/server/src/test/java/com/vaadin/tests/server/component/abstractcomponent/AbstractComponentDeclarativeTestBase.java @@ -92,16 +92,24 @@ public abstract class AbstractComponentDeclarativeTestBase", + + "%s responsive style-name='%s' visible='false' " + + "%s/>", getComponentTag(), id, caption, description, error, width, - height, icon, locale.toString(), primaryStyle, styleName); + height, icon, locale.toString(), primaryStyle, + hasReadOnly ? "readonly" : "", styleName, + hasRequiredIndicator ? "required-indicator-visible" : ""); - T component = getComponentClass().newInstance(); component.setId(id); component.setCaption(caption); component.setCaptionAsHtml(captionAsHtml); @@ -115,9 +123,6 @@ public abstract class AbstractComponentDeclarativeTestBase { + + @Test + public void gridAttributes() { + Grid grid = new Grid<>(); + int frozenColumns = 1; + HeightMode heightMode = HeightMode.ROW; + double heightByRows = 13.7d; + + grid.addColumn(Person::getFirstName); + grid.addColumn("id", Person::getLastName); + + grid.setFrozenColumnCount(frozenColumns); + grid.setSelectionMode(SelectionMode.MULTI); + grid.setHeightMode(heightMode); + grid.setHeightByRows(heightByRows); + + String design = String.format( + "<%s height-mode='%s' frozen-columns='%d' rows='%s' selection-mode='%s'>" + + "" + + "" + "" + + "" + + "" + + "
Generated Column0Id
", + getComponentTag(), + heightMode.toString().toLowerCase(Locale.ENGLISH), + frozenColumns, heightByRows, + SelectionMode.MULTI.toString().toLowerCase(Locale.ENGLISH), + getComponentTag()); + + testRead(design, grid); + testWrite(design, grid); + } + + @Test + public void mergedHeaderCells() { + Grid grid = new Grid<>(); + + Column column1 = grid.addColumn(Person::getFirstName); + Column column2 = grid.addColumn("id", + Person::getLastName); + Column column3 = grid.addColumn("mail", + Person::getEmail); + + HeaderRow header = grid.addHeaderRowAt(1); + String headerRowText1 = "foo"; + header.getCell(column1).setText(headerRowText1); + HeaderCell cell2 = header.getCell(column2); + HeaderCell join = header.join(cell2, header.getCell(column3)); + String headerRowText3 = "foobar"; + join.setText(headerRowText3); + + String design = String.format("<%s>" + + "" + + "" + + "" + "" + + "" + + "" + + "" + + "" + + "" + + "
Generated Column0IdMail
%sfoobar
", getComponentTag(), headerRowText1, + headerRowText3, getComponentTag()); + + testRead(design, grid); + testWrite(design, grid); + } + + @Test + public void mergedFooterCells() { + Grid grid = new Grid<>(); + + Column column1 = grid.addColumn(Person::getFirstName); + Column column2 = grid.addColumn("id", + Person::getLastName); + Column column3 = grid.addColumn("mail", + Person::getEmail); + + FooterRow footer = grid.addFooterRowAt(0); + + FooterCell cell1 = footer.getCell(column1); + String footerRowText1 = "foo"; + cell1.setText(footerRowText1); + + FooterCell cell2 = footer.getCell(column2); + + FooterCell cell3 = footer.getCell(column3); + String footerRowText2 = "foobar"; + footer.join(cell2, cell3).setHtml(footerRowText2); + + String design = String.format("<%s>" + + "" + + "" + + "" + "" + + "" + + "" + + "" + + "" + + "" + + "
Generated Column0IdMail
%s%s
", getComponentTag(), footerRowText1, + footerRowText2, getComponentTag()); + + testRead(design, grid); + testWrite(design, grid); + } + + @Test + public void columnAttributes() { + Grid grid = new Grid<>(); + + String secondColumnId = "id"; + Column column1 = grid.addColumn(Person::getFirstName); + Column column2 = grid.addColumn(secondColumnId, + Person::getLastName); + + String caption = "test-caption"; + column1.setCaption(caption); + boolean sortable = false; + column1.setSortable(sortable); + boolean editable = true; + column1.setEditorComponentGenerator(component -> null); + column1.setEditable(editable); + boolean resizable = false; + column1.setResizable(resizable); + boolean hidable = true; + column1.setHidable(hidable); + boolean hidden = true; + column1.setHidden(hidden); + + String hidingToggleCaption = "toggle-caption"; + column2.setHidingToggleCaption(hidingToggleCaption); + double width = 17.3; + column2.setWidth(width); + double minWidth = 37.3; + column2.setMinimumWidth(minWidth); + double maxWidth = 63.4; + column2.setMaximumWidth(maxWidth); + int expandRatio = 83; + column2.setExpandRatio(expandRatio); + + String design = String.format( + "<%s>" + + "" + + "" + + "" + + "" + + "" + + "" + "
%s%s
", + getComponentTag(), sortable, resizable, hidingToggleCaption, + width, minWidth, maxWidth, expandRatio, caption, "Id", + getComponentTag()); + + testRead(design, grid, true); + testWrite(design, grid); + } + + @Test + public void headerFooterSerialization() { + Grid grid = new Grid<>(); + + Column column1 = grid.addColumn(Person::getFirstName); + Column column2 = grid.addColumn("id", + Person::getLastName); + + FooterRow footerRow = grid.addFooterRowAt(0); + footerRow.getCell(column1).setText("x"); + footerRow.getCell(column2).setHtml("y"); + + String design = String.format("<%s>" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + "
Generated Column0Id
xy
", + getComponentTag(), getComponentTag()); + + testRead(design, grid); + testWrite(design, grid, true); + } + + @Override + public void dataSerialization() throws InstantiationException, + IllegalAccessException, InvocationTargetException { + Grid grid = new Grid<>(); + + Person person1 = createPerson("foo", "bar"); + Person person2 = createPerson("name", "last-name"); + grid.setItems(person1, person2); + + grid.addColumn(Person::getFirstName); + grid.addColumn("id", Person::getLastName); + + String design = String.format( + "<%s>" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
Generated Column0Id
%s%s
%s%s
", + getComponentTag(), person1.toString(), person1.getFirstName(), + person1.getLastName(), person2.toString(), + person2.getFirstName(), person2.getLastName(), + getComponentTag()); + + testRead(design, grid); + testWrite(design, grid, true); + } + + /** + * Value for single select + */ + @Override + @Test + public void valueSerialization() throws InstantiationException, + IllegalAccessException, InvocationTargetException { + valueSingleSelectSerialization(); + } + + @SuppressWarnings("unchecked") + @Test + public void valueMultiSelectSerialization() throws InstantiationException, + IllegalAccessException, InvocationTargetException { + Grid grid = new Grid<>(); + + Person person1 = createPerson("foo", "bar"); + Person person2 = createPerson("name", "last-name"); + Person person3 = createPerson("foo", "last-name"); + grid.setItems(person1, person2, person3); + + grid.addColumn(Person::getFirstName); + grid.addColumn("id", Person::getLastName); + + Multi model = (Multi) grid + .setSelectionMode(SelectionMode.MULTI); + model.selectItems(person1, person3); + + String design = String.format( + "<%s selection-mode='multi'>" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
Generated Column0Id
%s%s
%s%s
%s%s
", + getComponentTag(), person1.toString(), person1.getFirstName(), + person1.getLastName(), person2.toString(), + person2.getFirstName(), person2.getLastName(), + person3.toString(), person3.getFirstName(), + person3.getLastName(), getComponentTag()); + + testRead(design, grid); + testWrite(design, grid, true); + } + + @SuppressWarnings("unchecked") + private void valueSingleSelectSerialization() throws InstantiationException, + IllegalAccessException, InvocationTargetException { + Grid grid = new Grid<>(); + + Person person1 = createPerson("foo", "bar"); + Person person2 = createPerson("name", "last-name"); + grid.setItems(person1, person2); + + grid.addColumn(Person::getFirstName); + grid.addColumn("id", Person::getLastName); + + Single model = (Single) grid + .setSelectionMode(SelectionMode.SINGLE); + model.select(person2); + + String design = String.format( + "<%s>" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
Generated Column0Id
%s%s
%s%s
", + getComponentTag(), person1.toString(), person1.getFirstName(), + person1.getLastName(), person2.toString(), + person2.getFirstName(), person2.getLastName(), + getComponentTag()); + + testRead(design, grid); + testWrite(design, grid, true); + } + + @Override + public void readOnlySelection() throws InstantiationException, + IllegalAccessException, InvocationTargetException { + Grid grid = new Grid<>(); + + Person person1 = createPerson("foo", "bar"); + Person person2 = createPerson("name", "last-name"); + grid.setItems(person1, person2); + + grid.addColumn(Person::getFirstName); + grid.addColumn("id", Person::getLastName); + + grid.setSelectionMode(SelectionMode.MULTI); + grid.asMultiSelect().setReadOnly(true); + + String formatString = "<%s %s selection-allowed>" + + "" + + "" + "" + + "" + + "" + + "" + + "" + + "" + + "
Generated Column0Id
%s%s
%s%s
"; + + String design = String.format(formatString, getComponentTag(), + "selection-mode='multi'", person1.toString(), + person1.getFirstName(), person1.getLastName(), + person2.toString(), person2.getFirstName(), + person2.getLastName(), getComponentTag()); + + testRead(design, grid); + testWrite(design, grid, true); + + grid.setSelectionMode(SelectionMode.SINGLE); + grid.asSingleSelect().setReadOnly(true); + + design = String.format(formatString, getComponentTag(), "", + person1.toString(), person1.getFirstName(), + person1.getLastName(), person2.toString(), + person2.getFirstName(), person2.getLastName(), + getComponentTag()); + + testRead(design, grid); + testWrite(design, grid, true); + } + + @Test + public void testComponentInGridHeader() { + Grid grid = new Grid<>(); + Column column = grid.addColumn(Person::getFirstName); + + String html = "Foo"; + Label component = new Label(html); + component.setContentMode(ContentMode.HTML); + + //@formatter:off + String design = String.format( "<%s>" + + "" + + " " + + "" + + "" + + "" + + "" + + "
%s
", getComponentTag(), html, getComponentTag()); + //@formatter:on + + grid.getDefaultHeaderRow().getCell(column.getId()) + .setComponent(component); + + testRead(design, grid, true); + testWrite(design, grid); + } + + @Test + public void testComponentInGridFooter() { + Grid grid = new Grid<>(); + Column column = grid.addColumn(Person::getFirstName); + + String html = "Foo"; + Label component = new Label(html); + component.setContentMode(ContentMode.HTML); + + grid.prependFooterRow().getCell(column).setComponent(component); + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + //@formatter:off + String design = String.format( "<%s>" + + "" + + " " + + "" + + "" + +"" + + "" + + "" + + "
%s
" + + "", getComponentTag(), html, getComponentTag()); + //@formatter:on + + testRead(design, grid, true); + testWrite(design, grid); + } + + @Test + public void testNoHeaderRows() { + //@formatter:off + String design = "" + + "" + + " " + + "" + + "" + + "
" + + "
"; + //@formatter:on + Grid grid = new Grid<>(); + grid.addColumn(Person::getFirstName); + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + testWrite(design, grid); + testRead(design, grid, true); + } + + @Test + public void testReadEmptyGrid() { + String design = ""; + testRead(design, new Grid(), false); + } + + @Test + public void testEmptyGrid() { + String design = ""; + Grid expected = new Grid<>(); + testWrite(design, expected); + testRead(design, expected, true); + } + + @Test(expected = DesignException.class) + public void testMalformedGrid() { + String design = ""; + testRead(design, new Grid()); + } + + @Test(expected = DesignException.class) + public void testGridWithNoColGroup() { + String design = "
Foo
"; + testRead(design, new Grid()); + } + + @Test + @SuppressWarnings("unchecked") + public void testHtmlEntitiesinGridHeaderFooter() { + String id = "> id"; + String plainText = "plain-text"; + //@formatter:off + String design = String.format( "<%s>" + + "" + + " " + + "" + + "" + +" " + + "" + + "" + + "" + + "" + + "" + + "
> Test
> Test
", + getComponentTag() , id, plainText, id, plainText, id, getComponentTag()); + //@formatter:on + + Grid grid = read(design); + String actualHeader = grid.getHeaderRow(0).getCell(id).getText(); + String actualFooter = grid.getFooterRow(0).getCell(id).getText(); + String expected = "> Test"; + + Assert.assertEquals(expected, actualHeader); + Assert.assertEquals(expected, actualFooter); + + design = design.replace(plainText, ""); + grid = read(design); + actualHeader = grid.getHeaderRow(0).getCell(id).getHtml(); + actualFooter = grid.getFooterRow(0).getCell(id).getHtml(); + expected = "> Test"; + + Assert.assertEquals(expected, actualHeader); + Assert.assertEquals(expected, actualFooter); + + grid = new Grid<>(); + Column column = grid.addColumn(id, + Person::getFirstName); + HeaderRow header = grid.addHeaderRowAt(0); + FooterRow footer = grid.addFooterRowAt(0); + grid.removeHeaderRow(grid.getDefaultHeaderRow()); + + // entities should be encoded when writing back, not interpreted as HTML + header.getCell(column).setText("& Test"); + footer.getCell(column).setText("& Test"); + + Element root = new Element(Tag.valueOf(getComponentTag()), ""); + grid.writeDesign(root, new DesignContext()); + + Assert.assertEquals("&amp; Test", + root.getElementsByTag("th").get(0).html()); + Assert.assertEquals("&amp; Test", + root.getElementsByTag("td").get(0).html()); + + header = grid.addHeaderRowAt(0); + footer = grid.addFooterRowAt(0); + + // entities should not be encoded, this is already given as HTML + header.getCell(id).setHtml("& Test"); + footer.getCell(id).setHtml("& Test"); + + root = new Element(Tag.valueOf(getComponentTag()), ""); + grid.writeDesign(root, new DesignContext()); + + Assert.assertEquals("& Test", + root.getElementsByTag("th").get(0).html()); + Assert.assertEquals("& Test", + root.getElementsByTag("td").get(0).html()); + + } + + @SuppressWarnings("rawtypes") + @Override + public Grid testRead(String design, Grid expected) { + return testRead(design, expected, false); + } + + @SuppressWarnings("rawtypes") + public Grid testRead(String design, Grid expected, boolean retestWrite) { + return testRead(design, expected, retestWrite, false); + } + + @SuppressWarnings("rawtypes") + public Grid testRead(String design, Grid expected, boolean retestWrite, + boolean writeData) { + Grid actual = super.testRead(design, expected); + + compareGridColumns(expected, actual); + compareHeaders(expected, actual); + compareFooters(expected, actual); + + if (retestWrite) { + testWrite(design, actual, writeData); + } + + return actual; + } + + private void compareHeaders(Grid expected, Grid actual) { + Assert.assertEquals("Different header row count", + expected.getHeaderRowCount(), actual.getHeaderRowCount()); + for (int i = 0; i < expected.getHeaderRowCount(); ++i) { + HeaderRow expectedRow = expected.getHeaderRow(i); + HeaderRow actualRow = actual.getHeaderRow(i); + + if (expectedRow.equals(expected.getDefaultHeaderRow())) { + Assert.assertEquals("Different index for default header row", + actual.getDefaultHeaderRow(), actualRow); + } + + for (Column column : expected.getColumns()) { + String baseError = "Difference when comparing cell for " + + column.toString() + " on header row " + i + ": "; + HeaderCell expectedCell = expectedRow.getCell(column); + HeaderCell actualCell = actualRow.getCell(column); + + switch (expectedCell.getCellType()) { + case TEXT: + Assert.assertEquals(baseError + "Text content", + expectedCell.getText(), actualCell.getText()); + break; + case HTML: + Assert.assertEquals(baseError + "HTML content", + expectedCell.getHtml(), actualCell.getHtml()); + break; + case WIDGET: + assertEquals(baseError + "Component content", + expectedCell.getComponent(), + actualCell.getComponent()); + break; + } + } + } + } + + private void compareFooters(Grid expected, Grid actual) { + Assert.assertEquals("Different footer row count", + expected.getFooterRowCount(), actual.getFooterRowCount()); + for (int i = 0; i < expected.getFooterRowCount(); ++i) { + FooterRow expectedRow = expected.getFooterRow(i); + FooterRow actualRow = actual.getFooterRow(i); + + for (Column column : expected.getColumns()) { + String baseError = "Difference when comparing cell for " + + column.toString() + " on footer row " + i + ": "; + FooterCell expectedCell = expectedRow.getCell(column); + FooterCell actualCell = actualRow.getCell(column); + + switch (expectedCell.getCellType()) { + case TEXT: + Assert.assertEquals(baseError + "Text content", + expectedCell.getText(), actualCell.getText()); + break; + case HTML: + Assert.assertEquals(baseError + "HTML content", + expectedCell.getHtml(), actualCell.getHtml()); + break; + case WIDGET: + assertEquals(baseError + "Component content", + expectedCell.getComponent(), + actualCell.getComponent()); + break; + } + } + } + } + + private void compareGridColumns(Grid expected, Grid actual) { + List columns = expected.getColumns(); + List actualColumns = actual.getColumns(); + Assert.assertEquals("Different amount of columns", columns.size(), + actualColumns.size()); + for (int i = 0; i < columns.size(); ++i) { + Column col1 = (Column) columns.get(i); + Column col2 = (Column) actualColumns.get(i); + String baseError = "Error when comparing columns for property " + + col1.getId() + ": "; + assertEquals(baseError + "Width", col1.getWidth(), col2.getWidth()); + assertEquals(baseError + "Maximum width", col1.getMaximumWidth(), + col2.getMaximumWidth()); + assertEquals(baseError + "Minimum width", col1.getMinimumWidth(), + col2.getMinimumWidth()); + assertEquals(baseError + "Expand ratio", col1.getExpandRatio(), + col2.getExpandRatio()); + assertEquals(baseError + "Sortable", col1.isSortable(), + col2.isSortable()); + assertEquals(baseError + "Editable", col1.isEditable(), + col2.isEditable()); + assertEquals(baseError + "Hidable", col1.isHidable(), + col2.isHidable()); + assertEquals(baseError + "Hidden", col1.isHidden(), + col2.isHidden()); + assertEquals(baseError + "HidingToggleCaption", + col1.getHidingToggleCaption(), + col2.getHidingToggleCaption()); + } + } + + @Override + protected String getComponentTag() { + return "vaadin-grid"; + } + + @Override + protected Class getComponentClass() { + return Grid.class; + } + + @Override + protected boolean acceptProperty(Class clazz, Method readMethod, + Method writeMethod) { + if (readMethod != null) { + Class returnType = readMethod.getReturnType(); + if (HeaderRow.class.equals(returnType) + || DataProvider.class.equals(returnType)) { + return false; + } + } + return super.acceptProperty(clazz, readMethod, writeMethod); + } + + private Person createPerson(String name, String lastName) { + Person person = new Person() { + @Override + public String toString() { + return getFirstName() + " " + getLastName(); + } + }; + person.setFirstName(name); + person.setLastName(lastName); + return person; + } + +} -- cgit v1.2.3