PageRenderTime 46ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/behat/form_field/behat_form_select.php

https://gitlab.com/unofficial-mirrors/moodle
PHP | 232 lines | 104 code | 30 blank | 98 comment | 19 complexity | 1c4e28ae920e2365ec3a6d9b40ddaa81 MD5 | raw file
  1. <?php
  2. // This file is part of Moodle - http://moodle.org/
  3. //
  4. // Moodle is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // Moodle is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
  16. /**
  17. * Single select form field class.
  18. *
  19. * @package core_form
  20. * @category test
  21. * @copyright 2012 David MonllaĆ³
  22. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23. */
  24. // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
  25. require_once(__DIR__ . '/behat_form_field.php');
  26. /**
  27. * Single select form field.
  28. *
  29. * @package core_form
  30. * @category test
  31. * @copyright 2012 David MonllaĆ³
  32. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  33. */
  34. class behat_form_select extends behat_form_field {
  35. /**
  36. * Sets the value(s) of a select element.
  37. *
  38. * Seems an easy select, but there are lots of combinations
  39. * of browsers and operative systems and each one manages the
  40. * autosubmits and the multiple option selects in a different way.
  41. *
  42. * @param string $value plain value or comma separated values if multiple. Commas in values escaped with backslash.
  43. * @return void
  44. */
  45. public function set_value($value) {
  46. // Is the select multiple?
  47. $multiple = $this->field->hasAttribute('multiple');
  48. $singleselect = ($this->field->hasClass('singleselect') || $this->field->hasClass('urlselect'));
  49. // Here we select the option(s).
  50. if ($multiple) {
  51. // Split and decode values. Comma separated list of values allowed. With valuable commas escaped with backslash.
  52. $options = preg_replace('/\\\,/', ',', preg_split('/(?<!\\\),/', trim($value)));
  53. // This is a multiple select, let's pass the multiple flag after first option.
  54. $afterfirstoption = false;
  55. foreach ($options as $option) {
  56. $this->field->selectOption(trim($option), $afterfirstoption);
  57. $afterfirstoption = true;
  58. }
  59. } else {
  60. // By default, assume the passed value is a non-multiple option.
  61. $this->field->selectOption(trim($value));
  62. }
  63. // Wait for all the possible AJAX requests that have been
  64. // already triggered by selectOption() to be finished.
  65. if ($this->running_javascript()) {
  66. // Trigger change event and click on first skip link, as some OS/browsers (Phantomjs, Mac-FF),
  67. // don't close select option field and trigger event.
  68. if (!$singleselect) {
  69. $dialoguexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' moodle-dialogue-focused ')]";
  70. if (!$node = $this->session->getDriver()->find($dialoguexpath)) {
  71. $script = "Syn.trigger('change', {}, {{ELEMENT}})";
  72. try {
  73. $driver = $this->session->getDriver();
  74. if ($driver instanceof \Moodle\BehatExtension\Driver\MoodleSelenium2Driver) {
  75. $driver->triggerSynScript($this->field->getXpath(), $script);
  76. }
  77. $driver->click('//body//div[@class="skiplinks"]');
  78. } catch (\Exception $e) {
  79. return;
  80. }
  81. } else {
  82. try {
  83. $this->session->getDriver()->click($dialoguexpath);
  84. } catch (\Exception $e) {
  85. return;
  86. }
  87. }
  88. }
  89. $this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
  90. }
  91. }
  92. /**
  93. * Returns the text of the currently selected options.
  94. *
  95. * @return string Comma separated if multiple options are selected. Commas in option texts escaped with backslash.
  96. */
  97. public function get_value() {
  98. return $this->get_selected_options();
  99. }
  100. /**
  101. * Returns whether the provided argument matches the current value.
  102. *
  103. * @param mixed $expectedvalue
  104. * @return bool
  105. */
  106. public function matches($expectedvalue) {
  107. $multiple = $this->field->hasAttribute('multiple');
  108. // Same implementation as the parent if it is a single select.
  109. if (!$multiple) {
  110. $cleanexpectedvalue = trim($expectedvalue);
  111. $selectedtext = trim($this->get_selected_options());
  112. $selectedvalue = trim($this->get_selected_options(false));
  113. if ($cleanexpectedvalue != $selectedvalue && $cleanexpectedvalue != $selectedtext) {
  114. return false;
  115. }
  116. return true;
  117. }
  118. // We are dealing with a multi-select.
  119. // Unescape + trim all options and flip it to have the expected values as keys.
  120. $expectedoptions = $this->get_unescaped_options($expectedvalue);
  121. // Get currently selected option's texts.
  122. $texts = $this->get_selected_options(true);
  123. $selectedoptiontexts = $this->get_unescaped_options($texts);
  124. // Get currently selected option's values.
  125. $values = $this->get_selected_options(false);
  126. $selectedoptionvalues = $this->get_unescaped_options($values);
  127. // We check against string-ordered lists of options.
  128. if ($expectedoptions !== $selectedoptiontexts &&
  129. $expectedoptions !== $selectedoptionvalues) {
  130. return false;
  131. }
  132. return true;
  133. }
  134. /**
  135. * Cleans the list of options and returns it as a string separating options with |||.
  136. *
  137. * @param string $value The string containing the escaped options.
  138. * @return string The options
  139. */
  140. protected function get_unescaped_options($value) {
  141. // Can be multiple comma separated, with valuable commas escaped with backslash.
  142. $optionsarray = array_map(
  143. 'trim',
  144. preg_replace('/\\\,/', ',',
  145. preg_split('/(?<!\\\),/', $value)
  146. )
  147. );
  148. // Sort by value (keeping the keys is irrelevant).
  149. core_collator::asort($optionsarray, SORT_STRING);
  150. // Returning it as a string which is easier to match against other values.
  151. return implode('|||', $optionsarray);
  152. }
  153. /**
  154. * Returns the field selected values.
  155. *
  156. * Externalized from the common behat_form_field API method get_value() as
  157. * matches() needs to check against both values and texts.
  158. *
  159. * @param bool $returntexts Returns the options texts or the options values.
  160. * @return string
  161. */
  162. protected function get_selected_options($returntexts = true) {
  163. $method = 'getHtml';
  164. if ($returntexts === false) {
  165. $method = 'getValue';
  166. }
  167. // Is the select multiple?
  168. $multiple = $this->field->hasAttribute('multiple');
  169. $selectedoptions = array(); // To accumulate found selected options.
  170. // Driver returns the values as an array or as a string depending
  171. // on whether multiple options are selected or not.
  172. $values = $this->field->getValue();
  173. if (!is_array($values)) {
  174. $values = array($values);
  175. }
  176. // Get all the options in the select and extract their value/text pairs.
  177. $alloptions = $this->field->findAll('xpath', '//option');
  178. foreach ($alloptions as $option) {
  179. // Is it selected?
  180. if (in_array($option->getValue(), $values)) {
  181. if ($multiple) {
  182. // If the select is multiple, text commas must be encoded.
  183. $selectedoptions[] = trim(str_replace(',', '\,', $option->{$method}()));
  184. } else {
  185. $selectedoptions[] = trim($option->{$method}());
  186. }
  187. }
  188. }
  189. return implode(', ', $selectedoptions);
  190. }
  191. /**
  192. * Returns the opton XPath based on it's select xpath.
  193. *
  194. * @param string $option
  195. * @param string $selectxpath
  196. * @return string xpath
  197. */
  198. protected function get_option_xpath($option, $selectxpath) {
  199. $valueliteral = behat_context_helper::escape(trim($option));
  200. return $selectxpath . "/descendant::option[(./@value=$valueliteral or normalize-space(.)=$valueliteral)]";
  201. }
  202. }