1*04fd306cSNickeau<?php 2*04fd306cSNickeau 3*04fd306cSNickeaunamespace Facebook\WebDriver; 4*04fd306cSNickeau 5*04fd306cSNickeauuse Facebook\WebDriver\Exception\NoSuchElementException; 6*04fd306cSNickeauuse Facebook\WebDriver\Exception\UnexpectedTagNameException; 7*04fd306cSNickeauuse Facebook\WebDriver\Exception\UnsupportedOperationException; 8*04fd306cSNickeauuse Facebook\WebDriver\Support\XPathEscaper; 9*04fd306cSNickeau 10*04fd306cSNickeau/** 11*04fd306cSNickeau * Models a default HTML `<select>` tag, providing helper methods to select and deselect options. 12*04fd306cSNickeau */ 13*04fd306cSNickeauclass WebDriverSelect implements WebDriverSelectInterface 14*04fd306cSNickeau{ 15*04fd306cSNickeau /** @var WebDriverElement */ 16*04fd306cSNickeau private $element; 17*04fd306cSNickeau /** @var bool */ 18*04fd306cSNickeau private $isMulti; 19*04fd306cSNickeau 20*04fd306cSNickeau public function __construct(WebDriverElement $element) 21*04fd306cSNickeau { 22*04fd306cSNickeau $tag_name = $element->getTagName(); 23*04fd306cSNickeau 24*04fd306cSNickeau if ($tag_name !== 'select') { 25*04fd306cSNickeau throw new UnexpectedTagNameException('select', $tag_name); 26*04fd306cSNickeau } 27*04fd306cSNickeau $this->element = $element; 28*04fd306cSNickeau $value = $element->getAttribute('multiple'); 29*04fd306cSNickeau $this->isMulti = $value === 'true'; 30*04fd306cSNickeau } 31*04fd306cSNickeau 32*04fd306cSNickeau public function isMultiple() 33*04fd306cSNickeau { 34*04fd306cSNickeau return $this->isMulti; 35*04fd306cSNickeau } 36*04fd306cSNickeau 37*04fd306cSNickeau public function getOptions() 38*04fd306cSNickeau { 39*04fd306cSNickeau return $this->element->findElements(WebDriverBy::tagName('option')); 40*04fd306cSNickeau } 41*04fd306cSNickeau 42*04fd306cSNickeau public function getAllSelectedOptions() 43*04fd306cSNickeau { 44*04fd306cSNickeau $selected_options = []; 45*04fd306cSNickeau foreach ($this->getOptions() as $option) { 46*04fd306cSNickeau if ($option->isSelected()) { 47*04fd306cSNickeau $selected_options[] = $option; 48*04fd306cSNickeau 49*04fd306cSNickeau if (!$this->isMultiple()) { 50*04fd306cSNickeau return $selected_options; 51*04fd306cSNickeau } 52*04fd306cSNickeau } 53*04fd306cSNickeau } 54*04fd306cSNickeau 55*04fd306cSNickeau return $selected_options; 56*04fd306cSNickeau } 57*04fd306cSNickeau 58*04fd306cSNickeau public function getFirstSelectedOption() 59*04fd306cSNickeau { 60*04fd306cSNickeau foreach ($this->getOptions() as $option) { 61*04fd306cSNickeau if ($option->isSelected()) { 62*04fd306cSNickeau return $option; 63*04fd306cSNickeau } 64*04fd306cSNickeau } 65*04fd306cSNickeau 66*04fd306cSNickeau throw new NoSuchElementException('No options are selected'); 67*04fd306cSNickeau } 68*04fd306cSNickeau 69*04fd306cSNickeau public function selectByIndex($index) 70*04fd306cSNickeau { 71*04fd306cSNickeau foreach ($this->getOptions() as $option) { 72*04fd306cSNickeau if ($option->getAttribute('index') === (string) $index) { 73*04fd306cSNickeau $this->selectOption($option); 74*04fd306cSNickeau 75*04fd306cSNickeau return; 76*04fd306cSNickeau } 77*04fd306cSNickeau } 78*04fd306cSNickeau 79*04fd306cSNickeau throw new NoSuchElementException(sprintf('Cannot locate option with index: %d', $index)); 80*04fd306cSNickeau } 81*04fd306cSNickeau 82*04fd306cSNickeau public function selectByValue($value) 83*04fd306cSNickeau { 84*04fd306cSNickeau $matched = false; 85*04fd306cSNickeau $xpath = './/option[@value = ' . XPathEscaper::escapeQuotes($value) . ']'; 86*04fd306cSNickeau $options = $this->element->findElements(WebDriverBy::xpath($xpath)); 87*04fd306cSNickeau 88*04fd306cSNickeau foreach ($options as $option) { 89*04fd306cSNickeau $this->selectOption($option); 90*04fd306cSNickeau if (!$this->isMultiple()) { 91*04fd306cSNickeau return; 92*04fd306cSNickeau } 93*04fd306cSNickeau $matched = true; 94*04fd306cSNickeau } 95*04fd306cSNickeau 96*04fd306cSNickeau if (!$matched) { 97*04fd306cSNickeau throw new NoSuchElementException( 98*04fd306cSNickeau sprintf('Cannot locate option with value: %s', $value) 99*04fd306cSNickeau ); 100*04fd306cSNickeau } 101*04fd306cSNickeau } 102*04fd306cSNickeau 103*04fd306cSNickeau public function selectByVisibleText($text) 104*04fd306cSNickeau { 105*04fd306cSNickeau $matched = false; 106*04fd306cSNickeau $xpath = './/option[normalize-space(.) = ' . XPathEscaper::escapeQuotes($text) . ']'; 107*04fd306cSNickeau $options = $this->element->findElements(WebDriverBy::xpath($xpath)); 108*04fd306cSNickeau 109*04fd306cSNickeau foreach ($options as $option) { 110*04fd306cSNickeau $this->selectOption($option); 111*04fd306cSNickeau if (!$this->isMultiple()) { 112*04fd306cSNickeau return; 113*04fd306cSNickeau } 114*04fd306cSNickeau $matched = true; 115*04fd306cSNickeau } 116*04fd306cSNickeau 117*04fd306cSNickeau // Since the mechanism of getting the text in xpath is not the same as 118*04fd306cSNickeau // webdriver, use the expensive getText() to check if nothing is matched. 119*04fd306cSNickeau if (!$matched) { 120*04fd306cSNickeau foreach ($this->getOptions() as $option) { 121*04fd306cSNickeau if ($option->getText() === $text) { 122*04fd306cSNickeau $this->selectOption($option); 123*04fd306cSNickeau if (!$this->isMultiple()) { 124*04fd306cSNickeau return; 125*04fd306cSNickeau } 126*04fd306cSNickeau $matched = true; 127*04fd306cSNickeau } 128*04fd306cSNickeau } 129*04fd306cSNickeau } 130*04fd306cSNickeau 131*04fd306cSNickeau if (!$matched) { 132*04fd306cSNickeau throw new NoSuchElementException( 133*04fd306cSNickeau sprintf('Cannot locate option with text: %s', $text) 134*04fd306cSNickeau ); 135*04fd306cSNickeau } 136*04fd306cSNickeau } 137*04fd306cSNickeau 138*04fd306cSNickeau public function selectByVisiblePartialText($text) 139*04fd306cSNickeau { 140*04fd306cSNickeau $matched = false; 141*04fd306cSNickeau $xpath = './/option[contains(normalize-space(.), ' . XPathEscaper::escapeQuotes($text) . ')]'; 142*04fd306cSNickeau $options = $this->element->findElements(WebDriverBy::xpath($xpath)); 143*04fd306cSNickeau 144*04fd306cSNickeau foreach ($options as $option) { 145*04fd306cSNickeau $this->selectOption($option); 146*04fd306cSNickeau if (!$this->isMultiple()) { 147*04fd306cSNickeau return; 148*04fd306cSNickeau } 149*04fd306cSNickeau $matched = true; 150*04fd306cSNickeau } 151*04fd306cSNickeau 152*04fd306cSNickeau if (!$matched) { 153*04fd306cSNickeau throw new NoSuchElementException( 154*04fd306cSNickeau sprintf('Cannot locate option with text: %s', $text) 155*04fd306cSNickeau ); 156*04fd306cSNickeau } 157*04fd306cSNickeau } 158*04fd306cSNickeau 159*04fd306cSNickeau public function deselectAll() 160*04fd306cSNickeau { 161*04fd306cSNickeau if (!$this->isMultiple()) { 162*04fd306cSNickeau throw new UnsupportedOperationException('You may only deselect all options of a multi-select'); 163*04fd306cSNickeau } 164*04fd306cSNickeau 165*04fd306cSNickeau foreach ($this->getOptions() as $option) { 166*04fd306cSNickeau $this->deselectOption($option); 167*04fd306cSNickeau } 168*04fd306cSNickeau } 169*04fd306cSNickeau 170*04fd306cSNickeau public function deselectByIndex($index) 171*04fd306cSNickeau { 172*04fd306cSNickeau if (!$this->isMultiple()) { 173*04fd306cSNickeau throw new UnsupportedOperationException('You may only deselect options of a multi-select'); 174*04fd306cSNickeau } 175*04fd306cSNickeau 176*04fd306cSNickeau foreach ($this->getOptions() as $option) { 177*04fd306cSNickeau if ($option->getAttribute('index') === (string) $index) { 178*04fd306cSNickeau $this->deselectOption($option); 179*04fd306cSNickeau 180*04fd306cSNickeau return; 181*04fd306cSNickeau } 182*04fd306cSNickeau } 183*04fd306cSNickeau } 184*04fd306cSNickeau 185*04fd306cSNickeau public function deselectByValue($value) 186*04fd306cSNickeau { 187*04fd306cSNickeau if (!$this->isMultiple()) { 188*04fd306cSNickeau throw new UnsupportedOperationException('You may only deselect options of a multi-select'); 189*04fd306cSNickeau } 190*04fd306cSNickeau 191*04fd306cSNickeau $xpath = './/option[@value = ' . XPathEscaper::escapeQuotes($value) . ']'; 192*04fd306cSNickeau $options = $this->element->findElements(WebDriverBy::xpath($xpath)); 193*04fd306cSNickeau foreach ($options as $option) { 194*04fd306cSNickeau $this->deselectOption($option); 195*04fd306cSNickeau } 196*04fd306cSNickeau } 197*04fd306cSNickeau 198*04fd306cSNickeau public function deselectByVisibleText($text) 199*04fd306cSNickeau { 200*04fd306cSNickeau if (!$this->isMultiple()) { 201*04fd306cSNickeau throw new UnsupportedOperationException('You may only deselect options of a multi-select'); 202*04fd306cSNickeau } 203*04fd306cSNickeau 204*04fd306cSNickeau $xpath = './/option[normalize-space(.) = ' . XPathEscaper::escapeQuotes($text) . ']'; 205*04fd306cSNickeau $options = $this->element->findElements(WebDriverBy::xpath($xpath)); 206*04fd306cSNickeau foreach ($options as $option) { 207*04fd306cSNickeau $this->deselectOption($option); 208*04fd306cSNickeau } 209*04fd306cSNickeau } 210*04fd306cSNickeau 211*04fd306cSNickeau public function deselectByVisiblePartialText($text) 212*04fd306cSNickeau { 213*04fd306cSNickeau if (!$this->isMultiple()) { 214*04fd306cSNickeau throw new UnsupportedOperationException('You may only deselect options of a multi-select'); 215*04fd306cSNickeau } 216*04fd306cSNickeau 217*04fd306cSNickeau $xpath = './/option[contains(normalize-space(.), ' . XPathEscaper::escapeQuotes($text) . ')]'; 218*04fd306cSNickeau $options = $this->element->findElements(WebDriverBy::xpath($xpath)); 219*04fd306cSNickeau foreach ($options as $option) { 220*04fd306cSNickeau $this->deselectOption($option); 221*04fd306cSNickeau } 222*04fd306cSNickeau } 223*04fd306cSNickeau 224*04fd306cSNickeau /** 225*04fd306cSNickeau * Mark option selected 226*04fd306cSNickeau * @param WebDriverElement $option 227*04fd306cSNickeau */ 228*04fd306cSNickeau protected function selectOption(WebDriverElement $option) 229*04fd306cSNickeau { 230*04fd306cSNickeau if (!$option->isSelected()) { 231*04fd306cSNickeau $option->click(); 232*04fd306cSNickeau } 233*04fd306cSNickeau } 234*04fd306cSNickeau 235*04fd306cSNickeau /** 236*04fd306cSNickeau * Mark option not selected 237*04fd306cSNickeau * @param WebDriverElement $option 238*04fd306cSNickeau */ 239*04fd306cSNickeau protected function deselectOption(WebDriverElement $option) 240*04fd306cSNickeau { 241*04fd306cSNickeau if ($option->isSelected()) { 242*04fd306cSNickeau $option->click(); 243*04fd306cSNickeau } 244*04fd306cSNickeau } 245*04fd306cSNickeau} 246