mirrored from git://git.moodle.org/moodle.git
-
Notifications
You must be signed in to change notification settings - Fork 6.4k
/
behat_form_select.php
229 lines (198 loc) · 9.5 KB
/
behat_form_select.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Single select form field class.
*
* @package core_form
* @category test
* @copyright 2012 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/behat_form_field.php');
/**
* Single select form field.
*
* @package core_form
* @category test
* @copyright 2012 David Monllaó
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behat_form_select extends behat_form_field {
/**
* Sets the value(s) of a select element.
*
r * Seems an easy select, but there are lots of combinations
* of browsers and operative systems and each one manages the
* autosubmits and the multiple option selects in a different way.
*
* @param string $value plain value or comma separated values if multiple. Commas in values escaped with backslash.
* @return void
*/
public function set_value($value) {
// In some browsers we select an option and it triggers all the
// autosubmits and works as expected but not in all of them, so we
// try to catch all the possibilities to make this function work as
// expected.
// Get the internal id of the element we are going to click.
// This kind of internal IDs are only available in the selenium wire
// protocol, so only available using selenium drivers, phantomjs and family.
if ($this->running_javascript()) {
$currentelementid = $this->get_internal_field_id();
}
// Is the select multiple?
$multiple = $this->field->hasAttribute('multiple');
// By default, assume the passed value is a non-multiple option.
$options = array(trim($value));
// Here we select the option(s).
if ($multiple) {
// Split and decode values. Comma separated list of values allowed. With valuable commas escaped with backslash.
$options = preg_replace('/\\\,/', ',', preg_split('/(?<!\\\),/', $value));
// This is a multiple select, let's pass the multiple flag after first option.
$afterfirstoption = false;
foreach ($options as $option) {
$this->field->selectOption(trim($option), $afterfirstoption);
$afterfirstoption = true;
}
} else {
// This is a single select, let's pass the last one specified.
$this->field->selectOption(end($options));
}
// With JS disabled this is enough and we finish here.
if (!$this->running_javascript()) {
return;
}
// With JS enabled we add more clicks as some selenium
// drivers requires it to fire JS events.
// In some browsers the selectOption actions can perform a form submit or reload page
// so we need to ensure the element is still available to continue interacting
// with it. We don't wait here.
$selectxpath = $this->field->getXpath();
if (!$this->session->getDriver()->find($selectxpath)) {
return;
}
// We also check the selenium internal element id, if it have changed
// we are dealing with an autosubmit that was already executed, and we don't to
// execute anything else as the action we wanted was already performed.
if ($currentelementid != $this->get_internal_field_id()) {
return;
}
// We also check that the option(s) are still there. We neither wait.
foreach ($options as $option) {
$valueliteral = $this->session->getSelectorsHandler()->xpathLiteral(trim($option));
$optionxpath = $selectxpath . "/descendant::option[(./@value=$valueliteral or normalize-space(.)=$valueliteral)]";
if (!$this->session->getDriver()->find($optionxpath)) {
return;
}
}
// Single select sometimes needs an extra click in the option.
if (!$multiple) {
// Using the driver direcly because Element methods are messy when dealing
// with elements inside containers.
$optionnodes = $this->session->getDriver()->find($optionxpath);
if ($optionnodes) {
// Wrapped in a try & catch as we can fall into race conditions
// and the element may not be there.
try {
// Wait for all the possible AJAX requests that have been
// already triggered by selectOption() to be finished.
$this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
current($optionnodes)->click();
} catch (Exception $e) {
// We continue and return as this means that the element is not there or it is not the same.
return;
}
}
} else {
// Wait for all the possible AJAX requests that have been
// already triggered by selectOption() to be finished.
$this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
// Wrapped in a try & catch as we can fall into race conditions
// and the element may not be there.
try {
// Multiple ones needs the click in the select.
$this->field->click();
} catch (Exception $e) {
// We continue and return as this means that the element is not there or it is not the same.
return;
}
// We ensure that the option is still there.
if (!$this->session->getDriver()->find($optionxpath)) {
return;
}
// Wait for all the possible AJAX requests that have been
// already triggered by selectOption() to be finished.
$this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
// Wrapped in a try & catch as we can fall into race conditions
// and the element may not be there.
try {
// Repeating the select(s) as some drivers (chrome that I know) are moving
// to another option after the general select field click above.
foreach ($options as $option) {
$this->field->selectOption(trim($option), true);
}
} catch (Exception $e) {
// We continue and return as this means that the element is not there or it is not the same.
return;
}
}
}
/**
* Returns the text of the currently selected options.
*
* @return string Comma separated if multiple options are selected. Commas in option texts escaped with backslash.
*/
public function get_value() {
// Is the select multiple?
$multiple = $this->field->hasAttribute('multiple');
$selectedoptions = array(); // To accumulate found selected options.
// Selenium getValue() implementation breaks - separates - values having
// commas within them, so we'll be looking for options with the 'selected' attribute instead.
if ($this->running_javascript()) {
// Get all the options in the select and extract their value/text pairs.
$alloptions = $this->field->findAll('xpath', '//option');
foreach ($alloptions as $option) {
// Is it selected?
if ($option->hasAttribute('selected')) {
if ($multiple) {
// If the select is multiple, text commas must be encoded.
$selectedoptions[] = trim(str_replace(',', '\,', $option->getText()));
} else {
$selectedoptions[] = trim($option->getText());
}
}
}
// Goutte does not keep the 'selected' attribute updated, but its getValue() returns
// the selected elements correctly, also those having commas within them.
} else {
$values = $this->field->getValue();
// Get all the options in the select and extract their value/text pairs.
$alloptions = $this->field->findAll('xpath', '//option');
foreach ($alloptions as $option) {
// Is it selected?
if (in_array($option->getValue(), $values)) {
if ($multiple) {
// If the select is multiple, text commas must be encoded.
$selectedoptions[] = trim(str_replace(',', '\,', $option->getText()));
} else {
$selectedoptions[] = trim($option->getText());
}
}
}
}
return implode(', ', $selectedoptions);
}
}