diff options
129 files changed, 4550 insertions, 637 deletions
diff --git a/WebContent/VAADIN/themes/base/browserframe/browserframe.scss b/WebContent/VAADIN/themes/base/browserframe/browserframe.scss index 149e38a744..fce9c11513 100644 --- a/WebContent/VAADIN/themes/base/browserframe/browserframe.scss +++ b/WebContent/VAADIN/themes/base/browserframe/browserframe.scss @@ -4,6 +4,13 @@ .v-browserframe { font-size: 0; } + + /* fix for #14813 - unable to scroll on iOS devices */ + .v-webkit.v-ios & .v-browserframe { + -webkit-overflow-scrolling: touch; + overflow:auto; + } + /* Some times a browser frame can contain a span with altenate text */ .v-browserframe > span { font-size: $font-size; diff --git a/WebContent/VAADIN/themes/base/common/common.scss b/WebContent/VAADIN/themes/base/common/common.scss index 77248c0c96..ea8b5e5aa0 100644 --- a/WebContent/VAADIN/themes/base/common/common.scss +++ b/WebContent/VAADIN/themes/base/common/common.scss @@ -259,6 +259,18 @@ input::-ms-clear { -webkit-overflow-scrolling: none; } +/* "Unnecessary scrollbar" related fixes (#14631, copied from Valo) */ +.v-scrollable.v-panel-content > .v-widget { + /* This is needed for IE */ + vertical-align: middle; + + /* Needed for all browsers. Can't really show anything outside the + * scrolling area anyway, so we can safely hide any overflow */ + overflow: hidden; +} + + + &.v-overlay-container { width: 0; height: 0; @@ -274,3 +286,8 @@ input::-ms-clear { width: 10px; overflow: hidden; } +/* fix for #14681 - mobile safari 8 sometimes displayed text cursor on that element */ +.v-radiobutton { + -webkit-user-select: none; +} + diff --git a/WebContent/VAADIN/themes/valo/components/_datefield.scss b/WebContent/VAADIN/themes/valo/components/_datefield.scss index 48977d4d20..71b50b5a77 100644 --- a/WebContent/VAADIN/themes/valo/components/_datefield.scss +++ b/WebContent/VAADIN/themes/valo/components/_datefield.scss @@ -337,7 +337,10 @@ @include valo-datefield-calendarpanel-day-focused-style; } - + .#{$primary-stylename}-day.#{$primary-stylename}-day-outside-range, + .#{$primary-stylename}-day.#{$primary-stylename}-day-outside-range:hover { + @include valo-datefield-calendarpanel-outside-range-style; + } .#{$primary-stylename}-weekdays { height: round($v-unit-size * 0.7); @@ -349,13 +352,12 @@ } } - .#{$primary-stylename}-header { white-space: nowrap; } - td[class$="year"], - td[class$="month"] { + td[class*="year"], + td[class*="month"] { button { @include appearance(none); border: none; @@ -381,10 +383,16 @@ } } - &:hover { - @include opacity(1); - &:before { - color: $v-focus-color; + &:hover:before { + color: $v-focus-color; + } + + &.outside-range { + cursor: default; + @include opacity(.3); + + &:hover:before { + color: mix($v-background-color, valo-font-color($v-background-color)); } } } @@ -500,6 +508,15 @@ background: transparent; } +/** + * Outputs the styles for an individual day element, which are outside available range. + * + * @group datefield + */ +@mixin valo-datefield-calendarpanel-outside-range-style { + color: mix(valo-font-color($v-background-color), $v-background-color); + cursor: not-allowed; +} /** * Outputs the styles for todays day element in a calendar panel. diff --git a/WebContent/VAADIN/themes/valo/components/_table.scss b/WebContent/VAADIN/themes/valo/components/_table.scss index e040a62d0f..d8a673294d 100644 --- a/WebContent/VAADIN/themes/valo/components/_table.scss +++ b/WebContent/VAADIN/themes/valo/components/_table.scss @@ -191,7 +191,15 @@ $v-table-background-color: null !default; .#{$primary-stylename}-caption-container { overflow: hidden; line-height: 1; + min-height: $v-table-row-height; @include box-sizing(border-box); + + .v-ie8 & { + // IE8 has issues with border-box and min-height + // -> custom calculations to subtract vertical padding from row height. + $vertical-padding: round(($v-table-row-height - $v-table-header-font-size)/2); + min-height: $v-table-row-height - ($vertical-padding - $v-table-border-width) - $vertical-padding; + } } .#{$primary-stylename}-footer-container { diff --git a/WebContent/VAADIN/themes/valo/favicon.ico b/WebContent/VAADIN/themes/valo/favicon.ico Binary files differnew file mode 100644 index 0000000000..ffb34a65c7 --- /dev/null +++ b/WebContent/VAADIN/themes/valo/favicon.ico diff --git a/WebContent/VAADIN/themes/valo/shared/_global.scss b/WebContent/VAADIN/themes/valo/shared/_global.scss index 049518af73..4ce294b06a 100644 --- a/WebContent/VAADIN/themes/valo/shared/_global.scss +++ b/WebContent/VAADIN/themes/valo/shared/_global.scss @@ -121,6 +121,12 @@ $valo-global-included: false !default; -webkit-overflow-scrolling: none; } + //fix for #14813 - unable to scroll on iOS devices + .v-webkit.v-ios .v-browserframe { + -webkit-overflow-scrolling: touch; + overflow:auto; + } + .v-assistive-device-only { position: absolute; top: -2000px; diff --git a/WebContent/release-notes.html b/WebContent/release-notes.html index 3fa008f3f3..8ba85cfac1 100644 --- a/WebContent/release-notes.html +++ b/WebContent/release-notes.html @@ -67,7 +67,7 @@ <h2 id="overview">Overview of Vaadin @version@ Release</h2> <p> - Vaadin @version@ is a minor release that includes a + Vaadin @version@ is a maintenance release that includes a number of new features and bug fixes, as listed in the <a href="#enhancements">list of enhancements</a> and <a href="#changelog">change log</a> below. @@ -87,7 +87,7 @@ <br/> <p> You can also view the <a - href="http://dev.vaadin.com/query?status=closed&resolution=fixed&milestone=Vaadin+@version@&order=id">list + href="http://dev.vaadin.com/query?status=pending-release&status=released&resolution=fixed&milestone=Vaadin+@version@&order=id">list of the closed issues</a> at the Vaadin developer's site. </p> diff --git a/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java b/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java index 497d8c0ff1..64ab86b84e 100644 --- a/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java +++ b/buildhelpers/src/com/vaadin/buildhelpers/FetchReleaseNotesTickets.java @@ -99,12 +99,9 @@ public class FetchReleaseNotesTickets { continue; } String summary = fields[1]; - if (summary.startsWith("\"") && summary.endsWith("\"")) { - // If a summary starts with " and ends with " then all quotes in - // the summary are encoded as double quotes - summary = summary.substring(1, summary.length() - 1); - summary = summary.replace("\"\"", "\""); - } + + summary = modifySummaryString(summary); + String badge = "<td></td>"; if (fields.length >= 8 && !fields[7].equals("")) { badge = "<td class=\"bfp\"><span class=\"bfp\">Priority</span></td>"; @@ -119,6 +116,52 @@ public class FetchReleaseNotesTickets { urlStream.close(); } + private static String modifySummaryString(String summary) { + + if (summary.startsWith("\"") && summary.endsWith("\"")) { + // If a summary starts with " and ends with " then all quotes in + // the summary are encoded as double quotes + summary = summary.substring(1, summary.length() - 1); + summary = summary.replace("\"\"", "\""); + } + + // this is needed for escaping html + summary = escapeHtml(summary); + + return summary; + } + + /** + * @since + * @param string + * the string to be html-escaped + * @return string in html-escape format + */ + private static String escapeHtml(String string) { + + StringBuffer buf = new StringBuffer(string.length() * 2); + + // we check the string character by character and escape only special + // characters + for (int i = 0; i < string.length(); ++i) { + + char ch = string.charAt(i); + String charString = ch + ""; + + if ((charString).matches("[a-zA-Z0-9., ]")) { + // character is letter, digit, dot, comma or whitespace + buf.append(ch); + } else { + int charInt = ch; + buf.append("&"); + buf.append("#"); + buf.append(charInt); + buf.append(";"); + } + } + return buf.toString(); + } + private static void usage() { System.err.println("Usage: " + FetchReleaseNotesTickets.class.getSimpleName() diff --git a/client/src/com/vaadin/client/ApplicationConfiguration.java b/client/src/com/vaadin/client/ApplicationConfiguration.java index 87c8ea465f..83dd90b6c2 100644 --- a/client/src/com/vaadin/client/ApplicationConfiguration.java +++ b/client/src/com/vaadin/client/ApplicationConfiguration.java @@ -547,7 +547,11 @@ public class ApplicationConfiguration implements EntryPoint { String getUnknownServerClassNameByTag(int tag) { if (unknownComponents != null) { - return unknownComponents.get(tag); + String className = unknownComponents.get(tag); + if (className == null) { + className = "unknown class with id " + tag; + } + return className; } return null; } diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 3e3ad033a7..cba9639067 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -141,6 +141,8 @@ public class ApplicationConnection implements HasHandlers { public static final String DISABLED_CLASSNAME = "v-disabled"; + public static final String REQUIRED_CLASSNAME = "v-required"; + public static final String REQUIRED_CLASSNAME_EXT = "-required"; public static final String ERROR_CLASSNAME_EXT = "-error"; @@ -1455,8 +1457,9 @@ public class ApplicationConnection implements HasHandlers { VConsole.log("Handling message from server"); eventBus.fireEvent(new ResponseHandlingStartedEvent(this)); + final int syncId; if (json.containsKey(ApplicationConstants.SERVER_SYNC_ID)) { - int syncId = json.getInt(ApplicationConstants.SERVER_SYNC_ID); + syncId = json.getInt(ApplicationConstants.SERVER_SYNC_ID); /* * Use sync id unless explicitly set as undefined, as is done by @@ -1469,6 +1472,7 @@ public class ApplicationConnection implements HasHandlers { lastSeenServerSyncId = syncId; } } else { + syncId = -1; VConsole.error("Server response didn't contain a sync id. " + "Please verify that the server is up-to-date and that the response data has not been modified in transmission."); } @@ -1539,6 +1543,8 @@ public class ApplicationConnection implements HasHandlers { Command c = new Command() { @Override public void execute() { + assert syncId == -1 || syncId == lastSeenServerSyncId; + handleUIDLDuration.logDuration(" * Loading widgets completed", 10); diff --git a/client/src/com/vaadin/client/ui/AbstractFieldConnector.java b/client/src/com/vaadin/client/ui/AbstractFieldConnector.java index a3c3779eb6..965e79b6fd 100644 --- a/client/src/com/vaadin/client/ui/AbstractFieldConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractFieldConnector.java @@ -57,5 +57,8 @@ public abstract class AbstractFieldConnector extends AbstractComponentConnector // add / remove error style name to Fields setWidgetStyleNameWithPrefix(getWidget().getStylePrimaryName(), ApplicationConnection.REQUIRED_CLASSNAME_EXT, isRequired()); + + getWidget().setStyleName(ApplicationConnection.REQUIRED_CLASSNAME, + isRequired()); } } diff --git a/client/src/com/vaadin/client/ui/VButton.java b/client/src/com/vaadin/client/ui/VButton.java index 2e5494ec18..dcc364c1da 100644 --- a/client/src/com/vaadin/client/ui/VButton.java +++ b/client/src/com/vaadin/client/ui/VButton.java @@ -95,6 +95,7 @@ public class VButton extends FocusWidget implements ClickHandler { private HandlerRegistration focusHandlerRegistration; private HandlerRegistration blurHandlerRegistration; + private long lastClickTime = 0; public VButton() { super(DOM.createDiv()); @@ -163,13 +164,29 @@ public class VButton extends FocusWidget implements ClickHandler { int type = DOM.eventGetType(event); switch (type) { case Event.ONCLICK: - // If clicks are currently disallowed, keep it from bubbling or - // being passed to the superclass. - if (disallowNextClick) { + // fix for #14632 - on mobile safari 8, if we press the button long + // enough, we might get two click events, so we are suppressing + // second if it is too soon + boolean isPhantomClickPossible = BrowserInfo.get().isSafari() + && BrowserInfo.get().isTouchDevice() + && BrowserInfo.get().getBrowserMajorVersion() == 8; + long clickTime = isPhantomClickPossible ? System + .currentTimeMillis() : 0; + // If clicks are currently disallowed or phantom, keep it from + // bubbling or being passed to the superclass. + if (disallowNextClick || isPhantomClickPossible + && (clickTime - lastClickTime < 100)) { // the maximum + // interval observed + // for phantom click + // is 69, with + // majority under + // 50, so we select + // 100 to be safe event.stopPropagation(); disallowNextClick = false; return; } + lastClickTime = clickTime; break; case Event.ONMOUSEDOWN: if (DOM.isOrHasChild(getElement(), DOM.eventGetTarget(event))) { diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java index 356e7291c4..bb217f2de2 100644 --- a/client/src/com/vaadin/client/ui/VFilterSelect.java +++ b/client/src/com/vaadin/client/ui/VFilterSelect.java @@ -446,16 +446,13 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, private void selectItem(final MenuItem newSelectedItem) { menu.selectItem(newSelectedItem); - String text = newSelectedItem != null ? newSelectedItem.getText() - : ""; - // Set the icon. FilterSelectSuggestion suggestion = (FilterSelectSuggestion) newSelectedItem .getCommand(); setSelectedItemIcon(suggestion.getIconUri()); // Set the text. - setText(text); + setText(suggestion.getReplacementString()); menu.updateKeyboardSelectedItem(); } @@ -1145,6 +1142,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, private class IconWidget extends Widget { IconWidget(Icon icon) { setElement(icon.getElement()); + addDomHandler(VFilterSelect.this, ClickEvent.getType()); } } diff --git a/client/src/com/vaadin/client/ui/VPopupView.java b/client/src/com/vaadin/client/ui/VPopupView.java index 1a59501d38..7d98110446 100644 --- a/client/src/com/vaadin/client/ui/VPopupView.java +++ b/client/src/com/vaadin/client/ui/VPopupView.java @@ -33,13 +33,7 @@ import com.google.gwt.event.logical.shared.CloseHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.Focusable; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.HasWidgets; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.PopupPanel; -import com.google.gwt.user.client.ui.RootPanel; -import com.google.gwt.user.client.ui.Widget; +import com.google.gwt.user.client.ui.*; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ComponentConnector; import com.vaadin.client.DeferredWorker; @@ -51,7 +45,7 @@ import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; import com.vaadin.client.ui.popupview.VisibilityChangeEvent; import com.vaadin.client.ui.popupview.VisibilityChangeHandler; -public class VPopupView extends HTML implements Iterable<Widget>, +public class VPopupView extends HTML implements HasEnabled, Iterable<Widget>, DeferredWorker { public static final String CLASSNAME = "v-popupview"; @@ -78,6 +72,7 @@ public class VPopupView extends HTML implements Iterable<Widget>, private final Label loading = new Label(); private boolean popupShowInProgress; + private boolean enabled = true; /** * loading constructor @@ -97,10 +92,12 @@ public class VPopupView extends HTML implements Iterable<Widget>, addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - preparePopup(popup); - showPopup(popup); - center(); - fireEvent(new VisibilityChangeEvent(true)); + if(isEnabled()) { + preparePopup(popup); + showPopup(popup); + center(); + fireEvent(new VisibilityChangeEvent(true)); + } } }); @@ -197,6 +194,25 @@ public class VPopupView extends HTML implements Iterable<Widget>, }-*/; /** + * Returns true if the popup is enabled, false if not. + */ + @Override + public boolean isEnabled() { + return enabled; + } + + /** + * Sets whether this popup is enabled. + * + * @param enabled <code>true</code> to enable the popup, <code>false</code> + * to disable it + */ + @Override + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + /** * This class is only public to enable overriding showPopup, and is * currently not intended to be extended or otherwise used directly. Its API * (other than it being a VOverlay) is to be considered private and diff --git a/client/src/com/vaadin/client/ui/VScrollTable.java b/client/src/com/vaadin/client/ui/VScrollTable.java index 859d600daf..42fef9f0c0 100644 --- a/client/src/com/vaadin/client/ui/VScrollTable.java +++ b/client/src/com/vaadin/client/ui/VScrollTable.java @@ -6989,8 +6989,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets, } } - Util.runWebkitOverflowAutoFixDeferred(scrollBodyPanel.getElement()); - forceRealignColumnHeaders(); } @@ -7119,15 +7117,6 @@ public class VScrollTable extends FlowPanel implements HasWidgets, if (initializedAndAttached) { updatePageLength(); } - if (!rendering) { - // Webkit may sometimes get an odd rendering bug (white space - // between header and body), see bug #3875. Running - // overflow hack here to shake body element a bit. - // We must run the fix as a deferred command to prevent it from - // overwriting the scroll position with an outdated value, see - // #7607. - Util.runWebkitOverflowAutoFixDeferred(scrollBodyPanel.getElement()); - } triggerLazyColumnAdjustment(false); @@ -7529,7 +7518,9 @@ public class VScrollTable extends FlowPanel implements HasWidgets, // Set new focused row focusedRow = row; - ensureRowIsVisible(row); + if (hasFocus) { + ensureRowIsVisible(row); + } return true; } diff --git a/client/src/com/vaadin/client/ui/VTabsheet.java b/client/src/com/vaadin/client/ui/VTabsheet.java index 745f2bca61..9c3af5c568 100644 --- a/client/src/com/vaadin/client/ui/VTabsheet.java +++ b/client/src/com/vaadin/client/ui/VTabsheet.java @@ -694,9 +694,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable, SubPartAware private final Element deco; - /** For internal use only. May be removed or replaced in the future. */ - public boolean waitingForResponse; - private String currentStyle; /** @@ -704,8 +701,7 @@ public class VTabsheet extends VTabsheetBase implements Focusable, SubPartAware */ private boolean canSelectTab(final int tabIndex) { Tab tab = tb.getTab(tabIndex); - if (getApplicationConnection() == null || disabled - || waitingForResponse) { + if (getApplicationConnection() == null || disabled) { return false; } if (!tab.isEnabledOnServer() || tab.isHiddenOnServer()) { @@ -739,8 +735,6 @@ public class VTabsheet extends VTabsheetBase implements Focusable, SubPartAware getRpcProxy().setSelected(tabKeys.get(tabIndex).toString()); - waitingForResponse = true; - tb.getTab(tabIndex).focus(); // move keyboard focus to active tab return true; diff --git a/client/src/com/vaadin/client/ui/VTree.java b/client/src/com/vaadin/client/ui/VTree.java index 02ad7cfb3e..82ffaced1f 100644 --- a/client/src/com/vaadin/client/ui/VTree.java +++ b/client/src/com/vaadin/client/ui/VTree.java @@ -162,10 +162,10 @@ public class VTree extends FocusElementPanel implements VHasDropHandler, /* * to fix #14388. The cause of defect #14388: event 'clickEvent' is sent to - * server before updating of "selected" variable, but should be send after + * server before updating of "selected" variable, but should be sent after * it */ - private boolean sendClickEventNow = false; + private boolean clickEventPending = false; /** For internal use only. May be removed or replaced in the future. */ public String[] bodyActionKeys; @@ -480,13 +480,13 @@ public class VTree extends FocusElementPanel implements VHasDropHandler, public void execute() { /* * we should send selection to server immediately in 2 cases: 1) - * 'immediate' property of Tree is true 2) sendClickEventNow is + * 'immediate' property of Tree is true 2) clickEventPending is * true */ client.updateVariable(paintableId, "selected", selectedIds.toArray(new String[selectedIds.size()]), - sendClickEventNow || immediate); - sendClickEventNow = false; + clickEventPending || immediate); + clickEventPending = false; selectionHasChanged = false; } }; @@ -844,41 +844,25 @@ public class VTree extends FocusElementPanel implements VHasDropHandler, // server. We do not want to send the event if there is a // selection event happening after this. In all other cases // we want to send it immediately. - sendClickEventNow = true; - - if (details.getButton() == MouseButton.LEFT && selectable) { - if (immediate) { - // event to be sent - sendClickEventNow = false; - - // The exception is that user clicked on the - // currently selected row and null selection is not - // allowed == no selection event - if (isSelected() && selectedIds.size() == 1 - && !isNullSelectionAllowed) { - sendClickEventNow = true; - } + clickEventPending = false; + if ((details.getButton() == MouseButton.LEFT || details + .getButton() == MouseButton.MIDDLE) + && !details.isDoubleClick() && selectable) { + // Probably a selection that will cause a value change + // event to be sent + clickEventPending = true; + + // The exception is that user clicked on the + // currently selected row and null selection is not + // allowed == no selection event + if (isSelected() && selectedIds.size() == 1 + && !isNullSelectionAllowed) { + clickEventPending = false; } - - client.updateVariable(paintableId, "clickedKey", key, - false); - - /* - * in any case event should not be send immediately here - * - send after updating of "selected" variable - */ - client.updateVariable(paintableId, "clickEvent", - details.toString(), false); - - } else { // for all another mouse buttons (RIGHT, MIDDLE) or - // if not selectable - client.updateVariable(paintableId, "clickedKey", key, - false); - - client.updateVariable(paintableId, "clickEvent", - details.toString(), sendClickEventNow); - sendClickEventNow = false; // reset it } + client.updateVariable(paintableId, "clickedKey", key, false); + client.updateVariable(paintableId, "clickEvent", + details.toString(), !clickEventPending); } }); } diff --git a/client/src/com/vaadin/client/ui/VTreeTable.java b/client/src/com/vaadin/client/ui/VTreeTable.java index 49d398246f..9b7e9702b2 100644 --- a/client/src/com/vaadin/client/ui/VTreeTable.java +++ b/client/src/com/vaadin/client/ui/VTreeTable.java @@ -26,7 +26,6 @@ import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.ImageElement; import com.google.gwt.dom.client.SpanElement; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.Unit; @@ -174,13 +173,10 @@ public class VTreeTable extends VScrollTable { .getFirstChild(); if (rowUidl.hasAttribute("icon")) { - // icons are in first content cell in TreeTable - ImageElement icon = Document.get().createImageElement(); - icon.setClassName("v-icon"); - icon.setAlt("icon"); - icon.setSrc(client.translateVaadinUri(rowUidl - .getStringAttribute("icon"))); - container.insertFirst(icon); + Icon icon = client.getIcon(rowUidl + .getStringAttribute("icon")); + icon.setAlternateText("icon"); + container.insertFirst(icon.getElement()); } String classname = "v-treetable-treespacer"; diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java index 58e7a83012..5c9a2ab47d 100644 --- a/client/src/com/vaadin/client/ui/VWindow.java +++ b/client/src/com/vaadin/client/ui/VWindow.java @@ -75,7 +75,7 @@ import com.vaadin.shared.ui.window.WindowRole; /** * "Sub window" component. - * + * * @author Vaadin Ltd */ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, @@ -233,7 +233,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /* * Stores the element that has focus in the application UI when the * window is opened, so it can be restored when the window closes. - * + * * This is currently implemented for the case when one non-modal window * can be open at the same time, and the focus is not changed while the * window is open. @@ -267,7 +267,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /* * Restores the previously stored focused element. - * + * * When the focus was changed outside the window while the window was * open, the originally stored element is restored. */ @@ -309,7 +309,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Returns true if this window is the topmost VWindow - * + * * @return */ private boolean isActive() { @@ -460,7 +460,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * is prevented. * <p> * This message is not visible on the screen. - * + * * @param topMessage * String provided when the user navigates with Shift-Tab keys to * the top of the window @@ -475,7 +475,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * key is prevented. * <p> * This message is not visible on the screen. - * + * * @param bottomMessage * String provided when the user navigates with the Tab key to * the bottom of the window @@ -488,7 +488,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * Gets the message that is provided to users of assistive devices when the * user reaches the top of the window when leaving a window with the tab key * is prevented. - * + * * @return the top message */ public String getTabStopTopAssistiveText() { @@ -499,7 +499,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * Gets the message that is provided to users of assistive devices when the * user reaches the bottom of the window when leaving a window with the tab * key is prevented. - * + * * @return the bottom message */ public String getTabStopBottomAssistiveText() { @@ -609,7 +609,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Sets the closable state of the window. Additionally hides/shows the close * button according to the new state. - * + * * @param closable * true if the window can be closed by the user */ @@ -631,7 +631,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * Returns the closable state of the sub window. If the sub window is * closable a decoration (typically an X) is shown to the user. By clicking * on the X the user can close the window. - * + * * @return true if the sub window is closable */ protected boolean isClosable() { @@ -663,7 +663,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * correctly if clicking on the "close" button in the window header but * closing the window from a button for example in the window will fail. * Symptom described in #10776 - * + * * The problematic part is that for the focus to be returned correctly * an input element needs to be focused in the root panel. Focusing some * other element apparently won't work. @@ -871,13 +871,8 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, setCaption(c, null); } - public void setCaption(String c, String icon) { + public void setCaption(String c, String iconURL) { String html = Util.escapeHTML(c); - if (icon != null) { - icon = client.translateVaadinUri(icon); - html = "<img src=\"" + Util.escapeAttribute(icon) - + "\" class=\"v-icon\" alt=\"\" />" + html; - } // Provide information to assistive device users that a sub window was // opened @@ -889,13 +884,18 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, + assistivePostfix + "</span>"; html = prefix + html + postfix; - DOM.setInnerHTML(headerText, html); + headerText.setInnerHTML(html); + + if (iconURL != null) { + Icon icon = client.getIcon(iconURL); + DOM.insertChild(headerText, icon.getElement(), 0); + } } /** * Setter for the text for assistive devices the window caption is prefixed * with. - * + * * @param assistivePrefix * the assistivePrefix to set */ @@ -906,7 +906,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Getter for the text for assistive devices the window caption is prefixed * with. - * + * * @return the assistivePrefix */ public String getAssistivePrefix() { @@ -916,7 +916,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Setter for the text for assistive devices the window caption is postfixed * with. - * + * * @param assistivePostfix * the assistivePostfix to set */ @@ -927,7 +927,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Getter for the text for assistive devices the window caption is postfixed * with. - * + * * @return the assistivePostfix */ public String getAssistivePostfix() { @@ -1079,14 +1079,14 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * TODO check if we need to support this with touch based devices. - * + * * Checks if the cursor was inside the browser content area when the event * happened. - * + * * @param event * The event to be checked * @return true, if the cursor is inside the browser content area - * + * * false, otherwise */ private boolean cursorInsideBrowserContentArea(Event event) { @@ -1372,7 +1372,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * assistive devices when it is opened. * <p> * When the provided array is empty, an existing description is removed. - * + * * @param connectors * with the connectors of the widgets to use as description */ @@ -1410,7 +1410,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * Gets the connectors that are used as assistive description. Text * contained in these connectors will be read by assistive devices when the * window is opened. - * + * * @return list of previously set connectors */ public List<Connector> getAssistiveDescription() { @@ -1419,14 +1419,14 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Sets the WAI-ARIA role the window. - * + * * This role defines how an assistive device handles a window. Available * roles are alertdialog and dialog (@see <a * href="http://www.w3.org/TR/2011/CR-wai-aria-20110118/roles">Roles * Model</a>). - * + * * The default role is dialog. - * + * * @param role * WAI-ARIA role to set for the window */ @@ -1445,7 +1445,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, * The value of the parameter doTabStop is stored and used for non-modal * windows. For modal windows, the handlers are always registered, while * preserving the stored value. - * + * * @param doTabStop * true to prevent leaving the window, false to allow leaving the * window for non modal windows @@ -1462,9 +1462,9 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, /** * Adds a Handler for when user moves the window. - * + * * @since 7.1.9 - * + * * @return {@link HandlerRegistration} used to remove the handler */ public HandlerRegistration addMoveHandler(WindowMoveHandler handler) { diff --git a/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java b/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java index 366775e9a2..e1234d436a 100644 --- a/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java @@ -183,8 +183,42 @@ public class AbsoluteLayoutConnector extends } private void setChildWidgetPosition(ComponentConnector child) { - getWidget().setWidgetPosition(child.getWidget(), - getState().connectorToCssPosition.get(child.getConnectorId())); + String position = getState().connectorToCssPosition.get(child + .getConnectorId()); + if (position == null) { + position = ""; + } + // make sure relative sizes get displayed correctly + String width = child.getState().width; + if (width != null && width.endsWith("%")) { + position = addDefaultPositionIfMissing(position, "left"); + position = addDefaultPositionIfMissing(position, "right"); + } + String height = child.getState().height; + if (height != null && height.endsWith("%")) { + position = addDefaultPositionIfMissing(position, "top"); + position = addDefaultPositionIfMissing(position, "bottom"); + } + getWidget().setWidgetPosition(child.getWidget(), position); + } + + /** + * Adds default value of 0.0px for the given property if it's missing from + * the position string altogether. If the property value is already set no + * changes are needed. + * + * @param position + * original position styles + * @param property + * the property that needs to have a value + * @return updated position, or the original string if no updates were + * needed + */ + private String addDefaultPositionIfMissing(String position, String property) { + if (!position.contains(property)) { + position = position + property + ":0.0px;"; + } + return position; } /* diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java index 56b35cce56..7fb4bc108a 100644 --- a/client/src/com/vaadin/client/ui/table/TableConnector.java +++ b/client/src/com/vaadin/client/ui/table/TableConnector.java @@ -198,19 +198,6 @@ public class TableConnector extends AbstractHasComponentsConnector implements uidl.getIntAttribute("rows")); if (getWidget().headerChangedDuringUpdate) { getWidget().triggerLazyColumnAdjustment(true); - } else if (!getWidget().isScrollPositionVisible() - || totalRowsHaveChanged - || getWidget().lastRenderedHeight != getWidget().scrollBody - .getOffsetHeight()) { - // webkits may still bug with their disturbing scrollbar - // bug, see #3457 - // Run overflow fix for the scrollable area - // #6698 - If there's a scroll going on, don't abort it - // by changing overflows as the length of the contents - // *shouldn't* have changed (unless the number of rows - // or the height of the widget has also changed) - Util.runWebkitOverflowAutoFixDeferred(getWidget().scrollBodyPanel - .getElement()); } } else { getWidget().initializeRows(uidl, rowData); diff --git a/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java b/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java index 8c6afd1c4f..94961a6a50 100644 --- a/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java +++ b/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java @@ -41,6 +41,7 @@ public class TabsheetConnector extends TabsheetBaseConnector implements final String key = getState().tabs.get(i).key; final boolean selected = key.equals(getState().selected); if (selected) { + getWidget().setActiveTabIndex(i); getWidget().selectTab(i); break; } @@ -92,8 +93,6 @@ public class TabsheetConnector extends TabsheetBaseConnector implements } getWidget().iLayout(); - - getWidget().waitingForResponse = false; } @Override diff --git a/client/src/com/vaadin/client/ui/window/WindowConnector.java b/client/src/com/vaadin/client/ui/window/WindowConnector.java index 4ee9f0c732..b3e3c9f70f 100644 --- a/client/src/com/vaadin/client/ui/window/WindowConnector.java +++ b/client/src/com/vaadin/client/ui/window/WindowConnector.java @@ -28,6 +28,7 @@ import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.dom.client.DoubleClickEvent; import com.google.gwt.event.dom.client.DoubleClickHandler; +import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Window; import com.vaadin.client.ApplicationConnection; @@ -291,37 +292,51 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector // the contents visible for the duration of a possible // 'out-animation') - // Fix for #14645 - as soon as we clone audio and video tags with - // autoplay attribute, they start playing immediately in background, - // so we have to clear them before cloning. And we can't just erase - // them, because there are corresponding player widgets to animate - Node content = getWidget().getElement().getFirstChild(); - toggleAutoPlay(content); - windowClone = content.cloneNode(true); - toggleAutoPlay(content); + // Fix for #14645 and #14785 - as soon as we clone audio and video + // tags, they start fetching data, and playing immediately in + // background, in case autoplay attribute is present. Therefore we + // have to replace them with stubs in the clone. And we can't just + // erase them, because there are corresponding player widgets to + // animate + windowClone = cloneNodeFilteringMedia(getWidget().getElement() + .getFirstChild()); } } - private void toggleAutoPlay(Node node) { + private Node cloneNodeFilteringMedia(Node node) { if (node instanceof Element) { - Element el = (Element) node; - if ("audio".equalsIgnoreCase(el.getTagName()) - || "video".equalsIgnoreCase(el.getTagName())) { - if (el.hasAttribute("autoplay")) { - el.removeAttribute("autoplay"); - el.setAttribute("_autoplay", ""); - } else if (el.hasAttribute("_autoplay")) { - el.removeAttribute("_autoplay"); - el.setAttribute("autoplay", ""); + Element old = (Element) node; + if ("audio".equalsIgnoreCase(old.getTagName()) + || "video".equalsIgnoreCase(old.getTagName())) { + if (!old.hasAttribute("controls") + && "audio".equalsIgnoreCase(old.getTagName())) { + return null; // nothing to animate, so we won't add this to + // the clone } + Element newEl = DOM.createElement(old.getTagName()); + if (old.hasAttribute("controls")) { + newEl.setAttribute("controls", old.getAttribute("controls")); + } + if (old.hasAttribute("style")) { + newEl.setAttribute("style", old.getAttribute("style")); + } + if (old.hasAttribute("class")) { + newEl.setAttribute("class", old.getAttribute("class")); + } + return newEl; } } + Node res = node.cloneNode(false); if (node.hasChildNodes()) { NodeList<Node> nl = node.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { - toggleAutoPlay(nl.getItem(i)); + Node clone = cloneNodeFilteringMedia(nl.getItem(i)); + if (clone != null) { + res.appendChild(clone); + } } } + return res; } @Override diff --git a/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java b/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java index dd892b19ec..6cd0630137 100644 --- a/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java +++ b/client/tests/src/com/vaadin/client/TestVBrowserDetailsUserAgentParser.java @@ -85,6 +85,7 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase { // assertBrowserMinorVersion(bd, 1); assertEngineVersion(bd, 536f); assertIOS(bd, 6, 1); + assertIPhone(bd); } public void testIPhoneIOS5() { @@ -95,6 +96,7 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase { assertBrowserMinorVersion(bd, 1); assertEngineVersion(bd, 534f); assertIOS(bd, 5, 1); + assertIPhone(bd); } public void testIPhoneIOS4() { @@ -105,6 +107,7 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase { assertBrowserMinorVersion(bd, 0); assertEngineVersion(bd, 532f); assertIOS(bd, 4, 0); + assertIPhone(bd); } public void testIPadIOS4() { @@ -115,6 +118,7 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase { assertBrowserMinorVersion(bd, 0); assertEngineVersion(bd, 533f); assertIOS(bd, 4, 3); + assertIPad(bd); } public void testAndroid21() { @@ -539,6 +543,16 @@ public class TestVBrowserDetailsUserAgentParser extends TestCase { assertOSMinorVersion(browserDetails, minorVersion); } + private void assertIPhone(VBrowserDetails browserDetails) { + assertTrue(browserDetails.isIPhone()); + assertFalse(browserDetails.isIPad()); + } + + private void assertIPad(VBrowserDetails browserDetails) { + assertFalse(browserDetails.isIPhone()); + assertTrue(browserDetails.isIPad()); + } + private void assertWindows(VBrowserDetails browserDetails) { assertWindows(browserDetails, false); } diff --git a/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java b/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java index b1bf58199a..9c2e4b2f83 100644 --- a/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java +++ b/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java @@ -36,10 +36,26 @@ import com.vaadin.ui.RichTextArea; import com.vaadin.ui.Table; import com.vaadin.ui.TextField; +/** + * This class contains a basic implementation for {@link FieldGroupFieldFactory} + * .The class is singleton, use {@link #get()} method to get reference to the + * instance. + * + * @author Vaadin Ltd + */ public class DefaultFieldGroupFieldFactory implements FieldGroupFieldFactory { + private static final DefaultFieldGroupFieldFactory INSTANCE = new DefaultFieldGroupFieldFactory(); + public static final Object CAPTION_PROPERTY_ID = "Caption"; + protected DefaultFieldGroupFieldFactory() { + } + + public static DefaultFieldGroupFieldFactory get() { + return INSTANCE; + } + @Override public <T extends Field> T createField(Class<?> type, Class<T> fieldType) { if (Enum.class.isAssignableFrom(type)) { diff --git a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java index e647bdbf6d..c5aab5a053 100644 --- a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java +++ b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java @@ -28,6 +28,7 @@ import com.vaadin.data.Item; import com.vaadin.data.Property; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.util.TransactionalPropertyWrapper; +import com.vaadin.ui.AbstractField; import com.vaadin.ui.DefaultFieldFactory; import com.vaadin.ui.Field; import com.vaadin.ui.Form; @@ -67,7 +68,8 @@ public class FieldGroup implements Serializable { /** * The field factory used by builder methods. */ - private FieldGroupFieldFactory fieldFactory = new DefaultFieldGroupFieldFactory(); + private FieldGroupFieldFactory fieldFactory = DefaultFieldGroupFieldFactory + .get(); /** * Constructs a field binder. Use {@link #setItemDataSource(Item)} to set a @@ -435,8 +437,14 @@ public class FieldGroup implements Serializable { return; } for (Field<?> f : fieldToPropertyId.keySet()) { - ((Property.Transactional<?>) f.getPropertyDataSource()) - .startTransaction(); + Property.Transactional<?> property = (Property.Transactional<?>) f + .getPropertyDataSource(); + if (property == null) { + throw new CommitException("Property \"" + + fieldToPropertyId.get(f) + + "\" not bound to datasource."); + } + property.startTransaction(); } try { firePreCommitEvent(); @@ -1095,4 +1103,18 @@ public class FieldGroup implements Serializable { } return memberFieldInOrder; } + + /** + * Clears the value of all fields. + * + * @since + */ + public void clear() { + for (Field<?> f : getFields()) { + if (f instanceof AbstractField) { + ((AbstractField) f).clear(); + } + } + + } } diff --git a/server/src/com/vaadin/data/util/BeanItem.java b/server/src/com/vaadin/data/util/BeanItem.java index ac3ef86434..12d9b23d0a 100644 --- a/server/src/com/vaadin/data/util/BeanItem.java +++ b/server/src/com/vaadin/data/util/BeanItem.java @@ -214,13 +214,75 @@ public class BeanItem<BT> extends PropertysetItem { } BeanInfo info = Introspector.getBeanInfo(beanClass); - propertyDescriptors.addAll(Arrays.asList(info - .getPropertyDescriptors())); + propertyDescriptors.addAll(getPropertyDescriptors(info)); return propertyDescriptors; } else { BeanInfo info = Introspector.getBeanInfo(beanClass); - return Arrays.asList(info.getPropertyDescriptors()); + return getPropertyDescriptors(info); + } + } + + // Workaround for Java6 bug JDK-6788525. Do nothing for JDK7+. + private static List<PropertyDescriptor> getPropertyDescriptors( + BeanInfo beanInfo) { + PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); + List<PropertyDescriptor> result = new ArrayList<PropertyDescriptor>( + descriptors.length); + for (PropertyDescriptor descriptor : descriptors) { + try { + Method readMethod = getMethodFromBridge(descriptor + .getReadMethod()); + if (readMethod != null) { + Method writeMethod = getMethodFromBridge( + descriptor.getWriteMethod(), + readMethod.getReturnType()); + if (writeMethod == null) { + writeMethod = descriptor.getWriteMethod(); + } + PropertyDescriptor descr = new PropertyDescriptor( + descriptor.getName(), readMethod, writeMethod); + result.add(descr); + } else { + result.add(descriptor); + } + } catch (SecurityException ignore) { + // handle next descriptor + } catch (IntrospectionException e) { + result.add(descriptor); + } + } + return result; + } + + /** + * Return not bridged method for bridge {@code bridgeMethod} method. If + * method {@code bridgeMethod} is not bridge method then return null. + */ + private static Method getMethodFromBridge(Method bridgeMethod) + throws SecurityException { + if (bridgeMethod == null) { + return null; + } + return getMethodFromBridge(bridgeMethod, + bridgeMethod.getParameterTypes()); + } + + /** + * Return not bridged method for bridge {@code bridgeMethod} method and + * declared {@code paramTypes}. If method {@code bridgeMethod} is not bridge + * method then return null. + */ + private static Method getMethodFromBridge(Method bridgeMethod, + Class<?>... paramTypes) throws SecurityException { + if (bridgeMethod == null || !bridgeMethod.isBridge()) { + return null; + } + try { + return bridgeMethod.getDeclaringClass().getMethod( + bridgeMethod.getName(), paramTypes); + } catch (NoSuchMethodException e) { + return null; } } diff --git a/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java b/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java index fdf858a528..26613c5d02 100644 --- a/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java +++ b/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java @@ -110,6 +110,8 @@ public class DefaultConverterFactory implements ConverterFactory { return new StringToBooleanConverter(); } else if (Date.class.isAssignableFrom(sourceType)) { return new StringToDateConverter(); + } else if (Enum.class.isAssignableFrom(sourceType)) { + return new StringToEnumConverter(); } else { return null; } diff --git a/server/src/com/vaadin/data/util/converter/StringToEnumConverter.java b/server/src/com/vaadin/data/util/converter/StringToEnumConverter.java new file mode 100644 index 0000000000..a1328d831c --- /dev/null +++ b/server/src/com/vaadin/data/util/converter/StringToEnumConverter.java @@ -0,0 +1,97 @@ +/* + * 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.data.util.converter; + +import java.util.EnumSet; +import java.util.Locale; + +/** + * A converter that converts from {@link String} to an {@link Enum} and back. + * <p> + * Designed to provide nice human readable strings for {@link Enum} classes + * where the constants are named SOME_UPPERCASE_WORDS. Will not necessarily work + * correctly for other cases. + * </p> + * + * @author Vaadin Ltd + * @since + */ +public class StringToEnumConverter implements Converter<String, Enum> { + + @Override + public Enum convertToModel(String value, Class<? extends Enum> targetType, + Locale locale) throws ConversionException { + if (value == null) { + return null; + } + if (locale == null) { + locale = Locale.getDefault(); + } + + // Foo -> FOO + // Foo bar -> FOO_BAR + String result = value.replace(" ", "_").toUpperCase(locale); + try { + return Enum.valueOf(targetType, result); + } catch (IllegalArgumentException ee) { + // There was no match. Try to compare the available values to see if + // the constant is using something else than all upper case + + EnumSet<?> set = EnumSet.allOf(targetType); + for (Enum e : set) { + if (e.name().toUpperCase(locale).equals(result)) { + return e; + } + } + + // Fallback did not work either, re-throw original exception so user + // knows what went wrong + throw new ConversionException(ee); + } + } + + @Override + public String convertToPresentation(Enum value, + Class<? extends String> targetType, Locale locale) + throws ConversionException { + if (value == null) { + return null; + } + if (locale == null) { + locale = Locale.getDefault(); + } + + String enumString = value.toString(); + // FOO -> Foo + // FOO_BAR -> Foo bar + // _FOO -> _foo + String result = enumString.substring(0, 1).toUpperCase(locale); + result += enumString.substring(1).toLowerCase(locale).replace('_', ' '); + + return result; + } + + @Override + public Class<Enum> getModelType() { + return Enum.class; + } + + @Override + public Class<String> getPresentationType() { + return String.class; + } + +} diff --git a/server/src/com/vaadin/server/AbstractDeploymentConfiguration.java b/server/src/com/vaadin/server/AbstractDeploymentConfiguration.java new file mode 100644 index 0000000000..43d4570d90 --- /dev/null +++ b/server/src/com/vaadin/server/AbstractDeploymentConfiguration.java @@ -0,0 +1,54 @@ +/* + * 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.server; + +/** + * An abstract base class for DeploymentConfiguration implementations. This + * class provides default implementation for common config properties. + * + * @author Vaadin Ltd + */ +public abstract class AbstractDeploymentConfiguration implements + DeploymentConfiguration { + + @Override + public String getUIClassName() { + return getApplicationOrSystemProperty(VaadinSession.UI_PARAMETER, null); + } + + @Override + public String getUIProviderClassName() { + return getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_UI_PROVIDER, null); + } + + @Override + public String getWidgetset(String defaultValue) { + return getApplicationOrSystemProperty(Constants.PARAMETER_WIDGETSET, + defaultValue); + } + + @Override + public String getResourcesPath() { + return getApplicationOrSystemProperty( + Constants.PARAMETER_VAADIN_RESOURCES, null); + } + + @Override + public String getClassLoaderName() { + return getApplicationOrSystemProperty("ClassLoader", null); + } +} diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index 0605d6a2b8..f0666f63fc 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -350,7 +350,6 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { /*- Add classnames; * .v-app * .v-app-loading - * .v-app-<simpleName for app class> *- Additionally added from javascript: * <themeName, remove non-alphanum> */ @@ -362,6 +361,8 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { mainDiv.attr("id", context.getAppId()); mainDiv.addClass("v-app"); mainDiv.addClass(context.getThemeName()); + mainDiv.addClass(context.getUIClass().getSimpleName() + .toLowerCase(Locale.ENGLISH)); if (style != null && style.length() != 0) { mainDiv.attr("style", style); } @@ -401,7 +402,7 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { builder.append("//<![CDATA[\n"); builder.append("if (!window.vaadin) alert(" + JsonUtil.quote("Failed to load the bootstrap javascript: " - + bootstrapLocation) + ");\n"); + + bootstrapLocation) + ");\n"); appendMainScriptTagContents(context, builder); @@ -501,7 +502,8 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler { if (systemMessages.getAuthenticationErrorURL() == null) { authErrMsg.put("url", Json.createNull()); } else { - authErrMsg.put("url", systemMessages.getAuthenticationErrorURL()); + authErrMsg.put("url", + systemMessages.getAuthenticationErrorURL()); } appConfig.put("authErrMsg", authErrMsg); diff --git a/server/src/com/vaadin/server/BrowserWindowOpener.java b/server/src/com/vaadin/server/BrowserWindowOpener.java index 44679fbfbb..8cc1faa728 100644 --- a/server/src/com/vaadin/server/BrowserWindowOpener.java +++ b/server/src/com/vaadin/server/BrowserWindowOpener.java @@ -126,6 +126,55 @@ public class BrowserWindowOpener extends AbstractExtension { } /** + * Sets the provided URL {@code url} for this instance. The {@code url} will + * be opened in a new browser window/tab when the extended component is + * clicked. + * + * @param url + * URL to open + */ + public void setUrl(String url) { + setResource(new ExternalResource(url)); + } + + /** + * Sets the provided {@code resource} for this instance. The + * {@code resource} will be opened in a new browser window/tab when the + * extended component is clicked. + * + * @param resource + * resource to open + */ + public void setResource(Resource resource) { + setResource(BrowserWindowOpenerState.locationResource, resource); + } + + /** + * Returns the resource for this instance. + * + * @return resource to open browser window + */ + public Resource getResource() { + return getResource(BrowserWindowOpenerState.locationResource); + } + + /** + * Returns the URL for this BrowserWindowOpener instance. Returns + * {@code null} if this instance is not URL resource based (a non URL based + * resource has been set for it). + * + * @return URL to open in the new browser window/tab when the extended + * component is clicked + */ + public String getUrl() { + Resource resource = getResource(); + if (resource instanceof ExternalResource) { + return ((ExternalResource) resource).getURL(); + } + return null; + } + + /** * Sets the target window name that will be used. If a window has already * been opened with the same name, the contents of that window will be * replaced instead of opening a new window. If the name is diff --git a/server/src/com/vaadin/server/Constants.java b/server/src/com/vaadin/server/Constants.java index 2b868c12a6..fc0bf7381a 100644 --- a/server/src/com/vaadin/server/Constants.java +++ b/server/src/com/vaadin/server/Constants.java @@ -162,4 +162,6 @@ public interface Constants { static final String PORTAL_PARAMETER_VAADIN_WIDGETSET = "vaadin.widgetset"; static final String PORTAL_PARAMETER_VAADIN_RESOURCE_PATH = "vaadin.resources.path"; static final String PORTAL_PARAMETER_VAADIN_THEME = "vaadin.theme"; + + static final String PORTLET_CONTEXT = "PORTLET_CONTEXT"; } diff --git a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java index fd14c3cd3f..22d5210eaa 100644 --- a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java +++ b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java @@ -29,7 +29,8 @@ import com.vaadin.shared.communication.PushMode; * @author Vaadin Ltd * @since 7.0.0 */ -public class DefaultDeploymentConfiguration implements DeploymentConfiguration { +public class DefaultDeploymentConfiguration extends + AbstractDeploymentConfiguration { /** * Default value for {@link #getResourceCacheTime()} = {@value} . */ diff --git a/server/src/com/vaadin/server/DefaultUIProvider.java b/server/src/com/vaadin/server/DefaultUIProvider.java index 2a1a59dbe6..38525fc020 100644 --- a/server/src/com/vaadin/server/DefaultUIProvider.java +++ b/server/src/com/vaadin/server/DefaultUIProvider.java @@ -24,15 +24,9 @@ public class DefaultUIProvider extends UIProvider { public Class<? extends UI> getUIClass(UIClassSelectionEvent event) { VaadinRequest request = event.getRequest(); - Object uiClassNameObj = request - .getService() - .getDeploymentConfiguration() - .getApplicationOrSystemProperty(VaadinSession.UI_PARAMETER, - null); - - if (uiClassNameObj instanceof String) { - String uiClassName = uiClassNameObj.toString(); - + String uiClassName = request.getService().getDeploymentConfiguration() + .getUIClassName(); + if (uiClassName != null) { ClassLoader classLoader = request.getService().getClassLoader(); try { Class<? extends UI> uiClass = Class.forName(uiClassName, true, diff --git a/server/src/com/vaadin/server/DeploymentConfiguration.java b/server/src/com/vaadin/server/DeploymentConfiguration.java index fcfeecc31f..3124729773 100644 --- a/server/src/com/vaadin/server/DeploymentConfiguration.java +++ b/server/src/com/vaadin/server/DeploymentConfiguration.java @@ -163,6 +163,38 @@ public interface DeploymentConfiguration extends Serializable { String defaultValue); /** + * Gets UI class configuration option value. + * + * @return UI class name + */ + public String getUIClassName(); + + /** + * Gets UI provider class configuration option value. + * + * @return UI class name + */ + public String getUIProviderClassName(); + + /** + * Gets Widgetset configuration option value. {@code defaultValue} is + * returned if widgetset parameter is not configured. + * + * @return UI class name + */ + public String getWidgetset(String defaultValue); + + /** + * Gets resources path configuration option value. + */ + public String getResourcesPath(); + + /** + * Gets class loader configuration option value. + */ + public String getClassLoaderName(); + + /** * Returns to legacy Property.toString() mode used. See * {@link AbstractProperty#isLegacyToStringEnabled()} for more information. * diff --git a/server/src/com/vaadin/server/DragAndDropService.java b/server/src/com/vaadin/server/DragAndDropService.java index c21f27de97..c0c3eebca3 100644 --- a/server/src/com/vaadin/server/DragAndDropService.java +++ b/server/src/com/vaadin/server/DragAndDropService.java @@ -38,6 +38,7 @@ import com.vaadin.shared.communication.SharedState; import com.vaadin.shared.ui.dd.DragEventType; import com.vaadin.ui.Component; import com.vaadin.ui.UI; + import elemental.json.JsonObject; public class DragAndDropService implements VariableOwner, ClientConnector { @@ -64,7 +65,7 @@ public class DragAndDropService implements VariableOwner, ClientConnector { final Component sourceComponent = (Component) variables .get("component"); - if (sourceComponent != null && !sourceComponent.isEnabled()) { + if (sourceComponent != null && !sourceComponent.isConnectorEnabled()) { // source component not supposed to be enabled getLogger().warning( "Client dropped from " + sourceComponent @@ -83,7 +84,7 @@ public class DragAndDropService implements VariableOwner, ClientConnector { DropTarget dropTarget = (DropTarget) owner; - if (!dropTarget.isEnabled()) { + if (!dropTarget.isConnectorEnabled()) { getLogger() .warning( "Client dropped on " + owner diff --git a/server/src/com/vaadin/server/Page.java b/server/src/com/vaadin/server/Page.java index 3acea97c0f..3ddf4862b2 100644 --- a/server/src/com/vaadin/server/Page.java +++ b/server/src/com/vaadin/server/Page.java @@ -485,14 +485,14 @@ public class Page implements Serializable { } private void addListener(Class<?> eventType, Object target, Method method) { - if (eventRouter == null) { + if (!hasEventRouter()) { eventRouter = new EventRouter(); } eventRouter.addListener(eventType, target, method); } private void removeListener(Class<?> eventType, Object target, Method method) { - if (eventRouter != null) { + if (hasEventRouter()) { eventRouter.removeListener(eventType, target, method); } } @@ -599,7 +599,7 @@ public class Page implements Serializable { } private void fireEvent(EventObject event) { - if (eventRouter != null) { + if (hasEventRouter()) { eventRouter.fireEvent(event); } } @@ -776,8 +776,8 @@ public class Page implements Serializable { BrowserWindowResizeListener resizeListener) { removeListener(BrowserWindowResizeEvent.class, resizeListener, BROWSER_RESIZE_METHOD); - getState(true).hasResizeListeners = eventRouter - .hasListeners(BrowserWindowResizeEvent.class); + getState(true).hasResizeListeners = hasEventRouter() + && eventRouter.hasListeners(BrowserWindowResizeEvent.class); } /** @@ -1242,4 +1242,7 @@ public class Page implements Serializable { return state; } + private boolean hasEventRouter() { + return eventRouter != null; + } } diff --git a/server/src/com/vaadin/server/ServletPortletHelper.java b/server/src/com/vaadin/server/ServletPortletHelper.java index 2ec747ba3a..197d9fe416 100644 --- a/server/src/com/vaadin/server/ServletPortletHelper.java +++ b/server/src/com/vaadin/server/ServletPortletHelper.java @@ -130,8 +130,7 @@ public class ServletPortletHelper implements Serializable { public static void initDefaultUIProvider(VaadinSession session, VaadinService vaadinService) throws ServiceException { String uiProperty = vaadinService.getDeploymentConfiguration() - .getApplicationOrSystemProperty(VaadinSession.UI_PARAMETER, - null); + .getUIClassName(); // Add provider for UI parameter first to give it lower priority // (providers are FILO) @@ -141,8 +140,7 @@ public class ServletPortletHelper implements Serializable { } String uiProviderProperty = vaadinService.getDeploymentConfiguration() - .getApplicationOrSystemProperty( - Constants.SERVLET_PARAMETER_UI_PROVIDER, null); + .getUIProviderClassName(); // Then add custom UI provider if defined if (uiProviderProperty != null) { UIProvider uiProvider = getUIProvider(uiProviderProperty, diff --git a/server/src/com/vaadin/server/VaadinPortletService.java b/server/src/com/vaadin/server/VaadinPortletService.java index 2b290b4cc4..cff024672c 100644 --- a/server/src/com/vaadin/server/VaadinPortletService.java +++ b/server/src/com/vaadin/server/VaadinPortletService.java @@ -124,9 +124,7 @@ public class VaadinPortletService extends VaadinService { @Override public String getConfiguredWidgetset(VaadinRequest request) { - String widgetset = getDeploymentConfiguration() - .getApplicationOrSystemProperty( - VaadinPortlet.PARAMETER_WIDGETSET, null); + String widgetset = getDeploymentConfiguration().getWidgetset(null); if (widgetset == null) { widgetset = getParameter(request, @@ -162,7 +160,11 @@ public class VaadinPortletService extends VaadinService { String staticFileLocation = getParameter(request, Constants.PORTAL_PARAMETER_VAADIN_RESOURCE_PATH, "/html"); - return trimTrailingSlashes(staticFileLocation); + if (Constants.PORTLET_CONTEXT.equals(staticFileLocation)) { + return request.getContextPath(); + } else{ + return trimTrailingSlashes(staticFileLocation); + } } private PortletContext getPortletContext() { diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java index 8fd6da8dee..4d8e7e9bc9 100644 --- a/server/src/com/vaadin/server/VaadinService.java +++ b/server/src/com/vaadin/server/VaadinService.java @@ -155,7 +155,7 @@ public abstract class VaadinService implements Serializable { this.deploymentConfiguration = deploymentConfiguration; final String classLoaderName = getDeploymentConfiguration() - .getApplicationOrSystemProperty("ClassLoader", null); + .getClassLoaderName(); if (classLoaderName != null) { try { final Class<?> classLoaderClass = getClass().getClassLoader() @@ -448,7 +448,7 @@ public abstract class VaadinService implements Serializable { */ public void fireSessionDestroy(VaadinSession vaadinSession) { final VaadinSession session = vaadinSession; - session.accessSynchronously(new Runnable() { + session.access(new Runnable() { @Override public void run() { if (session.getState() == State.CLOSED) { diff --git a/server/src/com/vaadin/server/VaadinServletService.java b/server/src/com/vaadin/server/VaadinServletService.java index a4ff3943c9..8946ac4fae 100644 --- a/server/src/com/vaadin/server/VaadinServletService.java +++ b/server/src/com/vaadin/server/VaadinServletService.java @@ -118,9 +118,7 @@ public class VaadinServletService extends VaadinService { VaadinServletRequest servletRequest = (VaadinServletRequest) request; String staticFileLocation; // if property is defined in configurations, use that - staticFileLocation = getDeploymentConfiguration() - .getApplicationOrSystemProperty( - VaadinServlet.PARAMETER_VAADIN_RESOURCES, null); + staticFileLocation = getDeploymentConfiguration().getResourcesPath(); if (staticFileLocation != null) { return staticFileLocation; } @@ -159,8 +157,7 @@ public class VaadinServletService extends VaadinService { @Override public String getConfiguredWidgetset(VaadinRequest request) { - return getDeploymentConfiguration().getApplicationOrSystemProperty( - VaadinServlet.PARAMETER_WIDGETSET, + return getDeploymentConfiguration().getWidgetset( VaadinServlet.DEFAULT_WIDGETSET); } diff --git a/server/src/com/vaadin/server/VaadinSession.java b/server/src/com/vaadin/server/VaadinSession.java index f93cb8e070..e5d63a6961 100644 --- a/server/src/com/vaadin/server/VaadinSession.java +++ b/server/src/com/vaadin/server/VaadinSession.java @@ -721,6 +721,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * {@link InheritableThreadLocal}). In other cases, (e.g. from background * threads started in some other way), the current session is not * automatically defined. + * <p> + * The session is stored using a weak reference to avoid leaking memory in + * case it is not explicitly cleared. * * @return the current session instance if available, otherwise * <code>null</code> @@ -741,9 +744,12 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * The application developer can also use this method to define the current * session outside the normal request handling and treads started from * request handling threads, e.g. when initiating custom background threads. - * </p> + * <p> + * The session is stored using a weak reference to avoid leaking memory in + * case it is not explicitly cleared. * * @param session + * the session to set as current * * @see #getCurrent() * @see ThreadLocal diff --git a/server/src/com/vaadin/server/WebBrowser.java b/server/src/com/vaadin/server/WebBrowser.java index 5ec4e6b19c..cb5979d612 100644 --- a/server/src/com/vaadin/server/WebBrowser.java +++ b/server/src/com/vaadin/server/WebBrowser.java @@ -264,6 +264,7 @@ public class WebBrowser implements Serializable { * @return true if the user is using Windows Phone, false if the user is not * using Windows Phone or if no information on the browser is * present + * @since 7.3.2 */ public boolean isWindowsPhone() { return browserDetails.isWindowsPhone(); @@ -290,6 +291,26 @@ public class WebBrowser implements Serializable { } /** + * Tests if the browser is run on IPhone. + * + * @return true if run on IPhone false if the user is not using IPhone or if + * no information on the browser is present + */ + public boolean isIPhone() { + return browserDetails.isIPhone(); + } + + /** + * Tests if the browser is run on IPad. + * + * @return true if run on IPad false if the user is not using IPad or if no + * information on the browser is present + */ + public boolean isIPad() { + return browserDetails.isIPad(); + } + + /** * Returns the browser-reported TimeZone offset in milliseconds from GMT. * This includes possible daylight saving adjustments, to figure out which * TimeZone the user actually might be in, see diff --git a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java index f8ef360eda..a274fbbb9b 100644 --- a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java +++ b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java @@ -269,6 +269,7 @@ public class AtmospherePushConnection implements PushConnection { // the resource is already resumed; this is a bit hacky and should // be implemented in a better way in 7.2. resource = null; + state = State.DISCONNECTED; return; } diff --git a/server/src/com/vaadin/ui/AbstractField.java b/server/src/com/vaadin/ui/AbstractField.java index 47ac953319..369ad1253c 100644 --- a/server/src/com/vaadin/ui/AbstractField.java +++ b/server/src/com/vaadin/ui/AbstractField.java @@ -1402,7 +1402,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements valueLocale); T newinternalValue = convertFromModel(convertedValue); if (!SharedUtil.equals(getInternalValue(), newinternalValue)) { - setConvertedValue(convertedValue); + setInternalValue(newinternalValue); + fireValueChange(false); } } } @@ -1512,6 +1513,19 @@ public abstract class AbstractField<T> extends AbstractComponent implements } /** + * Clear the value of the field. + * <p> + * The field value is typically reset to the initial value of the field but + * this is not mandatory. Calling {@link #isEmpty()} on a cleared field must + * always returns true. + * + * @since + */ + public void clear() { + setValue(null); + } + + /** * Is automatic, visible validation enabled? * * If automatic validation is enabled, any validators connected to this diff --git a/server/src/com/vaadin/ui/Button.java b/server/src/com/vaadin/ui/Button.java index 76b82aa034..e58ad7bee5 100644 --- a/server/src/com/vaadin/ui/Button.java +++ b/server/src/com/vaadin/ui/Button.java @@ -359,7 +359,7 @@ public class Button extends AbstractComponent implements * No action is taken is the button is disabled. */ public void click() { - if (isEnabled() && !isReadOnly()) { + if (isConnectorEnabled() && !isReadOnly()) { fireClick(); } } diff --git a/server/src/com/vaadin/ui/Calendar.java b/server/src/com/vaadin/ui/Calendar.java index 59dfceec9b..63ac9fe35c 100644 --- a/server/src/com/vaadin/ui/Calendar.java +++ b/server/src/com/vaadin/ui/Calendar.java @@ -890,17 +890,21 @@ public class Calendar extends AbstractComponent implements * @see #isEventClickAllowed() */ protected boolean isClientChangeAllowed() { - return !isReadOnly() && isEnabled(); + return !isReadOnly(); } /** - * Is the user allowed to trigger click events + * Is the user allowed to trigger click events. Returns {@code true} by + * default. Subclass can override this method to disallow firing event + * clicks got from the client side. * * @return true if the client is allowed to click events * @see #isClientChangeAllowed() + * @deprecated Override {@link #fireEventClick(Integer)} instead. */ + @Deprecated protected boolean isEventClickAllowed() { - return isEnabled(); + return true; } /** diff --git a/server/src/com/vaadin/ui/CustomLayout.java b/server/src/com/vaadin/ui/CustomLayout.java index 7f1aa1ce46..f4fe7fa66c 100644 --- a/server/src/com/vaadin/ui/CustomLayout.java +++ b/server/src/com/vaadin/ui/CustomLayout.java @@ -16,6 +16,7 @@ package com.vaadin.ui; +import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -101,22 +102,20 @@ public class CustomLayout extends AbstractLayout implements LegacyComponent { protected void initTemplateContentsFromInputStream( InputStream templateStream) throws IOException { - InputStreamReader reader = new InputStreamReader(templateStream, - "UTF-8"); - StringBuilder b = new StringBuilder(BUFFER_SIZE); - - char[] cbuf = new char[BUFFER_SIZE]; - int offset = 0; - - while (true) { - int nrRead = reader.read(cbuf, offset, BUFFER_SIZE); - b.append(cbuf, 0, nrRead); - if (nrRead < BUFFER_SIZE) { - break; + BufferedReader reader = new BufferedReader(new InputStreamReader( + templateStream, "UTF-8")); + StringBuilder builder = new StringBuilder(BUFFER_SIZE); + try { + char[] cbuf = new char[BUFFER_SIZE]; + int nRead; + while ((nRead = reader.read(cbuf, 0, BUFFER_SIZE)) > 0) { + builder.append(cbuf, 0, nRead); } + } finally { + reader.close(); } - setTemplateContents(b.toString()); + setTemplateContents(builder.toString()); } @Override diff --git a/server/src/com/vaadin/ui/DateField.java b/server/src/com/vaadin/ui/DateField.java index e88d767bc9..030bd5f6c2 100644 --- a/server/src/com/vaadin/ui/DateField.java +++ b/server/src/com/vaadin/ui/DateField.java @@ -815,17 +815,15 @@ public class DateField extends AbstractField<Date> implements // Clone the instance final Calendar newCal = (Calendar) calendar.clone(); - // Assigns the current time tom calendar. - final Date currentDate = getValue(); - if (currentDate != null) { - newCal.setTime(currentDate); - } - final TimeZone currentTimeZone = getTimeZone(); if (currentTimeZone != null) { newCal.setTimeZone(currentTimeZone); } + final Date currentDate = getValue(); + if (currentDate != null) { + newCal.setTime(currentDate); + } return newCal; } diff --git a/server/src/com/vaadin/ui/Form.java b/server/src/com/vaadin/ui/Form.java index 391ee45536..48239b09e3 100644 --- a/server/src/com/vaadin/ui/Form.java +++ b/server/src/com/vaadin/ui/Form.java @@ -1064,7 +1064,7 @@ public class Form extends AbstractField<Object> implements Item.Editor, for (Object id : itemPropertyIds) { if (id != null) { Field<?> field = getField(id); - if (field.isEnabled() && !field.isReadOnly()) { + if (field.isConnectorEnabled() && !field.isReadOnly()) { return field; } } @@ -1202,6 +1202,21 @@ public class Form extends AbstractField<Object> implements Item.Editor, return true; } + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.AbstractField#clear() + */ + @Override + public void clear() { + for (Iterator<Field<?>> i = fields.values().iterator(); i.hasNext();) { + Field<?> f = i.next(); + if (f instanceof AbstractField) { + ((AbstractField<?>) f).clear(); + } + } + } + /** * Adding validators directly to form is not supported. * diff --git a/server/src/com/vaadin/ui/Table.java b/server/src/com/vaadin/ui/Table.java index 3a1ab82be5..34d48a9b18 100644 --- a/server/src/com/vaadin/ui/Table.java +++ b/server/src/com/vaadin/ui/Table.java @@ -422,61 +422,11 @@ public class Table extends AbstractSelect implements Action.Container, private Object currentPageFirstItemId = null; /* - * This class stores the hashcode and scroll position of the previous - * container so that the scroll position can be restored if the same - * container is removed and then re-added. This resolves #14581. + * If all rows get removed then scroll position of the previous container + * can be restored after re-adding/replacing rows via addAll(). This + * resolves #14581. */ - protected static class ScrollPositionRepairOnReAddAllRowsData implements - Serializable { - - private static final long serialVersionUID = 1L; - // current page first item index (to repair scroll position) - private int itemIndex; - /* - * hashCode() of container before it was cleared via - * container.removeAllItems(); - */ - private int containerHashCode; - - public ScrollPositionRepairOnReAddAllRowsData() { - itemIndex = -1; - containerHashCode = -1; - } - - public int getItemId() { - return itemIndex; - } - - public void setItemId(int itemId) { - itemIndex = itemId; - } - - public void resetItemId() { - itemIndex = -1; - } - - public void setContainerData(Container container) { - if (container != null) { - containerHashCode = container.hashCode(); - } else { - containerHashCode = -1; - } - } - - public boolean needRepairScrollPosition(Container newContainer) { - return (itemIndex != -1) && isTheSameContainer(newContainer); - } - - private boolean isTheSameContainer(Container newContainer) { - boolean theSame = false; - if (newContainer != null) { - theSame = (newContainer.hashCode() == containerHashCode); - } - return theSame; - } - } - - private final ScrollPositionRepairOnReAddAllRowsData scrollRepairOnReAdding = new ScrollPositionRepairOnReAddAllRowsData(); + private int repairOnReAddAllRowsDataScrollPositionItemIndex = -1; /** * Index of the first item on the current page. @@ -2734,10 +2684,6 @@ public class Table extends AbstractSelect implements Action.Container, newDataSource = new IndexedContainer(); } - if (scrollRepairOnReAdding != null) { - // this is important if container is set for table after filling - scrollRepairOnReAdding.setContainerData(newDataSource); - } Collection<Object> generated; if (columnGenerators != null) { generated = columnGenerators.keySet(); @@ -4571,14 +4517,22 @@ public class Table extends AbstractSelect implements Action.Container, int currentFirstItemIndex = getCurrentPageFirstItemIndex(); if (event.getContainer().size() == 0) { - scrollRepairOnReAdding.setItemId(getCurrentPageFirstItemIndex()); + repairOnReAddAllRowsDataScrollPositionItemIndex = getCurrentPageFirstItemIndex(); } else { - if (scrollRepairOnReAdding.needRepairScrollPosition(event - .getContainer())) { - currentFirstItemIndex = scrollRepairOnReAdding.getItemId(); + if (repairOnReAddAllRowsDataScrollPositionItemIndex != -1) { + currentFirstItemIndex = repairOnReAddAllRowsDataScrollPositionItemIndex; + /* + * Reset repairOnReAddAllRowsDataScrollPositionItemIndex. + * + * Next string should be commented (removed) if we want to have + * possibility to restore scroll position during adding items to + * container one by one via add() but not only addAll(). The + * problem in this case: we cannot track what happened between + * add() and add()... So it is ambiguous where to stop restore + * scroll position. + */ + repairOnReAddAllRowsDataScrollPositionItemIndex = -1; } - scrollRepairOnReAdding.resetItemId(); - scrollRepairOnReAdding.setContainerData(event.getContainer()); } // ensure that page still has first item in page, ignore buffer refresh diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index d67e08828a..438b086ec2 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -719,9 +719,11 @@ public abstract class UI extends AbstractSingleComponentContainer implements * The application developer can also use this method to define the current * UI outside the normal request handling, e.g. when initiating custom * background threads. - * </p> + * <p> + * The UI is stored using a weak reference to avoid leaking memory in case + * it is not explicitly cleared. * - * @param uI + * @param ui * the UI to register as the current UI * * @see #getCurrent() @@ -735,6 +737,9 @@ public abstract class UI extends AbstractSingleComponentContainer implements * Gets the currently used UI. The current UI is automatically defined when * processing requests to the server. In other cases, (e.g. from background * threads), the current UI is not automatically defined. + * <p> + * The UI is stored using a weak reference to avoid leaking memory in case + * it is not explicitly cleared. * * @return the current UI instance if available, otherwise <code>null</code> * diff --git a/server/src/com/vaadin/ui/Upload.java b/server/src/com/vaadin/ui/Upload.java index 1c953779e4..693bd74dbf 100644 --- a/server/src/com/vaadin/ui/Upload.java +++ b/server/src/com/vaadin/ui/Upload.java @@ -1150,23 +1150,25 @@ public class Upload extends AbstractComponent implements Component.Focusable, fireUploadSuccess(event.getFileName(), event.getMimeType(), event.getContentLength()); endUpload(); - markAsDirty(); } @Override public void streamingFailed(StreamingErrorEvent event) { - Exception exception = event.getException(); - if (exception instanceof NoInputStreamException) { - fireNoInputStream(event.getFileName(), - event.getMimeType(), 0); - } else if (exception instanceof NoOutputStreamException) { - fireNoOutputStream(event.getFileName(), - event.getMimeType(), 0); - } else { - fireUploadInterrupted(event.getFileName(), - event.getMimeType(), 0, exception); + try { + Exception exception = event.getException(); + if (exception instanceof NoInputStreamException) { + fireNoInputStream(event.getFileName(), + event.getMimeType(), 0); + } else if (exception instanceof NoOutputStreamException) { + fireNoOutputStream(event.getFileName(), + event.getMimeType(), 0); + } else { + fireUploadInterrupted(event.getFileName(), + event.getMimeType(), 0, exception); + } + } finally { + endUpload(); } - endUpload(); } }; } diff --git a/server/tests/src/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java b/server/tests/src/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java index a5fee5175e..bbf74bfb21 100644 --- a/server/tests/src/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java +++ b/server/tests/src/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java @@ -15,6 +15,7 @@ */ package com.vaadin.data; +import java.lang.reflect.Constructor; import java.util.Date; import org.junit.Assert; @@ -34,7 +35,30 @@ public class DefaultFieldGroupFieldFactoryTest { @Before public void setupFieldFactory() { - fieldFactory = new DefaultFieldGroupFieldFactory(); + fieldFactory = DefaultFieldGroupFieldFactory.get(); + } + + @Test + public void noPublicConstructor() { + Class<DefaultFieldGroupFieldFactory> clazz = DefaultFieldGroupFieldFactory.class; + Constructor<?>[] constructors = clazz.getConstructors(); + Assert.assertEquals( + "DefaultFieldGroupFieldFactory contains public constructors", + 0, constructors.length); + } + + @Test + public void testSameInstance() { + DefaultFieldGroupFieldFactory factory1 = DefaultFieldGroupFieldFactory + .get(); + DefaultFieldGroupFieldFactory factory2 = DefaultFieldGroupFieldFactory + .get(); + Assert.assertTrue( + "DefaultFieldGroupFieldFactory.get() method returns different instances", + factory1 == factory2); + Assert.assertNotNull( + "DefaultFieldGroupFieldFactory.get() method returns null", + factory1); } @Test diff --git a/server/tests/src/com/vaadin/data/fieldgroup/FieldGroupDate.java b/server/tests/src/com/vaadin/data/fieldgroup/FieldGroupDate.java index 07e36b93f9..fd5d47b32f 100644 --- a/server/tests/src/com/vaadin/data/fieldgroup/FieldGroupDate.java +++ b/server/tests/src/com/vaadin/data/fieldgroup/FieldGroupDate.java @@ -78,4 +78,20 @@ public class FieldGroupDate { Assert.assertEquals(PopupDateField.class, f.getClass()); } + @Test + public void clearFields() { + PopupDateField sqlDate = new PopupDateField(); + PopupDateField javaDate = new PopupDateField(); + fieldGroup.bind(sqlDate, "sqlDate"); + fieldGroup.bind(javaDate, "javaDate"); + + Assert.assertEquals(new Date(2010, 5, 7), javaDate.getValue()); + Assert.assertEquals(new Date(2011, 6, 8), sqlDate.getValue()); + + fieldGroup.clear(); + Assert.assertEquals(null, javaDate.getValue()); + Assert.assertEquals(null, sqlDate.getValue()); + + } + } diff --git a/server/tests/src/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java b/server/tests/src/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java new file mode 100644 index 0000000000..636162de54 --- /dev/null +++ b/server/tests/src/com/vaadin/data/fieldgroup/FieldGroupExceptionTest.java @@ -0,0 +1,33 @@ +/* + * 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.data.fieldgroup; + +import org.junit.Test; + +import com.vaadin.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.ui.PopupDateField; + +public class FieldGroupExceptionTest { + + @Test(expected = CommitException.class) + public void testUnboundCommitException() throws CommitException { + FieldGroup fieldGroup = new FieldGroup(); + PopupDateField dateField = new PopupDateField(); + fieldGroup.bind(dateField, "date"); + fieldGroup.commit(); + } + +} diff --git a/server/tests/src/com/vaadin/data/util/BeanItemContainerGenerator.java b/server/tests/src/com/vaadin/data/util/BeanItemContainerGenerator.java new file mode 100644 index 0000000000..7400f0efcf --- /dev/null +++ b/server/tests/src/com/vaadin/data/util/BeanItemContainerGenerator.java @@ -0,0 +1,153 @@ +package com.vaadin.data.util; + +import java.util.Date; +import java.util.concurrent.atomic.AtomicLong; + +import org.junit.Ignore; + +@Ignore +public class BeanItemContainerGenerator { + + public static class PortableRandom { + private final static long multiplier = 0x5DEECE66DL; + private final static long addend = 0xBL; + private final static long mask = (1L << 48) - 1; + private AtomicLong seed; + + public PortableRandom(long seed) { + this.seed = new AtomicLong(0L); + setSeed(seed); + } + + synchronized public void setSeed(long seed) { + seed = (seed ^ multiplier) & mask; + this.seed.set(seed); + } + + public int nextInt(int n) { + if (n <= 0) { + throw new IllegalArgumentException("n must be positive"); + } + + if ((n & -n) == n) { + return (int) ((n * (long) next(31)) >> 31); + } + + int bits, val; + do { + bits = next(31); + val = bits % n; + } while (bits - val + (n - 1) < 0); + return val; + } + + protected int next(int bits) { + long oldseed, nextseed; + AtomicLong seed = this.seed; + do { + oldseed = seed.get(); + nextseed = (oldseed * multiplier + addend) & mask; + } while (!seed.compareAndSet(oldseed, nextseed)); + return (int) (nextseed >>> (48 - bits)); + } + + public boolean nextBoolean() { + return next(1) != 0; + } + + } + + public static BeanItemContainer<TestBean> createContainer(int size) { + return createContainer(size, new Date().getTime()); + } + + public static BeanItemContainer<TestBean> createContainer(int size, + long seed) { + + BeanItemContainer<TestBean> container = new BeanItemContainer<TestBean>( + TestBean.class); + PortableRandom r = new PortableRandom(seed); + for (int i = 0; i < size; i++) { + container.addBean(new TestBean(r)); + } + + return container; + + } + + public static class TestBean { + private String name, address, city, country; + private int age, shoesize; + + public int getAge() { + return age; + } + + public void setAge(int age) { + this.age = age; + } + + public int getShoesize() { + return shoesize; + } + + public void setShoesize(int shoesize) { + this.shoesize = shoesize; + } + + public TestBean(PortableRandom r) { + age = r.nextInt(100) + 5; + shoesize = r.nextInt(10) + 35; + name = createRandomString(r, r.nextInt(5) + 5); + address = createRandomString(r, r.nextInt(15) + 5) + " " + + r.nextInt(100) + 1; + city = createRandomString(r, r.nextInt(7) + 3); + if (r.nextBoolean()) { + country = createRandomString(r, r.nextInt(4) + 4); + } + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + } + + public static String createRandomString(PortableRandom r, int len) { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < len; i++) { + b.append((char) (r.nextInt('z' - 'a') + 'a')); + } + + return b.toString(); + } + +} diff --git a/server/tests/src/com/vaadin/data/util/BeanItemTest.java b/server/tests/src/com/vaadin/data/util/BeanItemTest.java index b458856826..89c56b1779 100644 --- a/server/tests/src/com/vaadin/data/util/BeanItemTest.java +++ b/server/tests/src/com/vaadin/data/util/BeanItemTest.java @@ -13,6 +13,8 @@ import junit.framework.TestCase; import org.junit.Assert; +import com.vaadin.data.Property; + /** * Test BeanItem specific features. * @@ -122,6 +124,31 @@ public class BeanItemTest extends TestCase { public int getSuper2(); } + protected static class Generic<T> { + + public T getProperty() { + return null; + } + + public void setProperty(T t) { + throw new UnsupportedOperationException(); + } + } + + protected static class SubClass extends Generic<String> { + + @Override + // Has a bridged method + public String getProperty() { + return ""; + } + + @Override + // Has a bridged method + public void setProperty(String t) { + } + } + protected static interface MySubInterface extends MySuperInterface, MySuperInterface2 { public int getSub(); @@ -331,4 +358,18 @@ public class BeanItemTest extends TestCase { Assert.assertEquals(6, item.getItemPropertyIds().size()); Assert.assertEquals(null, item.getItemProperty("myname")); } + + public void testOverridenGenericMethods() { + BeanItem<SubClass> item = new BeanItem<SubClass>(new SubClass()); + + Property<?> property = item.getItemProperty("property"); + Assert.assertEquals("Unexpected class for property type", String.class, + property.getType()); + + Assert.assertEquals("Unexpected property value", "", + property.getValue()); + + // Should not be exception + property.setValue(null); + } } diff --git a/server/tests/src/com/vaadin/server/AbstractDeploymentConfigurationTest.java b/server/tests/src/com/vaadin/server/AbstractDeploymentConfigurationTest.java new file mode 100644 index 0000000000..7370bd3fac --- /dev/null +++ b/server/tests/src/com/vaadin/server/AbstractDeploymentConfigurationTest.java @@ -0,0 +1,157 @@ +/* + * 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.server; + +import java.util.Properties; +import java.util.UUID; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.shared.communication.PushMode; + +/** + * Test for {@link AbstractDeploymentConfiguration} + * + * @author Vaadin Ltd + */ +public class AbstractDeploymentConfigurationTest { + + @Test + public void getUIClass_returnsUIParameterPropertyValue() { + String ui = UUID.randomUUID().toString(); + DeploymentConfiguration config = getConfig(VaadinSession.UI_PARAMETER, + ui); + Assert.assertEquals("Unexpected UI class configuration option value", + ui, config.getUIClassName()); + } + + @Test + public void getUIProviderClass_returnsUIProviderPropertyValue() { + String uiProvider = UUID.randomUUID().toString(); + DeploymentConfiguration config = getConfig( + Constants.SERVLET_PARAMETER_UI_PROVIDER, uiProvider); + Assert.assertEquals( + "Unexpected UI providerclass configuration option value", + uiProvider, config.getUIProviderClassName()); + } + + @Test + public void getWidgetset_returnsWidgetsetProviderPropertyValue() { + String widgetset = UUID.randomUUID().toString(); + DeploymentConfiguration config = getConfig( + Constants.PARAMETER_WIDGETSET, widgetset); + Assert.assertEquals("Unexpected widgetset configuration option value", + widgetset, config.getWidgetset(null)); + } + + @Test + public void getWidgetset_noWidgetsetPropertyValue_returnsProvidedDefaultValue() { + DeploymentConfiguration config = getConfig(null, null); + String widgetset = UUID.randomUUID().toString(); + Assert.assertEquals("Unexpected widgetset configuration option value", + widgetset, config.getWidgetset(widgetset)); + } + + @Test + public void getResourcesPath_returnsResourcesPathPropertyValue() { + String resources = UUID.randomUUID().toString(); + DeploymentConfiguration config = getConfig( + Constants.PARAMETER_VAADIN_RESOURCES, resources); + Assert.assertEquals( + "Unexpected resources path configuration option value", + resources, config.getResourcesPath()); + } + + @Test + public void getClassLoader_returnsClassloaderPropertyValue() { + String classLoader = UUID.randomUUID().toString(); + DeploymentConfiguration config = getConfig("ClassLoader", classLoader); + Assert.assertEquals( + "Unexpected classLoader configuration option value", + classLoader, config.getClassLoaderName()); + } + + private DeploymentConfiguration getConfig(String property, String value) { + Properties props = new Properties(); + if (property != null) { + props.put(property, value); + } + return new DeploymentConfigImpl(props); + } + + private static class DeploymentConfigImpl extends + AbstractDeploymentConfiguration { + + private Properties properties; + + DeploymentConfigImpl(Properties props) { + properties = props; + } + + @Override + public boolean isProductionMode() { + return false; + } + + @Override + public boolean isXsrfProtectionEnabled() { + return false; + } + + @Override + public boolean isSyncIdCheckEnabled() { + return false; + } + + @Override + public int getResourceCacheTime() { + return 0; + } + + @Override + public int getHeartbeatInterval() { + return 0; + } + + @Override + public boolean isCloseIdleSessions() { + return false; + } + + @Override + public PushMode getPushMode() { + return null; + } + + @Override + public Properties getInitParameters() { + return null; + } + + @Override + public String getApplicationOrSystemProperty(String propertyName, + String defaultValue) { + return properties.getProperty(propertyName, defaultValue); + } + + @Override + public LegacyProperyToStringMode getLegacyPropertyToStringMode() { + return null; + } + + } +} diff --git a/server/tests/src/com/vaadin/server/BrowserWindowOpenerTest.java b/server/tests/src/com/vaadin/server/BrowserWindowOpenerTest.java new file mode 100644 index 0000000000..7c76f7d421 --- /dev/null +++ b/server/tests/src/com/vaadin/server/BrowserWindowOpenerTest.java @@ -0,0 +1,79 @@ +/* + * 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.server; + +import static org.junit.Assert.assertEquals; + +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.shared.communication.URLReference; +import com.vaadin.shared.ui.BrowserWindowOpenerState; + +/** + * + * @author Vaadin Ltd + */ +public class BrowserWindowOpenerTest { + + @Test + public void setResource_urlBasedOpener_resourceIsSetAndUrlIsNull() { + BrowserWindowOpener opener = new BrowserWindowOpener("url"); + + StreamResource resource = EasyMock.createMock(StreamResource.class); + opener.setResource(resource); + + assertEquals("Unexpected resource is got on getResource() method", + resource, opener.getResource()); + Assert.assertNull("Unexpected resource is got on getUrl() method", + opener.getUrl()); + + URLReference ref = opener.getState(false).resources + .get(BrowserWindowOpenerState.locationResource); + Assert.assertTrue( + "Url reference in the state is not ResourceReference", + ref instanceof ResourceReference); + Assert.assertEquals("Unexpected resource saved in state", resource, + ((ResourceReference) ref).getResource()); + } + + @Test + public void setUrl_urlBasedOpener_urlIsSet() { + BrowserWindowOpener opener = new BrowserWindowOpener("url"); + + String url = "newUrl"; + opener.setUrl(url); + + assertEquals("Unexpected URL is got on getURL() method", url, + opener.getUrl()); + Assert.assertNotNull( + "Unexpected resource is got on getResource() method", + opener.getResource()); + + URLReference ref = opener.getState(false).resources + .get(BrowserWindowOpenerState.locationResource); + Assert.assertTrue( + "Url reference in the state is not ResourceReference", + ref instanceof ResourceReference); + Resource resource = ((ResourceReference) ref).getResource(); + Assert.assertTrue("Resource reference is not ExternalResource", + resource instanceof ExternalResource); + Assert.assertEquals("Unexpected URL in resource saved in state", url, + ((ExternalResource) resource).getURL()); + } + +} diff --git a/server/tests/src/com/vaadin/server/DragAndDropServiceTest.java b/server/tests/src/com/vaadin/server/DragAndDropServiceTest.java new file mode 100644 index 0000000000..d0cb0ca5a6 --- /dev/null +++ b/server/tests/src/com/vaadin/server/DragAndDropServiceTest.java @@ -0,0 +1,125 @@ +/* + * 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.server; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; +import java.util.logging.StreamHandler; + +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.event.dd.DropHandler; +import com.vaadin.event.dd.TargetDetails; +import com.vaadin.ui.AbstractComponent; + +/** + * Tests for {@link DragAndDropService}. + * + * @author Vaadin Ltd + */ +public class DragAndDropServiceTest { + + @Test + public void changeVariables_isSourceConnectorEnabledCalled() { + final List<Level> levels = new ArrayList<Level>(); + Logger.getLogger(DragAndDropService.class.getName()).addHandler( + new StreamHandler() { + @Override + public synchronized void publish(LogRecord record) { + levels.add(record.getLevel()); + } + }); + Map<String, Object> variables = new HashMap<String, Object>(); + final boolean[] isConnectorEnabledCalled = new boolean[1]; + AbstractComponent component = new AbstractComponent() { + @Override + public boolean isConnectorEnabled() { + isConnectorEnabledCalled[0] = true; + return false; + } + }; + variables.put("component", component); + + DragAndDropService service = new DragAndDropService( + EasyMock.createMock(VaadinSession.class)); + service.changeVariables(null, variables); + + Assert.assertTrue("isConnectorEnabled() method is not called", + isConnectorEnabledCalled[0]); + Assert.assertTrue("No warning on drop from disabled source", + levels.contains(Level.WARNING)); + + } + + @Test + public void changeVariables_isTargetConnectorEnabledCalled() { + final List<Level> levels = new ArrayList<Level>(); + Logger.getLogger(DragAndDropService.class.getName()).addHandler( + new StreamHandler() { + @Override + public void publish(LogRecord record) { + levels.add(record.getLevel()); + } + }); + Map<String, Object> variables = new HashMap<String, Object>(); + TestDropTarget target = new TestDropTarget(); + variables.put("dhowner", target); + + DragAndDropService service = new DragAndDropService( + EasyMock.createMock(VaadinSession.class)); + service.changeVariables(null, variables); + + Assert.assertTrue("isConnectorEnabled() method is not called", + target.isConnectorEnabledCalled()); + Assert.assertTrue("No warning on drop to disabled target", + levels.contains(Level.WARNING)); + + } + + private static class TestDropTarget extends AbstractComponent implements + com.vaadin.event.dd.DropTarget { + @Override + public boolean isConnectorEnabled() { + isConnectorEnabledCalled = true; + return false; + } + + @Override + public DropHandler getDropHandler() { + return null; + } + + @Override + public TargetDetails translateDropTargetDetails( + Map<String, Object> clientVariables) { + return null; + } + + boolean isConnectorEnabledCalled() { + return isConnectorEnabledCalled; + } + + private boolean isConnectorEnabledCalled; + + } +} diff --git a/server/tests/src/com/vaadin/server/PageTest.java b/server/tests/src/com/vaadin/server/PageTest.java new file mode 100644 index 0000000000..b782b1f67d --- /dev/null +++ b/server/tests/src/com/vaadin/server/PageTest.java @@ -0,0 +1,92 @@ +/* + * 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.server; + +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.server.Page.BrowserWindowResizeEvent; +import com.vaadin.server.Page.BrowserWindowResizeListener; +import com.vaadin.shared.ui.ui.PageState; +import com.vaadin.ui.UI; + +/** + * + * Tests for {@link Page} + * + * @author Vaadin Ltd + */ +public class PageTest { + + @Test + public void removeBrowserWindowResizeListener_listenerIsAttached_listenerRemoved() { + Page page = new Page(EasyMock.createMock(UI.class), + EasyMock.createMock(PageState.class)); + + TestBrowserWindowResizeListener listener = new TestBrowserWindowResizeListener(); + page.addBrowserWindowResizeListener(listener); + page.removeBrowserWindowResizeListener(listener); + + page.updateBrowserWindowSize(0, 0, true); + + Assert.assertFalse("Listener is called after removal", + listener.isCalled()); + } + + @Test + public void removeBrowserWindowResizeListener_listenerIsNotAttached_stateIsUpdated() { + TestPage page = new TestPage(EasyMock.createMock(UI.class), + EasyMock.createMock(PageState.class)); + + BrowserWindowResizeListener listener = EasyMock + .createMock(BrowserWindowResizeListener.class); + page.removeBrowserWindowResizeListener(listener); + + Assert.assertFalse( + "Page state 'hasResizeListeners' property has wrong value", + page.getState(false).hasResizeListeners); + } + + private static class TestPage extends Page { + + public TestPage(UI uI, PageState state) { + super(uI, state); + } + + @Override + protected PageState getState(boolean markAsDirty) { + return super.getState(markAsDirty); + } + + } + + private static class TestBrowserWindowResizeListener implements + BrowserWindowResizeListener { + + @Override + public void browserWindowResized(BrowserWindowResizeEvent event) { + isCalled = true; + } + + public boolean isCalled() { + return isCalled; + } + + private boolean isCalled; + + } +} diff --git a/server/tests/src/com/vaadin/server/VaadinPortletServiceTests.java b/server/tests/src/com/vaadin/server/VaadinPortletServiceTests.java index f7a69c2edb..0e094bfabb 100644 --- a/server/tests/src/com/vaadin/server/VaadinPortletServiceTests.java +++ b/server/tests/src/com/vaadin/server/VaadinPortletServiceTests.java @@ -86,10 +86,7 @@ public class VaadinPortletServiceTests { } private void mockWidgetsetConfiguration(String widgetset) { - when( - conf.getApplicationOrSystemProperty( - Constants.PARAMETER_WIDGETSET, null)).thenReturn( - widgetset); + when(conf.getWidgetset(null)).thenReturn(widgetset); } @Test diff --git a/server/tests/src/com/vaadin/server/VaadinSessionTest.java b/server/tests/src/com/vaadin/server/VaadinSessionTest.java index b710ee483f..fa7e892066 100644 --- a/server/tests/src/com/vaadin/server/VaadinSessionTest.java +++ b/server/tests/src/com/vaadin/server/VaadinSessionTest.java @@ -15,7 +15,9 @@ */ package com.vaadin.server; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import javax.servlet.ServletConfig; @@ -30,6 +32,7 @@ import org.junit.Test; import com.vaadin.server.ClientConnector.DetachEvent; import com.vaadin.server.ClientConnector.DetachListener; +import com.vaadin.server.communication.UIInitHandler; import com.vaadin.ui.UI; import com.vaadin.util.CurrentInstance; @@ -43,9 +46,11 @@ public class VaadinSessionTest { private WrappedSession mockWrappedSession; private VaadinServletRequest vaadinRequest; private UI ui; + private Lock httpSessionLock; @Before public void setup() throws Exception { + httpSessionLock = new ReentrantLock(); mockServletConfig = new MockServletConfig(); mockServlet = new VaadinServlet(); mockServlet.init(mockServletConfig); @@ -60,11 +65,30 @@ public class VaadinSessionTest { @Override public Object getAttribute(String name) { - String lockAttribute = mockService.getServiceName() + ".lock"; - if (lockAttribute.equals(name)) { - return lock; + Object res; + try { + Thread.sleep(100); // for deadlock testing + org.junit.Assert.assertTrue("Deadlock detected", + httpSessionLock.tryLock(5, TimeUnit.SECONDS)); // simulates + // servlet + // container's + // session + // locking + String lockAttribute = mockService.getServiceName() + + ".lock"; + if (lockAttribute.equals(name)) { + res = lock; + } else if ("com.vaadin.server.VaadinSession.Mock Servlet" + .equals(name)) { + res = session; + } else { + res = super.getAttribute(name); + } + httpSessionLock.unlock(); + } catch (InterruptedException e) { + throw new RuntimeException(e); } - return super.getAttribute(name); + return res; } }; @@ -91,12 +115,26 @@ public class VaadinSessionTest { EasyMock.createMock(HttpServletRequest.class), mockService) { @Override public String getParameter(String name) { - if ("theme".equals(name)) { + if ("theme".equals(name) || "restartApplication".equals(name) + || "ignoreRestart".equals(name) + || "closeApplication".equals(name)) { return null; + } else if (UIInitHandler.BROWSER_DETAILS_PARAMETER.equals(name)) { + return "1"; } - return super.getParameter(name); } + + @Override + public String getMethod() { + return "POST"; + } + + @Override + public WrappedSession getWrappedSession(boolean allowSessionCreation) { + return mockWrappedSession; + } + }; ui.doInit(vaadinRequest, session.getNextUIid(), null); @@ -106,8 +144,43 @@ public class VaadinSessionTest { } + /** + * This reproduces #14452 situation with deadlock - see diagram + */ + @Test + public void testInvalidationDeadlock() { + + // this simulates servlet container's session invalidation from another + // thread + new Thread(new Runnable() { + @Override + public void run() { + try { + Thread.sleep(150); // delay selected so that VaadinSession + // will be already locked by the main + // thread + // when we get here + httpSessionLock.lock();// simulating servlet container's + // session lock + mockService.fireSessionDestroy(session); + httpSessionLock.unlock(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + }).start(); + + try { + mockService.findVaadinSession(vaadinRequest); + } catch (Exception e) { + throw new RuntimeException(e); + } + + } + @Test - public void threadLocalsAfterUnderlyingSessionTimeout() { + public void threadLocalsAfterUnderlyingSessionTimeout() + throws InterruptedException { final AtomicBoolean detachCalled = new AtomicBoolean(false); ui.addDetachListener(new DetachListener() { @@ -123,11 +196,17 @@ public class VaadinSessionTest { }); session.valueUnbound(EasyMock.createMock(HttpSessionBindingEvent.class)); + mockService.runPendingAccessTasks(session); // as soon as we changed + // session.accessSynchronously + // to session.access in + // VaadinService.fireSessionDestroy, + // we need to run the + // pending task ourselves Assert.assertTrue(detachCalled.get()); } @Test - public void threadLocalsAfterSessionDestroy() { + public void threadLocalsAfterSessionDestroy() throws InterruptedException { final AtomicBoolean detachCalled = new AtomicBoolean(false); ui.addDetachListener(new DetachListener() { @Override @@ -143,6 +222,12 @@ public class VaadinSessionTest { CurrentInstance.clearAll(); session.close(); mockService.cleanupSession(session); + mockService.runPendingAccessTasks(session); // as soon as we changed + // session.accessSynchronously + // to session.access in + // VaadinService.fireSessionDestroy, + // we need to run the + // pending task ourselves Assert.assertTrue(detachCalled.get()); } diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestStringToEnumConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestStringToEnumConverter.java new file mode 100644 index 0000000000..2b19395c08 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/data/converter/TestStringToEnumConverter.java @@ -0,0 +1,59 @@ +package com.vaadin.tests.data.converter; + +import junit.framework.TestCase; + +import com.vaadin.data.util.converter.Converter; +import com.vaadin.data.util.converter.ReverseConverter; +import com.vaadin.data.util.converter.StringToEnumConverter; + +public class TestStringToEnumConverter extends TestCase { + + public static enum FooEnum { + VALUE1, SOME_VALUE, FOO_BAR_BAZ, Bar, nonStandardCase, _HUGH; + } + + StringToEnumConverter converter = new StringToEnumConverter(); + Converter<Enum, String> reverseConverter = new ReverseConverter<Enum, String>( + converter); + + public void testNullConversion() { + assertEquals(null, converter.convertToModel(null, Enum.class, null)); + } + + public void testReverseNullConversion() { + assertEquals(null, + reverseConverter.convertToModel(null, String.class, null)); + } + + public void testValueConversion() { + assertEquals(FooEnum.VALUE1, + converter.convertToModel("Value1", FooEnum.class, null)); + assertEquals(FooEnum.SOME_VALUE, + converter.convertToModel("Some value", FooEnum.class, null)); + assertEquals(FooEnum.FOO_BAR_BAZ, + converter.convertToModel("Foo bar baz", FooEnum.class, null)); + assertEquals(FooEnum.Bar, + converter.convertToModel("Bar", FooEnum.class, null)); + assertEquals(FooEnum.nonStandardCase, converter.convertToModel( + "Nonstandardcase", FooEnum.class, null)); + assertEquals(FooEnum._HUGH, + converter.convertToModel("_hugh", FooEnum.class, null)); + } + + public void testReverseValueConversion() { + assertEquals("Value1", reverseConverter.convertToModel(FooEnum.VALUE1, + String.class, null)); + assertEquals("Some value", reverseConverter.convertToModel( + FooEnum.SOME_VALUE, String.class, null)); + assertEquals("Foo bar baz", reverseConverter.convertToModel( + FooEnum.FOO_BAR_BAZ, String.class, null)); + assertEquals("Bar", reverseConverter.convertToModel(FooEnum.Bar, + String.class, null)); + assertEquals("Nonstandardcase", reverseConverter.convertToModel( + FooEnum.nonStandardCase, String.class, null)); + assertEquals("_hugh", reverseConverter.convertToModel(FooEnum._HUGH, + String.class, null)); + + } + +} diff --git a/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java b/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java index 63f79504ff..1220209479 100644 --- a/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java +++ b/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java @@ -16,6 +16,7 @@ import java.util.jar.JarFile; import junit.framework.TestCase; +import org.junit.Ignore; import org.junit.Test; public class TestClassesSerializable extends TestCase { @@ -95,15 +96,8 @@ public class TestClassesSerializable extends TestCase { if (cls.isAnnotation() || cls.isSynthetic()) { continue; } - // Don't add classes that have a @Test annotation on any methods - boolean testPresent = false; - for (Method method : cls.getMethods()) { - if (method.isAnnotationPresent(Test.class)) { - testPresent = true; - break; - } - } - if (testPresent) { + // Don't add classes that have a @Ignore annotation on the class + if (isTestClass(cls)) { continue; } @@ -152,6 +146,27 @@ public class TestClassesSerializable extends TestCase { } } + private boolean isTestClass(Class<?> cls) { + // @Ignore is used on test util classes + if (cls.isAnnotationPresent(Ignore.class)) { + return true; + } + + if (cls.getEnclosingClass() != null + && isTestClass(cls.getEnclosingClass())) { + return true; + } + + // Test classes with a @Test annotation on some method + for (Method method : cls.getMethods()) { + if (method.isAnnotationPresent(Test.class)) { + return true; + } + } + + return false; + } + /** * Lists all class path entries by splitting the class path string. * diff --git a/server/tests/src/com/vaadin/tests/server/component/button/ButtonClick.java b/server/tests/src/com/vaadin/tests/server/component/button/ButtonClick.java index f82bbfe907..b41e93900f 100644 --- a/server/tests/src/com/vaadin/tests/server/component/button/ButtonClick.java +++ b/server/tests/src/com/vaadin/tests/server/component/button/ButtonClick.java @@ -1,11 +1,12 @@ package com.vaadin.tests.server.component.button; -import static org.junit.Assert.assertEquals; - +import org.junit.Assert; import org.junit.Test; +import com.vaadin.server.VaadinRequest; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.UI; /** * Tests the public click() method. @@ -16,7 +17,7 @@ public class ButtonClick { @Test public void testClick() { getButton().click(); - assertEquals(clicked, true); + Assert.assertTrue("Button doesn't fire clicks", clicked); } @Test @@ -24,7 +25,7 @@ public class ButtonClick { Button b = getButton(); b.setEnabled(false); b.click(); - assertEquals(clicked, false); + Assert.assertFalse("Disabled button fires click events", clicked); } @Test @@ -32,17 +33,50 @@ public class ButtonClick { Button b = getButton(); b.setReadOnly(true); b.click(); - assertEquals(clicked, false); + Assert.assertFalse("Read only button fires click events", clicked); + } + + @Test + public void testClickConnectorDisabled() { + Button b = new Button() { + @Override + public boolean isConnectorEnabled() { + return false; + } + }; + UI ui = createUI(); + b.setParent(ui); + addClickListener(b); + b.click(); + Assert.assertFalse("Button with disabled connector fires click events", + clicked); } private Button getButton() { Button b = new Button(); - b.addListener(new Button.ClickListener() { + UI ui = createUI(); + b.setParent(ui); + addClickListener(b); + return b; + } + + private UI createUI() { + UI ui = new UI() { + + @Override + protected void init(VaadinRequest request) { + } + }; + return ui; + } + + private void addClickListener(Button b) { + clicked = false; + b.addClickListener(new Button.ClickListener() { @Override public void buttonClick(ClickEvent ev) { clicked = true; } }); - return b; } } diff --git a/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java b/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java index ab2bc7c8c0..773631642a 100644 --- a/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java +++ b/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java @@ -24,6 +24,7 @@ import java.util.GregorianCalendar; import java.util.Locale; import java.util.TimeZone; +import org.junit.Assert; import org.junit.Test; import com.vaadin.ui.Calendar; @@ -207,4 +208,29 @@ public class CalendarBasics { assertEquals(23, calendar.getLastVisibleHourOfDay()); } + @Test + public void isClientChangeAllowed_connectorEnabled() { + TestCalendar calendar = new TestCalendar(true); + Assert.assertTrue( + "Calendar with enabled connector doesn't allow client change", + calendar.isClientChangeAllowed()); + } + + private static class TestCalendar extends Calendar { + TestCalendar(boolean connectorEnabled) { + isConnectorEnabled = connectorEnabled; + } + + @Override + public boolean isConnectorEnabled() { + return isConnectorEnabled; + } + + @Override + public boolean isClientChangeAllowed() { + return super.isClientChangeAllowed(); + } + + private final boolean isConnectorEnabled; + } } diff --git a/server/tests/src/com/vaadin/tests/server/component/customlayout/CustomLayoutTest.java b/server/tests/src/com/vaadin/tests/server/component/customlayout/CustomLayoutTest.java new file mode 100644 index 0000000000..4d327e70a6 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/customlayout/CustomLayoutTest.java @@ -0,0 +1,148 @@ +/* + * 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.server.component.customlayout; + +import java.io.ByteArrayInputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.ui.CustomLayout; + +/** + * + * Tests for {@link CustomLayout} + * + * @author Vaadin Ltd + */ +public class CustomLayoutTest { + + @Test + public void ctor_inputStreamProvided_inputStreamIsRead() + throws IOException, IllegalArgumentException, + IllegalAccessException { + Integer buffer = getBufferSize(); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < buffer; i++) { + builder.append('a'); + } + byte[] bytes = builder.toString().getBytes(Charset.forName("UTF-8")); + ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes); + InputStreamImpl stream = new InputStreamImpl(inputStream, buffer / 2); + new CustomLayout(stream); + + Assert.assertTrue("Stream is not closed in CustomLayout CTOR ", + stream.isClosed()); + Assert.assertEquals("Number of read bytes is incorrect", bytes.length, + stream.getCount()); + } + + private Integer getBufferSize() throws IllegalAccessException { + Field[] fields = CustomLayout.class.getDeclaredFields(); + List<Field> list = new ArrayList<Field>(fields.length); + for (Field field : fields) { + if ((field.getModifiers() & Modifier.STATIC) > 0) { + list.add(field); + } + } + Field field = null; + if (list.size() == 1) { + field = list.get(0); + } else { + for (Field fld : list) { + if (fld.getName().toLowerCase(Locale.ENGLISH) + .startsWith("buffer")) { + field = fld; + break; + } + } + } + Assert.assertNotNull( + "Unable to find default buffer size in CustomLayout class", + field); + field.setAccessible(true); + Integer buffer = (Integer) field.get(null); + return buffer; + } + + private static class InputStreamImpl extends FilterInputStream { + + InputStreamImpl(InputStream inputStream, int maxArrayLength) { + super(inputStream); + this.maxArrayLength = maxArrayLength; + } + + @Override + public int read() throws IOException { + int read = super.read(); + if (read != -1) { + readCount++; + } + return read; + } + + @Override + public int read(byte[] b) throws IOException { + if (b.length > maxArrayLength) { + return read(b, 0, maxArrayLength); + } + int count = super.read(b); + if (count != -1) { + readCount += count; + } + return count; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (len > maxArrayLength) { + return read(b, off, maxArrayLength); + } + int count = super.read(b, off, len); + if (count != -1) { + readCount += count; + } + return count; + } + + @Override + public void close() throws IOException { + isClosed = true; + super.close(); + } + + int getCount() { + return readCount; + } + + boolean isClosed() { + return isClosed; + } + + private int readCount; + private boolean isClosed; + private int maxArrayLength; + } +} diff --git a/server/tests/src/com/vaadin/tests/server/component/form/FormTest.java b/server/tests/src/com/vaadin/tests/server/component/form/FormTest.java new file mode 100644 index 0000000000..2075f7b115 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/form/FormTest.java @@ -0,0 +1,68 @@ +/* + * 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.server.component.form; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.ui.Form; +import com.vaadin.ui.TextField; + +/** + * Test for {@link Form}. + * + * @author Vaadin Ltd + */ +public class FormTest { + + @Test + public void testFocus() { + Form form = new Form(); + final boolean firstFieldIsFocused[] = new boolean[1]; + TextField field1 = new TextField() { + @Override + public boolean isConnectorEnabled() { + return false; + } + + @Override + public void focus() { + firstFieldIsFocused[0] = true; + } + }; + + final boolean secondFieldIsFocused[] = new boolean[1]; + TextField field2 = new TextField() { + @Override + public boolean isConnectorEnabled() { + return true; + } + + @Override + public void focus() { + secondFieldIsFocused[0] = true; + } + }; + form.addField("a", field1); + form.addField("b", field2); + form.focus(); + + Assert.assertTrue("Field with enabled connector is not focused", + secondFieldIsFocused[0]); + Assert.assertFalse("Field with disabled connector is focused", + firstFieldIsFocused[0]); + } +} diff --git a/server/tests/src/com/vaadin/tests/server/component/upload/UploadTest.java b/server/tests/src/com/vaadin/tests/server/component/upload/UploadTest.java new file mode 100644 index 0000000000..2132829c0a --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/upload/UploadTest.java @@ -0,0 +1,101 @@ +/* + * 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.server.component.upload; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.server.StreamVariable; +import com.vaadin.server.StreamVariable.StreamingErrorEvent; +import com.vaadin.ui.Upload; + +/** + * + * @author Vaadin Ltd + */ +public class UploadTest { + + @Test + public void getStreamVariable_streamingFailed_endUploadIsCalled() { + TestUpload upload = new TestUpload(); + upload.startUpload(); + StreamVariable variable = upload.getStreamVariable(); + try { + variable.streamingFailed(new TestStreamingErrorEvent()); + } catch (Exception e) { + } + Assert.assertFalse(upload.isUploading()); + } + + private static class TestStreamingErrorEvent implements StreamingErrorEvent { + + @Override + public String getFileName() { + return null; + } + + @Override + public String getMimeType() { + return null; + } + + @Override + public long getContentLength() { + return 0; + } + + @Override + public long getBytesReceived() { + return 0; + } + + @Override + public Exception getException() { + return new Exception(); + } + + } + + private static class TestUpload extends Upload { + + @Override + public StreamVariable getStreamVariable() { + return super.getStreamVariable(); + } + + @Override + protected void fireNoInputStream(String filename, String MIMEType, + long length) { + fireEvent(); + } + + @Override + protected void fireNoOutputStream(String filename, String MIMEType, + long length) { + fireEvent(); + } + + @Override + protected void fireUploadInterrupted(String filename, String MIMEType, + long length, Exception e) { + fireEvent(); + } + + private void fireEvent() { + throw new NullPointerException(); + } + } +} diff --git a/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java b/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java index fe7f9ba03e..8eceaea53f 100644 --- a/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java +++ b/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java @@ -4,10 +4,11 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; -import com.vaadin.server.DeploymentConfiguration; +import com.vaadin.server.AbstractDeploymentConfiguration; import com.vaadin.shared.communication.PushMode; -public class MockDeploymentConfiguration implements DeploymentConfiguration { +public class MockDeploymentConfiguration extends + AbstractDeploymentConfiguration { private boolean productionMode = false; private boolean xsrfProtectionEnabled = true; diff --git a/server/tests/src/com/vaadin/ui/TableTest.java b/server/tests/src/com/vaadin/ui/TableTest.java new file mode 100644 index 0000000000..86237abbe0 --- /dev/null +++ b/server/tests/src/com/vaadin/ui/TableTest.java @@ -0,0 +1,78 @@ +/* + * 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.ui; + +import java.util.Collection; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.util.BeanItemContainerGenerator; + +public class TableTest { + + Table table; + + @Before + public void init() { + table = new Table(); + } + + @Test + public void initiallyEmpty() { + Assert.assertTrue(table.isEmpty()); + } + + @Test + public void emptyAfterClearSingleSelect() { + table.setContainerDataSource(BeanItemContainerGenerator + .createContainer(100)); + Assert.assertTrue(table.isEmpty()); + Object first = table.getContainerDataSource().getItemIds().iterator() + .next(); + table.setValue(first); + Assert.assertEquals(first, table.getValue()); + Assert.assertFalse(table.isEmpty()); + table.clear(); + Assert.assertEquals(null, table.getValue()); + Assert.assertTrue(table.isEmpty()); + } + + @Test + public void emptyAfterClearMultiSelect() { + table.setMultiSelect(true); + table.setContainerDataSource(BeanItemContainerGenerator + .createContainer(100)); + + Assert.assertTrue(table.isEmpty()); + Assert.assertArrayEquals(new Object[] {}, + ((Collection) table.getValue()).toArray()); + + Object first = table.getContainerDataSource().getItemIds().iterator() + .next(); + table.select(first); + Assert.assertArrayEquals(new Object[] { first }, + ((Collection) table.getValue()).toArray()); + Assert.assertFalse(table.isEmpty()); + + table.clear(); + Assert.assertArrayEquals(new Object[] {}, + ((Collection) table.getValue()).toArray()); + Assert.assertTrue(table.isEmpty()); + } + +} diff --git a/server/tests/src/com/vaadin/ui/TextFieldTest.java b/server/tests/src/com/vaadin/ui/TextFieldTest.java new file mode 100644 index 0000000000..bfd452bd3b --- /dev/null +++ b/server/tests/src/com/vaadin/ui/TextFieldTest.java @@ -0,0 +1,48 @@ +/* + * 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.ui; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.data.util.ObjectProperty; + +public class TextFieldTest { + + @Test + public void initiallyEmpty() { + TextField tf = new TextField(); + Assert.assertTrue(tf.isEmpty()); + } + + @Test + public void emptyAfterClearUsingPDS() { + TextField tf = new TextField(new ObjectProperty<String>("foo")); + Assert.assertFalse(tf.isEmpty()); + tf.clear(); + Assert.assertTrue(tf.isEmpty()); + } + + @Test + public void emptyAfterClear() { + TextField tf = new TextField(); + tf.setValue("foobar"); + Assert.assertFalse(tf.isEmpty()); + tf.clear(); + Assert.assertTrue(tf.isEmpty()); + } + +} diff --git a/shared/src/com/vaadin/shared/VBrowserDetails.java b/shared/src/com/vaadin/shared/VBrowserDetails.java index 3c89bdb6bb..a85d031c49 100644 --- a/shared/src/com/vaadin/shared/VBrowserDetails.java +++ b/shared/src/com/vaadin/shared/VBrowserDetails.java @@ -43,6 +43,8 @@ public class VBrowserDetails implements Serializable { private boolean isIE = false; private boolean isWindowsPhone; + private boolean isIPad; + private boolean isIPhone; private OperatingSystem os = OperatingSystem.UNKNOWN; @@ -176,8 +178,9 @@ public class VBrowserDetails implements Serializable { } else if (userAgent.contains("macintosh") || userAgent.contains("mac osx") || userAgent.contains("mac os x")) { - if (userAgent.contains("ipad") || userAgent.contains("ipod") - || userAgent.contains("iphone")) { + isIPad = userAgent.contains("ipad"); + isIPhone = userAgent.contains("iphone"); + if (isIPad || userAgent.contains("ipod") || isIPhone) { os = OperatingSystem.IOS; parseIOSVersion(userAgent); } else { @@ -431,6 +434,7 @@ public class VBrowserDetails implements Serializable { * Tests if the browser is run on Windows Phone. * * @return true if run on Windows Phone, false otherwise + * @since 7.3.2 */ public boolean isWindowsPhone() { return isWindowsPhone; @@ -473,6 +477,24 @@ public class VBrowserDetails implements Serializable { } /** + * Tests if the browser is run on iPhone. + * + * @return + */ + public boolean isIPhone() { + return isIPhone; + } + + /** + * Tests if the browser is run on iPad. + * + * @return + */ + public boolean isIPad() { + return isIPad; + } + + /** * Returns the major version of the operating system. Currently only * supported for mobile devices (iOS/Android) * diff --git a/shared/src/com/vaadin/shared/ui/datefield/Resolution.java b/shared/src/com/vaadin/shared/ui/datefield/Resolution.java index 6d467e233c..689db11188 100644 --- a/shared/src/com/vaadin/shared/ui/datefield/Resolution.java +++ b/shared/src/com/vaadin/shared/ui/datefield/Resolution.java @@ -16,7 +16,6 @@ package com.vaadin.shared.ui.datefield; import java.util.ArrayList; -import java.util.Calendar; import java.util.List; /** @@ -26,8 +25,9 @@ import java.util.List; * @since 7.0 */ public enum Resolution { - SECOND(Calendar.SECOND), MINUTE(Calendar.MINUTE), HOUR(Calendar.HOUR_OF_DAY), DAY( - Calendar.DAY_OF_MONTH), MONTH(Calendar.MONTH), YEAR(Calendar.YEAR); + // Values from Calendar.SECOND etc. Set as ints to avoid Calendar dependency + // (does not exist on the client side) + SECOND(13), MINUTE(12), HOUR(11), DAY(5), MONTH(2), YEAR(1); private int calendarField; @@ -36,7 +36,7 @@ public enum Resolution { } /** - * Returns the field in {@link Calendar} that corresponds to this + * Returns the field in java.util.Calendar that corresponds to this * resolution. * * @return one of the field numbers used by Calendar diff --git a/uitest/eclipse-run-selected-test.properties b/uitest/eclipse-run-selected-test.properties index 70010fd1da..f8fb0a8c14 100644 --- a/uitest/eclipse-run-selected-test.properties +++ b/uitest/eclipse-run-selected-test.properties @@ -14,6 +14,9 @@ com.vaadin.testbench.screenshot.directory=<enter the full path to the screenshots directory, parent of "references" directory> +; Deployment url to use for testing. Context path must be / +; com.vaadin.testbench.deployment.url=http://<enter your ip here>:8888/ + ; ; For only TestBench 3 ; @@ -29,9 +32,6 @@ com.vaadin.testbench.screenshot.directory=<enter the full path to the screenshot ; Location where TestBench 2 jar can be found com.vaadin.testbench.lib.dir=<enter location of testbench here> -; Deployment url to use for testing. Context path must be / -com.vaadin.testbench.deployment.url=http://<enter your ip here>:8888/ - ; Run the whole test even if a screenshot comparison fails com.vaadin.testbench.screenshot.softfail=true diff --git a/uitest/src/com/vaadin/tests/components/AbstractTestUI.java b/uitest/src/com/vaadin/tests/components/AbstractTestUI.java index d7fb7d03fb..558379260b 100644 --- a/uitest/src/com/vaadin/tests/components/AbstractTestUI.java +++ b/uitest/src/com/vaadin/tests/components/AbstractTestUI.java @@ -181,9 +181,13 @@ public abstract class AbstractTestUI extends UI { getLayout().replaceComponent(oldComponent, newComponent); } - protected abstract String getTestDescription(); + protected String getTestDescription() { + return null; + }; - protected abstract Integer getTicketNumber(); + protected Integer getTicketNumber() { + return null; + }; protected WebBrowser getBrowser() { return getSession().getBrowser(); diff --git a/uitest/src/com/vaadin/tests/components/absolutelayout/AbsoluteLayoutRelativeSizeContent.java b/uitest/src/com/vaadin/tests/components/absolutelayout/AbsoluteLayoutRelativeSizeContent.java new file mode 100644 index 0000000000..4e1c8f5ca2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/absolutelayout/AbsoluteLayoutRelativeSizeContent.java @@ -0,0 +1,218 @@ +/* + * 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.absolutelayout; + +import com.vaadin.annotations.Theme; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.MarginInfo; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.AbsoluteLayout; +import com.vaadin.ui.Component; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Table; + +/** + * Tests how AbsoluteLayout handles relative sized contents. + * + * @author Vaadin Ltd + */ +@Theme("tests-tickets") +public class AbsoluteLayoutRelativeSizeContent extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + HorizontalLayout level1 = new HorizontalLayout( + createComparisonTableOnFixed(), createTableOnFixed(), + createHalfTableOnFixed(), + createHalfTableAndFixedTableOnFixed(), createHalfTableOnFull()); + level1.setSpacing(true); + level1.setWidth(100, Unit.PERCENTAGE); + level1.setExpandRatio( + level1.getComponent(level1.getComponentCount() - 1), 1); + level1.setMargin(new MarginInfo(true, false, false, false)); + + HorizontalLayout level2 = new HorizontalLayout(createFullOnFixed(), + createFullOnFull()); + level2.setSpacing(true); + level2.setWidth(100, Unit.PERCENTAGE); + level2.setExpandRatio( + level2.getComponent(level2.getComponentCount() - 1), 1); + level2.setMargin(new MarginInfo(true, false, false, false)); + + addComponent(level1); + addComponent(level2); + } + + /** + * Creates an {@link AbsoluteLayout} of fixed size that contains a + * full-sized {@link Table} that has been forced to full size with css. + * Represents the workaround given for this ticket. + * + * @return the created layout + */ + private Component createComparisonTableOnFixed() { + AbsoluteLayout absoluteLayout = new AbsoluteLayout(); + absoluteLayout.setWidth(200, Unit.PIXELS); + absoluteLayout.setHeight(200, Unit.PIXELS); + absoluteLayout.setCaption("comparison table in full size"); + + Table table = new Table(); + table.setSizeFull(); + table.setId("comparison-table"); + absoluteLayout.addComponent(table, "top:0;bottom:0;left:0;right:0;"); + return absoluteLayout; + } + + /** + * Creates an {@link AbsoluteLayout} of fixed size that contains a + * full-sized {@link Table}. + * + * @return the created layout + */ + private Component createTableOnFixed() { + AbsoluteLayout absoluteLayout = new AbsoluteLayout(); + absoluteLayout.setWidth(200, Unit.PIXELS); + absoluteLayout.setHeight(200, Unit.PIXELS); + absoluteLayout.setCaption("full-sized table expected"); + + Table table = new Table(); + table.setSizeFull(); + table.setId("full-table"); + absoluteLayout.addComponent(table); + return absoluteLayout; + } + + /** + * Creates an {@link AbsoluteLayout} of fixed size that contains a + * half-sized {@link Table}. + * + * @return the created layout + */ + private Component createHalfTableOnFixed() { + AbsoluteLayout absoluteLayout = new AbsoluteLayout(); + absoluteLayout.setWidth(200, Unit.PIXELS); + absoluteLayout.setHeight(200, Unit.PIXELS); + absoluteLayout.setCaption("half-sized table expected"); + + Table table = new Table(); + table.setWidth(50, Unit.PERCENTAGE); + table.setHeight(50, Unit.PERCENTAGE); + table.setId("half-table"); + absoluteLayout.addComponent(table); + return absoluteLayout; + } + + /** + * Creates an {@link AbsoluteLayout} of fixed size that contains a + * half-sized {@link Table} and a fixed size {@link Table}. + * + * @return the created layout + */ + private Component createHalfTableAndFixedTableOnFixed() { + AbsoluteLayout absoluteLayout = new AbsoluteLayout(); + absoluteLayout.setWidth(200, Unit.PIXELS); + absoluteLayout.setHeight(200, Unit.PIXELS); + absoluteLayout.setCaption("half-sized and tiny expected"); + + Table table = new Table(); + table.setWidth(50, Unit.PERCENTAGE); + table.setHeight(50, Unit.PERCENTAGE); + table.setId("halfwithtiny-table"); + absoluteLayout.addComponent(table); + + Table tableTiny = new Table(); + tableTiny.setWidth(50, Unit.PIXELS); + tableTiny.setHeight(50, Unit.PIXELS); + absoluteLayout.addComponent(tableTiny, "right:50;"); + return absoluteLayout; + } + + /** + * Creates an {@link AbsoluteLayout} of full size that contains a half-sized + * {@link Table}. + * + * @return the created layout + */ + private Component createHalfTableOnFull() { + AbsoluteLayout absoluteLayout = new AbsoluteLayout(); + absoluteLayout.setSizeFull(); + absoluteLayout.setId("halfinfull-layout"); + absoluteLayout.setCaption("half-sized table expected"); + + Table table = new Table(); + table.setWidth(50, Unit.PERCENTAGE); + table.setHeight(50, Unit.PERCENTAGE); + table.setId("halfinfull-table"); + absoluteLayout.addComponent(table); + return absoluteLayout; + } + + /** + * Creates an {@link AbsoluteLayout} of fixed size that contains a + * fixed-sized {@link AbsoluteLayout}. + * + * @return the created layout + */ + private Component createFullOnFixed() { + AbsoluteLayout absoluteLayout = new AbsoluteLayout(); + absoluteLayout.setWidth(200, Unit.PIXELS); + absoluteLayout.setHeight(200, Unit.PIXELS); + absoluteLayout.setId("fullonfixed-outer"); + absoluteLayout.addStyleName("green"); + absoluteLayout.setCaption("yellow area expected"); + + AbsoluteLayout absoluteLayout2 = new AbsoluteLayout(); + absoluteLayout2.setSizeFull(); + absoluteLayout2.setId("fullonfixed-inner"); + absoluteLayout2.addStyleName("yellow"); + + absoluteLayout.addComponent(absoluteLayout2, "top:50px;left:100px;"); + return absoluteLayout; + } + + /** + * Creates an {@link AbsoluteLayout} of full size that contains another + * full-sized {@link AbsoluteLayout}. + * + * @return the created layout + */ + private AbsoluteLayout createFullOnFull() { + AbsoluteLayout absoluteLayout = new AbsoluteLayout(); + absoluteLayout.setSizeFull(); + absoluteLayout.setId("fullonfull-outer"); + absoluteLayout.addStyleName("cyan"); + absoluteLayout.setCaption("area with red border expected"); + + AbsoluteLayout absoluteLayout2 = new AbsoluteLayout(); + absoluteLayout2.setSizeFull(); + absoluteLayout2.setId("fullonfull-inner"); + absoluteLayout2.addStyleName("redborder"); + + absoluteLayout.addComponent(absoluteLayout2, "top:50px;left:100px;"); + return absoluteLayout; + } + + @Override + protected String getTestDescription() { + return "Full size component in AbsoluteLayout shouldn't get undefined size"; + } + + @Override + protected Integer getTicketNumber() { + return 13131; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/absolutelayout/AbsoluteLayoutRelativeSizeContentTest.java b/uitest/src/com/vaadin/tests/components/absolutelayout/AbsoluteLayoutRelativeSizeContentTest.java new file mode 100644 index 0000000000..6cb8c476c9 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/absolutelayout/AbsoluteLayoutRelativeSizeContentTest.java @@ -0,0 +1,120 @@ +/* + * 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.absolutelayout; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.number.IsCloseTo.closeTo; + +import org.junit.Before; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Tests how AbsoluteLayout handles relative sized contents. + * + * @author Vaadin Ltd + */ +public class AbsoluteLayoutRelativeSizeContentTest extends MultiBrowserTest { + + @Override + @Before + public void setup() throws Exception { + super.setup(); + openTestURL(); + + waitForElementPresent(By.id("comparison-table")); + }; + + @Test + public void testFullAgainstComparison() { + WebElement comparison = findElement(By.id("comparison-table")); + WebElement full = findElement(By.id("full-table")); + + assertThat("Full table should be as wide as comparison table", + full.getSize().width, is(comparison.getSize().width)); + assertThat("Full table should be as high as comparison table", + full.getSize().height, is(comparison.getSize().height)); + } + + @Test + public void testHalfAgainstComparison() { + WebElement comparison = findElement(By.id("comparison-table")); + WebElement half = findElement(By.id("half-table")); + + assertThat( + "Half-sized table should be half as wide as comparison table", + half.getSize().width, is(comparison.getSize().width / 2)); + assertThat( + "Half-sized table should be half as high as comparison table", + half.getSize().height, is(comparison.getSize().height / 2)); + } + + @Test + public void testHalfWithTinyAgainstComparison() { + WebElement comparison = findElement(By.id("comparison-table")); + WebElement half = findElement(By.id("halfwithtiny-table")); + + assertThat( + "Half-sized table should be half as wide as comparison table even if there are other components in the layout", + half.getSize().width, is(comparison.getSize().width / 2)); + assertThat( + "Half-sized table should be half as high as comparison table even if there are other components in the layout", + half.getSize().height, is(comparison.getSize().height / 2)); + } + + @Test + public void testHalfAgainstFullLayout() { + WebElement layout = findElement(By.id("halfinfull-layout")); + WebElement half = findElement(By.id("halfinfull-table")); + + assertThat("Half-sized table should be half as wide as full layout", + (double) half.getSize().width, + closeTo(((double) layout.getSize().width) / 2, 0.5)); + assertThat("Half-sized table should be half as high as full layout", + (double) half.getSize().height, + closeTo(((double) layout.getSize().height) / 2, 0.5)); + } + + @Test + public void testFullOnFixedWithSetLocation() { + WebElement outer = findElement(By.id("fullonfixed-outer")); + WebElement inner = findElement(By.id("fullonfixed-inner")); + + assertThat( + "Inner layout should be as wide as outer layout minus left position", + inner.getSize().width, is(outer.getSize().width - 100)); + assertThat( + "Inner layout should be as high as outer layout minus top position", + inner.getSize().height, is(outer.getSize().height - 50)); + } + + @Test + public void testFullOnFullWithSetLocation() { + WebElement outer = findElement(By.id("fullonfull-outer")); + WebElement inner = findElement(By.id("fullonfull-inner")); + + assertThat( + "Inner layout should be as wide as outer layout minus left position", + inner.getSize().width, is(outer.getSize().width - 100)); + assertThat( + "Inner layout should be as high as outer layout minus top position", + inner.getSize().height, is(outer.getSize().height - 50)); + } +} diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxClickIcon.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxClickIcon.java new file mode 100644 index 0000000000..b0450e22c3 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxClickIcon.java @@ -0,0 +1,59 @@ +/* + * 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.combobox; + +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.server.FontAwesome; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.ComboBox; + +/** + * Test UI to check click on icon in the combobox. + * + * @author Vaadin Ltd + */ +public class ComboBoxClickIcon extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final List<String> items = new ArrayList<String>(); + items.add("A"); + items.add("B"); + items.add("C"); + final ComboBox combo = new ComboBox(); + combo.setImmediate(true); + combo.setItemIcon(items.get(0), FontAwesome.ALIGN_CENTER); + combo.setItemIcon(items.get(1), FontAwesome.ALIGN_CENTER); + combo.setItemIcon(items.get(2), FontAwesome.ALIGN_CENTER); + combo.addItems(items); + combo.setTextInputAllowed(false); + addComponent(combo); + } + + @Override + protected String getTestDescription() { + return "Combobox icon should handle click events"; + } + + @Override + protected Integer getTicketNumber() { + return 14624; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxClickIconTest.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxClickIconTest.java new file mode 100644 index 0000000000..949fcdb882 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxClickIconTest.java @@ -0,0 +1,47 @@ +/* + * 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.combobox; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; + +import com.vaadin.testbench.elements.ComboBoxElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test to check whether combobox is expanded when icon is clicked. + * + * @author Vaadin Ltd + */ +public class ComboBoxClickIconTest extends MultiBrowserTest { + + @Test + public void testClickOnIconInCombobox() { + openTestURL(); + + $(ComboBoxElement.class).first().openPopup(); + + getDriver().findElements(By.className("gwt-MenuItem")).get(1).click(); + + getDriver().findElement(By.className("v-filterselect")) + .findElement(By.className("v-icon")).click(); + + Assert.assertTrue("Unable to find menu items in combobox popup", + isElementPresent(By.className("gwt-MenuItem"))); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxInputPromptTest.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxInputPromptTest.java index cbd83c5734..1e6f7e4170 100644 --- a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxInputPromptTest.java +++ b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxInputPromptTest.java @@ -18,16 +18,13 @@ package com.vaadin.tests.components.combobox; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.isEmptyString; -import static org.junit.Assert.assertEquals; -import com.vaadin.testbench.elements.ButtonElement; -import com.vaadin.testbench.elements.ComboBoxElement; -import com.vaadin.testbench.elements.TextFieldElement; -import com.vaadin.tests.tb3.AbstractTB3Test; 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.ComboBoxElement; import com.vaadin.tests.tb3.MultiBrowserTest; public class ComboBoxInputPromptTest extends MultiBrowserTest { diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffset.java b/uitest/src/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffset.java new file mode 100644 index 0000000000..62db60be76 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffset.java @@ -0,0 +1,68 @@ +package com.vaadin.tests.components.datefield; + +import com.vaadin.data.Property; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.datefield.Resolution; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.DateField; +import com.vaadin.ui.Label; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.TimeZone; + +public class DateFieldDayResolutionOffset extends AbstractTestUI { + + private final String initialDateString = "09/01/2014 00:00:00"; + + @Override + protected void setup(VaadinRequest request) { + final Label dateValue = new Label(initialDateString); + dateValue.setId("dateValue"); + + final TimeZone timezone = TimeZone.getTimeZone("GMT"); + final SimpleDateFormat dateformat = getDateFormat(timezone); + final DateField dateField = getDateField(timezone, dateformat); + + addComponent(dateValue); + addComponent(dateField); + + dateField.addValueChangeListener( new Property.ValueChangeListener(){ + @Override + public void valueChange(Property.ValueChangeEvent event) { + dateValue.setValue(dateformat.format(dateField.getValue())); + } + }); + } + + private DateField getDateField(TimeZone timezone, SimpleDateFormat dateformat) { + final DateField dateField = new DateField(); + try { + Date initialDate = dateformat.parse(initialDateString); + dateField.setResolution(Resolution.DAY); + dateField.setTimeZone(timezone); + dateField.setValue(initialDate); + } catch (ParseException e) { + e.printStackTrace(); + } + return dateField; + } + + private SimpleDateFormat getDateFormat(TimeZone timezone) { + final SimpleDateFormat dateformat = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss"); + dateformat.setTimeZone(timezone); + return dateformat; + } + + @Override + protected String getTestDescription() { + return "The time should stay at 00:00:00 when selecting dates with Resolution.DAY selected."; + } + + @Override + protected Integer getTicketNumber() { + return 14653; + } +} diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffsetTest.java b/uitest/src/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffsetTest.java new file mode 100644 index 0000000000..c3b3af9aa4 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldDayResolutionOffsetTest.java @@ -0,0 +1,43 @@ +package com.vaadin.tests.components.datefield; + +import com.vaadin.testbench.elements.DateFieldElement; +import com.vaadin.testbench.elements.LabelElement; +import com.vaadin.tests.tb3.AbstractTB3Test; +import com.vaadin.tests.tb3.MultiBrowserTest; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class DateFieldDayResolutionOffsetTest extends MultiBrowserTest { + + @Test + public void dateValueDoesNotHaveOffset() throws InterruptedException { + openTestURL(); + + openDatePicker(); + select2ndOfSeptember(); + + LabelElement dateValue = $(LabelElement.class).id("dateValue"); + assertThat(dateValue.getText(), is("09/02/2014 00:00:00")); + } + + private void select2ndOfSeptember() { + for(WebElement e : findElements(By.className("v-datefield-calendarpanel-day"))) { + if(e.getText().equals("2")) { + e.click(); + break; + } + } + } + + private void openDatePicker() { + DateFieldElement dateField = $(DateFieldElement.class).first(); + + dateField.findElement(By.tagName("button")) + .click(); + } + +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/formlayout/TableInFormLayoutCausesScrollingTest.java b/uitest/src/com/vaadin/tests/components/formlayout/TableInFormLayoutCausesScrollingTest.java index dc10217efb..3a0dcafe1f 100644 --- a/uitest/src/com/vaadin/tests/components/formlayout/TableInFormLayoutCausesScrollingTest.java +++ b/uitest/src/com/vaadin/tests/components/formlayout/TableInFormLayoutCausesScrollingTest.java @@ -2,6 +2,7 @@ package com.vaadin.tests.components.formlayout; import com.vaadin.testbench.elements.TableElement; import com.vaadin.tests.tb3.MultiBrowserTest; +import org.junit.Ignore; import org.junit.Test; import org.openqa.selenium.Keys; import org.openqa.selenium.interactions.Actions; @@ -11,6 +12,12 @@ import java.io.IOException; public class TableInFormLayoutCausesScrollingTest extends MultiBrowserTest { @Test + @Ignore + //This test is actually testing that #7309 is NOT fixed. + //Ignoring the test because it is not stable and it's + //occasionally failing on browsers even when it shouldn't. + + //There's no point fixing this test before #7309 is actually fixed. public void pageIsNotScrolled() throws IOException { openTestURL(); diff --git a/uitest/src/com/vaadin/tests/components/panel/UndefinedSizeScrollbars.java b/uitest/src/com/vaadin/tests/components/panel/UndefinedSizeScrollbars.java new file mode 100644 index 0000000000..ef535baa0a --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/panel/UndefinedSizeScrollbars.java @@ -0,0 +1,71 @@ +/* + * 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.panel; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.Panel; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; + +public class UndefinedSizeScrollbars extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + VerticalLayout layout = new VerticalLayout(); + layout.setSpacing(true); + layout.setSizeFull(); + setContent(layout); + + GridLayout grid = new GridLayout(); + grid.setSpacing(true); + + TextField text1 = new TextField(); + text1.setCaption("Text1"); + text1.setRequired(true); + + TextField text2 = new TextField(); + text2.setCaption("Text2"); + text2.setRequired(true); + + ComboBox combo = new ComboBox(); + combo.setCaption("Combo1"); + + CheckBox check = new CheckBox(); + check.setCaption("Check"); + + grid.setColumns(2); + grid.setRows(2); + + grid.addComponent(text1); + grid.addComponent(text2); + grid.addComponent(combo); + grid.addComponent(check); + + grid.setSizeUndefined(); + + Panel panel = new Panel(); + panel.setContent(grid); + + panel.setSizeUndefined(); + + layout.addComponent(panel); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/panel/UndefinedSizeScrollbarsTest.java b/uitest/src/com/vaadin/tests/components/panel/UndefinedSizeScrollbarsTest.java new file mode 100644 index 0000000000..c055356624 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/panel/UndefinedSizeScrollbarsTest.java @@ -0,0 +1,31 @@ +/* + * 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.panel; + +import java.io.IOException; + +import org.junit.Test; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class UndefinedSizeScrollbarsTest extends MultiBrowserTest { + + @Test + public void testNoScrollbars() throws IOException { + openTestURL(); + compareScreen("noscrollbars"); + } +} diff --git a/uitest/src/com/vaadin/tests/components/popupview/DisabledPopupView.java b/uitest/src/com/vaadin/tests/components/popupview/DisabledPopupView.java new file mode 100644 index 0000000000..ecce1781a5 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/popupview/DisabledPopupView.java @@ -0,0 +1,23 @@ +package com.vaadin.tests.components.popupview; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.PopupView; + +public class DisabledPopupView extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + PopupView popupView = new PopupView("Disabled Popup", new Button("Hi!")); + + popupView.setEnabled(false); + + addComponent(popupView); + } + + @Override + protected Integer getTicketNumber() { + return 14797; + } +} diff --git a/uitest/src/com/vaadin/tests/components/popupview/DisabledPopupViewTest.java b/uitest/src/com/vaadin/tests/components/popupview/DisabledPopupViewTest.java new file mode 100644 index 0000000000..be9345d588 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/popupview/DisabledPopupViewTest.java @@ -0,0 +1,20 @@ +package com.vaadin.tests.components.popupview; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.PopupViewElement; +import com.vaadin.tests.tb3.MultiBrowserTest; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; + +public class DisabledPopupViewTest extends MultiBrowserTest { + + @Test + public void disabledPopupDoesNotOpen() { + openTestURL(); + + $(PopupViewElement.class).first().click(); + + assertFalse($(ButtonElement.class).exists()); + } +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeTest.java b/uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeTest.java index 041b23749c..e13238e10d 100644 --- a/uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeTest.java +++ b/uitest/src/com/vaadin/tests/components/table/ContainerSizeChangeTest.java @@ -1,5 +1,6 @@ package com.vaadin.tests.components.table; +import static junit.framework.TestCase.fail; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -19,9 +20,6 @@ import com.vaadin.tests.tb3.MultiBrowserTest; public class ContainerSizeChangeTest extends MultiBrowserTest { - @Rule - public ExpectedException thrown = ExpectedException.none(); - @Test public void tableShouldLoadCorrectItems() throws IOException, InterruptedException { @@ -52,11 +50,12 @@ public class ContainerSizeChangeTest extends MultiBrowserTest { } private void assertRowDoesNotExist(TableElement table, int rowIndex) { - // This is a really crappy way to workaround JUnit's limitation to - // provide a proper assert.throws method... - thrown.expect(NoSuchElementException.class); - table.getCell(rowIndex, 0); + try { + table.getCell(rowIndex, 0); + + fail(String.format("Row %s should not exists.", rowIndex)); + } catch (NoSuchElementException e) { - Assert.fail(String.format("Row %s should not exists.", rowIndex)); + } } } diff --git a/uitest/src/com/vaadin/tests/components/table/TableClickAndDragOnIconAndComponentsTest.java b/uitest/src/com/vaadin/tests/components/table/TableClickAndDragOnIconAndComponentsTest.java index 4a46342cab..aee8cc96d9 100644 --- a/uitest/src/com/vaadin/tests/components/table/TableClickAndDragOnIconAndComponentsTest.java +++ b/uitest/src/com/vaadin/tests/components/table/TableClickAndDragOnIconAndComponentsTest.java @@ -15,19 +15,22 @@ */ package com.vaadin.tests.components.table; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.util.List; +import com.vaadin.tests.tb3.MultiBrowserTest; +import com.vaadin.tests.tb3.SingleBrowserTest; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebElement; -import org.openqa.selenium.interactions.Actions; import com.vaadin.testbench.elements.TableElement; -import com.vaadin.tests.tb3.MultiBrowserTest; +import org.openqa.selenium.interactions.Actions; /** * Tests that clicking on active fields doesn't change Table selection, nor does @@ -36,203 +39,174 @@ import com.vaadin.tests.tb3.MultiBrowserTest; * @author Vaadin Ltd */ public class TableClickAndDragOnIconAndComponentsTest extends MultiBrowserTest { - @Test - public void testClickingAndDragging() { + + @Override + public void setup() throws Exception { + super.setup(); + openTestURL(); + } - TableElement table = $(TableElement.class).first(); + @Test + public void clickOnTextFieldDoesNotSelectRow() { + selectRow(1); - // ensure there's no initial selection - List<WebElement> selected = table.findElements(By - .className("v-selected")); - assertTrue("selection found when there should be none", - selected.isEmpty()); - - // click a cell - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 2nd row expected)", - "red 1foo", table.getCell(1, 2).getText()); - table.getCell(1, 2).click(); - - // ensure the correct row and nothing but that got selected - selected = table.findElements(By.className("v-selected")); - assertFalse("no selection found when there should be some", - selected.isEmpty()); - // find cell contents (row header included) - List<WebElement> cellContents = selected.get(0).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 2nd row expected)", - "red 1foo", cellContents.get(2).getText()); - assertEquals("unexpected table selection size", 1, selected.size()); + clickOnTextField(2); + assertThatFocusTextFieldHasText("foo 2foo"); - List<WebElement> rows = table.findElement(By.className("v-table-body")) - .findElements(By.tagName("tr")); - assertEquals("unexpected table row count", 5, rows.size()); + assertThat(getSelectedRowTextValue(), is(1)); + } - // find a row that isn't the one selected - cellContents = rows.get(2).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 3rd row expected)", - "red 2foo", cellContents.get(2).getText()); - - // click on a TextField on that row - WebElement textField = rows.get(2) - .findElements(By.className("v-textfield")).get(0); - assertEquals( - "expected value not found, wrong cell or contents (6th column of the 3rd row expected)", - "foo 2foo", textField.getAttribute("value")); - textField.click(); + @Test + public void clickOnReadOnlyTextFieldSelectsRow() { + selectRow(1); + + clickOnReadOnlyTextField(2); + + assertThat(getSelectedRowTextValue(), is(2)); + } + + @Test + public void clickOnLabelSelectsRow() { + selectRow(1); + + clickOnLabel(2); - // ensure the focus shifted correctly - List<WebElement> focused = table.findElements(By + assertThat(getSelectedRowTextValue(), is(2)); + } + + @Test + public void clickOnEmbeddedIconSelectsRow() { + selectRow(1); + + clickOnEmbeddedIcon(2); + + assertThat(getSelectedRowTextValue(), is(2)); + } + + @Test + public void dragAndDroppingRowDoesNotSelectRow() { + selectRow(1); + + moveRow(0, 3); + + assertThat(getSelectedRowTextValue(), is(1)); + assertThat(getSelectedRowIndex(), is(0)); + } + + @Test + public void dragAndDroppingSelectedRowStaysSelected() { + selectRow(1); + + moveRow(1, 4); + + assertThat(getSelectedRowTextValue(), is(1)); + assertThat(getSelectedRowIndex(), is(4)); + } + + private void assertThatFocusTextFieldHasText(String text) { + List<WebElement> focused = getTable().findElements(By .className("v-textfield-focus")); - assertEquals("unexpected amount of focused textfields", 1, - focused.size()); - assertEquals( - "expected value not found, wrong cell or contents (6th column of the 3rd row expected)", - "foo 2foo", focused.get(0).getAttribute("value")); - - // ensure the selection didn't change - selected = table.findElements(By.className("v-selected")); - assertEquals("unexpected table selection size", 1, selected.size()); - cellContents = selected.get(0).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 2nd row expected)", - "red 1foo", cellContents.get(2).getText()); - - // click on a Label on that row - WebElement label = rows.get(2).findElements(By.className("v-label")) - .get(0); - assertEquals( - "expected value not found, wrong cell or contents (5th column of the 3rd row expected)", - "foo 2foo", label.getText()); - label.click(); - // ensure the focus shifted correctly - focused = table.findElements(By.className("v-textfield-focus")); - assertTrue("focused textfields found when there should be none", - focused.isEmpty()); + assertThat(focused.get(0).getAttribute("value"), is(text)); + } + + private int getSelectedRowTextValue() { + WebElement selectedRow = getSelectedRow(); - // ensure the selection changed - selected = table.findElements(By.className("v-selected")); - assertEquals("unexpected table selection size", 1, selected.size()); - cellContents = selected.get(0).findElements( + //i.e. 'red 1foo' + String text = getText(selectedRow, 2); + + return Integer.parseInt(text.substring(4, 5)); + } + + private String getText(WebElement row, int column) { + List<WebElement> cellContents = getCellContents(row); + + return cellContents.get(column).getText(); + } + + private List<WebElement> getCellContents(WebElement row) { + return row.findElements( By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 3rd row expected)", - "red 2foo", cellContents.get(2).getText()); + } + + private WebElement getSelectedRow() { + return getTable().findElement(By + .className("v-selected")); + } + + private void clickOnTextField(int row) { + WebElement textField = getTextField(row, 0); - // click on the selected row's textfield (same as earlier) textField.click(); + } + + private void clickOnReadOnlyTextField(int row) { + WebElement textField = getTextField(row, 1); - // ensure the focus shifted correctly - focused = table.findElements(By.className("v-textfield-focus")); - assertEquals("unexpected amount of focused textfields", 1, - focused.size()); - assertEquals( - "expected value not found, wrong cell or contents (6th column of the 3rd row expected)", - "foo 2foo", focused.get(0).getAttribute("value")); - - // ensure the selection didn't change - selected = table.findElements(By.className("v-selected")); - assertEquals("unexpected table selection size", 1, selected.size()); - cellContents = selected.get(0).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 3rd row expected)", - "red 2foo", cellContents.get(2).getText()); - - // find the readOnly TextField of the previously selected row - textField = rows.get(1).findElements(By.className("v-textfield")) - .get(1); - assertEquals( - "expected value not found, wrong cell or contents (7th column of the 2nd row expected)", - "foo 1foo", textField.getAttribute("value")); - assertEquals( - "expected readonly status not found, wrong cell or contents (7th column of the 2nd row expected)", - "true", textField.getAttribute("readonly")); - - // click on that TextField textField.click(); + } - // ensure the focus shifted correctly - focused = table.findElements(By.className("v-textfield-focus")); - assertTrue("focused textfields found when there should be none", - focused.isEmpty()); + private WebElement getTextField(int row, int index) { + return getElement(row, index, "v-textfield"); + } - // ensure the selection changed - selected = table.findElements(By.className("v-selected")); - assertEquals("unexpected table selection size", 1, selected.size()); - cellContents = selected.get(0).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 2nd row expected)", - "red 1foo", cellContents.get(2).getText()); - - // click the embedded icon of the other row - WebElement embedded = rows.get(2).findElement( - By.className("v-embedded")); - embedded.click(); - - // ensure the selection changed - selected = table.findElements(By.className("v-selected")); - assertEquals("unexpected table selection size", 1, selected.size()); - cellContents = selected.get(0).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 3rd row expected)", - "red 2foo", cellContents.get(2).getText()); + private WebElement getElement(int row, String className) { + return getElement(row, 0, className); + } - // check row you are about to drag - cellContents = rows.get(4).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 5th row expected)", - "red 4foo", cellContents.get(2).getText()); + private WebElement getElement(int row, int index, String className) { + return getRows() + .get(row) + .findElements(By.className(className)) + .get(index); + } - // check the row above it - cellContents = rows.get(3).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 4th row expected)", - "red 3foo", cellContents.get(2).getText()); + private List<WebElement> getRows() { + return getTable().findElement(By.className("v-table-body")) + .findElements(By.tagName("tr")); + } - // drag the row to the row that's two places above it (gets dropped - // below that) - cellContents = rows.get(4).findElements( - By.className("v-table-cell-content")); - new Actions(getDriver()).moveToElement(cellContents.get(2)) - .clickAndHold().moveToElement(rows.get(2)).release().perform(); + private void selectRow(int row) { + TableElement table = getTable(); + + table.getCell(row, 2).click(); + } - // find the current order of the rows - rows = table.findElement(By.className("v-table-body")).findElements( - By.tagName("tr")); - assertEquals("unexpected table row count", 5, rows.size()); + private TableElement getTable() { + return $(TableElement.class).first(); + } - // ensure the row got dragged - cellContents = rows.get(3).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the dragged row expected, should be on 4th row now)", - "red 4foo", cellContents.get(2).getText()); - cellContents = rows.get(4).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the previous 4th row expected, should be on 5th row now)", - "red 3foo", cellContents.get(2).getText()); - - // ensure the selection didn't change - selected = table.findElements(By.className("v-selected")); - assertEquals("unexpected table selection size", 1, selected.size()); - cellContents = selected.get(0).findElements( - By.className("v-table-cell-content")); - assertEquals( - "expected value not found, wrong cell or contents (3rd column of the 3rd row expected)", - "red 2foo", cellContents.get(2).getText()); + private void clickOnLabel(int row) { + WebElement label = getElement(row, "v-label"); + label.click(); + } + + private void clickOnEmbeddedIcon(int row) { + WebElement embeddedIcon = getElement(row, "v-embedded"); + embeddedIcon.click(); + } + + private void moveRow(int from, int to) { + List<WebElement> rows = getRows(); + List<WebElement> cellContents = getCellContents(rows.get(from)); + + new Actions(getDriver()).moveToElement(cellContents.get(2)) + .clickAndHold().moveToElement(rows.get(to)).release().perform(); } + private int getSelectedRowIndex() { + List<WebElement> rows = getRows(); + + //Unfortunately rows.getIndexOf(getSelectedRow()) doesn't work. + for(WebElement r : rows) { + if(r.getAttribute("class").contains("v-selected")) { + return rows.indexOf(r); + } + } + + return -1; + } } diff --git a/uitest/src/com/vaadin/tests/components/table/TableRepairsScrollPositionOnReAddingAllRows.java b/uitest/src/com/vaadin/tests/components/table/TableRepairsScrollPositionOnReAddingAllRows.java index d9cbf007df..df06580dae 100644 --- a/uitest/src/com/vaadin/tests/components/table/TableRepairsScrollPositionOnReAddingAllRows.java +++ b/uitest/src/com/vaadin/tests/components/table/TableRepairsScrollPositionOnReAddingAllRows.java @@ -11,6 +11,12 @@ import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickListener; import com.vaadin.ui.Table; +/** + * Scroll position should be restored when removing and re-adding all rows in + * Table. + * + * @author Vaadin Ltd + */ public class TableRepairsScrollPositionOnReAddingAllRows extends AbstractTestUI { private static final long serialVersionUID = 1L; @@ -19,32 +25,130 @@ public class TableRepairsScrollPositionOnReAddingAllRows extends AbstractTestUI protected void setup(VaadinRequest request) { final BeanItemContainer<TableItem> cont = new BeanItemContainer<TableItem>( TableItem.class); - final List<TableItem> itemList = new ArrayList<TableItem>(); + final List<TableItem> restoringItemList = new ArrayList<TableItem>(); - Button button1 = new Button("ReAdd rows"); - button1.setId("button1"); - button1.addClickListener(new ClickListener() { + final Table table = new Table(); + table.setWidth("400px"); + table.setPageLength(-1); + table.setContainerDataSource(cont); + table.setSelectable(true); + + Button buttonRestore = new Button("Restore table rows"); + buttonRestore.setId("buttonRestore"); + buttonRestore.addClickListener(new ClickListener() { @Override public void buttonClick(com.vaadin.ui.Button.ClickEvent event) { cont.removeAllItems(); - cont.addAll(itemList); + cont.addAll(restoringItemList); } }); + Button buttonReAddAllViaAddAll = new Button("Re-add rows all at once"); + buttonReAddAllViaAddAll.setId("buttonReAddAllViaAddAll"); + buttonReAddAllViaAddAll.addClickListener(new ClickListener() { + + @Override + public void buttonClick(com.vaadin.ui.Button.ClickEvent event) { + List<TableItem> originalItemIds = new ArrayList<TableItem>(cont + .getItemIds()); + cont.removeAllItems(); + cont.addAll(originalItemIds); + } + }); + + Button buttonReplaceByAnotherCollectionViaAddAll = new Button( + "Replace by another items (via addAll())"); + buttonReplaceByAnotherCollectionViaAddAll + .setId("buttonReplaceByAnotherCollectionViaAddAll"); + buttonReplaceByAnotherCollectionViaAddAll + .addClickListener(new ClickListener() { + + @Override + public void buttonClick( + com.vaadin.ui.Button.ClickEvent event) { + cont.removeAllItems(); + // create new collection (of different items) with other + // size + List<TableItem> itemList = new ArrayList<TableItem>(); + for (int i = 0; i < 79; i++) { + TableItem ti = new TableItem(); + ti.setName("AnotherItem1_" + i); + itemList.add(ti); + } + cont.addAll(itemList); + } + }); + + Button buttonReplaceByAnotherCollectionViaAdd = new Button( + "Replace by another items (via add(), add()..)"); + buttonReplaceByAnotherCollectionViaAdd + .setId("buttonReplaceByAnotherCollectionViaAdd"); + buttonReplaceByAnotherCollectionViaAdd + .addClickListener(new ClickListener() { + + @Override + public void buttonClick( + com.vaadin.ui.Button.ClickEvent event) { + cont.removeAllItems(); + for (int i = 0; i < 81; i++) { + TableItem ti = new TableItem(); + ti.setName("AnotherItem2_" + i); + // add one by one in container + cont.addBean(ti); + } + } + }); + + Button buttonReplaceBySubsetOfSmallerSize = new Button( + "Replace rows by sub-set of smaller size (size not enought for restoring scroll position)"); + buttonReplaceBySubsetOfSmallerSize + .setId("buttonReplaceBySubsetOfSmallerSize"); + buttonReplaceBySubsetOfSmallerSize + .addClickListener(new ClickListener() { + + @Override + public void buttonClick( + com.vaadin.ui.Button.ClickEvent event) { + cont.removeAllItems(); + cont.addAll(restoringItemList.subList(0, 20)); + } + }); + + Button buttonReplaceByWholeSubsetPlusOnNew = new Button( + "Replace rows by whole subset plus one new item"); + buttonReplaceByWholeSubsetPlusOnNew + .setId("buttonReplaceByWholeSubsetPlusOnNew"); + buttonReplaceByWholeSubsetPlusOnNew + .addClickListener(new ClickListener() { + + @Override + public void buttonClick( + com.vaadin.ui.Button.ClickEvent event) { + cont.removeAllItems(); + + List<TableItem> list = new ArrayList<TableItem>( + restoringItemList); + TableItem ti = new TableItem(); + ti.setName("AnotherItem3_" + 80); + list.add(ti); + cont.addAll(list); + } + }); + for (int i = 0; i < 80; i++) { TableItem ti = new TableItem(); - ti.setName("Name_" + i); - itemList.add(ti); + ti.setName("Item_" + i); + restoringItemList.add(ti); cont.addBean(ti); } - final Table table = new Table(); - table.setPageLength(-1); - table.setContainerDataSource(cont); - table.setSelectable(true); - - getLayout().addComponent(button1); + getLayout().addComponent(buttonReAddAllViaAddAll); + getLayout().addComponent(buttonReplaceByAnotherCollectionViaAddAll); + getLayout().addComponent(buttonReplaceByAnotherCollectionViaAdd); + getLayout().addComponent(buttonReplaceBySubsetOfSmallerSize); + getLayout().addComponent(buttonReplaceByWholeSubsetPlusOnNew); + getLayout().addComponent(buttonRestore); getLayout().addComponent(table); } diff --git a/uitest/src/com/vaadin/tests/components/table/TableRepairsScrollPositionOnReAddingAllRowsTest.java b/uitest/src/com/vaadin/tests/components/table/TableRepairsScrollPositionOnReAddingAllRowsTest.java index 807a80d182..a3e7f29dfe 100644 --- a/uitest/src/com/vaadin/tests/components/table/TableRepairsScrollPositionOnReAddingAllRowsTest.java +++ b/uitest/src/com/vaadin/tests/components/table/TableRepairsScrollPositionOnReAddingAllRowsTest.java @@ -15,8 +15,8 @@ */ package com.vaadin.tests.components.table; -import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.number.IsCloseTo.closeTo; import org.junit.Test; import org.openqa.selenium.By; @@ -31,6 +31,12 @@ import com.vaadin.testbench.screenshot.ImageComparison; import com.vaadin.testbench.screenshot.ReferenceNameGenerator; import com.vaadin.tests.tb3.MultiBrowserTest; +/** + * Scroll position should be restored when removing and re-adding all rows in + * Table. + * + * @author Vaadin Ltd + */ public class TableRepairsScrollPositionOnReAddingAllRowsTest extends MultiBrowserTest { @@ -39,28 +45,126 @@ public class TableRepairsScrollPositionOnReAddingAllRowsTest extends throws InterruptedException { openTestURL(); - WebElement buttonReAddRows = findElement(By.id("button1")); + WebElement row0 = $(TableElement.class).first().getCell(0, 0); + int rowLocation0 = row0.getLocation().getY(); + // case 1 scrollUp(); waitUntilNot(new ExpectedCondition<Boolean>() { @Override public Boolean apply(WebDriver input) { - return $(TableElement.class).first().getCell(49, 0) == null; + return $(TableElement.class).first().getCell(48, 0) == null; } }, 10); - WebElement row = $(TableElement.class).first().getCell(49, 0); + WebElement row = $(TableElement.class).first().getCell(48, 0); int rowLocation = row.getLocation().getY(); - buttonReAddRows.click(); + // This button is for re-adding all rows (original itemIds) at once + // (removeAll() + addAll()) + hitButton("buttonReAddAllViaAddAll"); - row = $(TableElement.class).first().getCell(49, 0); + row = $(TableElement.class).first().getCell(48, 0); int newRowLocation = row.getLocation().getY(); + // ranged check because IE9 consistently misses the mark by 1 pixel + assertThat( + "Scroll position should be the same as before Re-Adding rows via addAll()", + (double) newRowLocation, + closeTo(rowLocation, row.getSize().height + 1)); + + // case 2 + scrollUp(); + + waitUntilNot(new ExpectedCondition<Boolean>() { + @Override + public Boolean apply(WebDriver input) { + return $(TableElement.class).first().getCell(48, 0) == null; + } + }, 10); + + row = $(TableElement.class).first().getCell(48, 0); + rowLocation = row.getLocation().getY(); + + // This button is for replacing all rows at once (removeAll() + + // addAll()) + hitButton("buttonReplaceByAnotherCollectionViaAddAll"); + + row = $(TableElement.class).first().getCell(48, 0); + newRowLocation = row.getLocation().getY(); + + // ranged check because IE9 consistently misses the mark by 1 pixel assertThat( - "Scroll position should be the same as before Re-Adding all rows", - rowLocation == newRowLocation, is(true)); + "Scroll position should be the same as before Replacing rows via addAll()", + (double) newRowLocation, + closeTo(rowLocation, row.getSize().height + 1)); + + // case 3 + // This button is for replacing all rows one by one (removeAll() + add() + // + add()..) + hitButton("buttonReplaceByAnotherCollectionViaAdd"); + + row = $(TableElement.class).first().getCell(0, 0); + newRowLocation = row.getLocation().getY(); + + // ranged check because IE9 consistently misses the mark by 1 pixel + assertThat("Scroll position should be 0", (double) newRowLocation, + closeTo(rowLocation0, 1)); + + // case 4 + // This button is for restoring initial list and for scrolling to 0 + // position + hitButton("buttonRestore"); + scrollUp(); + + waitUntilNot(new ExpectedCondition<Boolean>() { + @Override + public Boolean apply(WebDriver input) { + return $(TableElement.class).first().getCell(48, 0) == null; + } + }, 10); + + // This button is for replacing all rows at once but the count of rows + // is less then first index to scroll + hitButton("buttonReplaceBySubsetOfSmallerSize"); + + row = $(TableElement.class).first().getCell(5, 0); + + newRowLocation = row.getLocation().getY(); + + // ranged check because IE9 consistently misses the mark by 1 pixel + assertThat("Scroll position should be 0", (double) newRowLocation, + closeTo(rowLocation0, 1)); + + // case 5 + // This button is for restoring initial list and for scrolling to 0 + // position + hitButton("buttonRestore"); + scrollUp(); + + waitUntilNot(new ExpectedCondition<Boolean>() { + @Override + public Boolean apply(WebDriver input) { + return $(TableElement.class).first().getCell(48, 0) == null; + } + }, 10); + + row = $(TableElement.class).first().getCell(48, 0); + rowLocation = row.getLocation().getY(); + + // This button is for replacing by whole original sub-set of items plus + // one new + hitButton("buttonReplaceByWholeSubsetPlusOnNew"); + + row = $(TableElement.class).first().getCell(48, 0); + newRowLocation = row.getLocation().getY(); + + // ranged check because IE9 consistently misses the mark by 1 pixel + assertThat("Scroll position should be the same as before Replacing", + (double) newRowLocation, + closeTo(rowLocation, row.getSize().height + 1)); + } private void scrollUp() { @@ -68,6 +172,6 @@ public class TableRepairsScrollPositionOnReAddingAllRowsTest extends By.className("v-table-body-wrapper")); JavascriptExecutor js = new TestBenchCommandExecutor(getDriver(), new ImageComparison(), new ReferenceNameGenerator()); - js.executeScript("arguments[0].scrollTop = " + 1200, actualElement); + js.executeScript("arguments[0].scrollTop = " + 1205, actualElement); } } diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabSelectionRevertedByServer.java b/uitest/src/com/vaadin/tests/components/tabsheet/TabSelectionRevertedByServer.java new file mode 100644 index 0000000000..b51a8dde08 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabSelectionRevertedByServer.java @@ -0,0 +1,83 @@ +package com.vaadin.tests.components.tabsheet; + +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.Button.ClickListener; +import com.vaadin.ui.Component; +import com.vaadin.ui.Label; +import com.vaadin.ui.Notification; +import com.vaadin.ui.TabSheet; +import com.vaadin.ui.TabSheet.SelectedTabChangeEvent; +import com.vaadin.ui.TabSheet.SelectedTabChangeListener; + +/** + * TabSheet test in case user selects a tab and on the selection listener the + * selected tab is changed to another one. + * + * This test used to cause nonfunctional TabSheet if the current tab was 1, user + * selects 5, then the selection listener will revert the selected tab to 1. + * + * @since + * @author Vaadin Ltd + */ +public class TabSelectionRevertedByServer extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + + final TabSheet tabsheet = new TabSheet(); + tabsheet.setWidth("400px"); + + Component lastLabel = null; + + for (int i = 1; i <= 5; i++) { + String caption = "Tab " + i; + Label label = new Label(caption); + tabsheet.addTab(label, caption); + + lastLabel = label; + } + + tabsheet.setSelectedTab(0); + + final Component lastTab = lastLabel; + + tabsheet.addSelectedTabChangeListener(new SelectedTabChangeListener() { + + @Override + public void selectedTabChange(SelectedTabChangeEvent event) { + if (tabsheet.getSelectedTab().equals(lastTab)) { + + // Set focus back to first tab in tabsheet + tabsheet.setSelectedTab(0); + Notification.show("Focus set back to tab at position 0"); + } + } + }); + + addComponent(tabsheet); + + Button button = new Button("Select Last Tab"); + button.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + tabsheet.setSelectedTab(lastTab); + } + }); + addComponent(button); + } + + @Override + protected String getTestDescription() { + return "Clicking on Tab 5 will revert to Tab 1. The action is handled on the server side and will set the selected tab to 1 if Tab 5 is selected."; + } + + @Override + protected Integer getTicketNumber() { + return 14710; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/TabSelectionRevertedByServerTest.java b/uitest/src/com/vaadin/tests/components/tabsheet/TabSelectionRevertedByServerTest.java new file mode 100644 index 0000000000..64aee8768b --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tabsheet/TabSelectionRevertedByServerTest.java @@ -0,0 +1,112 @@ +/* + * 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.tabsheet; + +import java.io.IOException; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; + +import com.vaadin.testbench.TestBenchElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * If user selected the last tab the test will change it back to the first one + * from a server side selection listener. This test makes sure that actually + * happen. + * + * @since + * @author Vaadin Ltd + */ +public class TabSelectionRevertedByServerTest extends MultiBrowserTest { + + @Test + public void testFocus() throws InterruptedException, IOException { + openTestURL(); + + // Selects Tab 4 which should be selected. + click(4); + assertSelection(4, 1); + + // Select Tab 5 which should revert to Tab 1. + click(5); + assertSelection(1, 5); + + // Make sure after reverting the selection the tab selection still + // works. + click(3); + assertSelection(3, 1); + + } + + private void assertSelection(int expectedIndex, int wrongIndex) { + TestBenchElement tabExpected = tab(expectedIndex); + String attributeClassExpected = tabExpected.getAttribute("class"); + + Assert.assertTrue("Tab " + expectedIndex + " should be selected.", + attributeClassExpected + .contains("v-tabsheet-tabitemcell-selected")); + + TestBenchElement tabWrong = tab(wrongIndex); + String attributeClassWrong = tabWrong.getAttribute("class"); + + Assert.assertTrue("Tab " + wrongIndex + + " should be selected when click on Tab 4", + !attributeClassWrong + .contains("v-tabsheet-tabitemcell-selected")); + } + + /* + * Click on the element. + */ + private void click(int tabIndex) throws InterruptedException { + click(tab(tabIndex)); + } + + /* + * Click on the element. + */ + private void click(TestBenchElement element) throws InterruptedException { + + element.click(10, 10); + if (DELAY > 0) { + sleep(DELAY); + } + } + + /* + * Delay for PhantomJS. + */ + private final static int DELAY = 10; + + /* + * Provide the tab at specified index. + */ + private TestBenchElement tab(int index) { + By by = By.className("v-tabsheet-tabitemcell"); + + TestBenchElement element = (TestBenchElement) getDriver().findElements( + by).get(index - 1); + + String expected = "Tab " + index; + Assert.assertEquals(expected, + element.getText().substring(0, expected.length())); + + return element; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/textfield/EnumTextField.java b/uitest/src/com/vaadin/tests/components/textfield/EnumTextField.java new file mode 100644 index 0000000000..67b3b84688 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/textfield/EnumTextField.java @@ -0,0 +1,50 @@ +/* + * 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.textfield; + +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.data.util.ObjectProperty; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.TextField; + +public class EnumTextField extends AbstractTestUIWithLog { + + public enum MyEnum { + FIRST_VALUE, VALUE, THE_LAST_VALUE; + } + + @Override + protected void setup(VaadinRequest request) { + final TextField tf = new TextField(); + tf.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + if (tf.isValid()) { + log(tf.getValue() + " (valid)"); + } else { + log(tf.getValue() + " (INVALID)"); + } + } + }); + + tf.setPropertyDataSource(new ObjectProperty<Enum>(MyEnum.FIRST_VALUE)); + addComponent(tf); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/textfield/EnumTextFieldTest.java b/uitest/src/com/vaadin/tests/components/textfield/EnumTextFieldTest.java new file mode 100644 index 0000000000..113acee3a2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/textfield/EnumTextFieldTest.java @@ -0,0 +1,57 @@ +/* + * 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.textfield; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.Keys; + +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.SingleBrowserTest; + +public class EnumTextFieldTest extends SingleBrowserTest { + @Test + public void validValues() { + openTestURL(); + $(TextFieldElement.class).first().clear(); + $(TextFieldElement.class).first().sendKeys("Value"); + $(TextFieldElement.class).first().sendKeys(Keys.TAB); + Assert.assertEquals("3. Value (valid)", getLogRow(0)); + + $(TextFieldElement.class).first().clear(); + $(TextFieldElement.class).first().sendKeys("VaLuE"); + $(TextFieldElement.class).first().sendKeys(Keys.TAB); + Assert.assertEquals("5. Value (valid)", getLogRow(0)); + + $(TextFieldElement.class).first().clear(); + $(TextFieldElement.class).first().sendKeys("The last value"); + $(TextFieldElement.class).first().sendKeys(Keys.TAB); + Assert.assertEquals("7. The last value (valid)", getLogRow(0)); + + } + + @Test + public void invalidValue() { + openTestURL(); + $(TextFieldElement.class).first().clear(); + Assert.assertEquals("2. (INVALID)", getLogRow(0)); + + $(TextFieldElement.class).first().sendKeys("bar"); + $(TextFieldElement.class).first().sendKeys(Keys.TAB); + Assert.assertEquals("3. bar (INVALID)", getLogRow(0)); + + } +} diff --git a/uitest/src/com/vaadin/tests/components/textfield/LocaleChangeOnReadOnlyField.java b/uitest/src/com/vaadin/tests/components/textfield/LocaleChangeOnReadOnlyField.java new file mode 100644 index 0000000000..a1cabe914e --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/textfield/LocaleChangeOnReadOnlyField.java @@ -0,0 +1,56 @@ +package com.vaadin.tests.components.textfield; + + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.TextField; + +import java.math.BigDecimal; +import java.util.Locale; + +public class LocaleChangeOnReadOnlyField extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final TextField textField = getReadOnlyTextField(); + addComponent(textField); + + Button changeLocaleButton = addLocaleChangeButton(textField); + addComponent(changeLocaleButton); + } + + private TextField getReadOnlyTextField() { + final TextField textField = new TextField(); + + textField.setConverter(BigDecimal.class); + textField.setLocale(Locale.US); + textField.setValue("1024000"); + textField.setReadOnly(true); + + return textField; + } + + private Button addLocaleChangeButton(final TextField textField) { + Button changeLocaleButton = new Button(); + changeLocaleButton.setCaption("Change Locale"); + changeLocaleButton.addClickListener(new Button.ClickListener() { + @Override + public void buttonClick(Button.ClickEvent event) { + textField.setLocale(Locale.GERMANY); + } + }); + + return changeLocaleButton; + } + + @Override + protected String getTestDescription() { + return "Read-only fields throw exception when setting converted value in localeMightHaveChanged()"; + } + + @Override + protected Integer getTicketNumber() { + return 14400; + } +} diff --git a/uitest/src/com/vaadin/tests/components/textfield/LocaleChangeOnReadOnlyFieldTest.java b/uitest/src/com/vaadin/tests/components/textfield/LocaleChangeOnReadOnlyFieldTest.java new file mode 100644 index 0000000000..03f490c73c --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/textfield/LocaleChangeOnReadOnlyFieldTest.java @@ -0,0 +1,24 @@ +package com.vaadin.tests.components.textfield; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.MultiBrowserTest; +import org.junit.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.core.Is.is; + +public class LocaleChangeOnReadOnlyFieldTest extends MultiBrowserTest { + + @Test + public void localeIsChangedOnReadOnlyField() { + openTestURL(); + + TextFieldElement textField = $(TextFieldElement.class).first(); + assertThat(textField.getValue(), is("1,024,000")); + + $(ButtonElement.class).caption("Change Locale").first().click(); + assertThat(textField.getValue(), is("1.024.000")); + } + +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/textfield/RequiredTextField.java b/uitest/src/com/vaadin/tests/components/textfield/RequiredTextField.java new file mode 100644 index 0000000000..79f5a7e83b --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/textfield/RequiredTextField.java @@ -0,0 +1,57 @@ +/* + * 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.textfield; + +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.Button.ClickListener; +import com.vaadin.ui.TextField; + +/** + * Test for required text field. + * + * @author Vaadin Ltd + */ +public class RequiredTextField extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final TextField field = new TextField(); + + addComponent(field); + + Button button = new Button("Set/unset required", new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + field.setRequired(!field.isRequired()); + } + }); + addComponent(button); + } + + @Override + protected String getTestDescription() { + return "Add .v-required style when setRequired() is used"; + } + + @Override + protected Integer getTicketNumber() { + return 10201; + } +} diff --git a/uitest/src/com/vaadin/tests/components/textfield/RequiredTextFieldTest.java b/uitest/src/com/vaadin/tests/components/textfield/RequiredTextFieldTest.java new file mode 100644 index 0000000000..c6ed6c1a58 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/textfield/RequiredTextFieldTest.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.components.textfield; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.testbench.elements.TextFieldElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +/** + * Test for .v-required style + * + * @author Vaadin Ltd + */ +public class RequiredTextFieldTest extends MultiBrowserTest { + + @Test + public void testRequiredStyleName() { + openTestURL(); + + $(ButtonElement.class).first().click(); + + Assert.assertTrue("Text field doesn't contain .v-required style", + getStyles().contains("v-required")); + + $(ButtonElement.class).first().click(); + + Assert.assertFalse( + "Text field contains .v-required style for non-required field", + getStyles().contains("v-required")); + } + + private String getStyles() { + return $(TextFieldElement.class).first().getAttribute("class"); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/tree/TreeItemDoubleClick.java b/uitest/src/com/vaadin/tests/components/tree/TreeItemDoubleClick.java new file mode 100644 index 0000000000..8b7890e63c --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tree/TreeItemDoubleClick.java @@ -0,0 +1,61 @@ +package com.vaadin.tests.components.tree; + +import com.vaadin.event.ItemClickEvent; +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.Tree; + +public class TreeItemDoubleClick extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + final Tree tree = new Tree("Immediate With ItemClickListener"); + tree.setImmediate(true); + tree.setNullSelectionAllowed(false); + + for (int i = 1; i < 6; i++) { + tree.addItem("Tree Item " + i); + } + + ItemClickEvent.ItemClickListener listener = new ItemClickEvent.ItemClickListener() { + @Override + public void itemClick(ItemClickEvent event) { + if (event.isDoubleClick()) { + log.log("Double Click " + event.getItemId()); + } + } + }; + + tree.addItemClickListener(listener); + + addComponent(tree); + + Button button = new Button("Change immediate flag"); + button.addClickListener(new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + // this wouldn't work if tree had a value change listener + tree.setImmediate(!tree.isImmediate()); + log.log("tree.isImmediate() is now " + tree.isImmediate()); + } + + }); + + addComponent(button); + + } + + @Override + protected String getTestDescription() { + return "Tests that double click is fired"; + } + + @Override + protected Integer getTicketNumber() { + return 14745; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/tree/TreeItemDoubleClickTest.java b/uitest/src/com/vaadin/tests/components/tree/TreeItemDoubleClickTest.java new file mode 100644 index 0000000000..95a3f02d60 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tree/TreeItemDoubleClickTest.java @@ -0,0 +1,65 @@ +/* + * 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 static org.hamcrest.MatcherAssert.assertThat; + +import org.junit.Test; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +import com.vaadin.testbench.By; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class TreeItemDoubleClickTest extends MultiBrowserTest { + + @Test + public void test() throws InterruptedException { + openTestURL(); + String caption = "Tree Item 2"; + doubleClick(getTreeNodeByCaption(caption)); + assertLogText("Double Click " + caption); + + changeImmediate(); + + caption = "Tree Item 3"; + doubleClick(getTreeNodeByCaption(caption)); + assertLogText("Double Click " + caption); + } + + private void changeImmediate() { + $(ButtonElement.class).caption("Change immediate flag").first().click(); + assertLogText("tree.isImmediate() is now"); + } + + private WebElement getTreeNodeByCaption(String caption) { + return getDriver().findElement( + By.xpath("//span[text() = '" + caption + "']")); + } + + private void doubleClick(WebElement element) { + new Actions(getDriver()).doubleClick(element).build().perform(); + + } + + private void assertLogText(String text) { + assertThat( + String.format("Couldn't find text '%s' from the log.", text), + logContainsText(text)); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableRowIcons.java b/uitest/src/com/vaadin/tests/components/treetable/TreeTableRowIcons.java new file mode 100644 index 0000000000..0668a6aeeb --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableRowIcons.java @@ -0,0 +1,57 @@ +package com.vaadin.tests.components.treetable; + +import static com.vaadin.server.Sizeable.Unit.PIXELS; + +import com.vaadin.server.FontAwesome; +import com.vaadin.server.Resource; +import com.vaadin.server.ThemeResource; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.TreeTable; + +public class TreeTableRowIcons extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + HorizontalLayout layout = new HorizontalLayout(); + layout.setSpacing(true); + addComponent(layout); + + layout.addComponent(createTreeTableAndPopulate(new ThemeResource( + "../runo/icons/16/ok.png"))); + layout.addComponent(createTreeTableAndPopulate(FontAwesome.ANDROID)); + } + + private TreeTable createTreeTableAndPopulate(Resource icon) { + TreeTable tt = new TreeTable(); + tt.addContainerProperty("Foo", String.class, ""); + tt.setColumnWidth("Foo", 100); + tt.addContainerProperty("Bar", String.class, ""); + tt.setColumnWidth("Bar", 100); + tt.setIcon(icon); + tt.setHeight(400, PIXELS); + + Object item1 = tt.addItem(new Object[] { "Foo", "Bar" }, null); + Object item2 = tt.addItem(new Object[] { "Foo2", "Bar2" }, null); + tt.setItemIcon(item1, icon); + tt.setItemIcon(item2, icon); + + tt.setParent(item2, item1); + + tt.setCollapsed(item1, false); + + return tt; + } + + @Override + protected String getTestDescription() { + return "TreeTable should support font icons for items"; + } + + @Override + protected Integer getTicketNumber() { + return 14077; + } + +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableRowIconsTest.java b/uitest/src/com/vaadin/tests/components/treetable/TreeTableRowIconsTest.java new file mode 100644 index 0000000000..2e299d62ea --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableRowIconsTest.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.components.treetable; + +import java.io.IOException; + +import org.junit.Test; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class TreeTableRowIconsTest extends MultiBrowserTest { + + public final String SCREENSHOT_NAME = "TreeTableRowIcons"; + + @Test + public void checkScreenshot() throws IOException { + openTestURL(); + compareScreen(SCREENSHOT_NAME); + } + +} diff --git a/uitest/src/com/vaadin/tests/components/ui/ComboboxSelectedItemTextTest.java b/uitest/src/com/vaadin/tests/components/ui/ComboboxSelectedItemTextTest.java index f826654022..ab398035ac 100644 --- a/uitest/src/com/vaadin/tests/components/ui/ComboboxSelectedItemTextTest.java +++ b/uitest/src/com/vaadin/tests/components/ui/ComboboxSelectedItemTextTest.java @@ -18,9 +18,11 @@ package com.vaadin.tests.components.ui; import java.io.IOException; import org.junit.Test; -import org.openqa.selenium.By; +import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedCondition; +import com.vaadin.testbench.By; import com.vaadin.tests.tb3.MultiBrowserTest; /** @@ -59,24 +61,26 @@ public class ComboboxSelectedItemTextTest extends MultiBrowserTest { WebElement comboBoxFocus = vaadinElement("/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[" + indexToFocus + "]/VFilterSelect[0]"); - // Select an element from the first (editable) combobox. + // Select an element from the first (to test) combobox. comboBox.findElement(By.className("v-filterselect-button")).click(); + waitForPopup(comboBox); WebElement comboBoxPopup = vaadinElement("/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[" + indexToTest + "]/VFilterSelect[0]#popup"); comboBoxPopup.findElements(By.tagName("td")).get(2).click(); - // Select an element from the second (non-editable combobox) to remove + // Select an element from the second (to focus) combobox to remove // focus from the first combobox comboBoxFocus.findElement(By.className("v-filterselect-button")) .click(); + waitForPopup(comboBoxFocus); comboBoxPopup = vaadinElement("/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[" + indexToFocus + "]/VFilterSelect[0]#popup"); comboBoxPopup.findElements(By.tagName("td")).get(2).click(); - // click the popup on the first combobox. This would reveal the unwanted - // behaviour. + // click the button of the first combobox. This would reveal the + // unwanted behaviour. comboBox.findElement(By.className("v-filterselect-button")).click(); @@ -87,4 +91,13 @@ public class ComboboxSelectedItemTextTest extends MultiBrowserTest { } + private void waitForPopup(final WebElement comboBox) { + waitUntilNot(new ExpectedCondition<Boolean>() { + @Override + public Boolean apply(WebDriver input) { + return comboBox.findElements(By.vaadin("#popup")).isEmpty(); + } + }, 10); + } + }
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/ui/UIAutoGeneratedStyleName.java b/uitest/src/com/vaadin/tests/components/ui/UIAutoGeneratedStyleName.java new file mode 100644 index 0000000000..35dc4cd325 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/UIAutoGeneratedStyleName.java @@ -0,0 +1,43 @@ +/* + * 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.ui; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; + +/** + * Empty test ui to check auto-generated style name for UI. + * + * @author Vaadin Ltd + */ +public class UIAutoGeneratedStyleName extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + } + + @Override + protected String getTestDescription() { + return "Add UI specific autogenerated style name to ui div element" + + " and overlays container"; + } + + @Override + protected Integer getTicketNumber() { + return 14670; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/ui/UIAutoGeneratedStyleNameTest.java b/uitest/src/com/vaadin/tests/components/ui/UIAutoGeneratedStyleNameTest.java new file mode 100644 index 0000000000..73e0a7f6b9 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/UIAutoGeneratedStyleNameTest.java @@ -0,0 +1,55 @@ +/* + * 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.ui; + +import com.vaadin.tests.tb3.MultiBrowserTest; +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; + +import java.util.Locale; + +/** + * Test to check auto-generated style name for UI div and overlays div. + * + * @author Vaadin Ltd + */ +public class UIAutoGeneratedStyleNameTest extends MultiBrowserTest { + + @Test + public void testUiStyleName() { + openTestURL(); + + Assert.assertTrue( + "UI div element doesn't contain autogenerated style name", + containsStyle(getDriver().findElement(By.className("v-app")), + UIAutoGeneratedStyleName.class.getSimpleName() + .toLowerCase(Locale.ENGLISH))); + + Assert.assertTrue( + "Overlays div element doesn't contain autogenerated style name", + containsStyle( + getDriver().findElement( + By.className("v-overlay-container")), + UIAutoGeneratedStyleName.class.getSimpleName() + .toLowerCase(Locale.ENGLISH))); + } + + private boolean containsStyle(WebElement element, String style) { + return element.getAttribute("class").contains(style); + } +} diff --git a/uitest/src/com/vaadin/tests/components/ui/UISerializationTest.java b/uitest/src/com/vaadin/tests/components/ui/UISerializationTest.java index cbe4dbdf29..2b6ba40e8c 100644 --- a/uitest/src/com/vaadin/tests/components/ui/UISerializationTest.java +++ b/uitest/src/com/vaadin/tests/components/ui/UISerializationTest.java @@ -1,18 +1,17 @@ package com.vaadin.tests.components.ui; -import static org.hamcrest.Matchers.allOf; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.endsWith; -import static org.hamcrest.Matchers.startsWith; -import static org.junit.Assert.assertThat; - -import org.junit.Test; - import com.vaadin.testbench.elements.ButtonElement; import com.vaadin.tests.tb3.SingleBrowserTest; +import org.junit.Ignore; +import org.junit.Test; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.assertThat; public class UISerializationTest extends SingleBrowserTest { + @Test + @Ignore //Broken on all browsers since 9696e6c3e7e952b66ac3f5c9ddc3dfca4233451e public void tb2test() throws Exception { openTestURL(); $(ButtonElement.class).first().click(); diff --git a/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAriaTest.java b/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAriaTest.java index 44aea3522a..4ec1dc5c98 100644 --- a/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAriaTest.java +++ b/uitest/src/com/vaadin/tests/components/window/ExtraWindowShownWaiAriaTest.java @@ -47,26 +47,26 @@ public class ExtraWindowShownWaiAriaTest extends MultiBrowserTest { assertFalse(buttons.isEmpty()); // open alert dialog - buttons.get(0).click(); + ButtonElement button = buttons.get(0); + button.click(); // ensure dialog opened - List<WindowElement> windows = $(WindowElement.class).all(); - assertFalse(windows.isEmpty()); + waitForElementPresent(By.className("v-window")); + WindowElement window = $(WindowElement.class).first(); // ensure correct attributes - assertEquals("alertdialog", windows.get(0).getAttribute("role")); + assertEquals("alertdialog", window.getAttribute("role")); - WebElement header = windows.get(0).findElement( - By.className("v-window-header")); + WebElement header = window.findElement(By.className("v-window-header")); assertEquals(header.getAttribute("id"), - windows.get(0).getAttribute("aria-labelledby")); + window.getAttribute("aria-labelledby")); - WebElement label = windows.get(0).findElement(By.className("v-label")); + WebElement label = window.findElement(By.className("v-label")); assertEquals(label.getAttribute("id"), - windows.get(0).getAttribute("aria-describedby")); + window.getAttribute("aria-describedby")); - List<WebElement> wButtons = windows.get(0).findElements( - By.className("v-button")); + List<WebElement> wButtons = window.findElements(By + .className("v-button")); assertEquals("button", wButtons.get(0).getAttribute("role")); assertEquals("button", wButtons.get(1).getAttribute("role")); @@ -74,7 +74,7 @@ public class ExtraWindowShownWaiAriaTest extends MultiBrowserTest { wButtons.get(0).click(); // ensure dialog closed - windows = $(WindowElement.class).all(); + List<WindowElement> windows = $(WindowElement.class).all(); assertTrue(windows.isEmpty()); // check additional description (second checkbox on the page) @@ -87,19 +87,20 @@ public class ExtraWindowShownWaiAriaTest extends MultiBrowserTest { assertEquals("true", input.getAttribute("checked")); // open alert dialog - buttons = $(ButtonElement.class).all(); - buttons.get(0).click(); + button = $(ButtonElement.class).first(); + button.click(); + + waitForElementPresent(By.className("v-window")); // ensure correct attributes - windows = $(WindowElement.class).all(); - List<WebElement> labels = windows.get(0).findElements( - By.className("v-label")); + window = $(WindowElement.class).first(); + List<WebElement> labels = window.findElements(By.className("v-label")); assertEquals(labels.get(0).getAttribute("id") + " " - + labels.get(1).getAttribute("id"), windows.get(0) - .getAttribute("aria-describedby")); + + labels.get(1).getAttribute("id"), + window.getAttribute("aria-describedby")); // close dialog - wButtons = windows.get(0).findElements(By.className("v-button")); + wButtons = window.findElements(By.className("v-button")); wButtons.get(0).click(); // ensure dialog closed @@ -112,12 +113,14 @@ public class ExtraWindowShownWaiAriaTest extends MultiBrowserTest { textFields.get(1).sendKeys(" - do ASAP"); // open alert dialog - buttons = $(ButtonElement.class).all(); - buttons.get(0).click(); + button = $(ButtonElement.class).first(); + button.click(); + + waitForElementPresent(By.className("v-window")); // ensure the assistive spans have been added to the header - windows = $(WindowElement.class).all(); - header = windows.get(0).findElement(By.className("v-window-header")); + window = $(WindowElement.class).first(); + header = window.findElement(By.className("v-window-header")); List<WebElement> assistiveElements = header.findElements(By .className("v-assistive-device-only")); assertEquals("Important", diff --git a/uitest/src/com/vaadin/tests/components/window/WindowWithIcon.java b/uitest/src/com/vaadin/tests/components/window/WindowWithIcon.java new file mode 100644 index 0000000000..15ceb569b4 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/window/WindowWithIcon.java @@ -0,0 +1,27 @@ +package com.vaadin.tests.components.window; + +import com.vaadin.server.FontAwesome; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Window; + +public class WindowWithIcon extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Window window = new Window("Window Caption"); + window.setIcon(FontAwesome.ROCKET); + addWindow(window); + } + + @Override + protected String getTestDescription() { + return "Window should work properly with font icons."; + } + + @Override + protected Integer getTicketNumber() { + return 14481; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/window/WindowWithIconTest.java b/uitest/src/com/vaadin/tests/components/window/WindowWithIconTest.java new file mode 100644 index 0000000000..25c7f57238 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/window/WindowWithIconTest.java @@ -0,0 +1,16 @@ +package com.vaadin.tests.components.window; + +import org.junit.Test; + +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class WindowWithIconTest extends MultiBrowserTest { + + @Test + public void testWindowWithIcon() throws Exception { + openTestURL(); + + compareScreen("icon-rendered-properly"); + } + +} diff --git a/uitest/src/com/vaadin/tests/fonticon/FontIconsTest.java b/uitest/src/com/vaadin/tests/fonticon/FontIconsTest.java index 61a38bf552..948c3c13b2 100644 --- a/uitest/src/com/vaadin/tests/fonticon/FontIconsTest.java +++ b/uitest/src/com/vaadin/tests/fonticon/FontIconsTest.java @@ -15,9 +15,14 @@ */ package com.vaadin.tests.fonticon; +import static org.junit.Assert.assertEquals; + import java.io.IOException; import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; import com.vaadin.tests.tb3.MultiBrowserTest; @@ -28,4 +33,33 @@ public class FontIconsTest extends MultiBrowserTest { openTestURL(); compareScreen("all"); } + + @Test + public void comboBoxItemIconsOnKeyboardNavigation() throws Exception { + openTestURL(); + WebElement comboBoxInput = getDriver().findElement( + By.className("v-filterselect-input")); + + // No initial value. + assertEquals("", comboBoxInput.getText()); + + // Navigate to the first item with keyboard navigation. + sendKeys(comboBoxInput, Keys.ARROW_DOWN, Keys.ARROW_DOWN, + Keys.ARROW_DOWN); + + // Value must be "One" without any extra characters. + // See ticket #14660 + assertEquals("One", comboBoxInput.getAttribute("value")); + + // Check also the second item. + sendKeys(comboBoxInput, Keys.ARROW_DOWN); + assertEquals("Two", comboBoxInput.getAttribute("value")); + } + + private void sendKeys(WebElement element, Keys... keys) throws Exception { + for (Keys key : keys) { + element.sendKeys(key); + sleep(10); // For PhantomJS. + } + } } diff --git a/uitest/src/com/vaadin/tests/push/ReconnectLongPollingTest.java b/uitest/src/com/vaadin/tests/push/ReconnectLongPollingTest.java index 28ef30a04a..22d020b631 100644 --- a/uitest/src/com/vaadin/tests/push/ReconnectLongPollingTest.java +++ b/uitest/src/com/vaadin/tests/push/ReconnectLongPollingTest.java @@ -15,9 +15,22 @@ */ package com.vaadin.tests.push; +import org.openqa.selenium.remote.DesiredCapabilities; + +import java.util.List; + public class ReconnectLongPollingTest extends ReconnectTest { @Override + public List<DesiredCapabilities> getBrowsersToTest() { + + // PhantomJS doesn't seem to detect disconnection on + // Long-Polling/Streaming: + // https://github.com/ariya/phantomjs/issues/11938 + return getBrowsersExcludingPhantomJS(); + } + + @Override protected Class<?> getUIClass() { return BasicPushLongPolling.class; } diff --git a/uitest/src/com/vaadin/tests/push/ReconnectStreamingTest.java b/uitest/src/com/vaadin/tests/push/ReconnectStreamingTest.java index 0a0275c4d0..4a669e723c 100755 --- a/uitest/src/com/vaadin/tests/push/ReconnectStreamingTest.java +++ b/uitest/src/com/vaadin/tests/push/ReconnectStreamingTest.java @@ -15,9 +15,22 @@ */ package com.vaadin.tests.push; +import org.openqa.selenium.remote.DesiredCapabilities; + +import java.util.List; + public class ReconnectStreamingTest extends ReconnectTest { @Override + public List<DesiredCapabilities> getBrowsersToTest() { + + // PhantomJS doesn't seem to detect disconnection on + // Long-Polling/Streaming: + // https://github.com/ariya/phantomjs/issues/11938 + return getBrowsersExcludingPhantomJS(); + } + + @Override protected Class<?> getUIClass() { return BasicPushStreaming.class; } diff --git a/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java b/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java index 1d03a7df76..76b851fd23 100644 --- a/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java +++ b/uitest/src/com/vaadin/tests/tb3/AbstractTB3Test.java @@ -40,6 +40,7 @@ import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.runner.RunWith; import org.openqa.selenium.By; import org.openqa.selenium.JavascriptExecutor; @@ -94,6 +95,10 @@ import elemental.json.impl.JsonUtil; */ @RunWith(value = TB3Runner.class) public abstract class AbstractTB3Test extends TestBenchTestCase { + + @Rule + public RetryOnFail retry = new RetryOnFail(); + /** * Height of the screenshots we want to capture */ diff --git a/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java b/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java index 72fb2c18d1..d6eed3e5c8 100644 --- a/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java +++ b/uitest/src/com/vaadin/tests/tb3/MultiBrowserTest.java @@ -40,6 +40,14 @@ import org.openqa.selenium.remote.DesiredCapabilities; */ public abstract class MultiBrowserTest extends PrivateTB3Configuration { + protected List<DesiredCapabilities> getBrowsersExcludingPhantomJS() { + List<DesiredCapabilities> browsers = new ArrayList<DesiredCapabilities>(getAllBrowsers()); + + browsers.remove(Browser.PHANTOMJS.getDesiredCapabilities()); + + return browsers; + } + protected List<DesiredCapabilities> getBrowsersExcludingIE() { List<DesiredCapabilities> browsers = new ArrayList<DesiredCapabilities>(getAllBrowsers()); browsers.remove(Browser.IE8.getDesiredCapabilities()); @@ -50,7 +58,6 @@ public abstract class MultiBrowserTest extends PrivateTB3Configuration { return browsers; } - protected List<DesiredCapabilities> getBrowsersSupportingShiftClick() { List<DesiredCapabilities> browsers = new ArrayList<DesiredCapabilities>(getAllBrowsers()); diff --git a/uitest/src/com/vaadin/tests/tb3/PrivateTB3Configuration.java b/uitest/src/com/vaadin/tests/tb3/PrivateTB3Configuration.java index ff824ad98a..a4bb85bb53 100644 --- a/uitest/src/com/vaadin/tests/tb3/PrivateTB3Configuration.java +++ b/uitest/src/com/vaadin/tests/tb3/PrivateTB3Configuration.java @@ -49,6 +49,7 @@ public abstract class PrivateTB3Configuration extends ScreenshotTB3Test { private static final String RUN_LOCALLY_PROPERTY = "com.vaadin.testbench.runLocally"; private static final String HOSTNAME_PROPERTY = "com.vaadin.testbench.deployment.hostname"; private static final String PORT_PROPERTY = "com.vaadin.testbench.deployment.port"; + private static final String DEPLOYMENT_PROPERTY = "com.vaadin.testbench.deployment.url"; private static final Properties properties = new Properties(); private static final File propertiesFile = new File("work", "eclipse-run-selected-test.properties"); @@ -97,6 +98,15 @@ public abstract class PrivateTB3Configuration extends ScreenshotTB3Test { } @Override + protected String getBaseURL() { + String url = getProperty(DEPLOYMENT_PROPERTY); + if (url == null || url.trim().isEmpty()) { + return super.getBaseURL(); + } + return url; + } + + @Override protected String getDeploymentHostname() { if (getRunLocallyBrowser() != null) { return "localhost"; @@ -180,7 +190,7 @@ public abstract class PrivateTB3Configuration extends ScreenshotTB3Test { /* * (non-Javadoc) - * + * * @see com.vaadin.tests.tb3.AbstractTB3Test#setupLocalDriver() */ @Override diff --git a/uitest/src/com/vaadin/tests/tb3/RetryOnFail.java b/uitest/src/com/vaadin/tests/tb3/RetryOnFail.java index 9c12147314..3c22057863 100644 --- a/uitest/src/com/vaadin/tests/tb3/RetryOnFail.java +++ b/uitest/src/com/vaadin/tests/tb3/RetryOnFail.java @@ -19,20 +19,7 @@ import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; -/** - * <strong>ALWAYS</strong> declare the reason for using this test rule in a - * test. - * - * <p> - * Violators and abusers of this rule will be punished. - * </p> - * - * @since 7.1.14 - * @author Vaadin Ltd - */ public class RetryOnFail implements TestRule { - private int retryCount = 1; - @Override public Statement apply(Statement base, Description description) { return statement(base, description); @@ -44,6 +31,7 @@ public class RetryOnFail implements TestRule { @Override public void evaluate() throws Throwable { Throwable caughtThrowable = null; + int retryCount = getRetryCount(); for (int i = 0; i <= retryCount; i++) { try { @@ -60,6 +48,16 @@ public class RetryOnFail implements TestRule { } throw caughtThrowable; } + + private int getRetryCount() { + String retryCount = System.getProperty("com.vaadin.testbench.max.retries"); + + if(retryCount != null && retryCount != "") { + return Integer.parseInt(retryCount); + } + + return 0; + } }; } } diff --git a/uitest/src/com/vaadin/tests/tb3/ScreenshotTB3Test.java b/uitest/src/com/vaadin/tests/tb3/ScreenshotTB3Test.java index a99eea9bf6..ed5a2ed8aa 100644 --- a/uitest/src/com/vaadin/tests/tb3/ScreenshotTB3Test.java +++ b/uitest/src/com/vaadin/tests/tb3/ScreenshotTB3Test.java @@ -68,13 +68,15 @@ public abstract class ScreenshotTB3Test extends AbstractTB3Test { * Contains a list of screenshot identifiers for which * {@link #compareScreen(String)} has failed during the test */ - private List<String> screenshotFailures = new ArrayList<String>(); + private List<String> screenshotFailures; /** * Defines TestBench screen comparison parameters before each test run */ @Before public void setupScreenComparisonParameters() { + screenshotFailures = new ArrayList<String>(); + Parameters.setScreenshotErrorDirectory(getScreenshotErrorDirectory()); Parameters .setScreenshotReferenceDirectory(getScreenshotReferenceDirectory()); diff --git a/uitest/src/com/vaadin/tests/themes/FaviconTest.java b/uitest/src/com/vaadin/tests/themes/FaviconTest.java new file mode 100644 index 0000000000..31134f656f --- /dev/null +++ b/uitest/src/com/vaadin/tests/themes/FaviconTest.java @@ -0,0 +1,60 @@ +package com.vaadin.tests.themes; + +import com.vaadin.tests.tb3.SingleBrowserTest; +import org.junit.Test; + +import java.net.HttpURLConnection; +import java.net.URL; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; + +//Extending SingleBrowserTest just to include the test into our test suites. +public class FaviconTest extends SingleBrowserTest { + + @Test + public void chameleonHasFavicon() { + assertThatThemeHasFavicon("chameleon"); + } + + @Test + public void liferayHasFavicon() { + assertThatThemeHasFavicon("liferay"); + } + + @Test + public void runoHasFavicon() { + assertThatThemeHasFavicon("runo"); + } + + @Test + public void reindeerHasFavicon() { + assertThatThemeHasFavicon("reindeer"); + } + + @Test + public void valoHasFavicon() { + assertThatThemeHasFavicon("valo"); + } + + private void assertThatThemeHasFavicon(String theme) { + assertThat(getResponseCode(theme), is(200)); + } + + private int getResponseCode(String theme) { + try { + URL url = new URL(String.format("%s/VAADIN/themes/%s/favicon.ico", getBaseURL(), theme)); + HttpURLConnection connection = (HttpURLConnection)url.openConnection(); + connection.setRequestMethod("GET"); + connection.connect(); + + return connection.getResponseCode(); + + } catch (Exception e) { + fail(e.getMessage()); + } + + return 0; + } +} diff --git a/uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFlyTest.java b/uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFlyTest.java index eb010e82ee..a5657c4eec 100644 --- a/uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFlyTest.java +++ b/uitest/src/com/vaadin/tests/themes/ThemeChangeOnTheFlyTest.java @@ -15,10 +15,8 @@ */ package com.vaadin.tests.themes; -import java.io.IOException; -import java.util.List; - -import org.junit.Assert; +import com.vaadin.testbench.elements.ButtonElement; +import com.vaadin.tests.tb3.MultiBrowserTest; import org.junit.Test; import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; @@ -26,8 +24,11 @@ import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.support.ui.ExpectedCondition; -import com.vaadin.testbench.elements.ButtonElement; -import com.vaadin.tests.tb3.MultiBrowserTest; +import java.io.IOException; +import java.util.List; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; public class ThemeChangeOnTheFlyTest extends MultiBrowserTest { @@ -100,9 +101,8 @@ public class ThemeChangeOnTheFlyTest extends MultiBrowserTest { @Override public Boolean apply(WebDriver input) { String rootClass = rootDiv.getAttribute("class").trim(); - String expected = "v-app " + theme; - expected = expected.trim(); - return rootClass.equals(expected); + + return rootClass.contains(theme); } }, 30); } @@ -110,12 +110,9 @@ public class ThemeChangeOnTheFlyTest extends MultiBrowserTest { private void assertOverlayTheme(String theme) { final WebElement overlayContainerDiv = findElement(By .xpath("//div[contains(@class,'v-overlay-container')]")); - String expected = "v-app v-overlay-container " + theme; - expected = expected.trim(); - String overlayClass = overlayContainerDiv.getAttribute("class").trim(); - Assert.assertEquals(expected, overlayClass); + assertThat(overlayClass, containsString(theme)); } } diff --git a/uitest/src/com/vaadin/tests/themes/valo/DateFields.java b/uitest/src/com/vaadin/tests/themes/valo/DateFields.java index 5dca5ab9af..5a752cd985 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/DateFields.java +++ b/uitest/src/com/vaadin/tests/themes/valo/DateFields.java @@ -190,6 +190,7 @@ public class DateFields extends VerticalLayout implements View { date = new InlineDateField("Date picker"); setDate(date); + setDateRange(date); row.addComponent(date); date = new InlineDateField("Date picker with week numbers"); @@ -211,6 +212,14 @@ public class DateFields extends VerticalLayout implements View { row.addComponent(form); } + private void setDateRange(DateField date) { + date.setRangeStart(getDefaultDate()); + + Date endDate = getDefaultDate(); + endDate.setMonth(endDate.getMonth() + 1); + date.setRangeEnd(endDate); + } + private void setDate(DateField date) { date.setValue(getDefaultDate()); } diff --git a/uitest/src/com/vaadin/tests/themes/valo/TableWithEmptyCaption.java b/uitest/src/com/vaadin/tests/themes/valo/TableWithEmptyCaption.java new file mode 100644 index 0000000000..a3a81b3798 --- /dev/null +++ b/uitest/src/com/vaadin/tests/themes/valo/TableWithEmptyCaption.java @@ -0,0 +1,57 @@ +/* + * 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.themes.valo; + +import com.vaadin.annotations.Theme; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Component; +import com.vaadin.ui.Table; + +@Theme("valo") +public class TableWithEmptyCaption extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + Table table = new Table(); + table.addContainerProperty("first", String.class, ""); + table.addContainerProperty("last", String.class, ""); + table.addContainerProperty("actions", Component.class, null); + + table.addItem(new Object[] { "Teemu", "Test", new Button("Edit") }, 1); + table.addItem(new Object[] { "Dummy", "Test", new Button("Edit") }, 2); + + table.setPageLength(0); + table.setColumnHeaders("First Name", "Last Name", ""); + + table.setFooterVisible(true); + table.setColumnFooter("first", "Footer"); + table.setColumnFooter("last", ""); + table.setColumnFooter("actions", ""); + addComponent(table); + } + + @Override + protected String getTestDescription() { + return "Test that column headers (and footers) work properly with empty captions."; + } + + @Override + protected Integer getTicketNumber() { + return 14812; + } +} diff --git a/uitest/src/com/vaadin/tests/themes/valo/TableWithEmptyCaptionTest.java b/uitest/src/com/vaadin/tests/themes/valo/TableWithEmptyCaptionTest.java new file mode 100644 index 0000000000..04de8b498c --- /dev/null +++ b/uitest/src/com/vaadin/tests/themes/valo/TableWithEmptyCaptionTest.java @@ -0,0 +1,42 @@ +/* + * 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.themes.valo; + +import org.junit.Test; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.support.ui.ExpectedConditions; + +import com.vaadin.testbench.By; +import com.vaadin.tests.tb3.MultiBrowserTest; + +public class TableWithEmptyCaptionTest extends MultiBrowserTest { + + @Test + public void testEmptyCaption() throws Exception { + openTestURL(); + + // Wait for the loading bar to disappear before taking the screenshot. + try { + waitUntil(ExpectedConditions.invisibilityOfElementLocated(By + .className("v-loading-indicator")), 5); + } catch (TimeoutException e) { + // Just take the screenshot, PhantomJS always times out. + } + + compareScreen("table-empty-caption"); + } + +} diff --git a/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java b/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java index 20c74bff5e..d4ddf2dcf9 100644 --- a/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java +++ b/uitest/src/com/vaadin/tests/themes/valo/ValoThemeUITest.java @@ -64,7 +64,7 @@ public class ValoThemeUITest extends MultiBrowserTest { // Note that this can look broken in IE9 because of some browser // rendering issue... The problem seems to be in the customized // horizontal layout in the test app - compareScreen("datefields-with-disabled"); + compareScreen("datefields-with-range"); } @Test diff --git a/uitest/tb3test.xml b/uitest/tb3test.xml index a39725f1bb..975298926e 100644 --- a/uitest/tb3test.xml +++ b/uitest/tb3test.xml @@ -8,6 +8,7 @@ <property name="categories.include" value="" /> <property name="categories.exclude" value="" /> <property name="useLocalWebDriver" value="false" /> + <property name="com.vaadin.testbench.max.retries" value="0" /> <ivy:resolve file="${tb3test.dir}/ivy.xml" conf="build, build-provided" /> <ivy:cachepath pathid="classpath.tb3.lib" conf="build, build-provided" /> @@ -39,6 +40,7 @@ <jvmarg value="-Dcategories.include=${categories.include}" /> <jvmarg value="-Dcategories.exclude=${categories.exclude}" /> <jvmarg value="-DuseLocalWebDriver=${useLocalWebDriver}" /> + <jvmarg value="-Dcom.vaadin.testbench.max.retries=${com.vaadin.testbench.max.retries}" /> <test name="${junit.test.suite}" todir="${report.dir}" /> </junit> |