diff options
59 files changed, 2033 insertions, 632 deletions
diff --git a/.gitignore b/.gitignore index 1433651abc..4b28bcd6e0 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,8 @@ WebContent/VAADIN/vaadinPush.js WebContent/VAADIN/vaadinPush.debug.js +WebContent/VAADIN/vaadinPush.js.gz +WebContent/VAADIN/vaadinPush.debug.js.gz # /WebContent/WEB-INF/ /WebContent/WEB-INF/classes @@ -75,6 +77,9 @@ WebContent/VAADIN/vaadinPush.debug.js # Mac *.DS_Store +# Python +scripts/*.pyc + # build result folders */result result diff --git a/WebContent/release-notes.html b/WebContent/release-notes.html index ea65948baa..b7cdba7887 100644 --- a/WebContent/release-notes.html +++ b/WebContent/release-notes.html @@ -113,6 +113,7 @@ <li>Widgetset files and other pre-compressed resources are sent as gzip to compatible browsers. This may interfere with custom response compression solutions that do not respect the Content-Encoding response header.</li> <li>Unused methods related to the "out of sync" message have been removed from SystemMessages class.</li> + <li>All notifications use the WAI-ARIA alert role to be compatible with Jaws</li> </ul> <h3 id="knownissues">Known Issues and Limitations</h3> <ul> diff --git a/client/src/com/vaadin/client/ComputedStyle.java b/client/src/com/vaadin/client/ComputedStyle.java index 7a04296b4b..61cb3c2eb6 100644 --- a/client/src/com/vaadin/client/ComputedStyle.java +++ b/client/src/com/vaadin/client/ComputedStyle.java @@ -131,7 +131,7 @@ public class ComputedStyle { /** * Retrieves the given computed property as an integer - * + * * Returns 0 if the property cannot be converted to an integer * * @param name @@ -148,7 +148,7 @@ public class ComputedStyle { /** * Retrieves the given computed property as a double - * + * * Returns NaN if the property cannot be converted to a double * * @param name @@ -205,7 +205,7 @@ public class ComputedStyle { /** * Returns the current width from the DOM. * - * @since + * @since 7.5.1 * @return the computed width */ public double getWidth() { @@ -215,7 +215,7 @@ public class ComputedStyle { /** * Returns the current height from the DOM. * - * @since + * @since 7.5.1 * @return the computed height */ public double getHeight() { diff --git a/client/src/com/vaadin/client/WidgetUtil.java b/client/src/com/vaadin/client/WidgetUtil.java index b9d22193de..4906197b29 100644 --- a/client/src/com/vaadin/client/WidgetUtil.java +++ b/client/src/com/vaadin/client/WidgetUtil.java @@ -1641,7 +1641,7 @@ public class WidgetUtil { * Firefox uses 1/60th of a pixel because it is divisible by three * (https://bugzilla.mozilla.org/show_bug.cgi?id=1070940) * - * @since + * @since 7.5.1 * @param size * the value to round * @return the rounded value @@ -1661,7 +1661,7 @@ public class WidgetUtil { * * IE9+ uses 1/100th of a pixel * - * @since + * @since 7.5.1 * @param size * the value to round * @return the rounded value @@ -1694,7 +1694,7 @@ public class WidgetUtil { /** * Returns the factor used by browsers to round subpixel values * - * @since + * @since 7.5.1 * @return the factor N used by the browser when storing subpixels as X+Y/N */ private static double getSubPixelRoundingFactor() { diff --git a/client/src/com/vaadin/client/connectors/GridConnector.java b/client/src/com/vaadin/client/connectors/GridConnector.java index bee9cedc43..d42041670e 100644 --- a/client/src/com/vaadin/client/connectors/GridConnector.java +++ b/client/src/com/vaadin/client/connectors/GridConnector.java @@ -735,13 +735,20 @@ public class GridConnector extends AbstractHasComponentsConnector implements private final DetailsListener detailsListener = new DetailsListener() { @Override - public void reapplyDetailsVisibility(int rowIndex, JsonObject row) { - if (hasDetailsOpen(row)) { - getWidget().setDetailsVisible(rowIndex, true); - detailsConnectorFetcher.schedule(); - } else { - getWidget().setDetailsVisible(rowIndex, false); - } + public void reapplyDetailsVisibility(final int rowIndex, + final JsonObject row) { + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + + @Override + public void execute() { + if (hasDetailsOpen(row)) { + getWidget().setDetailsVisible(rowIndex, true); + detailsConnectorFetcher.schedule(); + } else { + getWidget().setDetailsVisible(rowIndex, false); + } + } + }); } private boolean hasDetailsOpen(JsonObject row) { @@ -945,6 +952,13 @@ public class GridConnector extends AbstractHasComponentsConnector implements } @Override + public void onUnregister() { + customDetailsGenerator.indexToDetailsMap.clear(); + + super.onUnregister(); + } + + @Override public void onStateChanged(final StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); @@ -1061,6 +1075,10 @@ public class GridConnector extends AbstractHasComponentsConnector implements for (RowState rowState : state.rows) { HeaderRow row = getWidget().appendHeaderRow(); + if (rowState.defaultRow) { + getWidget().setDefaultHeaderRow(row); + } + for (CellState cellState : rowState.cells) { CustomGridColumn column = columnIdToColumn .get(cellState.columnId); @@ -1082,10 +1100,6 @@ public class GridConnector extends AbstractHasComponentsConnector implements updateHeaderCellFromState(row.join(columns), cellState); } - if (rowState.defaultRow) { - getWidget().setDefaultHeaderRow(row); - } - row.setStyleName(rowState.styleName); } } diff --git a/client/src/com/vaadin/client/extensions/ResponsiveConnector.java b/client/src/com/vaadin/client/extensions/ResponsiveConnector.java index 2e1e75f6cd..621c69788c 100644 --- a/client/src/com/vaadin/client/extensions/ResponsiveConnector.java +++ b/client/src/com/vaadin/client/extensions/ResponsiveConnector.java @@ -84,6 +84,10 @@ public class ResponsiveConnector extends AbstractExtensionConnector implements getLogger().log(Level.SEVERE, message); } + private static void warning(String message) { + getLogger().warning(message); + } + @Override protected void extend(ServerConnector target) { this.target = (AbstractComponentConnector) target; @@ -204,10 +208,17 @@ public class ResponsiveConnector extends AbstractExtensionConnector implements var IE = @com.vaadin.client.BrowserInfo::get()().@com.vaadin.client.BrowserInfo::isIE()(); var IE8 = @com.vaadin.client.BrowserInfo::get()().@com.vaadin.client.BrowserInfo::isIE8()(); - if (sheet.cssRules) { - theRules = sheet.cssRules - } else if (sheet.rules) { - theRules = sheet.rules + try { + if (sheet.cssRules) { + theRules = sheet.cssRules + } else if (sheet.rules) { + theRules = sheet.rules + } + } catch (e) { + // FF spews if trying to access rules for cross domain styles + @ResponsiveConnector::warning(*)("Can't process styles from " + sheet.href + + ", probably because of cross domain issues: " + e); + return; } // Special import handling for IE8 diff --git a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java index 24a0438476..1f09c14fb0 100644 --- a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java @@ -103,11 +103,15 @@ public abstract class AbstractComponentConnector extends AbstractConnector @Override public Widget getWidget() { if (widget == null) { - Profiler.enter("AbstractComponentConnector.createWidget for " - + getClass().getSimpleName()); + if (Profiler.isEnabled()) { + Profiler.enter("AbstractComponentConnector.createWidget for " + + getClass().getSimpleName()); + } widget = createWidget(); - Profiler.leave("AbstractComponentConnector.createWidget for " - + getClass().getSimpleName()); + if (Profiler.isEnabled()) { + Profiler.leave("AbstractComponentConnector.createWidget for " + + getClass().getSimpleName()); + } } return widget; diff --git a/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java b/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java index 5565daf19b..6c11783d34 100644 --- a/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java +++ b/client/src/com/vaadin/client/ui/VAbstractSplitPanel.java @@ -49,7 +49,7 @@ import com.vaadin.client.ui.TouchScrollDelegate.TouchScrollHandler; import com.vaadin.client.ui.VAbstractSplitPanel.SplitterMoveHandler.SplitterMoveEvent; import com.vaadin.shared.ui.Orientation; -public class VAbstractSplitPanel extends ComplexPanel { +public abstract class VAbstractSplitPanel extends ComplexPanel { private boolean enabled = false; @@ -571,6 +571,7 @@ public class VAbstractSplitPanel extends ComplexPanel { } break; case Event.ONCLICK: + stopResize(); resizing = false; break; } @@ -590,6 +591,7 @@ public class VAbstractSplitPanel extends ComplexPanel { } final Element trg = event.getEventTarget().cast(); if (trg == splitter || trg == DOM.getChild(splitter, 0)) { + startResize(); resizing = true; DOM.setCapture(getElement()); origX = DOM.getElementPropertyInt(splitter, "offsetLeft"); @@ -601,6 +603,40 @@ public class VAbstractSplitPanel extends ComplexPanel { } } + /** + * Called when starting drag resize + * + * @since 7.5.1 + */ + abstract protected void startResize(); + + /** + * Called when stopping drag resize + * + * @since 7.5.1 + */ + abstract protected void stopResize(); + + /** + * Gets the first container + * + * @since 7.5.1 + * @return the firstContainer + */ + protected Element getFirstContainer() { + return firstContainer; + } + + /** + * Gets the second container + * + * @since 7.5.1 + * @return the secondContainer + */ + protected Element getSecondContainer() { + return secondContainer; + } + public void onMouseMove(Event event) { switch (orientation) { case HORIZONTAL: @@ -685,6 +721,7 @@ public class VAbstractSplitPanel extends ComplexPanel { public void onMouseUp(Event event) { DOM.releaseCapture(getElement()); hideDraggingCurtain(); + stopResize(); resizing = false; if (!WidgetUtil.isTouchEvent(event)) { onMouseMove(event); diff --git a/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java b/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java index defa27fbac..f3905f9e46 100644 --- a/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java +++ b/client/src/com/vaadin/client/ui/VDragAndDropWrapper.java @@ -472,6 +472,9 @@ public class VDragAndDropWrapper extends VCustomComponent implements /*-{ this.setRequestHeader('Content-Type', 'multipart/form-data'); + // Seems like IE10 will loose the file if we don't keep a reference to it... + this.fileBeingUploaded = file; + this.send(file); }-*/; diff --git a/client/src/com/vaadin/client/ui/VGridLayout.java b/client/src/com/vaadin/client/ui/VGridLayout.java index 0c1d4cec90..42bcb5060a 100644 --- a/client/src/com/vaadin/client/ui/VGridLayout.java +++ b/client/src/com/vaadin/client/ui/VGridLayout.java @@ -734,7 +734,8 @@ public class VGridLayout extends ComplexPanel { setAlignment(new AlignmentInfo(childComponentData.alignment)); } - public void setComponent(ComponentConnector component) { + public void setComponent(ComponentConnector component, + List<ComponentConnector> ordering) { if (slot == null || slot.getChild() != component) { slot = new ComponentConnectorLayoutSlot(CLASSNAME, component, getConnector()); @@ -743,7 +744,20 @@ public class VGridLayout extends ComplexPanel { slot.getWrapperElement().getStyle().setWidth(100, Unit.PCT); } Element slotWrapper = slot.getWrapperElement(); - getElement().appendChild(slotWrapper); + int childIndex = ordering.indexOf(component); + // insert new slot by proper index + // do not break default focus order + com.google.gwt.user.client.Element element = getElement(); + if (childIndex == ordering.size()) { + element.appendChild(slotWrapper); + } else if (childIndex == 0) { + element.insertAfter(slotWrapper, spacingMeasureElement); + } else { + // here we use childIndex - 1 + 1(spacingMeasureElement) + Element previousSlot = (Element) element + .getChild(childIndex); + element.insertAfter(slotWrapper, previousSlot); + } Widget widget = component.getWidget(); insert(widget, slotWrapper, getWidgetCount(), false); diff --git a/client/src/com/vaadin/client/ui/VSplitPanelHorizontal.java b/client/src/com/vaadin/client/ui/VSplitPanelHorizontal.java index c6919d456b..1a3e699b20 100644 --- a/client/src/com/vaadin/client/ui/VSplitPanelHorizontal.java +++ b/client/src/com/vaadin/client/ui/VSplitPanelHorizontal.java @@ -16,6 +16,8 @@ package com.vaadin.client.ui; +import com.google.gwt.dom.client.Style.Overflow; +import com.google.gwt.user.client.ui.Widget; import com.vaadin.shared.ui.Orientation; public class VSplitPanelHorizontal extends VAbstractSplitPanel { @@ -23,4 +25,25 @@ public class VSplitPanelHorizontal extends VAbstractSplitPanel { public VSplitPanelHorizontal() { super(Orientation.HORIZONTAL); } + + @Override + protected void startResize() { + if (getFirstWidget() != null && isWidgetFullWidth(getFirstWidget())) { + getFirstContainer().getStyle().setOverflow(Overflow.HIDDEN); + } + + if (getSecondWidget() != null && isWidgetFullWidth(getSecondWidget())) { + getSecondContainer().getStyle().setOverflow(Overflow.HIDDEN); + } + } + + @Override + protected void stopResize() { + getFirstContainer().getStyle().clearOverflow(); + getSecondContainer().getStyle().clearOverflow(); + } + + private boolean isWidgetFullWidth(Widget w) { + return w.getElement().getStyle().getWidth().equals("100%"); + } } diff --git a/client/src/com/vaadin/client/ui/VSplitPanelVertical.java b/client/src/com/vaadin/client/ui/VSplitPanelVertical.java index b008e5d3f0..7baed03ca3 100644 --- a/client/src/com/vaadin/client/ui/VSplitPanelVertical.java +++ b/client/src/com/vaadin/client/ui/VSplitPanelVertical.java @@ -16,6 +16,8 @@ package com.vaadin.client.ui; +import com.google.gwt.dom.client.Style.Overflow; +import com.google.gwt.user.client.ui.Widget; import com.vaadin.shared.ui.Orientation; public class VSplitPanelVertical extends VAbstractSplitPanel { @@ -23,4 +25,25 @@ public class VSplitPanelVertical extends VAbstractSplitPanel { public VSplitPanelVertical() { super(Orientation.VERTICAL); } + + @Override + protected void startResize() { + if (getFirstWidget() != null && isWidgetFullHeight(getFirstWidget())) { + getFirstContainer().getStyle().setOverflow(Overflow.HIDDEN); + } + + if (getSecondWidget() != null && isWidgetFullHeight(getSecondWidget())) { + getSecondContainer().getStyle().setOverflow(Overflow.HIDDEN); + } + } + + @Override + protected void stopResize() { + getFirstContainer().getStyle().clearOverflow(); + getSecondContainer().getStyle().clearOverflow(); + } + + private boolean isWidgetFullHeight(Widget w) { + return w.getElement().getStyle().getHeight().equals("100%"); + } } diff --git a/client/src/com/vaadin/client/ui/gridlayout/GridLayoutConnector.java b/client/src/com/vaadin/client/ui/gridlayout/GridLayoutConnector.java index 3102af8da9..4d1ce692ad 100644 --- a/client/src/com/vaadin/client/ui/gridlayout/GridLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/gridlayout/GridLayoutConnector.java @@ -159,7 +159,7 @@ public class GridLayoutConnector extends AbstractComponentContainerConnector for (ComponentConnector componentConnector : getChildComponents()) { Cell cell = getCell(componentConnector); - cell.setComponent(componentConnector); + cell.setComponent(componentConnector, getChildComponents()); } } diff --git a/client/src/com/vaadin/client/widgets/Grid.java b/client/src/com/vaadin/client/widgets/Grid.java index a0b0b08aaa..86d2ed5f00 100644 --- a/client/src/com/vaadin/client/widgets/Grid.java +++ b/client/src/com/vaadin/client/widgets/Grid.java @@ -867,6 +867,7 @@ public class Grid<T> extends ResizeComposite implements if (row != null) { row.setDefault(true); } + defaultRow = row; requestSectionRefresh(); } @@ -917,6 +918,16 @@ public class Grid<T> extends ResizeComposite implements BrowserEvents.TOUCHMOVE, BrowserEvents.TOUCHEND, BrowserEvents.TOUCHCANCEL, BrowserEvents.CLICK); } + + @Override + protected void addColumn(Column<?, ?> column) { + super.addColumn(column); + + // Add default content for new columns. + if (defaultRow != null) { + column.setDefaultHeaderContent(defaultRow.getCell(column)); + } + } } /** @@ -929,6 +940,11 @@ public class Grid<T> extends ResizeComposite implements protected void setDefault(boolean isDefault) { this.isDefault = isDefault; + if (isDefault) { + for (Column<?, ?> column : getSection().grid.getColumns()) { + column.setDefaultHeaderContent(getCell(column)); + } + } } public boolean isDefault() { @@ -2437,8 +2453,6 @@ public class Grid<T> extends ResizeComposite implements } void initDone() { - addSelectAllToDefaultHeader(); - setWidth(-1); setEditable(false); @@ -2446,62 +2460,52 @@ public class Grid<T> extends ResizeComposite implements initDone = true; } - protected void addSelectAllToDefaultHeader() { - if (getSelectionModel() instanceof SelectionModel.Multi - && header.getDefaultRow() != null) { - // If selection cell already contains a widget do not - // create a new CheckBox - HeaderCell selectionCell = header.getDefaultRow().getCell(this); - if (selectionCell.getType().equals(GridStaticCellType.WIDGET) - && selectionCell.getWidget() instanceof CheckBox) { - return; - } - /* - * TODO: Currently the select all check box is shown when multi - * selection is in use. This might result in malfunctions if no - * SelectAllHandlers are present. - * - * Later on this could be fixed so that it check such handlers - * exist. - */ - final SelectionModel.Multi<T> model = (Multi<T>) getSelectionModel(); - final CheckBox checkBox = GWT.create(CheckBox.class); - checkBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() { + @Override + protected void setDefaultHeaderContent(HeaderCell selectionCell) { + /* + * TODO: Currently the select all check box is shown when multi + * selection is in use. This might result in malfunctions if no + * SelectAllHandlers are present. + * + * Later on this could be fixed so that it check such handlers + * exist. + */ + final SelectionModel.Multi<T> model = (Multi<T>) getSelectionModel(); + final CheckBox checkBox = GWT.create(CheckBox.class); + checkBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() { - @Override - public void onValueChange(ValueChangeEvent<Boolean> event) { - if (event.getValue()) { - fireEvent(new SelectAllEvent<T>(model)); - selected = true; - } else { - model.deselectAll(); - selected = false; - } + @Override + public void onValueChange(ValueChangeEvent<Boolean> event) { + if (event.getValue()) { + fireEvent(new SelectAllEvent<T>(model)); + selected = true; + } else { + model.deselectAll(); + selected = false; } - }); - checkBox.setValue(selected); - selectionCell.setWidget(checkBox); - // Select all with space when "select all" cell is active - addHeaderKeyUpHandler(new HeaderKeyUpHandler() { - @Override - public void onKeyUp(GridKeyUpEvent event) { - if (event.getNativeKeyCode() != KeyCodes.KEY_SPACE) { - return; - } - HeaderRow targetHeaderRow = getHeader().getRow( - event.getFocusedCell().getRowIndex()); - if (!targetHeaderRow.isDefault()) { - return; - } - if (event.getFocusedCell().getColumn() == SelectionColumn.this) { - // Send events to ensure row selection state is - // updated - checkBox.setValue(!checkBox.getValue(), true); - } + } + }); + checkBox.setValue(selected); + selectionCell.setWidget(checkBox); + // Select all with space when "select all" cell is active + addHeaderKeyUpHandler(new HeaderKeyUpHandler() { + @Override + public void onKeyUp(GridKeyUpEvent event) { + if (event.getNativeKeyCode() != KeyCodes.KEY_SPACE) { + return; } - }); - - } + HeaderRow targetHeaderRow = getHeader().getRow( + event.getFocusedCell().getRowIndex()); + if (!targetHeaderRow.isDefault()) { + return; + } + if (event.getFocusedCell().getColumn() == SelectionColumn.this) { + // Send events to ensure row selection state is + // updated + checkBox.setValue(!checkBox.getValue(), true); + } + } + }); } @Override @@ -4300,7 +4304,6 @@ public class Grid<T> extends ResizeComposite implements this.grid = grid; if (this.grid != null) { this.grid.recalculateColumnWidths(); - updateHeader(); } } @@ -4838,6 +4841,17 @@ public class Grid<T> extends ResizeComposite implements */ } } + + /** + * Resets the default header cell contents to column header captions. + * + * @since 7.5.1 + * @param cell + * default header cell for this column + */ + protected void setDefaultHeaderContent(HeaderCell cell) { + cell.setText(headerCaption); + } } protected class BodyUpdater implements EscalatorUpdater { @@ -5091,35 +5105,29 @@ public class Grid<T> extends ResizeComposite implements final StaticSection.StaticCell metadata = staticRow .getCell(columns.get(cell.getColumn())); - boolean updateCellData = true; // Decorate default row with sorting indicators if (staticRow instanceof HeaderRow) { addSortingIndicatorsToHeaderRow((HeaderRow) staticRow, cell); - - if (isHeaderSelectionColumn(row, cell)) { - updateCellData = false; - } } // Assign colspan to cell before rendering cell.setColSpan(metadata.getColspan()); TableCellElement element = cell.getElement(); - if (updateCellData) { - switch (metadata.getType()) { - case TEXT: - element.setInnerText(metadata.getText()); - break; - case HTML: - element.setInnerHTML(metadata.getHtml()); - break; - case WIDGET: - preDetach(row, Arrays.asList(cell)); - element.setInnerHTML(""); - postAttach(row, Arrays.asList(cell)); - break; - } + switch (metadata.getType()) { + case TEXT: + element.setInnerText(metadata.getText()); + break; + case HTML: + element.setInnerHTML(metadata.getHtml()); + break; + case WIDGET: + preDetach(row, Arrays.asList(cell)); + element.setInnerHTML(""); + postAttach(row, Arrays.asList(cell)); + break; } + setCustomStyleName(element, metadata.getStyleName()); cellFocusHandler.updateFocusedCellStyle(cell, container); @@ -5178,27 +5186,6 @@ public class Grid<T> extends ResizeComposite implements @Override public void preAttach(Row row, Iterable<FlyweightCell> cellsToAttach) { - // Add select all checkbox if needed on rebuild. - for (FlyweightCell cell : cellsToAttach) { - if (isHeaderSelectionColumn(row, cell)) { - selectionColumn.addSelectAllToDefaultHeader(); - } - } - } - - /** - * Check if selectionColumn in the default header row - */ - private boolean isHeaderSelectionColumn(Row row, FlyweightCell cell) { - return selectionColumn != null && isDefaultHeaderRow(row) - && getColumn(cell.getColumn()).equals(selectionColumn); - } - - /** - * Row is the default header row. - */ - private boolean isDefaultHeaderRow(Row row) { - return section.getRow(row.getRow()).equals(header.getDefaultRow()); } @Override @@ -5827,6 +5814,9 @@ public class Grid<T> extends ResizeComposite implements /** * Sets the default row of the header. The default row is a special header * row providing a user interface for sorting columns. + * <p> + * Note: Setting the default header row will reset all cell contents to + * Column defaults. * * @param row * the new default row, or null for no default row @@ -6081,6 +6071,12 @@ public class Grid<T> extends ResizeComposite implements RowContainer body = escalator.getBody(); int oldSize = body.getRowCount(); + // Hide all details. + Set<Integer> oldDetails = new HashSet<Integer>(visibleDetails); + for (int i : oldDetails) { + setDetailsVisible(i, false); + } + if (newSize > oldSize) { body.insertRows(oldSize, newSize - oldSize); cellFocusHandler.rowsAddedToBody(Range.withLength(oldSize, @@ -6946,6 +6942,9 @@ public class Grid<T> extends ResizeComposite implements selectionModel.setGrid(this); setSelectColumnRenderer(this.selectionModel .getSelectionColumnRenderer()); + + // Refresh rendered rows to update selection, if it has changed + refreshBody(); } /** @@ -7782,6 +7781,16 @@ public class Grid<T> extends ResizeComposite implements } @Override + protected void onDetach() { + Set<Integer> details = new HashSet<Integer>(visibleDetails); + for (int row : details) { + setDetailsVisible(row, false); + } + + super.onDetach(); + } + + @Override public void onResize() { super.onResize(); /* diff --git a/scripts/BuildArchetypes.py b/scripts/BuildArchetypes.py index 80dd745bfb..fa6dba2c56 100644 --- a/scripts/BuildArchetypes.py +++ b/scripts/BuildArchetypes.py @@ -7,11 +7,11 @@ # Python3 is required as this script uses some Python3 specific features. # Might work with Python2, haven't tested. # -# python BuildArchetypes.py version fw-repo-id archetype-repo-id plugin-repo-id +# python BuildArchetypes.py version --repo staging-repo-url # -import subprocess -from BuildHelpers import mavenValidate, copyWarFiles, repo, getLogFile, parseArgs, mavenCmd, updateRepositories +import subprocess, sys +from os.path import join ## DEFAULT VARIABLES ## @@ -22,7 +22,7 @@ archetypeGroup = "com.vaadin" archetypes = [ "vaadin-archetype-widget", "vaadin-archetype-application", - "vaadin-archetype-application-example", + "vaadin-archetype-application-example", "vaadin-archetype-application-multimodule" ] @@ -35,35 +35,62 @@ args = None ## BUILDING METHODS ## # Generates and modifies a maven pom file -def generateArchetype(archetype): - artifactId = "test-%s-%s" % (archetype, args.version.replace(".", "-")) - +def generateArchetype(archetype, artifactId, repo): # Generate the required command line for archetype generation cmd = [mavenCmd, "archetype:generate"] cmd.append("-DarchetypeGroupId=%s" % (archetypeGroup)) cmd.append("-DarchetypeArtifactId=%s" % (archetype)) cmd.append("-DarchetypeVersion=%s" % (args.version)) - if hasattr(args, "archetype") and args.archetype != None: - cmd.append("-DarchetypeRepository=%s" % (repo % (args.archetype))) + if hasattr(args, "repo") and args.repo != None: + cmd.append("-DarchetypeRepository=%s" % repo) cmd.append("-DgroupId=%s" % (group)) cmd.append("-DartifactId=%s" % (artifactId)) cmd.append("-Dversion=1.0-SNAPSHOT") cmd.append("-DinteractiveMode=false") + if hasattr(args, "maven") and args.maven is not None: + cmd.extend(args.maven.strip('"').split(" ")) # Generate pom.xml print("Generating pom.xml for archetype %s" % (archetype)) - subprocess.check_call(cmd, stdout=log) - - # Return the artifactId so we know the name in the future - return artifactId + subprocess.check_call(cmd, cwd=resultPath, stdout=log) +def getDeploymentContext(archetype, version): + return "%s-%s" % (archetype.split("-", 2)[2], version) + ## DO THIS IF RUN AS A SCRIPT (not import) ## if __name__ == "__main__": - args = parseArgs() - for archetype in archetypes: - log = getLogFile(archetype) - artifactId = generateArchetype(archetype) - updateRepositories(artifactId) - mavenValidate(artifactId, logFile=log) - copyWarFiles(artifactId, name=archetype) + from BuildHelpers import mavenValidate, copyWarFiles, getLogFile, mavenCmd, updateRepositories, getArgs, removeDir, parser, resultPath + from DeployHelpers import deployWar + + # Add command line arguments for staging repos + parser.add_argument("--repo", type=str, help="Staging repository URL", required=True) + archetypesFailed = False + + # Parse the arguments + args = getArgs() + + if hasattr(args, "artifactPath") and args.artifactPath is not None: + raise Exception("Archetype validation build does not support artifactPath") + + for archetype in archetypes: + artifactId = "test-%s-%s" % (archetype, args.version.replace(".", "-")) + try: + log = getLogFile(archetype) + generateArchetype(archetype, artifactId, args.repo) + updateRepositories(join(resultPath, artifactId), args.repo) + mavenValidate(artifactId, logFile=log) + warFiles = copyWarFiles(artifactId, name=archetype) + for war in warFiles: + try: + deployWar(war, "%s.war" % (getDeploymentContext(archetype, args.version))) + except Exception as e: + print("War %s failed to deploy: %s" % (war, e)) + archetypesFailed = True + except Exception as e: + print("Archetype %s build failed:" % (archetype), e) + archetypesFailed = True +# removeDir(artifactId) + print("") + if archetypesFailed: + sys.exit(1) diff --git a/scripts/BuildDemos.py b/scripts/BuildDemos.py index f9f2ed1b48..ac161fc517 100644 --- a/scripts/BuildDemos.py +++ b/scripts/BuildDemos.py @@ -4,38 +4,80 @@ # BuildDemos needs git in PATH and depends on gitpython library # gitpython can be installed with python installer script "pip": # pip install gitpython +# +# Deployment dependency: requests +# pip install requests +# Deploy depends on .deployUrl and .deployCredentials files in home folder -from git import Repo -from BuildHelpers import updateRepositories, mavenValidate, copyWarFiles, VersionObject, getLogFile, parseArgs - -## Example of a non-staging test. -#version = VersionObject() -#version.version = "7.4.8" - -# Uncomment lines before this, and comment following line to make a non-staging test -version = None +import sys, os +from os.path import join, isfile +from fnmatch import fnmatch +from xml.etree.ElementTree import ElementTree +# Validated demos. name -> git url demos = { "dashboard" : "https://github.com/vaadin/dashboard-demo.git", "parking" : "https://github.com/vaadin/parking-demo.git", "addressbook" : "https://github.com/vaadin/addressbook.git", - "confirmdialog" : "https://github.com/samie/Vaadin-ConfirmDialog.git" + "grid-gwt" : "https://github.com/vaadin/grid-gwt.git" } def checkout(folder, url): - Repo.clone_from(url, folder) + Repo.clone_from(url, join(resultPath, folder)) if __name__ == "__main__": - if version is None: - version = parseArgs() + # Do imports. + try: + from git import Repo + except: + print("BuildDemos depends on gitpython. Install it with `pip install gitpython`") + sys.exit(1) + from BuildHelpers import updateRepositories, mavenValidate, copyWarFiles, getLogFile, removeDir, getArgs, mavenInstall, resultPath, readPomFile, parser + from DeployHelpers import deployWar + + # Add command line arguments for staging repos + parser.add_argument("--repo", type=str, help="Staging repository URL", default=None) + + args = getArgs() + if hasattr(args, "artifactPath") and args.artifactPath is not None: + version = False + basePath = args.artifactPath + poms = [] + for root, dirs, files in os.walk(basePath): + for name in files: + if fnmatch(name, "*.pom"): + poms.append(join(root, name)) + for pom in poms: + jarFile = pom.replace(".pom", ".jar") + if isfile(jarFile): + mavenInstall(pom, jarFile) + else: + mavenInstall(pom) + if "vaadin-server" in pom: + pomXml, nameSpace = readPomFile(pom) + for version in pomXml.getroot().findall("./{%s}version" % (nameSpace)): + args.version = version.text + demosFailed = False + for demo in demos: print("Validating demo %s" % (demo)) try: checkout(demo, demos[demo]) - updateRepositories(demo, repoIds = version) - mavenValidate(demo, repoIds = version, logFile = getLogFile(demo)) - copyWarFiles(demo) + if hasattr(args, "repo") and args.repo is not None: + updateRepositories(join(resultPath, demo), args.repo) + mavenValidate(demo, logFile=getLogFile(demo)) + resultWars = copyWarFiles(demo) + for war in resultWars: + try: + deployWar(war) + except Exception as e: + print("War %s failed to deploy: %s" % (war, e)) + demosFailed = True print("%s demo validation succeeded!" % (demo)) - except: - print("%s demo validation failed" % (demo)) + except Exception as e: + print("%s demo validation failed: %s" % (demo, e)) + demosFailed = True + removeDir(demo) print("") + if demosFailed: + sys.exit(1) diff --git a/scripts/BuildHelpers.py b/scripts/BuildHelpers.py index be21c0f721..7545467f98 100644 --- a/scripts/BuildHelpers.py +++ b/scripts/BuildHelpers.py @@ -5,43 +5,37 @@ import sys, argparse, subprocess, platform from xml.etree import ElementTree from os.path import join, isdir, isfile, basename, exists -from os import listdir, getcwd, mkdir -from shutil import copy +from os import listdir, makedirs +from shutil import copy, rmtree from glob import glob -class VersionObject(object): - pass - -# Staging repo base url -repo = "http://oss.sonatype.org/content/repositories/comvaadin-%d" - # Directory where the resulting war files are stored # TODO: deploy results -resultPath = "result" +resultPath = join("result", "demos") if not exists(resultPath): - mkdir(resultPath) + makedirs(resultPath) elif not isdir(resultPath): print("Result path is not a directory.") sys.exit(1) args = None -# Parse command line arguments <version> <framework-repo-id> <archetype-repo-id> <plugin-repo-id> +# Default argument parser +parser = argparse.ArgumentParser(description="Automated staging validation") +group = parser.add_mutually_exclusive_group(required=True) +group.add_argument("--version", help="Vaadin version to use") +group.add_argument("--artifactPath", help="Path to local folder with Vaadin artifacts") + +parser.add_argument("--maven", help="Additional maven command line parameters", default=None) + +# Parse command line arguments <version> def parseArgs(): - # Command line arguments for this script - parser = argparse.ArgumentParser(description="Automated staging validation") - parser.add_argument("version", type=str, help="Vaadin version to use") - parser.add_argument("framework", type=int, help="Framework repo id (comvaadin-XXXX)", nargs='?') - parser.add_argument("archetype", type=int, help="Archetype repo id (comvaadin-XXXX)", nargs='?') - parser.add_argument("plugin", type=int, help="Maven Plugin repo id (comvaadin-XXXX)", nargs='?') - # If no args, give help if len(sys.argv) == 1: args = parser.parse_args(["-h"]) else: args = parser.parse_args() - return args # Function for determining the path for maven executable @@ -73,48 +67,58 @@ def getArgs(): return args # Maven Package and Validation -def mavenValidate(artifactId, mvnCmd = mavenCmd, logFile = sys.stdout, repoIds = None): - if repoIds is None: - repoIds = getArgs() +def mavenValidate(artifactId, mvnCmd = mavenCmd, logFile = sys.stdout, version = None, mavenParams = None): + if version is None: + version = getArgs().version + if mavenParams is None: + mavenParams = getArgs().maven print("Do maven clean package validate") cmd = [mvnCmd] - if hasattr(repoIds, "version") and repoIds.version is not None: - cmd.append("-Dvaadin.version=%s" % (repoIds.version)) + cmd.append("-Dvaadin.version=%s" % (version)) + if mavenParams is not None: + cmd.extend(mavenParams.strip('"').split(" ")) cmd.extend(["clean", "package", "validate"]) print("executing: %s" % (" ".join(cmd))) - subprocess.check_call(cmd, cwd=join(getcwd(), artifactId), stdout=logFile) + subprocess.check_call(cmd, cwd=join(resultPath, artifactId), stdout=logFile) # Collect .war files to given folder with given naming def copyWarFiles(artifactId, resultDir = resultPath, name = None): if name is None: name = artifactId - warFiles = glob(join(getcwd(), artifactId, "target", "*.war")) - warFiles.extend(glob(join(getcwd(), artifactId, "*", "target", "*.war"))) + copiedWars = [] + warFiles = glob(join(resultDir, artifactId, "target", "*.war")) + warFiles.extend(glob(join(resultDir, artifactId, "*", "target", "*.war"))) for warFile in warFiles: if len(warFiles) == 1: deployName = "%s.war" % (name) else: deployName = "%s-%d.war" % (name, warFiles.index(warFile)) print("Copying .war file %s as %s to result folder" % (basename(warFile), deployName)) - copy(warFile, join(resultDir, "%s" % (deployName))) + copy(warFile, join(resultDir, deployName)) + copiedWars.append(join(resultDir, deployName)) + return copiedWars + +def readPomFile(pomFile): + # pom.xml namespace workaround + root = ElementTree.parse(pomFile).getroot() + nameSpace = root.tag[1:root.tag.index('}')] + ElementTree.register_namespace('', nameSpace) + + # Read the pom.xml correctly + return ElementTree.parse(pomFile), nameSpace # Recursive pom.xml update script -def updateRepositories(path, repoIds = None, repoUrl = repo): +def updateRepositories(path, repoUrl = None, version = None): # If versions are not supplied, parse arguments - if repoIds is None: - repoIds = getArgs() - + if version is None: + version = getArgs().version + # Read pom.xml pomXml = join(path, "pom.xml") if isfile(pomXml): - # pom.xml namespace workaround - root = ElementTree.parse(pomXml).getroot() - nameSpace = root.tag[1:root.tag.index('}')] - ElementTree.register_namespace('', nameSpace) - # Read the pom.xml correctly - tree = ElementTree.parse(pomXml) + tree, nameSpace = readPomFile(pomXml) # NameSpace needed for finding the repositories node repoNode = tree.getroot().find("{%s}repositories" % (nameSpace)) @@ -124,9 +128,8 @@ def updateRepositories(path, repoIds = None, repoUrl = repo): if repoNode is not None: print("Add staging repositories to " + pomXml) - if hasattr(repoIds, "framework") and repoIds.framework is not None: - # Add framework staging repository - addRepo(repoNode, "repository", "vaadin-%s-staging" % (repoIds.version), repoUrl % (repoIds.framework)) + # Add framework staging repository + addRepo(repoNode, "repository", "vaadin-%s-staging" % (version), repoUrl) # Find the correct pluginRepositories node pluginRepo = tree.getroot().find("{%s}pluginRepositories" % (nameSpace)) @@ -134,9 +137,8 @@ def updateRepositories(path, repoIds = None, repoUrl = repo): # Add pluginRepositories node if needed pluginRepo = ElementTree.SubElement(tree.getroot(), "pluginRepositories") - if hasattr(repoIds, "plugin") and repoIds.plugin is not None: - # Add plugin staging repository - addRepo(pluginRepo, "pluginRepository", "vaadin-%s-plugin-staging" % (repoIds.version), repoUrl % (repoIds.plugin)) + # Add plugin staging repository + addRepo(pluginRepo, "pluginRepository", "vaadin-%s-plugin-staging" % (version), repoUrl) # Overwrite the modified pom.xml tree.write(pomXml, encoding='UTF-8') @@ -145,7 +147,7 @@ def updateRepositories(path, repoIds = None, repoUrl = repo): for i in listdir(path): file = join(path, i) if isdir(file): - updateRepositories(join(path, i), repoIds, repoUrl) + updateRepositories(join(path, i), repoUrl, version) # Add a repository of repoType to given repoNode with id and URL def addRepo(repoNode, repoType, id, url): @@ -158,3 +160,16 @@ def addRepo(repoNode, repoType, id, url): # Get a logfile for given artifact def getLogFile(artifact, resultDir = resultPath): return open(join(resultDir, "%s.log" % (artifact)), 'w') + +def removeDir(subdir): + if '..' in subdir or '/' in subdir: + # Dangerous relative paths. + return + rmtree(join(resultPath, subdir)) + +def mavenInstall(pomFile, jarFile = None, mvnCmd = mavenCmd, logFile = sys.stdout): + cmd = [mvnCmd, "install:install-file"] + cmd.append("-Dfile=%s" % (jarFile if jarFile is not None else pomFile)) + cmd.append("-DpomFile=%s" % (pomFile)) + print("executing: %s" % (" ".join(cmd))) + subprocess.check_call(cmd, stdout=logFile) diff --git a/scripts/DeployHelpers.py b/scripts/DeployHelpers.py new file mode 100644 index 0000000000..2c879088ff --- /dev/null +++ b/scripts/DeployHelpers.py @@ -0,0 +1,82 @@ +#coding=UTF-8 + +### Helper class for wildfly deployments. ### +# Related files $HOME/.deploy-url $HOME/.deploy-credentials + +import sys, json +try: + import requests +except Exception as e: + print("DeployHelpers depends on requests library. Install it with `pip install requests`") + sys.exit(1) +from requests.auth import HTTPDigestAuth +from os.path import join, expanduser, basename +from BuildHelpers import parser, getArgs + +parser.add_argument("--deployUrl", help="Wildfly management URL") +parser.add_argument("--deployUser", help="Deployment user", default=None) +parser.add_argument("--deployPass", help="Deployment password", default=None) + +# Helper for handling the full deployment +# name should end with .war +def deployWar(warFile, name=None): + if name is None: + name = basename(warFile).replace('.war', "-%s.war" % (getArgs().version.split('-')[0])) + + print("Deploying to context %s" % (name[:-4])) + # Undeploy/Remove old version if needed + if deploymentExists(name): + removeDeployment(name) + # Do upload war file + hash = doUploadWarFile(warFile) + # Do deployment under name + doDeploy(hash, name) + +def deploymentExists(name): + # Deployment existence check data + data = {"operation" : "read-attribute", "name": "runtime-name", "address": [{"deployment" : name}]} + result = doPostJson(url=getUrl(), auth=getAuth(), data=json.dumps(data)) + return result.json()["outcome"] == "success" + +def doDeploy(hash, name): + # Deployment data + data = {} + data["content"] = [{"hash" : hash}] + data["address"] = [{"deployment" : name}] + data["operation"] = "add" + data["enabled"] = True + return doPostJson(data=json.dumps(data), auth=getAuth(), url=getUrl()) + +# Helper for adding Content-Type to headers +def doPostJson(**kwargs): + r = requests.post(headers={"Content-Type" : "application/json"}, **kwargs) + # Wildfly gives code 500 when asking for a non-existent deployment + if r.status_code == requests.codes.ok or r.status_code == 500: + return r + r.raise_for_status() + +def doUploadWarFile(warFile): + # Upload request, just see the outcome + result = requests.post("%s/add-content" % (getUrl()), files={"file" : open(warFile, 'rb')}, auth=getAuth()).json() + if "outcome" not in result or result["outcome"] != "success": + raise Exception("File upload failed.", result) + return result["result"] + +# Method for removing an existing deployment +def removeDeployment(name): + data = {} + data["address"] = [{"deployment" : name}] + for i in ["undeploy", "remove"]: + print("%s old deployment of %s" % (i, name)) + data["operation"] = i + doPostJson(data=json.dumps(data), auth=getAuth(), url=getUrl()) + +# Read credentials file and return a HTTPDigestAuth object +def getAuth(): + args = getArgs() + return HTTPDigestAuth(args.deployUser, args.deployPass) + +# Read the deploy url file and return the url +def getUrl(): + return getArgs().deployUrl + diff --git a/scripts/GenerateBuildReport.py b/scripts/GenerateBuildReport.py new file mode 100644 index 0000000000..8ee2472133 --- /dev/null +++ b/scripts/GenerateBuildReport.py @@ -0,0 +1,48 @@ +#coding=UTF-8 + +from BuildDemos import demos +import argparse, subprocess + +parser = argparse.ArgumentParser(description="Build report generator") +parser.add_argument("version", type=str, help="Vaadin version that was just built") +parser.add_argument("deployUrl", type=str, help="Base url of the deployment server") +parser.add_argument("buildResultUrl", type=str, help="URL for the build result page") + +args = parser.parse_args() + +content = """<html> +<head></head> +<body> +<table> +<tr><td><a href="https://dev.vaadin.com/milestone?action=new">Create milestone for next release</a></td></tr> +<tr><td><a href="https://dev.vaadin.com/query?status=closed&component=Core+Framework&resolution=fixed&milestone=Vaadin {version}&col=id&col=summary&col=component&col=milestone&col=status&col=type">Closed tickets with milestone {version}</a></td></tr> +<tr><td><a href="https://dev.vaadin.com/query?status=pending-release&component=Core+Framework&resolution=fixed&milestone=Vaadin {version}&col=id&col=summary&col=component&col=milestone&col=status&col=type">Pending-release tickets with milestone {version}</a></td></tr> +<tr><td><a href="https://dev.vaadin.com/query?status=pending-release&milestone=">Pending-release tickets without milestone</a></td></tr> +<tr><td><a href="apidiff/changes.html">API Diff</a></td></tr> +<tr><td><a href="release-notes/release-notes.html">Release Notes</a></td></tr> +""".format(version=args.version) + +try: + p1 = subprocess.Popen(['find', '.', '-name', '*.java'], stdout=subprocess.PIPE) + p2 = subprocess.Popen(['xargs', 'egrep', '@since ?$'], stdin=p1.stdout, stdout=subprocess.PIPE) + missing = subprocess.check_output(['grep', '-v', 'tests'], stdin=p2.stdout) + content += "<tr><td>Empty @since:<br>\n<pre>%s</pre></td></tr>\n" % (missing) +except subprocess.CalledProcessError as e: + if e.returncode == 1: + content += "<tr><td>No empty @since</td></tr>\n" + else: + raise e + +content += "<tr><td>Try demos<ul>" + +for demo in demos: + content += "<li><a href='{url}/{demoName}-{version}'>{demoName}</a></li>\n".format(url=args.deployUrl, demoName=demo, version=args.version) + +content += """</ul></td></tr> +<tr><td><a href="{url}">Build result page (See test results, pin and tag build and dependencies)</a></td></tr> +</table> +</body> +</html>""".format(url=args.buildResultUrl) + +f = open("result/report.html", 'w') +f.write(content) diff --git a/scripts/GenerateStagingReport.py b/scripts/GenerateStagingReport.py new file mode 100644 index 0000000000..fdcdc93fdb --- /dev/null +++ b/scripts/GenerateStagingReport.py @@ -0,0 +1,40 @@ +#coding=UTF-8 + +from BuildArchetypes import archetypes, getDeploymentContext +import argparse, cgi + +parser = argparse.ArgumentParser(description="Build report generator") +parser.add_argument("version", type=str, help="Vaadin version that was just built") +parser.add_argument("deployUrl", type=str, help="Base url of the deployment server") +parser.add_argument("buildResultUrl", type=str, help="URL for the build result page") +parser.add_argument("stagingRepo", type=str, help="URL for the staging repository") + +args = parser.parse_args() + +content = """<html> +<head></head> +<body> +<table> +""" + +content += "<tr><td>Try archetype demos<ul>" + +for archetype in archetypes: + content += "<li><a href='{url}/{context}'>{demo}</a></li>\n".format(url=args.deployUrl, demo=archetype, context=getDeploymentContext(archetype, args.version)) + +content += """</ul></td></tr> +<tr><td><a href="{repoUrl}">Staging repository</a></td></tr> +<tr><td>Eclipse Ivy Settings:<br><pre>""".format(repoUrl=args.stagingRepo) +content += cgi.escape(""" <ibiblio name="vaadin-staging" usepoms="true" m2compatible="true" + root="{repoUrl}" />""".format(repoUrl=args.stagingRepo)) +content += """</pre> +</td></tr> +<tr><td><a href="https://dev.vaadin.com/milestone/Vaadin {version}">Trac Milestone</a></td></tr> +<tr><td><a href="https://dev.vaadin.com/admin/ticket/versions">Add version {version} to Trac</td></tr> +<tr><td><a href="{url}">Staging result page (See test results, pin and tag build and dependencies)</a></td></tr> +</table> +</body> +</html>""".format(url=args.buildResultUrl, repoUrl=args.stagingRepo, version=args.version) + +f = open("result/report.html", 'w') +f.write(content) diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java index 71b597ff1d..55947c98d1 100644 --- a/server/src/com/vaadin/data/RpcDataProviderExtension.java +++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java @@ -947,6 +947,14 @@ public class RpcDataProviderExtension extends AbstractExtension { listener.removeListener(); } + // Wipe clean all details. + HashSet<Object> detailItemIds = new HashSet<Object>( + detailComponentManager.visibleDetailsComponents + .keySet()); + for (Object itemId : detailItemIds) { + detailComponentManager.destroyDetails(itemId); + } + listeners.clear(); activeRowHandler.activeRange = Range.withLength(0, 0); @@ -1387,4 +1395,14 @@ public class RpcDataProviderExtension extends AbstractExtension { public DetailComponentManager getDetailComponentManager() { return detailComponentManager; } + + @Override + public void detach() { + for (Object itemId : ImmutableSet + .copyOf(detailComponentManager.visibleDetails)) { + detailComponentManager.destroyDetails(itemId); + } + + super.detach(); + } } diff --git a/server/src/com/vaadin/data/util/converter/StringToCollectionConverter.java b/server/src/com/vaadin/data/util/converter/StringToCollectionConverter.java index 495bed74f8..b86fec5558 100644 --- a/server/src/com/vaadin/data/util/converter/StringToCollectionConverter.java +++ b/server/src/com/vaadin/data/util/converter/StringToCollectionConverter.java @@ -155,9 +155,7 @@ public class StringToCollectionConverter implements previous = index + delimiter.length(); index = value.indexOf(delimiter, previous); } - if (result.size() > 0) { - collectToken(value.substring(previous), result, converter, locale); - } + collectToken(value.substring(previous), result, converter, locale); return result; } diff --git a/server/src/com/vaadin/server/AbstractClientConnector.java b/server/src/com/vaadin/server/AbstractClientConnector.java index 0655b482ed..b6bcebd167 100644 --- a/server/src/com/vaadin/server/AbstractClientConnector.java +++ b/server/src/com/vaadin/server/AbstractClientConnector.java @@ -30,6 +30,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import com.vaadin.event.EventRouter; @@ -91,6 +92,8 @@ public abstract class AbstractClientConnector implements ClientConnector, private ErrorHandler errorHandler = null; + private static final ConcurrentHashMap<Class<? extends AbstractClientConnector>, Class<? extends SharedState>> stateTypeCache = new ConcurrentHashMap<Class<? extends AbstractClientConnector>, Class<? extends SharedState>>(); + @Override public void addAttachListener(AttachListener listener) { addListener(AttachEvent.ATTACH_EVENT_IDENTIFIER, AttachEvent.class, @@ -296,7 +299,12 @@ public abstract class AbstractClientConnector implements ClientConnector, // Lazy load because finding type can be expensive because of the // exceptions flying around if (stateType == null) { - stateType = findStateType(); + // Cache because we don't need to do this once per instance + stateType = stateTypeCache.get(this.getClass()); + if (stateType == null) { + stateType = findStateType(); + stateTypeCache.put(this.getClass(), stateType); + } } return stateType; diff --git a/server/src/com/vaadin/server/CustomizedSystemMessages.java b/server/src/com/vaadin/server/CustomizedSystemMessages.java index f7049e3688..9aef4f5cf3 100644 --- a/server/src/com/vaadin/server/CustomizedSystemMessages.java +++ b/server/src/com/vaadin/server/CustomizedSystemMessages.java @@ -240,50 +240,6 @@ public class CustomizedSystemMessages extends SystemMessages implements } /** - * Sets the URL to go to when the client is out-of-sync. - * - * @param outOfSyncURL - * the URL to go to, or null to reload current - */ - public void setOutOfSyncURL(String outOfSyncURL) { - this.outOfSyncURL = outOfSyncURL; - } - - /** - * Enables or disables the notification. If disabled, the set URL (or - * current) is loaded directly. - * - * @param outOfSyncNotificationEnabled - * true = enabled, false = disabled - */ - public void setOutOfSyncNotificationEnabled( - boolean outOfSyncNotificationEnabled) { - this.outOfSyncNotificationEnabled = outOfSyncNotificationEnabled; - } - - /** - * Sets the caption of the notification. Set to null for no caption. If both - * caption and message is null, the notification is disabled; - * - * @param outOfSyncCaption - * the caption - */ - public void setOutOfSyncCaption(String outOfSyncCaption) { - this.outOfSyncCaption = outOfSyncCaption; - } - - /** - * Sets the message of the notification. Set to null for no message. If both - * caption and message is null, the notification is disabled; - * - * @param outOfSyncMessage - * the message - */ - public void setOutOfSyncMessage(String outOfSyncMessage) { - this.outOfSyncMessage = outOfSyncMessage; - } - - /** * Sets the URL to redirect to when the browser has cookies disabled. * * @param cookiesDisabledURL diff --git a/server/src/com/vaadin/server/SystemMessages.java b/server/src/com/vaadin/server/SystemMessages.java index 51cc7d497d..3bcf0a90fa 100644 --- a/server/src/com/vaadin/server/SystemMessages.java +++ b/server/src/com/vaadin/server/SystemMessages.java @@ -43,12 +43,6 @@ import java.io.Serializable; * <li><b>internalErrorCaption</b> = "Internal error"</li> * <li><b>internalErrorMessage</b> = "Please notify the administrator.<br/> * Take note of any unsaved data, and <u>click here</u> to continue."</li> - * <li><b>outOfSyncURL</b> = null</li> - * <li><b>outOfSyncNotificationEnabled</b> = true</li> - * <li><b>outOfSyncCaption</b> = "Out of sync"</li> - * <li><b>outOfSyncMessage</b> = "Something has caused us to be out of sync with - * the server.<br/> - * Take note of any unsaved data, and <u>click here</u> to re-sync."</li> * <li><b>cookiesDisabledURL</b> = null</li> * <li><b>cookiesDisabledNotificationEnabled</b> = true</li> * <li><b>cookiesDisabledCaption</b> = "Cookies disabled"</li> @@ -80,11 +74,6 @@ public class SystemMessages implements Serializable { protected String internalErrorCaption = "Internal error"; protected String internalErrorMessage = "Please notify the administrator.<br/>Take note of any unsaved data, and <u>click here</u> or press ESC to continue."; - protected String outOfSyncURL = null; - protected boolean outOfSyncNotificationEnabled = true; - protected String outOfSyncCaption = "Out of sync"; - protected String outOfSyncMessage = "Something has caused us to be out of sync with the server.<br/>Take note of any unsaved data, and <u>click here</u> or press ESC to re-sync."; - protected String cookiesDisabledURL = null; protected boolean cookiesDisabledNotificationEnabled = true; protected String cookiesDisabledCaption = "Cookies disabled"; diff --git a/server/src/com/vaadin/ui/ConnectorTracker.java b/server/src/com/vaadin/ui/ConnectorTracker.java index 95a80b7be0..eba248fb00 100644 --- a/server/src/com/vaadin/ui/ConnectorTracker.java +++ b/server/src/com/vaadin/ui/ConnectorTracker.java @@ -24,6 +24,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; +import java.util.NavigableMap; import java.util.Set; import java.util.TreeMap; import java.util.UUID; @@ -591,8 +592,8 @@ public class ConnectorTracker implements Serializable { * <p> * This method has a side-effect of incrementing the sync id by one (see * {@link #getCurrentSyncId()}), if {@link #isWritingResponse()} returns - * <code>false</code> and <code>writingResponse</code> is set to - * <code>true</code>. + * <code>true</code> and <code>writingResponse</code> is set to + * <code>false</code>. * * @param writingResponse * the new response status. @@ -616,7 +617,9 @@ public class ConnectorTracker implements Serializable { * the right hand side of the && is unnecessary here because of the * if-clause above, but rigorous coding is always rigorous coding. */ - if (writingResponse && !this.writingResponse) { + if (!writingResponse && this.writingResponse) { + // Bump sync id when done writing - the client is not expected to + // know about anything happening after this moment. currentSyncId++; } this.writingResponse = writingResponse; @@ -784,34 +787,25 @@ public class ConnectorTracker implements Serializable { */ public boolean connectorWasPresentAsRequestWasSent(String connectorId, long lastSyncIdSeenByClient) { - assert getConnector(connectorId) == null : "Connector " + connectorId + " is still attached"; - boolean clientRequestIsTooOld = lastSyncIdSeenByClient < currentSyncId - && lastSyncIdSeenByClient != -1; - if (clientRequestIsTooOld) { - /* - * The headMap call is present here because we're only interested in - * connectors removed "in the past" (i.e. the server has removed - * them before the client ever knew about that), since those are the - * ones that we choose to handle as a special case. - */ - /*- - * Server Client - * [#1 add table] ---------. - * \ - * [push: #2 remove table]-. `--> [adding table, storing #1] - * \ .- [table from request #1 needs more data] - * \/ - * /`-> [removing table, storing #2] - * [#1 < #2 - ignoring] <---´ - */ - for (Set<String> unregisteredConnectors : syncIdToUnregisteredConnectorIds - .headMap(currentSyncId).values()) { - if (unregisteredConnectors.contains(connectorId)) { - return true; - } + if (lastSyncIdSeenByClient == -1) { + // Ignore potential problems + return true; + } + + /* + * Use non-inclusive tail map to find all connectors that were removed + * after the reported sync id was sent to the client. + */ + NavigableMap<Integer, Set<String>> unregisteredAfter = syncIdToUnregisteredConnectorIds + .tailMap(Integer.valueOf((int) lastSyncIdSeenByClient), false); + for (Set<String> unregisteredIds : unregisteredAfter.values()) { + if (unregisteredIds.contains(connectorId)) { + // Removed with a higher sync id, so it was most likely present + // when this sync id was sent. + return true; } } @@ -877,7 +871,7 @@ public class ConnectorTracker implements Serializable { * conflicts. In any case, it's better to clean up too little than too * much, especially as the data will hardly grow into the kilobytes. */ - syncIdToUnregisteredConnectorIds.headMap(lastSyncIdSeenByClient) + syncIdToUnregisteredConnectorIds.headMap(lastSyncIdSeenByClient, true) .clear(); } } diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java index af89064411..251ec0f678 100644 --- a/server/src/com/vaadin/ui/Grid.java +++ b/server/src/com/vaadin/ui/Grid.java @@ -6084,10 +6084,15 @@ public class Grid extends AbstractFocusable implements SelectionNotifier, editorActive = false; editorFieldGroup.discard(); editorFieldGroup.setItemDataSource(null); + if (datasource instanceof ItemSetChangeNotifier) { ((ItemSetChangeNotifier) datasource) .removeItemSetChangeListener(editorClosingItemSetListener); } + + // Mark Grid as dirty so the client side gets to know that the editors + // are no longer attached + markAsDirty(); } void resetEditor() { diff --git a/server/tests/src/com/vaadin/tests/data/converter/StringToCollectionConverterTest.java b/server/tests/src/com/vaadin/tests/data/converter/StringToCollectionConverterTest.java index 977985c6cb..bcd0dc15bd 100644 --- a/server/tests/src/com/vaadin/tests/data/converter/StringToCollectionConverterTest.java +++ b/server/tests/src/com/vaadin/tests/data/converter/StringToCollectionConverterTest.java @@ -141,6 +141,16 @@ public class StringToCollectionConverterTest { Assert.assertEquals("Z,Y", presentation); } + @Test + public void convertToModel_singleItem() { + StringToCollectionConverter converter = new StringToCollectionConverter(); + Collection<?> model = converter.convertToModel("a", List.class, null); + Iterator<?> iterator = model.iterator(); + Assert.assertEquals("Incorrect fist token", "a", iterator.next()); + Assert.assertFalse("More than one item detected after conversation", + iterator.hasNext()); + } + public enum TestEnum { X, Y, Z; } diff --git a/shared/src/com/vaadin/shared/ui/ui/UIState.java b/shared/src/com/vaadin/shared/ui/ui/UIState.java index 2f51fef6ee..6f7a531eb6 100644 --- a/shared/src/com/vaadin/shared/ui/ui/UIState.java +++ b/shared/src/com/vaadin/shared/ui/ui/UIState.java @@ -43,12 +43,16 @@ public class UIState extends TabIndexState { notificationConfigurations.put("humanized", new NotificationTypeConfiguration("Info: ", null, NotificationRole.ALERT)); + + // We use alert instead of status for all notifications because + // (at least) Jaws 16 and earlier fail to announce any role=status + // message in Chrome/Firefox notificationConfigurations.put("tray", new NotificationTypeConfiguration("Status: ", null, - NotificationRole.STATUS)); + NotificationRole.ALERT)); notificationConfigurations.put("assistive", new NotificationTypeConfiguration("Note: ", null, - NotificationRole.STATUS)); + NotificationRole.ALERT)); } /** * State related to the Page class. diff --git a/uitest/src/com/vaadin/tests/application/ResynchronizeAfterAsyncRemoval.java b/uitest/src/com/vaadin/tests/application/ResynchronizeAfterAsyncRemoval.java new file mode 100644 index 0000000000..d8f7fface3 --- /dev/null +++ b/uitest/src/com/vaadin/tests/application/ResynchronizeAfterAsyncRemoval.java @@ -0,0 +1,77 @@ +package com.vaadin.tests.application; + +import java.lang.reflect.Field; +import java.util.Map; +import java.util.Set; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.ConnectorTracker; +import com.vaadin.ui.Window; + +public class ResynchronizeAfterAsyncRemoval extends AbstractTestUIWithLog { + + @Override + public void setup(VaadinRequest vaadinRequest) { + final Window window = new Window("Asynchronously removed window"); + window.center(); + + // The window will enqueue a non-immediate message reporting its current + // position. + addWindow(window); + + // Remove window immediately when the current response is sent + runAfterResponse(new Runnable() { + @Override + public void run() { + removeWindow(window); + } + }); + + // Clicking the button will trigger sending the window coordinates, but + // the window is already removed at that point. + addComponent(new Button("Am I dirty?", new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + log("Window removed: " + (window.getParent() == null)); + + boolean dirty = getUI().getConnectorTracker().isDirty( + event.getButton()); + log("Dirty: " + dirty); + } + })); + addComponent(new Button("Log unregistered connector count", + new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + logUnregisteredConnectorCount(); + } + })); + } + + private void logUnregisteredConnectorCount() { + int count = 0; + + Map<Integer, Set<String>> unregisterIdMap = getUnregisterIdMap(); + for (Set<String> set : unregisterIdMap.values()) { + count += set.size(); + } + log("syncId: " + getConnectorTracker().getCurrentSyncId()); + log("Unregistered connector count: " + count); + } + + @SuppressWarnings("unchecked") + private Map<Integer, Set<String>> getUnregisterIdMap() { + try { + ConnectorTracker tracker = getConnectorTracker(); + Field field = tracker.getClass().getDeclaredField( + "syncIdToUnregisteredConnectorIds"); + field.setAccessible(true); + return (Map<Integer, Set<String>>) field.get(tracker); + } catch (Exception e) { + throw new RuntimeException(e); + } + } +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/application/ResynchronizeAfterAsyncRemovalTest.java b/uitest/src/com/vaadin/tests/application/ResynchronizeAfterAsyncRemovalTest.java new file mode 100644 index 0000000000..7f2dabe9f1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/application/ResynchronizeAfterAsyncRemovalTest.java @@ -0,0 +1,52 @@ +/* + * 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.application; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class ResynchronizeAfterAsyncRemovalTest extends SingleBrowserTest { + @Test + public void noResyncAfterAsyncRemoval() { + openTestURL(); + + $(ButtonElement.class).first().click(); + + Assert.assertEquals("Timing issue in the test?", + "1. Window removed: true", getLogRow(1)); + + Assert.assertEquals( + "Removing window should not cause button to be marked as dirty", + "2. Dirty: false", getLogRow(0)); + + ButtonElement logCountButton = $(ButtonElement.class).all().get(1); + logCountButton.click(); + + Assert.assertEquals("Sanity check", "3. syncId: 2", getLogRow(1)); + Assert.assertEquals("Sanity check", + "4. Unregistered connector count: 1", getLogRow(0)); + + logCountButton.click(); + + Assert.assertEquals("Sanity check", "5. syncId: 3", getLogRow(1)); + Assert.assertEquals( + "Unregistered connector map should have been cleared", + "6. Unregistered connector count: 0", getLogRow(0)); + } +} diff --git a/uitest/src/com/vaadin/tests/components/AbstractTestUI.java b/uitest/src/com/vaadin/tests/components/AbstractTestUI.java index dba055a65a..98b0f63ce1 100644 --- a/uitest/src/com/vaadin/tests/components/AbstractTestUI.java +++ b/uitest/src/com/vaadin/tests/components/AbstractTestUI.java @@ -205,4 +205,18 @@ public abstract class AbstractTestUI extends UI { return getSession().getBrowser(); } + /** + * Execute the provided runnable on the UI thread as soon as the current + * request has been sent. + */ + protected void runAfterResponse(final Runnable runnable) { + // Immediately start a thread that will start waiting for the session to + // get unlocked. + new Thread() { + @Override + public void run() { + accessSynchronously(runnable); + } + }.start(); + } } diff --git a/uitest/src/com/vaadin/tests/components/embedded/EmbeddedAltTextTest.java b/uitest/src/com/vaadin/tests/components/embedded/EmbeddedAltTextTest.java new file mode 100644 index 0000000000..23dcddd8d0 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/embedded/EmbeddedAltTextTest.java @@ -0,0 +1,60 @@ +/* + * 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.components.embedded; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.EmbeddedElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class EmbeddedAltTextTest extends MultiBrowserTest { + + @Before + @Override + public void setup() throws Exception { + super.setup(); + openTestURL(); + waitForElementPresent(By.className("v-embedded")); + } + + @Test + public void testEmbeddedAltText() { + EmbeddedElement embedded = $(EmbeddedElement.class).first(); + + Assert.assertEquals("Alt text of the image", getAltText(embedded)); + assertHtmlSource("Alt text of the object"); + + $(ButtonElement.class).first().click(); + + Assert.assertEquals("New alt text of the image!", getAltText(embedded)); + assertHtmlSource("New alt text of the object!"); + } + + private void assertHtmlSource(String html) { + String pageSource = driver.getPageSource(); + Assert.assertTrue("Page source does not contain '" + html + "'", + pageSource.contains(html)); + } + + private String getAltText(EmbeddedElement embedded) { + return embedded.findElement(By.vaadin("/domChild[0]")).getAttribute( + "alt"); + } +} diff --git a/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.java b/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.java index 3c5801e90e..28ffebcf56 100644 --- a/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.java +++ b/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.java @@ -5,6 +5,7 @@ import com.vaadin.event.MouseEvents.ClickListener; import com.vaadin.server.ThemeResource; import com.vaadin.tests.components.TestBase; import com.vaadin.ui.Embedded; +import com.vaadin.ui.Label; public class EmbeddedClickListenerRelativeCoordinates extends TestBase { @@ -12,17 +13,21 @@ public class EmbeddedClickListenerRelativeCoordinates extends TestBase { protected void setup() { Embedded e = new Embedded("Embedded caption", new ThemeResource( "../runo/icons/64/ok.png")); + final Label xLabel = new Label(); + xLabel.setId("x"); + final Label yLabel = new Label(); + yLabel.setId("y"); e.addListener(new ClickListener() { @Override public void click(ClickEvent event) { - getMainWindow() - .showNotification( - "" + event.getRelativeX() + ", " - + event.getRelativeY()); + xLabel.setValue("" + event.getRelativeX()); + yLabel.setValue("" + event.getRelativeY()); } }); addComponent(e); + addComponent(xLabel); + addComponent(yLabel); } @Override diff --git a/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinatesTest.java b/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinatesTest.java new file mode 100644 index 0000000000..6bed0117f8 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinatesTest.java @@ -0,0 +1,86 @@ +/* + * 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.components.embedded; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.By; + +import com.vaadin.testbench.elements.EmbeddedElement; +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.testbench.parallel.BrowserUtil; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class EmbeddedClickListenerRelativeCoordinatesTest extends + MultiBrowserTest { + + @Before + @Override + public void setup() throws Exception { + super.setup(); + openTestURL(); + waitForElementPresent(By.className("v-embedded")); + } + + @Test + public void testRelativeClick() { + clickAt(41, 22); + checkLocation(41, 22); + + clickAt(0, 0); + checkLocation(0, 0); + } + + private void clickAt(int x, int y) { + EmbeddedElement embedded = $(EmbeddedElement.class).first(); + + // IE8 consistently clicks two pixels left and above of the given + // position + if (isIE8()) { + x += 2; + y += 2; + } + embedded.click(x, y); + } + + private void checkLocation(int expectedX, int expectedY) { + LabelElement xLabel = $(LabelElement.class).id("x"); + LabelElement yLabel = $(LabelElement.class).id("y"); + + int x = Integer.parseInt(xLabel.getText()); + int y = Integer.parseInt(yLabel.getText()); + + Assert.assertEquals( + "Reported X-coordinate from Embedded does not match click location", + expectedX, x); + + // IE10 and IE11 sometimes click one pixel below the given position + int tolerance = isIE() ? 1 : 0; + Assert.assertTrue( + "Reported Y-coordinate from Embedded does not match click location", + Math.abs(expectedY - y) <= tolerance); + } + + private boolean isIE() { + return BrowserUtil.isIE(getDesiredCapabilities()); + } + + private boolean isIE8() { + return BrowserUtil.isIE8(getDesiredCapabilities()); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridCheckBoxDisplay.java b/uitest/src/com/vaadin/tests/components/grid/GridCheckBoxDisplay.java new file mode 100644 index 0000000000..e6aff73532 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridCheckBoxDisplay.java @@ -0,0 +1,91 @@ +/* + * 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.components.grid; + +import java.io.Serializable; + +import com.vaadin.data.util.BeanItemContainer; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Grid; + +public class GridCheckBoxDisplay extends AbstractTestUI { + + private static final long serialVersionUID = -5575892909354637168L; + private BeanItemContainer<Todo> todoContainer = new BeanItemContainer<Todo>( + Todo.class); + + @Override + protected void setup(VaadinRequest request) { + todoContainer.addBean(new Todo("Done task", true)); + todoContainer.addBean(new Todo("Not done", false)); + + Grid grid = new Grid(todoContainer); + grid.setSizeFull(); + + grid.setColumnOrder("done", "task"); + grid.getColumn("done").setWidth(75); + grid.getColumn("task").setExpandRatio(1); + + grid.setSelectionMode(Grid.SelectionMode.SINGLE); + + grid.setEditorEnabled(true); + grid.setImmediate(true); + + getLayout().addComponent(grid); + getLayout().setExpandRatio(grid, 1); + + } + + @Override + protected Integer getTicketNumber() { + return 16976; + } + + @Override + public String getDescription() { + return "Verify that checkbox state is correct for all items in editor"; + } + + public class Todo implements Serializable { + private static final long serialVersionUID = -5961103142478316018L; + + private boolean done; + private String task = ""; + + public Todo(String task, boolean done) { + this.task = task; + this.done = done; + } + + public boolean isDone() { + return done; + } + + public void setDone(boolean done) { + this.done = done; + } + + public String getTask() { + return task; + } + + public void setTask(String task) { + this.task = task; + } + } + +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridCheckBoxDisplayTest.java b/uitest/src/com/vaadin/tests/components/grid/GridCheckBoxDisplayTest.java new file mode 100644 index 0000000000..c430821534 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridCheckBoxDisplayTest.java @@ -0,0 +1,72 @@ +/* + * 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.components.grid; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; + +import com.vaadin.testbench.elements.CheckBoxElement; +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; +import com.vaadin.tests.tb3.SingleBrowserTest; + +@TestCategory("grid") +public class GridCheckBoxDisplayTest extends SingleBrowserTest { + @Test + public void testAddRow() { + openTestURL(); + + GridElement grid = $(GridElement.class).first(); + + Assert.assertEquals("First item had wrong value", "true", + grid.getCell(0, 0).getText()); + Assert.assertEquals("Second item had wrong value", "false", grid + .getCell(1, 0).getText()); + + // First edit false item and see that the CheckBox is unchecked + grid.getCell(1, 0).doubleClick(); + + CheckBoxElement checkbox = $(CheckBoxElement.class).first(); + Assert.assertEquals("CheckBox was checked", "unchecked", + checkbox.getValue()); + + closeEditor(); + + // Edit true item and see that the CheckBox is checked + grid.getCell(0, 0).doubleClick(); + + checkbox = $(CheckBoxElement.class).first(); + Assert.assertEquals("CheckBox was not checked.", "checked", + checkbox.getValue()); + + closeEditor(); + + // Edit false item and confirm that the CheckBox is unchecked again + grid.getCell(1, 0).doubleClick(); + + checkbox = $(CheckBoxElement.class).first(); + Assert.assertEquals("CheckBox was checked", "unchecked", + checkbox.getValue()); + } + + /** + * Closes the grids editor using the cancel button + */ + private void closeEditor() { + findElement(By.className("v-grid-editor-cancel")).click(); + } +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridDetailsDetach.java b/uitest/src/com/vaadin/tests/components/grid/GridDetailsDetach.java new file mode 100644 index 0000000000..1032378a2d --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridDetailsDetach.java @@ -0,0 +1,149 @@ +/* + * 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.components.grid; + +import com.vaadin.data.util.BeanItemContainer; +import com.vaadin.event.ItemClickEvent; +import com.vaadin.event.ItemClickEvent.ItemClickListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Component; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Grid.DetailsGenerator; +import com.vaadin.ui.Grid.RowReference; +import com.vaadin.ui.Label; +import com.vaadin.ui.VerticalLayout; + +public class GridDetailsDetach extends AbstractTestUI { + + private Grid currentGrid; + + @Override + protected void setup(VaadinRequest request) { + VerticalLayout layout = new VerticalLayout(); + layout.setSizeFull(); + + Button button = new Button("Test"); + layout.addComponent(button); + layout.setExpandRatio(button, 0f); + + currentGrid = generateGrid(); + final VerticalLayout gridContainer = new VerticalLayout(); + gridContainer.addComponent(currentGrid); + + button.addClickListener(new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + gridContainer.replaceComponent(currentGrid, new Label("Foo")); + } + }); + + layout.addComponent(new Button("Reattach Grid", + new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + gridContainer.removeAllComponents(); + gridContainer.addComponent(currentGrid); + } + })); + + layout.addComponent(gridContainer); + layout.setExpandRatio(gridContainer, 1f); + + addComponent(layout); + } + + private Grid generateGrid() { + BeanItemContainer<GridExampleBean> container = new BeanItemContainer<GridExampleBean>( + GridExampleBean.class); + for (int i = 0; i < 1000; i++) { + container.addItem(new GridExampleBean("Bean " + i, i * i, i / 10d)); + } + + final Grid grid = new Grid(container); + grid.setColumnOrder("name", "amount", "count"); + grid.setSizeFull(); + + grid.setDetailsGenerator(new DetailsGenerator() { + @Override + public Component getDetails(RowReference rowReference) { + final GridExampleBean bean = (GridExampleBean) rowReference + .getItemId(); + VerticalLayout layout = new VerticalLayout(new Label( + "Extra data for " + bean.getName())); + layout.setMargin(true); + return layout; + } + }); + + grid.addItemClickListener(new ItemClickListener() { + @Override + public void itemClick(ItemClickEvent event) { + Object itemId = event.getItemId(); + grid.setDetailsVisible(itemId, !grid.isDetailsVisible(itemId)); + } + }); + return grid; + } + + public class GridExampleBean { + + private String name; + + private int count; + + private double amount; + + public GridExampleBean() { + } + + public GridExampleBean(String name, int count, double amount) { + this.name = name; + this.count = count; + this.amount = amount; + } + + public String getName() { + return name; + } + + public int getCount() { + return count; + } + + public double getAmount() { + return amount; + } + + public void setName(String name) { + this.name = name; + } + + public void setCount(int count) { + this.count = count; + } + + public void setAmount(double amount) { + this.amount = amount; + } + + } + +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridDetailsDetachTest.java b/uitest/src/com/vaadin/tests/components/grid/GridDetailsDetachTest.java new file mode 100644 index 0000000000..fc79fd1b68 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/grid/GridDetailsDetachTest.java @@ -0,0 +1,73 @@ +/* + * 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.components.grid; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.parallel.TestCategory; +import com.vaadin.tests.tb3.MultiBrowserTest; + +@TestCategory("grid") +public class GridDetailsDetachTest extends MultiBrowserTest { + + @Test + public void testDetachGridWithDetailsOpen() { + setDebug(true); + openTestURL(); + + $(GridElement.class).first().getCell(3, 0).click(); + $(GridElement.class).first().getCell(5, 0).click(); + + assertNoErrorNotifications(); + + $(ButtonElement.class).first().click(); + + assertNoErrorNotifications(); + } + + @Test + public void testDetachAndReattachGridWithDetailsOpen() { + setDebug(true); + openTestURL(); + + $(GridElement.class).first().getCell(3, 0).click(); + $(GridElement.class).first().getCell(5, 0).click(); + + assertNoErrorNotifications(); + + $(ButtonElement.class).first().click(); + + assertNoErrorNotifications(); + + $(ButtonElement.class).get(1).click(); + + assertNoErrorNotifications(); + + List<WebElement> spacers = findElements(By.className("v-grid-spacer")); + Assert.assertEquals("Not enough spacers in DOM", 2, spacers.size()); + Assert.assertEquals("Spacer content not visible", + "Extra data for Bean 3", spacers.get(0).getText()); + Assert.assertEquals("Spacer content not visible", + "Extra data for Bean 5", spacers.get(1).getText()); + } +} diff --git a/uitest/src/com/vaadin/tests/components/grid/GridDetailsWidthTest.java b/uitest/src/com/vaadin/tests/components/grid/GridDetailsWidthTest.java index 41838b427b..2def2d0279 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridDetailsWidthTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridDetailsWidthTest.java @@ -23,6 +23,7 @@ import org.openqa.selenium.By; import org.openqa.selenium.WebElement; import com.vaadin.testbench.elements.GridElement; +import com.vaadin.testbench.elements.GridElement.GridCellElement; import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.SingleBrowserTest; @@ -66,4 +67,26 @@ public class GridDetailsWidthTest extends SingleBrowserTest { } } + @Test + public void testDetailsOnSort() { + openTestURL(); + GridElement grid = $(GridElement.class).first(); + + // Open a details rows + grid.getCell(0, 0).click(); + + GridCellElement cell = grid.getHeaderCell(0, 0); + cell.click(); + cell.click(); + + cell = grid.getCell(2, 0); + WebElement spacer = findElement(By.className("v-grid-spacer")); + Assert.assertEquals("Grid was not sorted correctly", "Hello 0", + cell.getText()); + Assert.assertEquals("Details row was not in correct location", cell + .getLocation().getY() + cell.getSize().getHeight(), spacer + .getLocation().getY()); + + } + } diff --git a/uitest/src/com/vaadin/tests/components/grid/GridSubPixelProblemWrappingTest.java b/uitest/src/com/vaadin/tests/components/grid/GridSubPixelProblemWrappingTest.java index 319cf3b8b8..4368fda158 100644 --- a/uitest/src/com/vaadin/tests/components/grid/GridSubPixelProblemWrappingTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/GridSubPixelProblemWrappingTest.java @@ -15,30 +15,18 @@ */ package com.vaadin.tests.components.grid; -import java.util.List; - import org.junit.Assert; import org.junit.Test; -import org.openqa.selenium.remote.DesiredCapabilities; import com.vaadin.testbench.elements.ButtonElement; import com.vaadin.testbench.elements.GridElement; import com.vaadin.testbench.elements.GridElement.GridRowElement; -import com.vaadin.testbench.parallel.Browser; import com.vaadin.testbench.parallel.TestCategory; import com.vaadin.tests.tb3.MultiBrowserTest; @TestCategory("grid") public class GridSubPixelProblemWrappingTest extends MultiBrowserTest { - @Override - public List<DesiredCapabilities> getBrowsersToTest() { - List<DesiredCapabilities> l = super.getBrowsersToTest(); - // Currently broken because of #18214 - l.remove(Browser.IE9.getDesiredCapabilities()); - return l; - } - @Test public void addedRowShouldNotWrap() { openTestURL(); diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientSelectionTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientSelectionTest.java index a341e39b74..d1d7b21e11 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientSelectionTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/client/GridClientSelectionTest.java @@ -195,4 +195,17 @@ public class GridClientSelectionTest extends GridBasicClientFeaturesTest { isRowSelected(1)); } + @Test + public void testChangeSelectionModelUpdatesUI() { + openTestURL(); + + setSelectionModelSingle(true); + getGridElement().getCell(5, 1).click(); + assertTrue("Row 5 should be selected after clicking", isRowSelected(5)); + setSelectionModelNone(); + assertFalse( + "Row 5 should not be selected after changing selection model", + isRowSelected(5)); + + } } diff --git a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java index b4eb473d4b..9953bbcae0 100644 --- a/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java +++ b/uitest/src/com/vaadin/tests/components/grid/basicfeatures/server/GridSelectionTest.java @@ -271,6 +271,20 @@ public class GridSelectionTest extends GridBasicFeaturesTest { } @Test + public void testSelectAllCheckboxWithHeaderOperations() { + openTestURL(); + + setSelectionModelMulti(); + selectMenuPath("Component", "Header", "Prepend row"); + selectMenuPath("Component", "Header", "Append row"); + + GridCellElement header = getGridElement().getHeaderCell(1, 0); + assertTrue("Multi Selection Model should have select all checkbox", + header.isElementPresent(By.tagName("input"))); + + } + + @Test public void testToggleDeselectAllowed() { openTestURL(); @@ -305,6 +319,22 @@ public class GridSelectionTest extends GridBasicFeaturesTest { .isSelected()); } + @Test + public void testChangeSelectionModelUpdatesUI() { + openTestURL(); + + setSelectionModelSingle(); + + getGridElement().getCell(5, 1).click(); + assertTrue("Row should be selected after clicking", getRow(5) + .isSelected()); + + setSelectionModelNone(); + assertFalse( + "Row should not be selected after changing selection model", + getRow(5).isSelected()); + } + private void setSelectionModelMulti() { selectMenuPath("Component", "State", "Selection mode", "multi"); } diff --git a/uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFocusOrderAfterShowChild.java b/uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFocusOrderAfterShowChild.java new file mode 100644 index 0000000000..034ff024d0 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFocusOrderAfterShowChild.java @@ -0,0 +1,108 @@ +/* + * 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.components.gridlayout; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.TextField; + +public class GridLayoutFocusOrderAfterShowChild extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + GridLayout gl = new GridLayout(2, 5); + gl.setId("grid"); + gl.setMargin(true); + gl.setSpacing(true); + + final Label l1 = new Label("First"); + l1.setWidthUndefined(); + l1.setVisible(false); + gl.addComponent(l1); + + final TextField t1 = new TextField(); + t1.setId("t1"); + t1.setVisible(false); + t1.setWidthUndefined(); + gl.addComponent(t1); + + Label l2 = new Label("Second"); + l2.setWidthUndefined(); + gl.addComponent(l2); + + TextField t2 = new TextField(); + t2.setId("t2"); + gl.addComponent(t2); + + final Label l3 = new Label("Third"); + l3.setWidthUndefined(); + l3.setVisible(false); + gl.addComponent(l3); + + final TextField t3 = new TextField(); + t3.setId("t3"); + t3.setVisible(false); + gl.addComponent(t3); + + Label l4 = new Label("Fourth"); + l4.setWidthUndefined(); + gl.addComponent(l4); + + TextField t4 = new TextField(); + t4.setId("t4"); + gl.addComponent(t4); + + final Label l5 = new Label("Fifth"); + l5.setWidthUndefined(); + l5.setVisible(false); + gl.addComponent(l5); + + final TextField t5 = new TextField(); + t5.setId("t5"); + t5.setVisible(false); + gl.addComponent(t5); + + addComponent(gl); + + addComponent(new Button("Show first", new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + t1.setVisible(true); + l1.setVisible(true); + } + })); + + addComponent(new Button("Show third", new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + t3.setVisible(true); + l3.setVisible(true); + } + })); + + addComponent(new Button("Show fifth", new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + t5.setVisible(true); + l5.setVisible(true); + } + })); + } +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFocusOrderAfterShowChildTest.java b/uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFocusOrderAfterShowChildTest.java new file mode 100644 index 0000000000..1913fbfdf9 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/gridlayout/GridLayoutFocusOrderAfterShowChildTest.java @@ -0,0 +1,88 @@ +/* + * 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.components.gridlayout; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.Keys; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.GridLayoutElement; +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class GridLayoutFocusOrderAfterShowChildTest extends MultiBrowserTest { + + @Test + public void showComponentBreaksFocusOrderFirst() + throws IOException, Exception { + openTestURL(); + + GridLayoutElement grid = $(GridLayoutElement.class).id("grid"); + + $(ButtonElement.class).first().click(); + + Assert.assertEquals("First", + grid.$(LabelElement.class).first().getText()); + grid.$(TextFieldElement.class).first().focus(); + + grid.$(TextFieldElement.class).first().sendKeys(Keys.TAB); + + Assert.assertEquals("t2", + driver.switchTo().activeElement().getAttribute("id")); + } + + @Test + public void showComponentBreaksFocusOrderMiddle() + throws IOException, Exception { + openTestURL(); + + GridLayoutElement grid = $(GridLayoutElement.class).id("grid"); + + $(ButtonElement.class).get(1).click(); + + Assert.assertEquals("Third", + grid.$(LabelElement.class).get(1).getText()); + grid.$(TextFieldElement.class).first().focus(); + + grid.$(TextFieldElement.class).first().sendKeys(Keys.TAB); + + Assert.assertEquals("t3", + driver.switchTo().activeElement().getAttribute("id")); + } + + @Test + public void showComponentBreaksFocusOrderLast() + throws IOException, Exception { + openTestURL(); + + GridLayoutElement grid = $(GridLayoutElement.class).id("grid"); + + $(ButtonElement.class).get(2).click(); + + Assert.assertEquals("Fifth", + grid.$(LabelElement.class).get(2).getText()); + grid.$(TextFieldElement.class).get(1).focus(); + + grid.$(TextFieldElement.class).get(1).sendKeys(Keys.TAB); + + Assert.assertEquals("t5", + driver.switchTo().activeElement().getAttribute("id")); + } +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/table/ContextMenuSizeTest.java b/uitest/src/com/vaadin/tests/components/table/ContextMenuSizeTest.java index e6b3ca2af4..e5e5163442 100644 --- a/uitest/src/com/vaadin/tests/components/table/ContextMenuSizeTest.java +++ b/uitest/src/com/vaadin/tests/components/table/ContextMenuSizeTest.java @@ -27,7 +27,6 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.remote.DesiredCapabilities; -import com.vaadin.testbench.parallel.Browser; import com.vaadin.tests.tb3.MultiBrowserTest; /** @@ -39,10 +38,7 @@ public class ContextMenuSizeTest extends MultiBrowserTest { @Override public List<DesiredCapabilities> getBrowsersToTest() { - // context menu doesn't work in phantom JS and works weirdly with IE8 - // and selenium. - return getBrowserCapabilities(Browser.IE9, Browser.IE10, Browser.IE11, - Browser.FIREFOX, Browser.CHROME); + return getBrowsersSupportingContextMenu(); } @Override diff --git a/uitest/src/com/vaadin/tests/components/tree/TreeContextMenuAndIconsTest.java b/uitest/src/com/vaadin/tests/components/tree/TreeContextMenuAndIconsTest.java new file mode 100644 index 0000000000..81d906bec3 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tree/TreeContextMenuAndIconsTest.java @@ -0,0 +1,130 @@ +/* + * 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.components.tree; + +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.Dimension; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.remote.DesiredCapabilities; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class TreeContextMenuAndIconsTest extends MultiBrowserTest { + + @Override + protected Class<?> getUIClass() { + return Trees.class; + } + + @Override + public List<DesiredCapabilities> getBrowsersToTest() { + return getBrowsersSupportingContextMenu(); + } + + @Test + public void testSimpleContextMenu() throws Exception { + openTestURL(); + + selectMenuPath("Settings", "Show event log"); + selectMenuPath("Component", "Features", "Context menu", + "Item without icon"); + + openContextMenu(getTreeNodeByCaption("Item 1")); + + compareScreen("contextmenu-noicon"); + + closeContextMenu(); + } + + @Test + public void testContextMenuWithAndWithoutIcon() throws Exception { + openTestURL(); + + selectMenuPath("Settings", "Show event log"); + selectMenuPath("Component", "Features", "Context menu", + "With and without icon"); + + openContextMenu(getTreeNodeByCaption("Item 1")); + + compareScreen("caption-only-and-has-icon"); + + closeContextMenu(); + } + + @Test + public void testContextLargeIcon() throws Exception { + openTestURL(); + + selectMenuPath("Settings", "Show event log"); + selectMenuPath("Component", "Features", "Context menu", + "Only one large icon"); + + WebElement menu = openContextMenu(getTreeNodeByCaption("Item 1")); + + // reindeer doesn't support menu with larger row height, so the + // background image contains parts of other sprites => + // just check that the menu is of correct size + Dimension size = menu.getSize(); + Assert.assertEquals("Menu height with large icons", 74, size.height); + + closeContextMenu(); + } + + @Test + public void testContextRemoveIcon() throws Exception { + openTestURL(); + + selectMenuPath("Settings", "Show event log"); + selectMenuPath("Component", "Features", "Context menu", + "Only one large icon"); + + openContextMenu(getTreeNodeByCaption("Item 1")); + closeContextMenu(); + + selectMenuPath("Component", "Features", "Context menu", + "Item without icon"); + + openContextMenu(getTreeNodeByCaption("Item 1")); + + compareScreen("contextmenu-noicon"); + + closeContextMenu(); + } + + private WebElement openContextMenu(WebElement element) { + Actions actions = new Actions(getDriver()); + // Note: on Firefox, the first menu item does not get focus; on other + // browsers it does + actions.contextClick(element); + actions.perform(); + return findElement(By.className("v-contextmenu")); + } + + private void closeContextMenu() { + findElement(By.className("v-app")).click(); + } + + private WebElement getTreeNodeByCaption(String caption) { + return getDriver().findElement( + By.xpath("//span[text() = '" + caption + "']")); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/upload/DragAndDropUploadAndInteractions.java b/uitest/src/com/vaadin/tests/components/upload/DragAndDropUploadAndInteractions.java new file mode 100644 index 0000000000..952fe08b79 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/upload/DragAndDropUploadAndInteractions.java @@ -0,0 +1,153 @@ +package com.vaadin.tests.components.upload; + +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Date; +import java.util.List; + +import org.apache.commons.io.output.ByteArrayOutputStream; + +import com.vaadin.event.dd.DragAndDropEvent; +import com.vaadin.event.dd.DropHandler; +import com.vaadin.event.dd.acceptcriteria.AcceptAll; +import com.vaadin.event.dd.acceptcriteria.AcceptCriterion; +import com.vaadin.server.StreamVariable; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Button; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.Component; +import com.vaadin.ui.DragAndDropWrapper; +import com.vaadin.ui.Html5File; +import com.vaadin.ui.Panel; + +public class DragAndDropUploadAndInteractions extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + ComboBox comboBox = new ComboBox(); + for (int i = 0; i < 10; i++) { + comboBox.addItem("Test " + i); + } + addComponent(comboBox); + Button b = new Button("Dummy"); + addComponent(b); + Panel p = new Panel(); + p.setHeight(200, Unit.PIXELS); + p.setWidth(200, Unit.PIXELS); + MyUploadPanel myUploadPanel = new MyUploadPanel(p); + addComponent(myUploadPanel); + } + + class MyUploadPanel extends DragAndDropWrapper implements DropHandler { + private static final long serialVersionUID = 1L; + + public MyUploadPanel(Component root) { + super(root); + setDropHandler(this); + } + + @Override + public void drop(DragAndDropEvent event) { + WrapperTransferable tr = (WrapperTransferable) event + .getTransferable(); + Html5File[] files = tr.getFiles(); + + if (files != null) { + List<Html5File> filesToUpload = Arrays.asList(files); + for (Html5File file : filesToUpload) { + file.setStreamVariable(new MyStreamVariable()); + } + } + } + + @Override + public AcceptCriterion getAcceptCriterion() { + return AcceptAll.get(); + } + + } + + class MyStreamVariable implements StreamVariable { + private static final long serialVersionUID = 1L; + + @Override + public OutputStream getOutputStream() { + return new ByteArrayOutputStream(); + } + + @Override + public boolean listenProgress() { + return true; + } + + long lastEvent = 0; + long lastTime = 0; + + @Override + public void onProgress(StreamingProgressEvent event) { + long received = event.getBytesReceived() - lastEvent; + long now = new Date().getTime(); + long time = now - lastTime; + lastTime = now; + lastEvent = event.getBytesReceived(); + if (time == 0) { + return; + } + log("Received " + received + " bytes in " + time + "ms: " + + formatSize(received / (time / 1000.0)) + "/s"); + log("Streaming OnProgress - ContentLength: " + + formatSize(event.getContentLength()) + + " - Bytes Received: " + + formatSize(event.getBytesReceived())); + } + + @Override + public void streamingStarted(StreamingStartEvent event) { + lastEvent = 0; + lastTime = new Date().getTime(); + log("Streaming Started - ContentLength: " + + formatSize(event.getContentLength()) + + " - Bytes Received: " + + formatSize(event.getBytesReceived())); + } + + @Override + public void streamingFinished(StreamingEndEvent event) { + log("Streaming Finished - ContentLength: " + + formatSize(event.getContentLength()) + + " - Bytes Received: " + + formatSize(event.getBytesReceived())); + } + + @Override + public void streamingFailed(StreamingErrorEvent event) { + log("Streaming Failed - ContentLength: " + + formatSize(event.getContentLength()) + + " - Bytes Received: " + + formatSize(event.getBytesReceived())); + } + + @Override + public boolean isInterrupted() { + return false; + } + + } + + protected String formatSize(double contentLength) { + double d = contentLength; + int suffix = 0; + String[] suffixes = new String[] { "B", "KB", "MB", "GB", "TB" }; + while (d > 1024) { + suffix++; + d /= 1024.0; + } + return String.format("%.1f %s", d, suffixes[suffix]); + } + + @Override + protected String getTestDescription() { + return "Drop a large (100 MB) file using IE10 and interact with the application while uploading. Ensure the uploads succeeds even though you are interacting with the app."; + } +} diff --git a/uitest/src/com/vaadin/tests/components/window/UndefinedHeightSubWindowAndContentTest.java b/uitest/src/com/vaadin/tests/components/window/UndefinedHeightSubWindowAndContentTest.java new file mode 100644 index 0000000000..057a43f495 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/window/UndefinedHeightSubWindowAndContentTest.java @@ -0,0 +1,34 @@ +package com.vaadin.tests.components.window; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.Keys; + +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.MultiBrowserTest; +import com.vaadin.tests.tb3.newelements.WindowElement; + +public class UndefinedHeightSubWindowAndContentTest extends MultiBrowserTest { + + @Test + public void testUndefinedHeight() { + openTestURL(); + + TextFieldElement textField = $(TextFieldElement.class).first(); + + textField.click(); + textField.sendKeys("invalid", Keys.ENTER); + + WindowElement window = $(WindowElement.class).first(); + int height = window.getSize().getHeight(); + Assert.assertTrue("Window height with validation failure", + 161 <= height && height <= 164); + + textField.setValue("valid"); + textField.sendKeys(Keys.ENTER); + height = window.getSize().getHeight(); + Assert.assertTrue("Window height with validation success", + 136 <= height && height <= 139); + } + +} diff --git a/uitest/src/com/vaadin/tests/extensions/ResponsiveWithCrossDomainStyles.java b/uitest/src/com/vaadin/tests/extensions/ResponsiveWithCrossDomainStyles.java new file mode 100644 index 0000000000..9f9453d505 --- /dev/null +++ b/uitest/src/com/vaadin/tests/extensions/ResponsiveWithCrossDomainStyles.java @@ -0,0 +1,37 @@ +/* + * 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.extensions; + +import com.vaadin.annotations.StyleSheet; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; + +@StyleSheet("http://fonts.googleapis.com/css?family=Cabin+Sketch") +public class ResponsiveWithCrossDomainStyles extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + addComponent(new Button("Make responsive", new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + event.getButton().setResponsive(true); + } + })); + } + +} diff --git a/uitest/src/com/vaadin/tests/extensions/ResponsiveWithCrossDomainStylesTest.java b/uitest/src/com/vaadin/tests/extensions/ResponsiveWithCrossDomainStylesTest.java new file mode 100644 index 0000000000..4089618635 --- /dev/null +++ b/uitest/src/com/vaadin/tests/extensions/ResponsiveWithCrossDomainStylesTest.java @@ -0,0 +1,34 @@ +/* + * 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.extensions; + +import org.junit.Test; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class ResponsiveWithCrossDomainStylesTest extends MultiBrowserTest { + @Test + public void testResponsive() { + setDebug(true); + openTestURL(); + + $(ButtonElement.class).first().click(); + + assertNoErrorNotifications(); + } + +} diff --git a/uitest/src/com/vaadin/tests/fieldgroup/BasicPersonForm.java b/uitest/src/com/vaadin/tests/fieldgroup/BasicPersonForm.java index a223cea6a0..52d3e60af5 100644 --- a/uitest/src/com/vaadin/tests/fieldgroup/BasicPersonForm.java +++ b/uitest/src/com/vaadin/tests/fieldgroup/BasicPersonForm.java @@ -131,7 +131,9 @@ public class BasicPersonForm extends AbstractTestUIWithLog { } catch (CommitException e) { msg = "Commit failed: " + e.getMessage(); } - Notification.show(msg); + Notification notification = new Notification(msg); + notification.setDelayMsec(Notification.DELAY_FOREVER); + notification.show(getPage()); log(msg); } diff --git a/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java b/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java index edcd07ee89..842fcbb859 100644 --- a/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java +++ b/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java @@ -240,6 +240,15 @@ public abstract class AbstractTB3Test extends ParallelTest { * debug window and/or push (depending on {@link #isDebug()} and * {@link #isPush()}. */ + protected void openTestURL() { + openTestURL(new String[0]); + } + + /** + * Opens the given test (defined by {@link #getTestUrl()}, optionally with + * debug window and/or push (depending on {@link #isDebug()} and + * {@link #isPush()}. + */ protected void openTestURL(String... parameters) { openTestURL(getUIClass(), parameters); } diff --git a/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java b/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java index 678b38c4f1..a678009d85 100644 --- a/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java +++ b/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java @@ -80,6 +80,13 @@ public abstract class MultiBrowserTest extends PrivateTB3Configuration { Browser.IE11); } + protected List<DesiredCapabilities> getBrowsersSupportingContextMenu() { + // context menu doesn't work in phantom JS and works weirdly with IE8 + // and selenium. + return getBrowserCapabilities(Browser.IE9, Browser.IE10, Browser.IE11, + Browser.FIREFOX, Browser.CHROME); + } + @Override public void setDesiredCapabilities(DesiredCapabilities desiredCapabilities) { if (BrowserUtil.isIE(desiredCapabilities)) { diff --git a/uitest/tb2/com/vaadin/tests/components/embedded/EmbeddedAltText.html b/uitest/tb2/com/vaadin/tests/components/embedded/EmbeddedAltText.html deleted file mode 100644 index 066c55fe55..0000000000 --- a/uitest/tb2/com/vaadin/tests/components/embedded/EmbeddedAltText.html +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head profile="http://selenium-ide.openqa.org/profiles/test-case"> -<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> -<link rel="selenium.base" href="http://localhost:8068" /> -<title>EmbeddedAltText</title> -</head> -<body> -<table cellpadding="1" cellspacing="1" border="1"> -<thead> -<tr><td rowspan="1" colspan="3">EmbeddedAltText</td></tr> -</thead><tbody> -<tr> - <td>open</td> - <td>/run/com.vaadin.tests.components.embedded.EmbeddedAltText?restartApplication</td> - <td></td> -</tr> -<tr> - <td>assertAttribute</td> - <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedAltText::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VEmbedded[0]/domChild[0]@alt</td> - <td>Alt text of the image</td> -</tr> -<tr> - <td>assertHtmlSource</td> - <td>*Alt text of the object*</td> - <td></td> -</tr> -<tr> - <td>click</td> - <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedAltText::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> - <td></td> -</tr> -<tr> - <td>assertAttribute</td> - <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedAltText::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VEmbedded[0]/domChild[0]@alt</td> - <td>New alt text of the image!</td> -</tr> -<tr> - <td>assertHtmlSource</td> - <td>*New alt text of the object!*</td> - <td></td> -</tr> - -</tbody></table> -</body> -</html> diff --git a/uitest/tb2/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.html b/uitest/tb2/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.html deleted file mode 100644 index ae81cfe61c..0000000000 --- a/uitest/tb2/com/vaadin/tests/components/embedded/EmbeddedClickListenerRelativeCoordinates.html +++ /dev/null @@ -1,51 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head profile="http://selenium-ide.openqa.org/profiles/test-case"> -<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> -<link rel="selenium.base" href="" /> -<title>EmbeddedClickListenerRelativeCoordinates</title> -</head> -<body> -<table cellpadding="1" cellspacing="1" border="1"> -<thead> -<tr><td rowspan="1" colspan="3">EmbeddedClickListenerRelativeCoordinates</td></tr> -</thead><tbody> -<tr> - <td>open</td> - <td>/run/com.vaadin.tests.components.embedded.EmbeddedClickListenerRelativeCoordinates?restartApplication</td> - <td></td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VEmbedded[0]/domChild[0]</td> - <td>41,22</td> -</tr> -<tr> - <td>assertText</td> - <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::Root/VNotification[0]/HTML[0]/domChild[1]</td> - <td>41, 22</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::Root/VNotification[0]</td> - <td>0,0</td> -</tr> -<tr> - <td>waitForElementNotPresent</td> - <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::Root/VNotification[0]/HTML[0]/domChild[1]</td> - <td></td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VEmbedded[0]/domChild[0]</td> - <td>0,0</td> -</tr> -<tr> - <td>assertText</td> - <td>vaadin=runcomvaadintestscomponentsembeddedEmbeddedClickListenerRelativeCoordinates::Root/VNotification[0]/HTML[0]/domChild[1]</td> - <td>0, 0</td> -</tr> -</tbody></table> -</body> -</html> diff --git a/uitest/tb2/com/vaadin/tests/components/tree/TreeContextMenuAndIcons.html b/uitest/tb2/com/vaadin/tests/components/tree/TreeContextMenuAndIcons.html deleted file mode 100644 index 6f9b5e81c3..0000000000 --- a/uitest/tb2/com/vaadin/tests/components/tree/TreeContextMenuAndIcons.html +++ /dev/null @@ -1,156 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head profile="http://selenium-ide.openqa.org/profiles/test-case"> -<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> -<link rel="selenium.base" href="http://localhost:8888/" /> -<title>New Test</title> -</head> -<body> -<table cellpadding="1" cellspacing="1" border="1"> -<thead> -<tr><td rowspan="1" colspan="3">New Test</td></tr> -</thead><tbody> -<tr> - <td>open</td> - <td>/run/com.vaadin.tests.components.tree.Trees?restartApplication</td> - <td></td> -</tr> -<!--Hide event log--> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::PID_Smenu#item1</td> - <td>19,7</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[0]/VMenuBar[0]#item0</td> - <td>32,7</td> -</tr> -<!--Simple context menu--> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::PID_Smenu#item0</td> - <td>28,7</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[0]/VMenuBar[0]#item6</td> - <td>57,11</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[1]/VMenuBar[0]#item4</td> - <td>68,4</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[2]/VMenuBar[0]#item1</td> - <td>69,7</td> -</tr> -<tr> - <td>contextMenuAt</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::PID_StestComponent#n[1]</td> - <td></td> -</tr> -<tr> - <td>screenCapture</td> - <td></td> - <td>contextmenu-noicon</td> -</tr> -<!--Two actions, without and with icon--> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::PID_Smenu#item0</td> - <td>42,7</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[0]/VMenuBar[0]#item6</td> - <td>52,6</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[1]/VMenuBar[0]#item4</td> - <td>53,3</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[2]/VMenuBar[0]#item2</td> - <td>31,4</td> -</tr> -<tr> - <td>contextMenuAt</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::PID_StestComponent#n[1]</td> - <td></td> -</tr> -<tr> - <td>screenCapture</td> - <td></td> - <td>caption-only-and-has-icon</td> -</tr> -<!--Large icon--> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::PID_Smenu#item0</td> - <td>42,7</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[0]/VMenuBar[0]#item6</td> - <td>52,6</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[1]/VMenuBar[0]#item4</td> - <td>53,3</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[2]/VMenuBar[0]#item3</td> - <td>31,4</td> -</tr> -<tr> - <td>contextMenuAt</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::PID_StestComponent#n[1]</td> - <td></td> -</tr> -<tr> - <td>screenCapture</td> - <td></td> - <td>large-icon</td> -</tr> -<!--Simple context menu again to ensure it is properly updated (icons removed)--> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::PID_Smenu#item0</td> - <td>28,7</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[0]/VMenuBar[0]#item6</td> - <td>57,11</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[1]/VMenuBar[0]#item4</td> - <td>68,4</td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::Root/VOverlay[2]/VMenuBar[0]#item1</td> - <td>69,7</td> -</tr> -<tr> - <td>contextMenuAt</td> - <td>vaadin=runcomvaadintestscomponentstreeTrees::PID_StestComponent#n[1]</td> - <td></td> -</tr> -<tr> - <td>screenCapture</td> - <td></td> - <td>contextmenu-noicon</td> -</tr> -</tbody></table> -</body> -</html> diff --git a/uitest/tb2/com/vaadin/tests/components/window/UndefinedHeightSubWindowAndContent.html b/uitest/tb2/com/vaadin/tests/components/window/UndefinedHeightSubWindowAndContent.html deleted file mode 100644 index a3b56cd12a..0000000000 --- a/uitest/tb2/com/vaadin/tests/components/window/UndefinedHeightSubWindowAndContent.html +++ /dev/null @@ -1,57 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> -<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> -<head profile="http://selenium-ide.openqa.org/profiles/test-case"> -<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> -<link rel="selenium.base" href="" /> -<title>UndefinedHeightSubWindowAndContent</title> -</head> -<body> -<table cellpadding="1" cellspacing="1" border="1"> -<thead> -<tr><td rowspan="1" colspan="3">UndefinedHeightSubWindowAndContent</td></tr> -</thead><tbody> -<tr> - <td>open</td> - <td>/run/com.vaadin.tests.components.window.UndefinedHeightSubWindowAndContent?restartApplication</td> - <td></td> -</tr> -<tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentswindowUndefinedHeightSubWindowAndContent::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VForm[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> - <td>52,11</td> -</tr> -<tr> - <td>enterCharacter</td> - <td>vaadin=runcomvaadintestscomponentswindowUndefinedHeightSubWindowAndContent::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VForm[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> - <td>invalid</td> -</tr> -<tr> - <td>pressSpecialKey</td> - <td>vaadin=runcomvaadintestscomponentswindowUndefinedHeightSubWindowAndContent::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VForm[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> - <td>enter</td> -</tr> -<tr> - <td>screenCapture</td> - <td></td> - <td>form_full_width_1_error</td> -</tr> -<tr> - <td>enterCharacter</td> - <td>vaadin=runcomvaadintestscomponentswindowUndefinedHeightSubWindowAndContent::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VForm[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> - <td>valid</td> -</tr> -<tr> - <td>pressSpecialKey</td> - <td>vaadin=runcomvaadintestscomponentswindowUndefinedHeightSubWindowAndContent::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[0]/VForm[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> - <td>enter</td> -</tr> -<tr> - <td>screenCapture</td> - <td></td> - <td>form_full_width_2_valid</td> -</tr> - -</tbody></table> -</body> -</html> diff --git a/widgets/build.xml b/widgets/build.xml index 63317fb5ad..a437fd1940 100644 --- a/widgets/build.xml +++ b/widgets/build.xml @@ -45,6 +45,7 @@ <fileset dir="${result.deps}"> <include name="com/vaadin/*.gwt.xml" /> <include name="com/vaadin/client/BrowserInfo.java" /> + <include name="com/vaadin/client/ComputedStyle.java" /> <include name="com/vaadin/client/DeferredWorker.java" /> <include name="com/vaadin/client/Profiler.java" /> <include name="com/vaadin/client/StyleConstants.java" /> |