xref: /plugin/combo/vendor/php-webdriver/webdriver/lib/WebDriverSelect.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\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