com.seleniumtests.uipage.htmlelements.SelectList Maven / Gradle / Ivy
/**
* Orignal work: Copyright 2015 www.seleniumtests.com
* Modified work: Copyright 2016 www.infotel.com
* Copyright 2017-2019 B.Hecquet
*
* 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.seleniumtests.uipage.htmlelements;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.Select;
import com.seleniumtests.customexception.ConfigurationException;
import com.seleniumtests.customexception.CustomSeleniumTestsException;
import com.seleniumtests.customexception.ScenarioException;
import com.seleniumtests.uipage.ReplayOnError;
import com.seleniumtests.uipage.htmlelements.select.ISelectList;
import com.seleniumtests.uipage.htmlelements.select.StubSelect;
import net.ricecode.similarity.JaroWinklerStrategy;
import net.ricecode.similarity.StringSimilarityService;
import net.ricecode.similarity.StringSimilarityServiceImpl;
/**
* Support both standard select tag and fake select consists of tag ul and li.
*/
public class SelectList extends HtmlElement {
private static List> selectImplementations;
private static Map> uiLibraries = Collections.synchronizedMap(new HashMap<>());
// register all UiLibraries
static {
if (selectImplementations == null) {
selectImplementations = searchRelevantImplementation();
// register UI library
for (Class extends ISelectList> selectClass: selectImplementations) {
try {
Method getUiLibraryMethod = selectClass.getDeclaredMethod("getUiLibrary");
String uiLibrary = (String) getUiLibraryMethod.invoke(null);
uiLibraries.put(uiLibrary, selectClass);
UiLibraryRegistry.register(uiLibrary);
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
throw new ConfigurationException(String.format("Error calling 'getUiLibrary' on %s: %s", selectClass.getCanonicalName(), e.getMessage()));
} catch (ClassCastException e) {
throw new ConfigurationException("Method 'getUiLibrary' must return an object of type String");
} catch (NoSuchMethodException e) {
throw new ConfigurationException(String.format("Class %s does not declare the static method 'public static String getUiLibrary(){return \"myLib\";}'", selectClass.getCanonicalName()));
} catch (SecurityException e) {
// nothing to do
}
}
}
}
private static final String ERROR_MULTI_SELECT = "You may only deselect all options of a multi-select";
protected ThreadLocal> options = new ThreadLocal<>();
private StringSimilarityService service = new StringSimilarityServiceImpl(new JaroWinklerStrategy());
private ThreadLocal selectImplementation = new ThreadLocal<>();
private List> implementationList;
protected ThreadLocal multiple = new ThreadLocal<>();
/**
* Creates a SelectList element which represents either
* - a standard HTML element => locator should point to the "select" element itself
* - a select list built with HTML lists (ul / li) => locator should point to element
* - an angular materials select => locator should point to the element
* @param text
* @param by
*/
public SelectList(final String label, final By by) {
super(label, by);
}
public SelectList(final String label, final By by, final Integer index) {
super(label, by, index);
}
public SelectList(final String label, final By by, final Integer index, Integer replayTimeout) {
super(label, by, index, replayTimeout);
}
public SelectList(final String label, final By by, final HtmlElement parent) {
super(label, by, parent);
}
public SelectList(final String label, final By by, final HtmlElement parent, final Integer index) {
super(label, by, parent, index);
}
public SelectList(final String label, final By by, final HtmlElement parent, final Integer index, Integer replayTimeout) {
super(label, by, parent, index, replayTimeout);
}
public SelectList(final String label, final By by, final FrameElement frame) {
super(label, by, frame);
}
public SelectList(final String label, final By by, final FrameElement frame, final Integer index) {
super(label, by, frame, index);
}
public SelectList(final String label, final By by, final FrameElement frame, final Integer index, Integer replayTimeout) {
super(label, by, frame, index, replayTimeout);
}
private static List> searchRelevantImplementation() {
List> selectImplementations = new ArrayList<>();
// load via SPI other Select implementations
ServiceLoader selectLoader = ServiceLoader.load(ISelectList.class);
Iterator selectsIterator = selectLoader.iterator();
while (selectsIterator.hasNext())
{
ISelectList selectClass = selectsIterator.next();
selectImplementations.add(selectClass.getClass());
}
if (selectImplementations.isEmpty()) {
throw new CustomSeleniumTestsException("Could not get list of Select classes");
}
return selectImplementations;
}
/**
* Reorder SelectList implementation depending on prefered libraries
* @param libraries
* @return
*/
private List> orderImplementations(List libraries) {
List> reorderedSelectImplementations = new ArrayList<>(selectImplementations);
for (String uiLibrary: libraries) {
if (uiLibraries.containsKey(uiLibrary) && reorderedSelectImplementations.remove(uiLibraries.get(uiLibrary))) {
reorderedSelectImplementations.add(0, uiLibraries.get(uiLibrary));
}
}
return reorderedSelectImplementations;
}
@Override
protected void findElement() {
// set a default type so that use of selectImplementation can be done
setSelectImplementation(new StubSelect());
super.findElement(true);
// if we have a prefered implementation, use it
// search the right select list handler
implementationList = orderImplementations(getPreferedUiLibraries());
for (Class extends ISelectList> selectClass: implementationList) {
try {
ISelectList selectInstance = selectClass.getConstructor(WebElement.class, FrameElement.class).newInstance(getRealElementNoSearch(), frameElement);
if (selectInstance.isApplicable()) {
setSelectImplementation(selectInstance);
selectInstance.setDriver(getDriver());
break;
}
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
logger.error(String.format("Cannot use Select implementation %s: %s", selectClass.getName(), e.getMessage()));
}
}
if (getSelectImplementation() instanceof StubSelect) {
throw new ScenarioException("Cannot find type of select " + getRealElementNoSearch().getTagName());
}
setOptions(getSelectImplementation().getOptions());
setMultipleSelect(getSelectImplementation().isMultipleWithoutFind());
}
/**
* Returns a new Select element (created to facilitate unit testing).
*
* @return
*/
protected Select getNewSelectElement(final WebElement element) {
return new Select(element);
}
protected void setOptions(List selectOptions) {
options.set(selectOptions);
}
protected List getOptionsNoSearch() {
return options.get();
}
@ReplayOnError
public List getOptions() {
try {
findElement();
return getOptionsNoSearch();
} finally {
getSelectImplementation().finalizeAction();
}
}
/**
* @return The first selected option in this select tag (or the currently selected option in a
* normal select)
* or null If no option is selected
*/
@ReplayOnError
public WebElement getFirstSelectedOption() {
return getTheFirstSelectedOption();
}
private WebElement getTheFirstSelectedOption() {
List allSelectedOptions = getAllTheSelectedOptions();
try {
return allSelectedOptions.get(0);
} catch (IndexOutOfBoundsException e) {
return null;
}
}
/**
* @return All selected options belonging to this select tag
*/
@ReplayOnError
public List getAllSelectedOptions() {
return getAllTheSelectedOptions();
}
private List getAllTheSelectedOptions() {
try {
findElement();
return getSelectImplementation().getAllSelectedOptions();
} finally {
getSelectImplementation().finalizeAction();
}
}
/**
* Do not replayOnError as the called method already does it
* @return
*/
public String getSelectedText() {
try {
return getSelectedTexts()[0];
} catch (IndexOutOfBoundsException e) {
return "";
}
}
@ReplayOnError
public String[] getSelectedTexts() {
List allSelectedOptions = getAllTheSelectedOptions();
List textList = new ArrayList<>();
for (WebElement option : allSelectedOptions) {
textList.add(getSelectImplementation().getOptionText(option));
}
String[] texts = new String[textList.size()];
return textList.toArray(texts);
}
@ReplayOnError
public String getSelectedValue() {
WebElement firstSelectedOption = getTheFirstSelectedOption();
if (firstSelectedOption != null) {
return getSelectImplementation().getOptionValue(firstSelectedOption);
} else {
return "";
}
}
@ReplayOnError
public String[] getSelectedValues() {
List allSelectedOptions = getAllTheSelectedOptions();
List valueList = new ArrayList<>();
for (WebElement option : allSelectedOptions) {
valueList.add(getSelectImplementation().getOptionValue(option));
}
String[] texts = new String[valueList.size()];
return valueList.toArray(texts);
}
@ReplayOnError
public boolean isMultiple() {
try {
findElement();
return getSelectImplementation().isMultipleWithoutFind();
} finally {
getSelectImplementation().finalizeAction();
}
}
protected boolean isMultipleSelect() {
return multiple.get();
}
protected void setMultipleSelect(boolean multipleSelect) {
multiple.set(multipleSelect);
}
/**
* De-selects all options in a multi-select list element.
*/
@ReplayOnError(waitAfterAction = true)
public void deselectAll() {
List allSelectedOptions = getAllTheSelectedOptions();
if (!isMultipleSelect()) {
throw new UnsupportedOperationException(ERROR_MULTI_SELECT);
}
for (WebElement option : allSelectedOptions) {
getSelectImplementation().setDeselected(option);
}
}
@ReplayOnError(waitAfterAction = true)
public void deselectByIndex(final Integer index) {
try {
findElement();
if (!isMultipleSelect()) {
throw new UnsupportedOperationException("You may only deselect options of a multi-select");
}
getSelectImplementation().deselectByIndex(index);
} finally {
getSelectImplementation().finalizeAction();
}
}
@ReplayOnError(waitAfterAction = true)
public void deselectByText(final String text) {
try {
findElement();
if (!isMultipleSelect()) {
throw new UnsupportedOperationException(ERROR_MULTI_SELECT);
}
getSelectImplementation().deselectByText(text);
} finally {
getSelectImplementation().finalizeAction();
}
}
@ReplayOnError(waitAfterAction = true)
public void deselectByValue(final String value) {
try {
findElement();
if (!isMultipleSelect()) {
throw new UnsupportedOperationException(ERROR_MULTI_SELECT);
}
getSelectImplementation().deselectByValue(value);
} finally {
getSelectImplementation().finalizeAction();
}
}
@ReplayOnError(waitAfterAction = true)
public void selectByIndex(final Integer index) {
try {
findElement();
getSelectImplementation().selectByIndex(index);
} finally {
getSelectImplementation().finalizeAction();
}
}
@ReplayOnError(waitAfterAction = true)
public void selectByIndex(int ... indexs) {
try {
findElement();
for (int i = 0; i < indexs.length; i++) {
getSelectImplementation().selectByIndex(indexs[i]);
}
} finally {
getSelectImplementation().finalizeAction();
}
}
/**
* Select standard select by attribute text, and select fake select with ul and li by attribute title.
*
* @param text
*/
@ReplayOnError(waitAfterAction = true)
public void selectByText(final String text) {
try {
findElement();
getSelectImplementation().selectByText(text);
} finally {
getSelectImplementation().finalizeAction();
}
}
@ReplayOnError(waitAfterAction = true)
public void selectByText(String ... texts) {
try {
findElement();
for (int i = 0; i < texts.length; i++) {
getSelectImplementation().selectByText(texts[i]);
}
} finally {
getSelectImplementation().finalizeAction();
}
}
/**
* Select Corresponding select by attribute text, select the most similar text
*
* @param text
*/
@ReplayOnError(waitAfterAction = true)
public void selectByCorrespondingText(String text) {
try {
findElement();
selectCorrespondingText(text);
} finally {
getSelectImplementation().finalizeAction();
}
}
/**
* Multiple select by attribute text, similar select
* For each text, only one option will be selected
*
* @param text
*/
@ReplayOnError(waitAfterAction = true)
public void selectByCorrespondingText(String ... text) {
try {
findElement();
for (int i = 0; i < text.length; i++) {
selectCorrespondingText(text[i]);
}
} finally {
getSelectImplementation().finalizeAction();
}
}
private void selectCorrespondingText(final String text) {
double score = 0;
WebElement optionToSelect = null;
for (WebElement option : getOptionsNoSearch()) {
String source = getSelectImplementation().getOptionText(option).trim();
if (service.score(source, text) > score) {
score = service.score(source, text);
optionToSelect = option;
}
}
if (optionToSelect != null) {
getSelectImplementation().setSelected(optionToSelect);
} else {
throw new NoSuchElementException("Cannot locate option with corresponding text " + text);
}
}
@ReplayOnError(waitAfterAction = true)
public void deselectByCorrespondingText(final String text) {
try {
findElement();
if (!isMultipleSelect()) {
throw new UnsupportedOperationException(ERROR_MULTI_SELECT);
}
double score = 0;
WebElement optionToSelect = null;
for (WebElement option : getOptionsNoSearch()) {
String source = option.getText().trim();
if (service.score(source, text) > score) {
score = service.score(source, text);
optionToSelect = option;
}
}
if (optionToSelect != null) {
getSelectImplementation().setDeselected(optionToSelect);
} else {
throw new NoSuchElementException("Cannot locate option with corresponding text " + text);
}
} finally {
getSelectImplementation().finalizeAction();
}
}
@ReplayOnError(waitAfterAction = true)
public void selectByValue(final String value) {
try {
findElement();
getSelectImplementation().selectByValue(value);
} finally {
getSelectImplementation().finalizeAction();
}
}
@ReplayOnError(waitAfterAction = true)
public void selectByValue(final String ... values) {
try {
findElement();
for (int i = 0; i < values.length; i++) {
getSelectImplementation().selectByValue(values[i]);
}
} finally {
getSelectImplementation().finalizeAction();
}
}
public ISelectList getSelectImplementation() {
return selectImplementation.get();
}
public void setSelectImplementation(ISelectList selectImplementation) {
this.selectImplementation.set(selectImplementation);
}
public List> getImplementationList() {
return implementationList;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy