getTagName();
if ($tag_name !== 'select') {
throw new UnexpectedTagNameException('select', $tag_name);
}
$this->element = $element;
$value = $element->getAttribute('multiple');
$this->isMulti = ($value === 'true');
}
/**
* @return bool Whether this select element support selecting multiple
* options. This is done by checking the value of the 'multiple'
* attribute.
*/
public function isMultiple() {
return $this->isMulti;
}
/**
* @return WebDriverElement[] All options belonging to this select tag.
*/
public function getOptions() {
return $this->element->findElements(WebDriverBy::tagName('option'));
}
/**
* @return WebDriverElement[] All selected options belonging to this select tag.
*/
public function getAllSelectedOptions() {
$selected_options = array();
foreach ($this->getOptions() as $option) {
if ($option->isSelected()) {
$selected_options[] = $option;
}
}
return $selected_options;
}
/**
* @throws NoSuchElementException
*
* @return WebDriverElement The first selected option in this select tag (or
* the currently selected option in a normal select)
*/
public function getFirstSelectedOption() {
foreach ($this->getOptions() as $option) {
if ($option->isSelected()) {
return $option;
}
}
throw new NoSuchElementException('No options are selected');
}
/**
* Deselect all options in multiple select tag.
*
* @throws UnsupportedOperationException
*
* @return void
*/
public function deselectAll() {
if (!$this->isMultiple()) {
throw new UnsupportedOperationException(
'You may only deselect all options of a multi-select'
);
}
foreach ($this->getOptions() as $option) {
if ($option->isSelected()) {
$option->click();
}
}
}
/**
* Select the option at the given index.
*
* @param int $index The index of the option. (0-based)
*
* @throws NoSuchElementException
*
* @return void
*/
public function selectByIndex($index) {
$matched = false;
foreach ($this->getOptions() as $option) {
if ($option->getAttribute('index') === (string)$index) {
if (!$option->isSelected()) {
$option->click();
if (!$this->isMultiple()) {
return;
}
}
$matched = true;
}
}
if (!$matched) {
throw new NoSuchElementException(
sprintf('Cannot locate option with index: %d', $index)
);
}
}
/**
* Select all options that have value attribute matching the argument. That
* is, when given "foo" this would select an option like:
*
* ;
*
* @param string $value The value to match against.
*
* @throws NoSuchElementException
*
* @return void
*/
public function selectByValue($value) {
$matched = false;
$xpath = './/option[@value = '.$this->escapeQuotes($value).']';
$options = $this->element->findElements(WebDriverBy::xpath($xpath));
foreach ($options as $option) {
if (!$option->isSelected()) {
$option->click();
}
if (!$this->isMultiple()) {
return;
}
$matched = true;
}
if (!$matched) {
throw new NoSuchElementException(
sprintf('Cannot locate option with value: %s', $value)
);
}
}
/**
* Select all options that display text matching the argument. That is, when
* given "Bar" this would select an option like:
*
* ;
*
* @param string $text The visible text to match against.
*
* @throws NoSuchElementException
*
* @return void
*/
public function selectByVisibleText($text) {
$matched = false;
$xpath = './/option[normalize-space(.) = '.$this->escapeQuotes($text).']';
$options = $this->element->findElements(WebDriverBy::xpath($xpath));
foreach ($options as $option) {
if (!$option->isSelected()) {
$option->click();
}
if (!$this->isMultiple()) {
return;
}
$matched = true;
}
// Since the mechanism of getting the text in xpath is not the same as
// webdriver, use the expensive getText() to check if nothing is matched.
if (!$matched) {
foreach ($this->getOptions() as $option) {
if ($option->getText() === $text) {
if (!$option->isSelected()) {
$option->click();
}
if (!$this->isMultiple()) {
return;
}
$matched = true;
}
}
}
if (!$matched) {
throw new NoSuchElementException(
sprintf('Cannot locate option with text: %s', $text)
);
}
}
/**
* Deselect the option at the given index.
*
* @param int $index The index of the option. (0-based)
* @return void
*/
public function deselectByIndex($index) {
foreach ($this->getOptions() as $option) {
if ($option->getAttribute('index') === (string)$index &&
$option->isSelected()) {
$option->click();
}
}
}
/**
* Deselect all options that have value attribute matching the argument. That
* is, when given "foo" this would select an option like:
*
* ;
*
* @param string $value The value to match against.
* @return void
*/
public function deselectByValue($value) {
$xpath = './/option[@value = '.$this->escapeQuotes($value).']';
$options = $this->element->findElements(WebDriverBy::xpath($xpath));
foreach ($options as $option) {
if ($option->isSelected()) {
$option->click();
}
}
}
/**
* Deselect all options that display text matching the argument. That is, when
* given "Bar" this would select an option like:
*
* ;
*
* @param string $text The visible text to match against.
* @return void
*/
public function deselectByVisibleText($text) {
$xpath = './/option[normalize-space(.) = '.$this->escapeQuotes($text).']';
$options = $this->element->findElements(WebDriverBy::xpath($xpath));
foreach ($options as $option) {
if ($option->isSelected()) {
$option->click();
}
}
}
/**
* Convert strings with both quotes and ticks into:
* foo'"bar -> concat("foo'", '"', "bar")
*
* @param string $to_escape The string to be converted.
* @return string The escaped string.
*/
protected function escapeQuotes($to_escape) {
if (strpos($to_escape, '"') !== false && strpos($to_escape, "'") !== false) {
$substrings = explode('"', $to_escape);
$escaped = "concat(";
$first = true;
foreach ($substrings as $string) {
if (!$first) {
$escaped .= ", '\"',";
$first = false;
}
$escaped .= '"' . $string . '"';
}
return $escaped;
}
if (strpos($to_escape, '"') !== false) {
return sprintf("'%s'", $to_escape);
}
return sprintf('"%s"', $to_escape);
}
}