mirrored from git://git.moodle.org/moodle.git
/
behat_form_select.php
232 lines (202 loc) · 8.64 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
230
231
232
<?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.
*
* 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) {
// Is the select multiple?
$multiple = $this->field->hasAttribute('multiple');
$singleselect = ($this->field->hasClass('singleselect') || $this->field->hasClass('urlselect'));
// 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('/(?<!\\\),/', trim($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 {
// By default, assume the passed value is a non-multiple option.
$this->field->selectOption(trim($value));
}
// Wait for all the possible AJAX requests that have been
// already triggered by selectOption() to be finished.
if ($this->running_javascript()) {
// Trigger change event and click on first skip link, as some OS/browsers (Phantomjs, Mac-FF),
// don't close select option field and trigger event.
if (!$singleselect) {
$dialoguexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' moodle-dialogue-focused ')]";
if (!$node = $this->session->getDriver()->find($dialoguexpath)) {
$script = "Syn.trigger('change', {}, {{ELEMENT}})";
try {
$driver = $this->session->getDriver();
if ($driver instanceof \Moodle\BehatExtension\Driver\MoodleSelenium2Driver) {
$driver->triggerSynScript($this->field->getXpath(), $script);
}
$driver->click('//body//div[@class="skiplinks"]');
} catch (\Exception $e) {
return;
}
} else {
try {
$this->session->getDriver()->click($dialoguexpath);
} catch (\Exception $e) {
return;
}
}
}
$this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
}
}
/**
* 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() {
return $this->get_selected_options();
}
/**
* Returns whether the provided argument matches the current value.
*
* @param mixed $expectedvalue
* @return bool
*/
public function matches($expectedvalue) {
$multiple = $this->field->hasAttribute('multiple');
// Same implementation as the parent if it is a single select.
if (!$multiple) {
$cleanexpectedvalue = trim($expectedvalue);
$selectedtext = trim($this->get_selected_options());
$selectedvalue = trim($this->get_selected_options(false));
if ($cleanexpectedvalue != $selectedvalue && $cleanexpectedvalue != $selectedtext) {
return false;
}
return true;
}
// We are dealing with a multi-select.
// Unescape + trim all options and flip it to have the expected values as keys.
$expectedoptions = $this->get_unescaped_options($expectedvalue);
// Get currently selected option's texts.
$texts = $this->get_selected_options(true);
$selectedoptiontexts = $this->get_unescaped_options($texts);
// Get currently selected option's values.
$values = $this->get_selected_options(false);
$selectedoptionvalues = $this->get_unescaped_options($values);
// We check against string-ordered lists of options.
if ($expectedoptions !== $selectedoptiontexts &&
$expectedoptions !== $selectedoptionvalues) {
return false;
}
return true;
}
/**
* Cleans the list of options and returns it as a string separating options with |||.
*
* @param string $value The string containing the escaped options.
* @return string The options
*/
protected function get_unescaped_options($value) {
// Can be multiple comma separated, with valuable commas escaped with backslash.
$optionsarray = array_map(
'trim',
preg_replace('/\\\,/', ',',
preg_split('/(?<!\\\),/', $value)
)
);
// Sort by value (keeping the keys is irrelevant).
core_collator::asort($optionsarray, SORT_STRING);
// Returning it as a string which is easier to match against other values.
return implode('|||', $optionsarray);
}
/**
* Returns the field selected values.
*
* Externalized from the common behat_form_field API method get_value() as
* matches() needs to check against both values and texts.
*
* @param bool $returntexts Returns the options texts or the options values.
* @return string
*/
protected function get_selected_options($returntexts = true) {
$method = 'getHtml';
if ($returntexts === false) {
$method = 'getValue';
}
// Is the select multiple?
$multiple = $this->field->hasAttribute('multiple');
$selectedoptions = array(); // To accumulate found selected options.
// Driver returns the values as an array or as a string depending
// on whether multiple options are selected or not.
$values = $this->field->getValue();
if (!is_array($values)) {
$values = array($values);
}
// 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->{$method}()));
} else {
$selectedoptions[] = trim($option->{$method}());
}
}
}
return implode(', ', $selectedoptions);
}
/**
* Returns the opton XPath based on it's select xpath.
*
* @param string $option
* @param string $selectxpath
* @return string xpath
*/
protected function get_option_xpath($option, $selectxpath) {
$valueliteral = behat_context_helper::escape(trim($option));
return $selectxpath . "/descendant::option[(./@value=$valueliteral or normalize-space(.)=$valueliteral)]";
}
}