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\WebDriverException; 8*04fd306cSNickeauuse Facebook\WebDriver\Support\XPathEscaper; 9*04fd306cSNickeau 10*04fd306cSNickeau/** 11*04fd306cSNickeau * Provides helper methods for checkboxes and radio buttons. 12*04fd306cSNickeau */ 13*04fd306cSNickeauabstract class AbstractWebDriverCheckboxOrRadio implements WebDriverSelectInterface 14*04fd306cSNickeau{ 15*04fd306cSNickeau /** @var WebDriverElement */ 16*04fd306cSNickeau protected $element; 17*04fd306cSNickeau 18*04fd306cSNickeau /** @var string */ 19*04fd306cSNickeau protected $type; 20*04fd306cSNickeau 21*04fd306cSNickeau /** @var string */ 22*04fd306cSNickeau protected $name; 23*04fd306cSNickeau 24*04fd306cSNickeau public function __construct(WebDriverElement $element) 25*04fd306cSNickeau { 26*04fd306cSNickeau $tagName = $element->getTagName(); 27*04fd306cSNickeau if ($tagName !== 'input') { 28*04fd306cSNickeau throw new UnexpectedTagNameException('input', $tagName); 29*04fd306cSNickeau } 30*04fd306cSNickeau 31*04fd306cSNickeau $this->name = $element->getAttribute('name'); 32*04fd306cSNickeau if ($this->name === null) { 33*04fd306cSNickeau throw new WebDriverException('The input does not have a "name" attribute.'); 34*04fd306cSNickeau } 35*04fd306cSNickeau 36*04fd306cSNickeau $this->element = $element; 37*04fd306cSNickeau } 38*04fd306cSNickeau 39*04fd306cSNickeau public function getOptions() 40*04fd306cSNickeau { 41*04fd306cSNickeau return $this->getRelatedElements(); 42*04fd306cSNickeau } 43*04fd306cSNickeau 44*04fd306cSNickeau public function getAllSelectedOptions() 45*04fd306cSNickeau { 46*04fd306cSNickeau $selectedElement = []; 47*04fd306cSNickeau foreach ($this->getRelatedElements() as $element) { 48*04fd306cSNickeau if ($element->isSelected()) { 49*04fd306cSNickeau $selectedElement[] = $element; 50*04fd306cSNickeau 51*04fd306cSNickeau if (!$this->isMultiple()) { 52*04fd306cSNickeau return $selectedElement; 53*04fd306cSNickeau } 54*04fd306cSNickeau } 55*04fd306cSNickeau } 56*04fd306cSNickeau 57*04fd306cSNickeau return $selectedElement; 58*04fd306cSNickeau } 59*04fd306cSNickeau 60*04fd306cSNickeau public function getFirstSelectedOption() 61*04fd306cSNickeau { 62*04fd306cSNickeau foreach ($this->getRelatedElements() as $element) { 63*04fd306cSNickeau if ($element->isSelected()) { 64*04fd306cSNickeau return $element; 65*04fd306cSNickeau } 66*04fd306cSNickeau } 67*04fd306cSNickeau 68*04fd306cSNickeau throw new NoSuchElementException( 69*04fd306cSNickeau sprintf('No %s are selected', $this->type === 'radio' ? 'radio buttons' : 'checkboxes') 70*04fd306cSNickeau ); 71*04fd306cSNickeau } 72*04fd306cSNickeau 73*04fd306cSNickeau public function selectByIndex($index) 74*04fd306cSNickeau { 75*04fd306cSNickeau $this->byIndex($index); 76*04fd306cSNickeau } 77*04fd306cSNickeau 78*04fd306cSNickeau public function selectByValue($value) 79*04fd306cSNickeau { 80*04fd306cSNickeau $this->byValue($value); 81*04fd306cSNickeau } 82*04fd306cSNickeau 83*04fd306cSNickeau public function selectByVisibleText($text) 84*04fd306cSNickeau { 85*04fd306cSNickeau $this->byVisibleText($text); 86*04fd306cSNickeau } 87*04fd306cSNickeau 88*04fd306cSNickeau public function selectByVisiblePartialText($text) 89*04fd306cSNickeau { 90*04fd306cSNickeau $this->byVisibleText($text, true); 91*04fd306cSNickeau } 92*04fd306cSNickeau 93*04fd306cSNickeau /** 94*04fd306cSNickeau * Selects or deselects a checkbox or a radio button by its value. 95*04fd306cSNickeau * 96*04fd306cSNickeau * @param string $value 97*04fd306cSNickeau * @param bool $select 98*04fd306cSNickeau * @throws NoSuchElementException 99*04fd306cSNickeau */ 100*04fd306cSNickeau protected function byValue($value, $select = true) 101*04fd306cSNickeau { 102*04fd306cSNickeau $matched = false; 103*04fd306cSNickeau foreach ($this->getRelatedElements($value) as $element) { 104*04fd306cSNickeau $select ? $this->selectOption($element) : $this->deselectOption($element); 105*04fd306cSNickeau if (!$this->isMultiple()) { 106*04fd306cSNickeau return; 107*04fd306cSNickeau } 108*04fd306cSNickeau 109*04fd306cSNickeau $matched = true; 110*04fd306cSNickeau } 111*04fd306cSNickeau 112*04fd306cSNickeau if (!$matched) { 113*04fd306cSNickeau throw new NoSuchElementException( 114*04fd306cSNickeau sprintf('Cannot locate %s with value: %s', $this->type, $value) 115*04fd306cSNickeau ); 116*04fd306cSNickeau } 117*04fd306cSNickeau } 118*04fd306cSNickeau 119*04fd306cSNickeau /** 120*04fd306cSNickeau * Selects or deselects a checkbox or a radio button by its index. 121*04fd306cSNickeau * 122*04fd306cSNickeau * @param int $index 123*04fd306cSNickeau * @param bool $select 124*04fd306cSNickeau * @throws NoSuchElementException 125*04fd306cSNickeau */ 126*04fd306cSNickeau protected function byIndex($index, $select = true) 127*04fd306cSNickeau { 128*04fd306cSNickeau $elements = $this->getRelatedElements(); 129*04fd306cSNickeau if (!isset($elements[$index])) { 130*04fd306cSNickeau throw new NoSuchElementException(sprintf('Cannot locate %s with index: %d', $this->type, $index)); 131*04fd306cSNickeau } 132*04fd306cSNickeau 133*04fd306cSNickeau $select ? $this->selectOption($elements[$index]) : $this->deselectOption($elements[$index]); 134*04fd306cSNickeau } 135*04fd306cSNickeau 136*04fd306cSNickeau /** 137*04fd306cSNickeau * Selects or deselects a checkbox or a radio button by its visible text. 138*04fd306cSNickeau * 139*04fd306cSNickeau * @param string $text 140*04fd306cSNickeau * @param bool $partial 141*04fd306cSNickeau * @param bool $select 142*04fd306cSNickeau */ 143*04fd306cSNickeau protected function byVisibleText($text, $partial = false, $select = true) 144*04fd306cSNickeau { 145*04fd306cSNickeau foreach ($this->getRelatedElements() as $element) { 146*04fd306cSNickeau $normalizeFilter = sprintf( 147*04fd306cSNickeau $partial ? 'contains(normalize-space(.), %s)' : 'normalize-space(.) = %s', 148*04fd306cSNickeau XPathEscaper::escapeQuotes($text) 149*04fd306cSNickeau ); 150*04fd306cSNickeau 151*04fd306cSNickeau $xpath = 'ancestor::label'; 152*04fd306cSNickeau $xpathNormalize = sprintf('%s[%s]', $xpath, $normalizeFilter); 153*04fd306cSNickeau 154*04fd306cSNickeau $id = $element->getAttribute('id'); 155*04fd306cSNickeau if ($id !== null) { 156*04fd306cSNickeau $idFilter = sprintf('@for = %s', XPathEscaper::escapeQuotes($id)); 157*04fd306cSNickeau 158*04fd306cSNickeau $xpath .= sprintf(' | //label[%s]', $idFilter); 159*04fd306cSNickeau $xpathNormalize .= sprintf(' | //label[%s and %s]', $idFilter, $normalizeFilter); 160*04fd306cSNickeau } 161*04fd306cSNickeau 162*04fd306cSNickeau try { 163*04fd306cSNickeau $element->findElement(WebDriverBy::xpath($xpathNormalize)); 164*04fd306cSNickeau } catch (NoSuchElementException $e) { 165*04fd306cSNickeau if ($partial) { 166*04fd306cSNickeau continue; 167*04fd306cSNickeau } 168*04fd306cSNickeau 169*04fd306cSNickeau try { 170*04fd306cSNickeau // Since the mechanism of getting the text in xpath is not the same as 171*04fd306cSNickeau // webdriver, use the expensive getText() to check if nothing is matched. 172*04fd306cSNickeau if ($text !== $element->findElement(WebDriverBy::xpath($xpath))->getText()) { 173*04fd306cSNickeau continue; 174*04fd306cSNickeau } 175*04fd306cSNickeau } catch (NoSuchElementException $e) { 176*04fd306cSNickeau continue; 177*04fd306cSNickeau } 178*04fd306cSNickeau } 179*04fd306cSNickeau 180*04fd306cSNickeau $select ? $this->selectOption($element) : $this->deselectOption($element); 181*04fd306cSNickeau if (!$this->isMultiple()) { 182*04fd306cSNickeau return; 183*04fd306cSNickeau } 184*04fd306cSNickeau } 185*04fd306cSNickeau } 186*04fd306cSNickeau 187*04fd306cSNickeau /** 188*04fd306cSNickeau * Gets checkboxes or radio buttons with the same name. 189*04fd306cSNickeau * 190*04fd306cSNickeau * @param string|null $value 191*04fd306cSNickeau * @return WebDriverElement[] 192*04fd306cSNickeau */ 193*04fd306cSNickeau protected function getRelatedElements($value = null) 194*04fd306cSNickeau { 195*04fd306cSNickeau $valueSelector = $value ? sprintf(' and @value = %s', XPathEscaper::escapeQuotes($value)) : ''; 196*04fd306cSNickeau $formId = $this->element->getAttribute('form'); 197*04fd306cSNickeau if ($formId === null) { 198*04fd306cSNickeau $form = $this->element->findElement(WebDriverBy::xpath('ancestor::form')); 199*04fd306cSNickeau 200*04fd306cSNickeau $formId = $form->getAttribute('id'); 201*04fd306cSNickeau if ($formId === '' || $formId === null) { 202*04fd306cSNickeau return $form->findElements(WebDriverBy::xpath( 203*04fd306cSNickeau sprintf('.//input[@name = %s%s]', XPathEscaper::escapeQuotes($this->name), $valueSelector) 204*04fd306cSNickeau )); 205*04fd306cSNickeau } 206*04fd306cSNickeau } 207*04fd306cSNickeau 208*04fd306cSNickeau // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#form 209*04fd306cSNickeau return $this->element->findElements( 210*04fd306cSNickeau WebDriverBy::xpath(sprintf( 211*04fd306cSNickeau '//form[@id = %1$s]//input[@name = %2$s%3$s' 212*04fd306cSNickeau . ' and ((boolean(@form) = true() and @form = %1$s) or boolean(@form) = false())]' 213*04fd306cSNickeau . ' | //input[@form = %1$s and @name = %2$s%3$s]', 214*04fd306cSNickeau XPathEscaper::escapeQuotes($formId), 215*04fd306cSNickeau XPathEscaper::escapeQuotes($this->name), 216*04fd306cSNickeau $valueSelector 217*04fd306cSNickeau )) 218*04fd306cSNickeau ); 219*04fd306cSNickeau } 220*04fd306cSNickeau 221*04fd306cSNickeau /** 222*04fd306cSNickeau * Selects a checkbox or a radio button. 223*04fd306cSNickeau */ 224*04fd306cSNickeau protected function selectOption(WebDriverElement $element) 225*04fd306cSNickeau { 226*04fd306cSNickeau if (!$element->isSelected()) { 227*04fd306cSNickeau $element->click(); 228*04fd306cSNickeau } 229*04fd306cSNickeau } 230*04fd306cSNickeau 231*04fd306cSNickeau /** 232*04fd306cSNickeau * Deselects a checkbox or a radio button. 233*04fd306cSNickeau */ 234*04fd306cSNickeau protected function deselectOption(WebDriverElement $element) 235*04fd306cSNickeau { 236*04fd306cSNickeau if ($element->isSelected()) { 237*04fd306cSNickeau $element->click(); 238*04fd306cSNickeau } 239*04fd306cSNickeau } 240*04fd306cSNickeau} 241