Advertisement
Guest User

Untitled

a guest
May 30th, 2016
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.46 KB | None | 0 0
  1. <?php
  2.  
  3. namespace Moodle\BehatExtension\Driver;
  4.  
  5. use Behat\Mink\Driver\Selenium2Driver as Selenium2Driver;
  6. use WebDriver\Key as key;
  7. use WebDriver\WebDriver;
  8. use Behat\Mink\Exception\DriverException;
  9. use Behat\Mink\Selector\Xpath\Escaper;
  10. use WebDriver\Element;
  11. use WebDriver\Exception\NoSuchElement;
  12. use WebDriver\Exception\UnknownError;
  13. use WebDriver\Exception;
  14.  
  15. /**
  16. * Selenium2 driver extension to allow extra selenium capabilities restricted by behat/mink-extension.
  17. */
  18. class MoodleSelenium2Driver extends Selenium2Driver
  19. {
  20.  
  21. /**
  22. * Dirty attribute to get the browser name; $browserName is private
  23. * @var string
  24. */
  25. protected static $browser;
  26.  
  27. /**
  28. * Instantiates the driver.
  29. *
  30. * @param string $browser Browser name
  31. * @param array $desiredCapabilities The desired capabilities
  32. * @param string $wdHost The WebDriver host
  33. * @param array $moodleParameters Moodle parameters including our non-behat-friendly selenium capabilities
  34. */
  35. public function __construct($browserName = 'firefox', $desiredCapabilities = null, $wdHost = 'http://localhost:4444/wd/hub', $moodleParameters = array()) {
  36. // If they are set add them overridding if it's the case (not likely).
  37. if (!empty($moodleParameters) && !empty($moodleParameters['capabilities'])) {
  38. foreach ($moodleParameters['capabilities'] as $key => $capability) {
  39. $desiredCapabilities[$key] = $capability;
  40. }
  41. }
  42.  
  43. parent::__construct($browserName, $desiredCapabilities, $wdHost);
  44.  
  45. // This class is instantiated by the dependencies injection system so
  46. // prior to all of beforeSuite subscribers which will call getBrowser*()
  47. self::$browser = $browserName;
  48. }
  49.  
  50. /**
  51. * Forwards to getBrowser() so we keep compatibility with both static and non-static accesses.
  52. *
  53. * @deprecated
  54. * @param string $name
  55. * @param array $arguments
  56. * @return mixed
  57. */
  58. public static function __callStatic($name, $arguments) {
  59. if ($name == 'getBrowserName') {
  60. return self::getBrowser();
  61. }
  62.  
  63. // Fallbacks calling the requested static method, we don't
  64. // even know if it exists or not.
  65. return call_user_func(array(self, $name), $arguments);
  66. }
  67.  
  68. /**
  69. * Forwards to getBrowser() so we keep compatibility with both static and non-static accesses.
  70. *
  71. * @deprecated
  72. * @param string $name
  73. * @param array $arguments
  74. * @return mixed
  75. */
  76. public function __call($name, $arguments) {
  77. if ($name == 'getBrowserName') {
  78. return self::getBrowser();
  79. }
  80.  
  81. // Fallbacks calling the requested instance method, we don't
  82. // even know if it exists or not.
  83. return call_user_func(array($this, $name), $arguments);
  84. }
  85.  
  86. /**
  87. * Returns the browser being used.
  88. *
  89. * We need to know it:
  90. * - To show info about the run.
  91. * - In case there are differences between browsers in the steps.
  92. *
  93. * @static
  94. * @return string
  95. */
  96. public static function getBrowser() {
  97. return self::$browser;
  98. }
  99.  
  100. /**
  101. * Drag one element onto another.
  102. *
  103. * Override the original one to give YUI drag & drop
  104. * time to consider it a valid drag & drop. It will need
  105. * more changes in future to properly adapt to how YUI dd
  106. * component behaves.
  107. *
  108. * @param string $sourceXpath
  109. * @param string $destinationXpath
  110. */
  111. public function dragTo($sourceXpath, $destinationXpath) {
  112. $source = $this->getWebDriverSession()->element('xpath', $sourceXpath);
  113. $destination = $this->getWebDriverSession()->element('xpath', $destinationXpath);
  114.  
  115. // TODO: MDL-39727 This method requires improvements according to the YUI drag and drop component.
  116.  
  117. $this->getWebDriverSession()->moveto(array(
  118. 'element' => $source->getID()
  119. ));
  120.  
  121. $script = <<<JS
  122. (function (element) {
  123. var event = document.createEvent("HTMLEvents");
  124.  
  125. event.initEvent("dragstart", true, true);
  126. event.dataTransfer = {};
  127.  
  128. element.dispatchEvent(event);
  129. }({{ELEMENT}}));
  130. JS;
  131. $this->withSyn()->executeJsOnXpath($sourceXpath, $script);
  132.  
  133. $this->getWebDriverSession()->buttondown();
  134. $this->getWebDriverSession()->moveto(array(
  135. 'element' => $destination->getID()
  136. ));
  137.  
  138. // We add a 2 seconds wait to make YUI dd happy.
  139. $this->wait(2 * 1000, false);
  140.  
  141. $this->getWebDriverSession()->buttonup();
  142.  
  143. $script = <<<JS
  144. (function (element) {
  145. var event = document.createEvent("HTMLEvents");
  146.  
  147. event.initEvent("drop", true, true);
  148. event.dataTransfer = {};
  149.  
  150. element.dispatchEvent(event);
  151. }({{ELEMENT}}));
  152. JS;
  153. $this->withSyn()->executeJsOnXpath($destinationXpath, $script);
  154. }
  155.  
  156. /**
  157. * Overwriten method to use our custom Syn library.
  158. *
  159. * Makes sure that the Syn event library has been injected into the current page,
  160. * and return $this for a fluid interface,
  161. *
  162. * $this->withSyn()->executeJsOnXpath($xpath, $script);
  163. *
  164. * @return Selenium2Driver
  165. */
  166. protected function withSyn()
  167. {
  168. $hasSyn = $this->getWebDriverSession()->execute(array(
  169. 'script' => 'return typeof window["Syn"]!=="undefined"',
  170. 'args' => array()
  171. ));
  172.  
  173. if (!$hasSyn) {
  174. $synJs = file_get_contents(__DIR__.'/Selenium2/moodle_syn-min.js');
  175. $this->getWebDriverSession()->execute(array(
  176. 'script' => $synJs,
  177. 'args' => array()
  178. ));
  179. }
  180.  
  181. return $this;
  182. }
  183.  
  184. /**
  185. * Public interface to run Syn scripts.
  186. *
  187. * @see self::executeJsOnXpath()
  188. *
  189. * @param string $xpath the xpath to search with
  190. * @param string $script the script to execute
  191. * @param Boolean $sync whether to run the script synchronously (default is TRUE)
  192. *
  193. * @return mixed
  194. */
  195. public function triggerSynScript($xpath, $script, $sync = true) {
  196. return $this->withSyn()->executeJsOnXpath($xpath, $script, $sync);
  197. }
  198.  
  199. /**
  200. * Overriding this as key::TAB is causing page scroll and rubrics scenarios are failing.
  201. * https://github.com/minkphp/MinkSelenium2Driver/issues/194
  202. * {@inheritdoc}
  203. */
  204. public function setValue($xpath, $value) {
  205. $value = strval($value);
  206. $element = $this->getWebDriverSession()->element('xpath', $xpath);
  207. $elementName = strtolower($element->name());
  208.  
  209. if ('select' === $elementName) {
  210. if (is_array($value)) {
  211. $this->deselectAllOptions($element);
  212.  
  213. foreach ($value as $option) {
  214. $this->selectOptionOnElement($element, $option, true);
  215. }
  216.  
  217. return;
  218. }
  219.  
  220. $this->selectOption($element, $value);
  221.  
  222. return;
  223. }
  224.  
  225. if ('input' === $elementName) {
  226. $elementType = strtolower($element->attribute('type'));
  227.  
  228. if (in_array($elementType, array('submit', 'image', 'button', 'reset'))) {
  229. throw new DriverException(sprintf('Impossible to set value an element with XPath "%s" as it is not a select, textarea or textbox', $xpath));
  230. }
  231.  
  232. if ('checkbox' === $elementType) {
  233. if ($element->selected() xor (bool) $value) {
  234. $this->clickOnElement($element);
  235. //$this->keyPress($xpath, 9);
  236. }
  237.  
  238. return;
  239. }
  240.  
  241. if ('radio' === $elementType) {
  242. $this->selectRadioValue($element, $value);
  243.  
  244. return;
  245. }
  246.  
  247. if ('file' === $elementType) {
  248. $element->postValue(array('value' => array(strval($value))));
  249.  
  250. return;
  251. }
  252. }
  253.  
  254. $value = strval($value);
  255.  
  256. if (in_array($elementName, array('input', 'textarea'))) {
  257. $existingValueLength = strlen($element->attribute('value'));
  258. //$this->moodle_moveto_element($element);
  259. $element->click();
  260. // Add the TAB key to ensure we unfocus the field as browsers are triggering the change event only
  261. // after leaving the field.
  262. $value = str_repeat(Key::BACKSPACE . Key::DELETE, $existingValueLength) . $value;
  263. }
  264.  
  265. $element->postValue(array('value' => array($value)));
  266. $script = "Syn.trigger('change', {}, {{ELEMENT}})";
  267. $this->withSyn()->executeJsOnXpath($xpath, $script);
  268. $this->keyPress($xpath, 9);
  269. $element->postValue(array('value' => array(key::SHIFT.key::TAB)));
  270. }
  271. /**
  272. * {@inheritdoc}
  273. */
  274. public function focusAndClickOnElement($xpath) {
  275. $element = $this->findElement($xpath);
  276. $this->moodle_moveto_element($element);
  277. $element->click();
  278. }
  279. /**
  280. * Move using JsonWireProtocol can fail on few OS/Browsers.
  281. * Use JS to scroll to the element, before clicking.
  282. *
  283. * @param ELEMENT $element element to move to.
  284. */
  285. public function moodle_moveto_element($element) {
  286. $script = <<<JS
  287. var node = {{ELEMENT}};
  288. node.scrollIntoView(true);
  289. JS;
  290. $this->executeJsOnElement($element, $script);
  291. }
  292. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement