xref: /template/strap/vendor/php-webdriver/webdriver/lib/AbstractWebDriverCheckboxOrRadio.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
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