/* * Copyright 2000-2016 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.testbench.elements; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; import com.vaadin.testbench.By; import com.vaadin.testbench.TestBenchElement; import com.vaadin.testbench.elementsbase.ServerClass; @ServerClass("com.vaadin.ui.ComboBox") public class ComboBoxElement extends AbstractSelectElement { private static org.openqa.selenium.By bySuggestionPopup = By .vaadin("#popup"); private static org.openqa.selenium.By byNextPage = By .className("v-filterselect-nextpage"); private static org.openqa.selenium.By byPrevPage = By .className("v-filterselect-prevpage"); /** * Selects the first option in the ComboBox which matches the given text. * * @param text * the text of the option to select */ public void selectByText(String text) { if (isReadOnly()) { throw new ReadOnlyException(); } if (!isTextInputAllowed()) { selectByTextFromPopup(text); return; } getInputField().clear(); getInputField().sendKeys(text); selectSuggestion(text); } /** * Selects, without filtering, the first option in the ComboBox which * matches the given text. * * @param text * the text of the option to select */ private void selectByTextFromPopup(String text) { // This method assumes there is no need to touch the filter string // 1. Find first page // 2. Select first matching text if found // 3. Iterate towards end while (openPrevPage()) { // Scroll until beginning } do { if (selectSuggestion(text)) { return; } } while (openNextPage()); } private boolean selectSuggestion(String text) { for (WebElement suggestion : getPopupSuggestionElements()) { if (text.equals(suggestion.getText())) { clickElement(suggestion); return true; } } return false; } private boolean isReadOnly(WebElement elem) { JavascriptExecutor js = (JavascriptExecutor) getDriver(); return (Boolean) js.executeScript("return arguments[0].readOnly", elem); } /** * Checks if text input is allowed for the combo box. * * @return <code>true</code> if text input is allowed, <code>false</code> * otherwise */ public boolean isTextInputAllowed() { return !isReadOnly(getInputField()); } /** * Checks whether the suggestion popup is open or not. * * @return {@code true} if popup is open, {@code false if not} */ public boolean isPopupOpen() { return isElementPresent(bySuggestionPopup); } /** * Open the suggestion popup */ public void openPopup() { findElement(By.vaadin("#button")).click(); } /** * Gets the text representation of all suggestions on the current page * * @return List of suggestion texts */ public List<String> getPopupSuggestions() { List<String> suggestionsTexts = new ArrayList<>(); List<WebElement> suggestions = getPopupSuggestionElements(); for (WebElement suggestion : suggestions) { String text = suggestion.getText(); if (!text.isEmpty()) { suggestionsTexts.add(text); } } return suggestionsTexts; } /** * Gets the elements of all suggestions on the current page. * <p> * Opens the popup if not already open. * * @return a list of elements for the suggestions on the current page */ public List<WebElement> getPopupSuggestionElements() { List<WebElement> tables = getSuggestionPopup() .findElements(By.tagName("table")); if (tables == null || tables.isEmpty()) { return Collections.emptyList(); } WebElement table = tables.get(0); return table.findElements(By.tagName("td")); } /** * Opens next popup page. * * @return True if next page opened. false if doesn't have next page */ public boolean openNextPage() { try { clickElement(getSuggestionPopup().findElement(byNextPage)); return true; } catch (WebDriverException e) { // PhantomJS driver can throw WDE instead of the more specific // NoSuchElementException return false; } } /** * Open previous popup page. * * @return True if previous page opened. False if doesn't have previous page */ public boolean openPrevPage() { try { clickElement(getSuggestionPopup().findElement(byPrevPage)); return true; } catch (WebDriverException e) { // PhantomJS driver can throw WDE instead of the more specific // NoSuchElementException return false; } } /** * Returns the suggestion popup element */ public WebElement getSuggestionPopup() { ensurePopupOpen(); return findElement(bySuggestionPopup); } /** * Return value of the combo box element * * @return value of the combo box element */ public String getValue() { return getInputField().getAttribute("value"); } /** * Returns the text input field element, used for entering text into the * combo box. * * @return the input field element */ public WebElement getInputField() { return findElement(By.vaadin("#textbox")); } private void ensurePopupOpen() { if (!isElementPresent(bySuggestionPopup)) { openPopup(); } } @Override public String getText() { return getInputField().getAttribute("value"); } @Override public void clear() { getInputField().clear(); } @Override public void sendKeys(CharSequence... keysToSend) { sendKeys(50, keysToSend); } /** * Use this method to simulate typing into an element, which may set its * value. * * @param delay * delay after sending each individual key (mainly needed for * PhantomJS) * @param keysToSend * keys to type into the element */ public void sendKeys(int delay, CharSequence... keysToSend) { WebElement input = getInputField(); for (CharSequence key : keysToSend) { input.sendKeys(key); try { Thread.sleep(delay); } catch (InterruptedException e) { } } } private void clickElement(WebElement element) { if (isFirefox()) { // Workaround for Selenium/TB and Firefox 45 issue ((TestBenchElement) element).clickHiddenElement(); } else { element.click(); } } }