[ Index ]

PHP Cross Reference of moodle-2.8

title

Body

[close]

/lib/tests/behat/ -> behat_general.php (source)

   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  /**
  18   * General use steps definitions.
  19   *
  20   * @package   core
  21   * @category  test
  22   * @copyright 2012 David Monllaó
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
  27  
  28  require_once (__DIR__ . '/../../behat/behat_base.php');
  29  
  30  use Behat\Mink\Exception\ExpectationException as ExpectationException,
  31      Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
  32      Behat\Mink\Exception\DriverException as DriverException,
  33      WebDriver\Exception\NoSuchElement as NoSuchElement,
  34      WebDriver\Exception\StaleElementReference as StaleElementReference,
  35      Behat\Gherkin\Node\TableNode as TableNode;
  36  
  37  /**
  38   * Cross component steps definitions.
  39   *
  40   * Basic web application definitions from MinkExtension and
  41   * BehatchExtension. Definitions modified according to our needs
  42   * when necessary and including only the ones we need to avoid
  43   * overlapping and confusion.
  44   *
  45   * @package   core
  46   * @category  test
  47   * @copyright 2012 David Monllaó
  48   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  49   */
  50  class behat_general extends behat_base {
  51  
  52      /**
  53       * @var string used by {@link switch_to_window()} and
  54       * {@link switch_to_the_main_window()} to work-around a Chrome browser issue.
  55       */
  56      const MAIN_WINDOW_NAME = '__moodle_behat_main_window_name';
  57  
  58      /**
  59       * Opens Moodle homepage.
  60       *
  61       * @Given /^I am on homepage$/
  62       */
  63      public function i_am_on_homepage() {
  64          $this->getSession()->visit($this->locate_path('/'));
  65      }
  66  
  67      /**
  68       * Reloads the current page.
  69       *
  70       * @Given /^I reload the page$/
  71       */
  72      public function reload() {
  73          $this->getSession()->reload();
  74      }
  75  
  76      /**
  77       * Follows the page redirection. Use this step after any action that shows a message and waits for a redirection
  78       *
  79       * @Given /^I wait to be redirected$/
  80       */
  81      public function i_wait_to_be_redirected() {
  82  
  83          // Xpath and processes based on core_renderer::redirect_message(), core_renderer::$metarefreshtag and
  84          // moodle_page::$periodicrefreshdelay possible values.
  85          if (!$metarefresh = $this->getSession()->getPage()->find('xpath', "//head/descendant::meta[@http-equiv='refresh']")) {
  86              // We don't fail the scenario if no redirection with message is found to avoid race condition false failures.
  87              return true;
  88          }
  89  
  90          // Wrapped in try & catch in case the redirection has already been executed.
  91          try {
  92              $content = $metarefresh->getAttribute('content');
  93          } catch (NoSuchElement $e) {
  94              return true;
  95          } catch (StaleElementReference $e) {
  96              return true;
  97          }
  98  
  99          // Getting the refresh time and the url if present.
 100          if (strstr($content, 'url') != false) {
 101  
 102              list($waittime, $url) = explode(';', $content);
 103  
 104              // Cleaning the URL value.
 105              $url = trim(substr($url, strpos($url, 'http')));
 106  
 107          } else {
 108              // Just wait then.
 109              $waittime = $content;
 110          }
 111  
 112  
 113          // Wait until the URL change is executed.
 114          if ($this->running_javascript()) {
 115              $this->getSession()->wait($waittime * 1000, false);
 116  
 117          } else if (!empty($url)) {
 118              // We redirect directly as we can not wait for an automatic redirection.
 119              $this->getSession()->getDriver()->getClient()->request('get', $url);
 120  
 121          } else {
 122              // Reload the page if no URL was provided.
 123              $this->getSession()->getDriver()->reload();
 124          }
 125      }
 126  
 127      /**
 128       * Switches to the specified iframe.
 129       *
 130       * @Given /^I switch to "(?P<iframe_name_string>(?:[^"]|\\")*)" iframe$/
 131       * @param string $iframename
 132       */
 133      public function switch_to_iframe($iframename) {
 134  
 135          // We spin to give time to the iframe to be loaded.
 136          // Using extended timeout as we don't know about which
 137          // kind of iframe will be loaded.
 138          $this->spin(
 139              function($context, $iframename) {
 140                  $context->getSession()->switchToIFrame($iframename);
 141  
 142                  // If no exception we are done.
 143                  return true;
 144              },
 145              $iframename,
 146              self::EXTENDED_TIMEOUT
 147          );
 148      }
 149  
 150      /**
 151       * Switches to the main Moodle frame.
 152       *
 153       * @Given /^I switch to the main frame$/
 154       */
 155      public function switch_to_the_main_frame() {
 156          $this->getSession()->switchToIFrame();
 157      }
 158  
 159      /**
 160       * Switches to the specified window. Useful when interacting with popup windows.
 161       *
 162       * @Given /^I switch to "(?P<window_name_string>(?:[^"]|\\")*)" window$/
 163       * @param string $windowname
 164       */
 165      public function switch_to_window($windowname) {
 166          // In Behat, some browsers (e.g. Chrome) are unable to switch to a
 167          // window without a name, and by default the main browser window does
 168          // not have a name. To work-around this, when we switch away from an
 169          // unnamed window (presumably the main window) to some other named
 170          // window, then we first set the main window name to a conventional
 171          // value that we can later use this name to switch back.
 172          $this->getSession()->evaluateScript(
 173                  'if (window.name == "") window.name = "' . self::MAIN_WINDOW_NAME . '"');
 174  
 175          $this->getSession()->switchToWindow($windowname);
 176      }
 177  
 178      /**
 179       * Switches to the main Moodle window. Useful when you finish interacting with popup windows.
 180       *
 181       * @Given /^I switch to the main window$/
 182       */
 183      public function switch_to_the_main_window() {
 184          $this->getSession()->switchToWindow(self::MAIN_WINDOW_NAME);
 185      }
 186  
 187      /**
 188       * Accepts the currently displayed alert dialog. This step does not work in all the browsers, consider it experimental.
 189       * @Given /^I accept the currently displayed dialog$/
 190       */
 191      public function accept_currently_displayed_alert_dialog() {
 192          $this->getSession()->getDriver()->getWebDriverSession()->accept_alert();
 193      }
 194  
 195      /**
 196       * Clicks link with specified id|title|alt|text.
 197       *
 198       * @When /^I follow "(?P<link_string>(?:[^"]|\\")*)"$/
 199       * @throws ElementNotFoundException Thrown by behat_base::find
 200       * @param string $link
 201       */
 202      public function click_link($link) {
 203  
 204          $linknode = $this->find_link($link);
 205          $this->ensure_node_is_visible($linknode);
 206          $linknode->click();
 207      }
 208  
 209      /**
 210       * Waits X seconds. Required after an action that requires data from an AJAX request.
 211       *
 212       * @Then /^I wait "(?P<seconds_number>\d+)" seconds$/
 213       * @param int $seconds
 214       */
 215      public function i_wait_seconds($seconds) {
 216  
 217          if (!$this->running_javascript()) {
 218              throw new DriverException('Waits are disabled in scenarios without Javascript support');
 219          }
 220  
 221          $this->getSession()->wait($seconds * 1000, false);
 222      }
 223  
 224      /**
 225       * Waits until the page is completely loaded. This step is auto-executed after every step.
 226       *
 227       * @Given /^I wait until the page is ready$/
 228       */
 229      public function wait_until_the_page_is_ready() {
 230  
 231          if (!$this->running_javascript()) {
 232              throw new DriverException('Waits are disabled in scenarios without Javascript support');
 233          }
 234  
 235          $this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
 236      }
 237  
 238      /**
 239       * Waits until the provided element selector exists in the DOM
 240       *
 241       * Using the protected method as this method will be usually
 242       * called by other methods which are not returning a set of
 243       * steps and performs the actions directly, so it would not
 244       * be executed if it returns another step.
 245  
 246       * @Given /^I wait until "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" exists$/
 247       * @param string $element
 248       * @param string $selector
 249       * @return void
 250       */
 251      public function wait_until_exists($element, $selectortype) {
 252          $this->ensure_element_exists($element, $selectortype);
 253      }
 254  
 255      /**
 256       * Waits until the provided element does not exist in the DOM
 257       *
 258       * Using the protected method as this method will be usually
 259       * called by other methods which are not returning a set of
 260       * steps and performs the actions directly, so it would not
 261       * be executed if it returns another step.
 262  
 263       * @Given /^I wait until "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" does not exist$/
 264       * @param string $element
 265       * @param string $selector
 266       * @return void
 267       */
 268      public function wait_until_does_not_exists($element, $selectortype) {
 269          $this->ensure_element_does_not_exist($element, $selectortype);
 270      }
 271  
 272      /**
 273       * Generic mouse over action. Mouse over a element of the specified type.
 274       *
 275       * @When /^I hover "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)"$/
 276       * @param string $element Element we look for
 277       * @param string $selectortype The type of what we look for
 278       */
 279      public function i_hover($element, $selectortype) {
 280  
 281          // Gets the node based on the requested selector type and locator.
 282          $node = $this->get_selected_node($selectortype, $element);
 283          $node->mouseOver();
 284      }
 285  
 286      /**
 287       * Generic click action. Click on the element of the specified type.
 288       *
 289       * @When /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)"$/
 290       * @param string $element Element we look for
 291       * @param string $selectortype The type of what we look for
 292       */
 293      public function i_click_on($element, $selectortype) {
 294  
 295          // Gets the node based on the requested selector type and locator.
 296          $node = $this->get_selected_node($selectortype, $element);
 297          $this->ensure_node_is_visible($node);
 298          $node->click();
 299      }
 300  
 301      /**
 302       * Sets the focus and takes away the focus from an element, generating blur JS event.
 303       *
 304       * @When /^I take focus off "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)"$/
 305       * @param string $element Element we look for
 306       * @param string $selectortype The type of what we look for
 307       */
 308      public function i_take_focus_off_field($element, $selectortype) {
 309          if (!$this->running_javascript()) {
 310              throw new ExpectationException('Can\'t take focus off from "' . $element . '" in non-js mode', $this->getSession());
 311          }
 312          // Gets the node based on the requested selector type and locator.
 313          $node = $this->get_selected_node($selectortype, $element);
 314          $this->ensure_node_is_visible($node);
 315  
 316          // Ensure element is focused before taking it off.
 317          $node->focus();
 318          $node->blur();
 319      }
 320  
 321      /**
 322       * Clicks the specified element and confirms the expected dialogue.
 323       *
 324       * @When /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" confirming the dialogue$/
 325       * @throws ElementNotFoundException Thrown by behat_base::find
 326       * @param string $link
 327       */
 328      public function i_click_on_confirming_the_dialogue($element, $selectortype) {
 329          $this->i_click_on($element, $selectortype);
 330          $this->accept_currently_displayed_alert_dialog();
 331      }
 332  
 333      /**
 334       * Click on the element of the specified type which is located inside the second element.
 335       *
 336       * @When /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" in the "(?P<element_container_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)"$/
 337       * @param string $element Element we look for
 338       * @param string $selectortype The type of what we look for
 339       * @param string $nodeelement Element we look in
 340       * @param string $nodeselectortype The type of selector where we look in
 341       */
 342      public function i_click_on_in_the($element, $selectortype, $nodeelement, $nodeselectortype) {
 343  
 344          $node = $this->get_node_in_container($selectortype, $element, $nodeselectortype, $nodeelement);
 345          $this->ensure_node_is_visible($node);
 346          $node->click();
 347      }
 348  
 349      /**
 350       * Drags and drops the specified element to the specified container. This step does not work in all the browsers, consider it experimental.
 351       *
 352       * The steps definitions calling this step as part of them should
 353       * manage the wait times by themselves as the times and when the
 354       * waits should be done depends on what is being dragged & dropper.
 355       *
 356       * @Given /^I drag "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector1_string>(?:[^"]|\\")*)" and I drop it in "(?P<container_element_string>(?:[^"]|\\")*)" "(?P<selector2_string>(?:[^"]|\\")*)"$/
 357       * @param string $element
 358       * @param string $selectortype
 359       * @param string $containerelement
 360       * @param string $containerselectortype
 361       */
 362      public function i_drag_and_i_drop_it_in($element, $selectortype, $containerelement, $containerselectortype) {
 363  
 364          list($sourceselector, $sourcelocator) = $this->transform_selector($selectortype, $element);
 365          $sourcexpath = $this->getSession()->getSelectorsHandler()->selectorToXpath($sourceselector, $sourcelocator);
 366  
 367          list($containerselector, $containerlocator) = $this->transform_selector($containerselectortype, $containerelement);
 368          $destinationxpath = $this->getSession()->getSelectorsHandler()->selectorToXpath($containerselector, $containerlocator);
 369  
 370          $this->getSession()->getDriver()->dragTo($sourcexpath, $destinationxpath);
 371      }
 372  
 373      /**
 374       * Checks, that the specified element is visible. Only available in tests using Javascript.
 375       *
 376       * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>(?:[^"]|\\")*)" should be visible$/
 377       * @throws ElementNotFoundException
 378       * @throws ExpectationException
 379       * @throws DriverException
 380       * @param string $element
 381       * @param string $selectortype
 382       * @return void
 383       */
 384      public function should_be_visible($element, $selectortype) {
 385  
 386          if (!$this->running_javascript()) {
 387              throw new DriverException('Visible checks are disabled in scenarios without Javascript support');
 388          }
 389  
 390          $node = $this->get_selected_node($selectortype, $element);
 391          if (!$node->isVisible()) {
 392              throw new ExpectationException('"' . $element . '" "' . $selectortype . '" is not visible', $this->getSession());
 393          }
 394      }
 395  
 396      /**
 397       * Checks, that the existing element is not visible. Only available in tests using Javascript.
 398       *
 399       * As a "not" method, it's performance could not be good, but in this
 400       * case the performance is good because the element must exist,
 401       * otherwise there would be a ElementNotFoundException, also here we are
 402       * not spinning until the element is visible.
 403       *
 404       * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>(?:[^"]|\\")*)" should not be visible$/
 405       * @throws ElementNotFoundException
 406       * @throws ExpectationException
 407       * @param string $element
 408       * @param string $selectortype
 409       * @return void
 410       */
 411      public function should_not_be_visible($element, $selectortype) {
 412  
 413          try {
 414              $this->should_be_visible($element, $selectortype);
 415              throw new ExpectationException('"' . $element . '" "' . $selectortype . '" is visible', $this->getSession());
 416          } catch (ExpectationException $e) {
 417              // All as expected.
 418          }
 419      }
 420  
 421      /**
 422       * Checks, that the specified element is visible inside the specified container. Only available in tests using Javascript.
 423       *
 424       * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" in the "(?P<element_container_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)" should be visible$/
 425       * @throws ElementNotFoundException
 426       * @throws DriverException
 427       * @throws ExpectationException
 428       * @param string $element Element we look for
 429       * @param string $selectortype The type of what we look for
 430       * @param string $nodeelement Element we look in
 431       * @param string $nodeselectortype The type of selector where we look in
 432       */
 433      public function in_the_should_be_visible($element, $selectortype, $nodeelement, $nodeselectortype) {
 434  
 435          if (!$this->running_javascript()) {
 436              throw new DriverException('Visible checks are disabled in scenarios without Javascript support');
 437          }
 438  
 439          $node = $this->get_node_in_container($selectortype, $element, $nodeselectortype, $nodeelement);
 440          if (!$node->isVisible()) {
 441              throw new ExpectationException(
 442                  '"' . $element . '" "' . $selectortype . '" in the "' . $nodeelement . '" "' . $nodeselectortype . '" is not visible',
 443                  $this->getSession()
 444              );
 445          }
 446      }
 447  
 448      /**
 449       * Checks, that the existing element is not visible inside the existing container. Only available in tests using Javascript.
 450       *
 451       * As a "not" method, it's performance could not be good, but in this
 452       * case the performance is good because the element must exist,
 453       * otherwise there would be a ElementNotFoundException, also here we are
 454       * not spinning until the element is visible.
 455       *
 456       * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" in the "(?P<element_container_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)" should not be visible$/
 457       * @throws ElementNotFoundException
 458       * @throws ExpectationException
 459       * @param string $element Element we look for
 460       * @param string $selectortype The type of what we look for
 461       * @param string $nodeelement Element we look in
 462       * @param string $nodeselectortype The type of selector where we look in
 463       */
 464      public function in_the_should_not_be_visible($element, $selectortype, $nodeelement, $nodeselectortype) {
 465  
 466          try {
 467              $this->in_the_should_be_visible($element, $selectortype, $nodeelement, $nodeselectortype);
 468              throw new ExpectationException(
 469                  '"' . $element . '" "' . $selectortype . '" in the "' . $nodeelement . '" "' . $nodeselectortype . '" is visible',
 470                  $this->getSession()
 471              );
 472          } catch (ExpectationException $e) {
 473              // All as expected.
 474          }
 475      }
 476  
 477      /**
 478       * Checks, that page contains specified text. It also checks if the text is visible when running Javascript tests.
 479       *
 480       * @Then /^I should see "(?P<text_string>(?:[^"]|\\")*)"$/
 481       * @throws ExpectationException
 482       * @param string $text
 483       */
 484      public function assert_page_contains_text($text) {
 485  
 486          // Looking for all the matching nodes without any other descendant matching the
 487          // same xpath (we are using contains(., ....).
 488          $xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
 489          $xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
 490              "[count(descendant::*[contains(., $xpathliteral)]) = 0]";
 491  
 492          try {
 493              $nodes = $this->find_all('xpath', $xpath);
 494          } catch (ElementNotFoundException $e) {
 495              throw new ExpectationException('"' . $text . '" text was not found in the page', $this->getSession());
 496          }
 497  
 498          // If we are not running javascript we have enough with the
 499          // element existing as we can't check if it is visible.
 500          if (!$this->running_javascript()) {
 501              return;
 502          }
 503  
 504          // We spin as we don't have enough checking that the element is there, we
 505          // should also ensure that the element is visible. Using microsleep as this
 506          // is a repeated step and global performance is important.
 507          $this->spin(
 508              function($context, $args) {
 509  
 510                  foreach ($args['nodes'] as $node) {
 511                      if ($node->isVisible()) {
 512                          return true;
 513                      }
 514                  }
 515  
 516                  // If non of the nodes is visible we loop again.
 517                  throw new ExpectationException('"' . $args['text'] . '" text was found but was not visible', $context->getSession());
 518              },
 519              array('nodes' => $nodes, 'text' => $text),
 520              false,
 521              false,
 522              true
 523          );
 524  
 525      }
 526  
 527      /**
 528       * Checks, that page doesn't contain specified text. When running Javascript tests it also considers that texts may be hidden.
 529       *
 530       * @Then /^I should not see "(?P<text_string>(?:[^"]|\\")*)"$/
 531       * @throws ExpectationException
 532       * @param string $text
 533       */
 534      public function assert_page_not_contains_text($text) {
 535  
 536          // Looking for all the matching nodes without any other descendant matching the
 537          // same xpath (we are using contains(., ....).
 538          $xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
 539          $xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
 540              "[count(descendant::*[contains(., $xpathliteral)]) = 0]";
 541  
 542          // We should wait a while to ensure that the page is not still loading elements.
 543          // Waiting less than self::TIMEOUT as we already waited for the DOM to be ready and
 544          // all JS to be executed.
 545          try {
 546              $nodes = $this->find_all('xpath', $xpath, false, false, self::REDUCED_TIMEOUT);
 547          } catch (ElementNotFoundException $e) {
 548              // All ok.
 549              return;
 550          }
 551  
 552          // If we are not running javascript we have enough with the
 553          // element existing as we can't check if it is hidden.
 554          if (!$this->running_javascript()) {
 555              throw new ExpectationException('"' . $text . '" text was found in the page', $this->getSession());
 556          }
 557  
 558          // If the element is there we should be sure that it is not visible.
 559          $this->spin(
 560              function($context, $args) {
 561  
 562                  foreach ($args['nodes'] as $node) {
 563                      if ($node->isVisible()) {
 564                          throw new ExpectationException('"' . $args['text'] . '" text was found in the page', $context->getSession());
 565                      }
 566                  }
 567  
 568                  // If non of the found nodes is visible we consider that the text is not visible.
 569                  return true;
 570              },
 571              array('nodes' => $nodes, 'text' => $text),
 572              self::REDUCED_TIMEOUT,
 573              false,
 574              true
 575          );
 576  
 577      }
 578  
 579      /**
 580       * Checks, that the specified element contains the specified text. When running Javascript tests it also considers that texts may be hidden.
 581       *
 582       * @Then /^I should see "(?P<text_string>(?:[^"]|\\")*)" in the "(?P<element_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)"$/
 583       * @throws ElementNotFoundException
 584       * @throws ExpectationException
 585       * @param string $text
 586       * @param string $element Element we look in.
 587       * @param string $selectortype The type of element where we are looking in.
 588       */
 589      public function assert_element_contains_text($text, $element, $selectortype) {
 590  
 591          // Getting the container where the text should be found.
 592          $container = $this->get_selected_node($selectortype, $element);
 593  
 594          // Looking for all the matching nodes without any other descendant matching the
 595          // same xpath (we are using contains(., ....).
 596          $xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
 597          $xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
 598              "[count(descendant::*[contains(., $xpathliteral)]) = 0]";
 599  
 600          // Wait until it finds the text inside the container, otherwise custom exception.
 601          try {
 602              $nodes = $this->find_all('xpath', $xpath, false, $container);
 603          } catch (ElementNotFoundException $e) {
 604              throw new ExpectationException('"' . $text . '" text was not found in the "' . $element . '" element', $this->getSession());
 605          }
 606  
 607          // If we are not running javascript we have enough with the
 608          // element existing as we can't check if it is visible.
 609          if (!$this->running_javascript()) {
 610              return;
 611          }
 612  
 613          // We also check the element visibility when running JS tests. Using microsleep as this
 614          // is a repeated step and global performance is important.
 615          $this->spin(
 616              function($context, $args) {
 617  
 618                  foreach ($args['nodes'] as $node) {
 619                      if ($node->isVisible()) {
 620                          return true;
 621                      }
 622                  }
 623  
 624                  throw new ExpectationException('"' . $args['text'] . '" text was found in the "' . $args['element'] . '" element but was not visible', $context->getSession());
 625              },
 626              array('nodes' => $nodes, 'text' => $text, 'element' => $element),
 627              false,
 628              false,
 629              true
 630          );
 631      }
 632  
 633      /**
 634       * Checks, that the specified element does not contain the specified text. When running Javascript tests it also considers that texts may be hidden.
 635       *
 636       * @Then /^I should not see "(?P<text_string>(?:[^"]|\\")*)" in the "(?P<element_string>(?:[^"]|\\")*)" "(?P<text_selector_string>[^"]*)"$/
 637       * @throws ElementNotFoundException
 638       * @throws ExpectationException
 639       * @param string $text
 640       * @param string $element Element we look in.
 641       * @param string $selectortype The type of element where we are looking in.
 642       */
 643      public function assert_element_not_contains_text($text, $element, $selectortype) {
 644  
 645          // Getting the container where the text should be found.
 646          $container = $this->get_selected_node($selectortype, $element);
 647  
 648          // Looking for all the matching nodes without any other descendant matching the
 649          // same xpath (we are using contains(., ....).
 650          $xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
 651          $xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
 652              "[count(descendant::*[contains(., $xpathliteral)]) = 0]";
 653  
 654          // We should wait a while to ensure that the page is not still loading elements.
 655          // Giving preference to the reliability of the results rather than to the performance.
 656          try {
 657              $nodes = $this->find_all('xpath', $xpath, false, $container, self::REDUCED_TIMEOUT);
 658          } catch (ElementNotFoundException $e) {
 659              // All ok.
 660              return;
 661          }
 662  
 663          // If we are not running javascript we have enough with the
 664          // element not being found as we can't check if it is visible.
 665          if (!$this->running_javascript()) {
 666              throw new ExpectationException('"' . $text . '" text was found in the "' . $element . '" element', $this->getSession());
 667          }
 668  
 669          // We need to ensure all the found nodes are hidden.
 670          $this->spin(
 671              function($context, $args) {
 672  
 673                  foreach ($args['nodes'] as $node) {
 674                      if ($node->isVisible()) {
 675                          throw new ExpectationException('"' . $args['text'] . '" text was found in the "' . $args['element'] . '" element', $context->getSession());
 676                      }
 677                  }
 678  
 679                  // If all the found nodes are hidden we are happy.
 680                  return true;
 681              },
 682              array('nodes' => $nodes, 'text' => $text, 'element' => $element),
 683              self::REDUCED_TIMEOUT,
 684              false,
 685              true
 686          );
 687      }
 688  
 689      /**
 690       * Checks, that the first specified element appears before the second one.
 691       *
 692       * @Given /^"(?P<preceding_element_string>(?:[^"]|\\")*)" "(?P<selector1_string>(?:[^"]|\\")*)" should appear before "(?P<following_element_string>(?:[^"]|\\")*)" "(?P<selector2_string>(?:[^"]|\\")*)"$/
 693       * @throws ExpectationException
 694       * @param string $preelement The locator of the preceding element
 695       * @param string $preselectortype The locator of the preceding element
 696       * @param string $postelement The locator of the latest element
 697       * @param string $postselectortype The selector type of the latest element
 698       */
 699      public function should_appear_before($preelement, $preselectortype, $postelement, $postselectortype) {
 700  
 701          // We allow postselectortype as a non-text based selector.
 702          list($preselector, $prelocator) = $this->transform_selector($preselectortype, $preelement);
 703          list($postselector, $postlocator) = $this->transform_selector($postselectortype, $postelement);
 704  
 705          $prexpath = $this->find($preselector, $prelocator)->getXpath();
 706          $postxpath = $this->find($postselector, $postlocator)->getXpath();
 707  
 708          // Using following xpath axe to find it.
 709          $msg = '"'.$preelement.'" "'.$preselectortype.'" does not appear before "'.$postelement.'" "'.$postselectortype.'"';
 710          $xpath = $prexpath.'/following::*[contains(., '.$postxpath.')]';
 711          if (!$this->getSession()->getDriver()->find($xpath)) {
 712              throw new ExpectationException($msg, $this->getSession());
 713          }
 714      }
 715  
 716      /**
 717       * Checks, that the first specified element appears after the second one.
 718       *
 719       * @Given /^"(?P<following_element_string>(?:[^"]|\\")*)" "(?P<selector1_string>(?:[^"]|\\")*)" should appear after "(?P<preceding_element_string>(?:[^"]|\\")*)" "(?P<selector2_string>(?:[^"]|\\")*)"$/
 720       * @throws ExpectationException
 721       * @param string $postelement The locator of the latest element
 722       * @param string $postselectortype The selector type of the latest element
 723       * @param string $preelement The locator of the preceding element
 724       * @param string $preselectortype The locator of the preceding element
 725       */
 726      public function should_appear_after($postelement, $postselectortype, $preelement, $preselectortype) {
 727  
 728          // We allow postselectortype as a non-text based selector.
 729          list($postselector, $postlocator) = $this->transform_selector($postselectortype, $postelement);
 730          list($preselector, $prelocator) = $this->transform_selector($preselectortype, $preelement);
 731  
 732          $postxpath = $this->find($postselector, $postlocator)->getXpath();
 733          $prexpath = $this->find($preselector, $prelocator)->getXpath();
 734  
 735          // Using preceding xpath axe to find it.
 736          $msg = '"'.$postelement.'" "'.$postselectortype.'" does not appear after "'.$preelement.'" "'.$preselectortype.'"';
 737          $xpath = $postxpath.'/preceding::*[contains(., '.$prexpath.')]';
 738          if (!$this->getSession()->getDriver()->find($xpath)) {
 739              throw new ExpectationException($msg, $this->getSession());
 740          }
 741      }
 742  
 743      /**
 744       * Checks, that element of specified type is disabled.
 745       *
 746       * @Then /^the "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should be disabled$/
 747       * @throws ExpectationException Thrown by behat_base::find
 748       * @param string $element Element we look in
 749       * @param string $selectortype The type of element where we are looking in.
 750       */
 751      public function the_element_should_be_disabled($element, $selectortype) {
 752  
 753          // Transforming from steps definitions selector/locator format to Mink format and getting the NodeElement.
 754          $node = $this->get_selected_node($selectortype, $element);
 755  
 756          if (!$node->hasAttribute('disabled')) {
 757              throw new ExpectationException('The element "' . $element . '" is not disabled', $this->getSession());
 758          }
 759      }
 760  
 761      /**
 762       * Checks, that element of specified type is enabled.
 763       *
 764       * @Then /^the "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should be enabled$/
 765       * @throws ExpectationException Thrown by behat_base::find
 766       * @param string $element Element we look on
 767       * @param string $selectortype The type of where we look
 768       */
 769      public function the_element_should_be_enabled($element, $selectortype) {
 770  
 771          // Transforming from steps definitions selector/locator format to mink format and getting the NodeElement.
 772          $node = $this->get_selected_node($selectortype, $element);
 773  
 774          if ($node->hasAttribute('disabled')) {
 775              throw new ExpectationException('The element "' . $element . '" is not enabled', $this->getSession());
 776          }
 777      }
 778  
 779      /**
 780       * Checks the provided element and selector type are readonly on the current page.
 781       *
 782       * @Then /^the "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should be readonly$/
 783       * @throws ExpectationException Thrown by behat_base::find
 784       * @param string $element Element we look in
 785       * @param string $selectortype The type of element where we are looking in.
 786       */
 787      public function the_element_should_be_readonly($element, $selectortype) {
 788          // Transforming from steps definitions selector/locator format to Mink format and getting the NodeElement.
 789          $node = $this->get_selected_node($selectortype, $element);
 790  
 791          if (!$node->hasAttribute('readonly')) {
 792              throw new ExpectationException('The element "' . $element . '" is not readonly', $this->getSession());
 793          }
 794      }
 795  
 796      /**
 797       * Checks the provided element and selector type are not readonly on the current page.
 798       *
 799       * @Then /^the "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not be readonly$/
 800       * @throws ExpectationException Thrown by behat_base::find
 801       * @param string $element Element we look in
 802       * @param string $selectortype The type of element where we are looking in.
 803       */
 804      public function the_element_should_not_be_readonly($element, $selectortype) {
 805          // Transforming from steps definitions selector/locator format to Mink format and getting the NodeElement.
 806          $node = $this->get_selected_node($selectortype, $element);
 807  
 808          if ($node->hasAttribute('readonly')) {
 809              throw new ExpectationException('The element "' . $element . '" is readonly', $this->getSession());
 810          }
 811      }
 812  
 813      /**
 814       * Checks the provided element and selector type exists in the current page.
 815       *
 816       * This step is for advanced users, use it if you don't find anything else suitable for what you need.
 817       *
 818       * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should exist$/
 819       * @throws ElementNotFoundException Thrown by behat_base::find
 820       * @param string $element The locator of the specified selector
 821       * @param string $selectortype The selector type
 822       */
 823      public function should_exist($element, $selectortype) {
 824  
 825          // Getting Mink selector and locator.
 826          list($selector, $locator) = $this->transform_selector($selectortype, $element);
 827  
 828          // Will throw an ElementNotFoundException if it does not exist.
 829          $this->find($selector, $locator);
 830      }
 831  
 832      /**
 833       * Checks that the provided element and selector type not exists in the current page.
 834       *
 835       * This step is for advanced users, use it if you don't find anything else suitable for what you need.
 836       *
 837       * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not exist$/
 838       * @throws ExpectationException
 839       * @param string $element The locator of the specified selector
 840       * @param string $selectortype The selector type
 841       */
 842      public function should_not_exist($element, $selectortype) {
 843  
 844          // Getting Mink selector and locator.
 845          list($selector, $locator) = $this->transform_selector($selectortype, $element);
 846  
 847          try {
 848  
 849              // Using directly the spin method as we want a reduced timeout but there is no
 850              // need for a 0.1 seconds interval because in the optimistic case we will timeout.
 851              $params = array('selector' => $selector, 'locator' => $locator);
 852              // The exception does not really matter as we will catch it and will never "explode".
 853              $exception = new ElementNotFoundException($this->getSession(), $selectortype, null, $element);
 854  
 855              // If all goes good it will throw an ElementNotFoundExceptionn that we will catch.
 856              $this->spin(
 857                  function($context, $args) {
 858                      return $context->getSession()->getPage()->findAll($args['selector'], $args['locator']);
 859                  },
 860                  $params,
 861                  self::REDUCED_TIMEOUT,
 862                  $exception,
 863                  false
 864              );
 865  
 866              throw new ExpectationException('The "' . $element . '" "' . $selectortype . '" exists in the current page', $this->getSession());
 867          } catch (ElementNotFoundException $e) {
 868              // It passes.
 869              return;
 870          }
 871      }
 872  
 873      /**
 874       * This step triggers cron like a user would do going to admin/cron.php.
 875       *
 876       * @Given /^I trigger cron$/
 877       */
 878      public function i_trigger_cron() {
 879          $this->getSession()->visit($this->locate_path('/admin/cron.php'));
 880      }
 881  
 882      /**
 883       * Checks that an element and selector type exists in another element and selector type on the current page.
 884       *
 885       * This step is for advanced users, use it if you don't find anything else suitable for what you need.
 886       *
 887       * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should exist in the "(?P<element2_string>(?:[^"]|\\")*)" "(?P<selector2_string>[^"]*)"$/
 888       * @throws ElementNotFoundException Thrown by behat_base::find
 889       * @param string $element The locator of the specified selector
 890       * @param string $selectortype The selector type
 891       * @param string $containerelement The container selector type
 892       * @param string $containerselectortype The container locator
 893       */
 894      public function should_exist_in_the($element, $selectortype, $containerelement, $containerselectortype) {
 895          // Get the container node.
 896          $containernode = $this->get_selected_node($containerselectortype, $containerelement);
 897  
 898          list($selector, $locator) = $this->transform_selector($selectortype, $element);
 899  
 900          // Specific exception giving info about where can't we find the element.
 901          $locatorexceptionmsg = $element . '" in the "' . $containerelement. '" "' . $containerselectortype. '"';
 902          $exception = new ElementNotFoundException($this->getSession(), $selectortype, null, $locatorexceptionmsg);
 903  
 904          // Looks for the requested node inside the container node.
 905          $this->find($selector, $locator, $exception, $containernode);
 906      }
 907  
 908      /**
 909       * Checks that an element and selector type does not exist in another element and selector type on the current page.
 910       *
 911       * This step is for advanced users, use it if you don't find anything else suitable for what you need.
 912       *
 913       * @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not exist in the "(?P<element2_string>(?:[^"]|\\")*)" "(?P<selector2_string>[^"]*)"$/
 914       * @throws ExpectationException
 915       * @param string $element The locator of the specified selector
 916       * @param string $selectortype The selector type
 917       * @param string $containerelement The container selector type
 918       * @param string $containerselectortype The container locator
 919       */
 920      public function should_not_exist_in_the($element, $selectortype, $containerelement, $containerselectortype) {
 921  
 922          // Get the container node; here we throw an exception
 923          // if the container node does not exist.
 924          $containernode = $this->get_selected_node($containerselectortype, $containerelement);
 925  
 926          list($selector, $locator) = $this->transform_selector($selectortype, $element);
 927  
 928          // Will throw an ElementNotFoundException if it does not exist, but, actually
 929          // it should not exist, so we try & catch it.
 930          try {
 931              // Would be better to use a 1 second sleep because the element should not be there,
 932              // but we would need to duplicate the whole find_all() logic to do it, the benefit of
 933              // changing to 1 second sleep is not significant.
 934              $this->find($selector, $locator, false, $containernode, self::REDUCED_TIMEOUT);
 935              throw new ExpectationException('The "' . $element . '" "' . $selectortype . '" exists in the "' .
 936                  $containerelement . '" "' . $containerselectortype . '"', $this->getSession());
 937          } catch (ElementNotFoundException $e) {
 938              // It passes.
 939              return;
 940          }
 941      }
 942  
 943      /**
 944       * Change browser window size small: 640x480, medium: 1024x768, large: 2560x1600, custom: widthxheight
 945       *
 946       * Example: I change window size to "small" or I change window size to "1024x768"
 947       *
 948       * @throws ExpectationException
 949       * @Then /^I change window size to "([^"](small|medium|large|\d+x\d+))"$/
 950       * @param string $windowsize size of the window (small|medium|large|wxh).
 951       */
 952      public function i_change_window_size_to($windowsize) {
 953          $this->resize_window($windowsize);
 954      }
 955  
 956      /**
 957       * Checks whether there is an attribute on the given element that contains the specified text.
 958       *
 959       * @Then /^the "(?P<attribute_string>[^"]*)" attribute of "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should contain "(?P<text_string>(?:[^"]|\\")*)"$/
 960       * @throws ExpectationException
 961       * @param string $attribute Name of attribute
 962       * @param string $element The locator of the specified selector
 963       * @param string $selectortype The selector type
 964       * @param string $text Expected substring
 965       */
 966      public function the_attribute_of_should_contain($attribute, $element, $selectortype, $text) {
 967          // Get the container node (exception if it doesn't exist).
 968          $containernode = $this->get_selected_node($selectortype, $element);
 969          $value = $containernode->getAttribute($attribute);
 970          if ($value == null) {
 971              throw new ExpectationException('The attribute "' . $attribute. '" does not exist',
 972                      $this->getSession());
 973          } else if (strpos($value, $text) === false) {
 974              throw new ExpectationException('The attribute "' . $attribute .
 975                      '" does not contain "' . $text . '" (actual value: "' . $value . '")',
 976                      $this->getSession());
 977          }
 978      }
 979  
 980      /**
 981       * Checks that the attribute on the given element does not contain the specified text.
 982       *
 983       * @Then /^the "(?P<attribute_string>[^"]*)" attribute of "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" should not contain "(?P<text_string>(?:[^"]|\\")*)"$/
 984       * @throws ExpectationException
 985       * @param string $attribute Name of attribute
 986       * @param string $element The locator of the specified selector
 987       * @param string $selectortype The selector type
 988       * @param string $text Expected substring
 989       */
 990      public function the_attribute_of_should_not_contain($attribute, $element, $selectortype, $text) {
 991          // Get the container node (exception if it doesn't exist).
 992          $containernode = $this->get_selected_node($selectortype, $element);
 993          $value = $containernode->getAttribute($attribute);
 994          if ($value == null) {
 995              throw new ExpectationException('The attribute "' . $attribute. '" does not exist',
 996                      $this->getSession());
 997          } else if (strpos($value, $text) !== false) {
 998              throw new ExpectationException('The attribute "' . $attribute .
 999                      '" contains "' . $text . '" (value: "' . $value . '")',
1000                      $this->getSession());
1001          }
1002      }
1003  
1004      /**
1005       * Checks the provided value exists in specific row/column of table.
1006       *
1007       * @Then /^"(?P<row_string>[^"]*)" row "(?P<column_string>[^"]*)" column of "(?P<table_string>[^"]*)" table should contain "(?P<value_string>[^"]*)"$/
1008       * @throws ElementNotFoundException
1009       * @param string $row row text which will be looked in.
1010       * @param string $column column text to search (or numeric value for the column position)
1011       * @param string $table table id/class/caption
1012       * @param string $value text to check.
1013       */
1014      public function row_column_of_table_should_contain($row, $column, $table, $value) {
1015          $tablenode = $this->get_selected_node('table', $table);
1016          $tablexpath = $tablenode->getXpath();
1017  
1018          $rowliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($row);
1019          $valueliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($value);
1020          $columnliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($column);
1021  
1022          if (preg_match('/^-?(\d+)-?$/', $column, $columnasnumber)) {
1023              // Column indicated as a number, just use it as position of the column.
1024              $columnpositionxpath = "/child::*[position() = {$columnasnumber[1]}]";
1025          } else {
1026              // Header can be in thead or tbody (first row), following xpath should work.
1027              $theadheaderxpath = "thead/tr[1]/th[(normalize-space(.)=" . $columnliteral . " or a[normalize-space(text())=" .
1028                  $columnliteral . "])]";
1029              $tbodyheaderxpath = "tbody/tr[1]/td[(normalize-space(.)=" . $columnliteral . " or a[normalize-space(text())=" .
1030                  $columnliteral . "])]";
1031  
1032              // Check if column exists.
1033              $columnheaderxpath = $tablexpath . "[" . $theadheaderxpath . " | " . $tbodyheaderxpath . "]";
1034              $columnheader = $this->getSession()->getDriver()->find($columnheaderxpath);
1035              if (empty($columnheader)) {
1036                  $columnexceptionmsg = $column . '" in table "' . $table . '"';
1037                  throw new ElementNotFoundException($this->getSession(), "\n$columnheaderxpath\n\n".'Column', null, $columnexceptionmsg);
1038              }
1039              // Following conditions were considered before finding column count.
1040              // 1. Table header can be in thead/tr/th or tbody/tr/td[1].
1041              // 2. First column can have th (Gradebook -> user report), so having lenient sibling check.
1042              $columnpositionxpath = "/child::*[position() = count(" . $tablexpath . "/" . $theadheaderxpath .
1043                  "/preceding-sibling::*) + 1]";
1044          }
1045  
1046          // Check if value exists in specific row/column.
1047          // Get row xpath.
1048          $rowxpath = $tablexpath."/tbody/tr[th[normalize-space(.)=" . $rowliteral . "] | td[normalize-space(.)=" . $rowliteral . "]]";
1049  
1050          $columnvaluexpath = $rowxpath . $columnpositionxpath . "[contains(normalize-space(.)," . $valueliteral . ")]";
1051  
1052          // Looks for the requested node inside the container node.
1053          $coumnnode = $this->getSession()->getDriver()->find($columnvaluexpath);
1054          if (empty($coumnnode)) {
1055              $locatorexceptionmsg = $value . '" in "' . $row . '" row with column "' . $column;
1056              throw new ElementNotFoundException($this->getSession(), "\n$columnvaluexpath\n\n".'Column value', null, $locatorexceptionmsg);
1057          }
1058      }
1059  
1060      /**
1061       * Checks the provided value should not exist in specific row/column of table.
1062       *
1063       * @Then /^"(?P<row_string>[^"]*)" row "(?P<column_string>[^"]*)" column of "(?P<table_string>[^"]*)" table should not contain "(?P<value_string>[^"]*)"$/
1064       * @throws ElementNotFoundException
1065       * @param string $row row text which will be looked in.
1066       * @param string $column column text to search
1067       * @param string $table table id/class/caption
1068       * @param string $value text to check.
1069       */
1070      public function row_column_of_table_should_not_contain($row, $column, $table, $value) {
1071          try {
1072              $this->row_column_of_table_should_contain($row, $column, $table, $value);
1073              // Throw exception if found.
1074              throw new ExpectationException(
1075                  '"' . $column . '" with value "' . $value . '" is present in "' . $row . '"  row for table "' . $table . '"',
1076                  $this->getSession()
1077              );
1078          } catch (ElementNotFoundException $e) {
1079              // Table row/column doesn't contain this value. Nothing to do.
1080              return;
1081          }
1082      }
1083  
1084      /**
1085       * Checks that the provided value exist in table.
1086       * More info in http://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps.
1087       *
1088       * First row may contain column headers or numeric indexes of the columns
1089       * (syntax -1- is also considered to be column index). Column indexes are
1090       * useful in case of multirow headers and/or presence of cells with colspan.
1091       *
1092       * @Then /^the following should exist in the "(?P<table_string>[^"]*)" table:$/
1093       * @throws ExpectationException
1094       * @param string $table name of table
1095       * @param TableNode $data table with first row as header and following values
1096       *        | Header 1 | Header 2 | Header 3 |
1097       *        | Value 1 | Value 2 | Value 3|
1098       */
1099      public function following_should_exist_in_the_table($table, TableNode $data) {
1100          $datahash = $data->getHash();
1101  
1102          foreach ($datahash as $row) {
1103              $firstcell = null;
1104              foreach ($row as $column => $value) {
1105                  if ($firstcell === null) {
1106                      $firstcell = $value;
1107                  } else {
1108                      $this->row_column_of_table_should_contain($firstcell, $column, $table, $value);
1109                  }
1110              }
1111          }
1112      }
1113  
1114      /**
1115       * Checks that the provided value exist in table.
1116       * More info in http://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps.
1117       *
1118       * @Then /^the following should not exist in the "(?P<table_string>[^"]*)" table:$/
1119       * @throws ExpectationException
1120       * @param string $table name of table
1121       * @param TableNode $data table with first row as header and following values
1122       *        | Header 1 | Header 2 | Header 3 |
1123       *        | Value 1 | Value 2 | Value 3|
1124       */
1125      public function following_should_not_exist_in_the_table($table, TableNode $data) {
1126          $datahash = $data->getHash();
1127  
1128          foreach ($datahash as $value) {
1129              $row = array_shift($value);
1130              foreach ($value as $column => $value) {
1131                  try {
1132                      $this->row_column_of_table_should_contain($row, $column, $table, $value);
1133                      // Throw exception if found.
1134                      throw new ExpectationException('"' . $column . '" with value "' . $value . '" is present in "' .
1135                          $row . '"  row for table "' . $table . '"', $this->getSession()
1136                      );
1137                  } catch (ElementNotFoundException $e) {
1138                      // Table row/column doesn't contain this value. Nothing to do.
1139                      continue;
1140                  }
1141              }
1142          }
1143      }
1144  
1145      /**
1146       * Given the text of a link, download the linked file and return the contents.
1147       *
1148       * This is a helper method used by {@link following_should_download_bytes()}
1149       * and {@link following_should_download_between_and_bytes()}
1150       *
1151       * @param string $link the text of the link.
1152       * @return string the content of the downloaded file.
1153       */
1154      protected function download_file_from_link($link) {
1155          // Find the link.
1156          $linknode = $this->find_link($link);
1157          $this->ensure_node_is_visible($linknode);
1158  
1159          // Get the href and check it.
1160          $url = $linknode->getAttribute('href');
1161          if (!$url) {
1162              throw new ExpectationException('Download link does not have href attribute',
1163                      $this->getSession());
1164          }
1165          if (!preg_match('~^https?://~', $url)) {
1166              throw new ExpectationException('Download link not an absolute URL: ' . $url,
1167                      $this->getSession());
1168          }
1169  
1170          // Download the URL and check the size.
1171          $session = $this->getSession()->getCookie('MoodleSession');
1172          return download_file_content($url, array('Cookie' => 'MoodleSession=' . $session));
1173      }
1174  
1175      /**
1176       * Downloads the file from a link on the page and checks the size.
1177       *
1178       * Only works if the link has an href attribute. Javascript downloads are
1179       * not supported. Currently, the href must be an absolute URL.
1180       *
1181       * @Then /^following "(?P<link_string>[^"]*)" should download "(?P<expected_bytes>\d+)" bytes$/
1182       * @throws ExpectationException
1183       * @param string $link the text of the link.
1184       * @param number $expectedsize the expected file size in bytes.
1185       */
1186      public function following_should_download_bytes($link, $expectedsize) {
1187          $result = $this->download_file_from_link($link);
1188          $actualsize = (int)strlen($result);
1189          if ($actualsize !== (int)$expectedsize) {
1190              throw new ExpectationException('Downloaded data was ' . $actualsize .
1191                      ' bytes, expecting ' . $expectedsize, $this->getSession());
1192          }
1193      }
1194  
1195      /**
1196       * Downloads the file from a link on the page and checks the size is in a given range.
1197       *
1198       * Only works if the link has an href attribute. Javascript downloads are
1199       * not supported. Currently, the href must be an absolute URL.
1200       *
1201       * The range includes the endpoints. That is, a 10 byte file in considered to
1202       * be between "5" and "10" bytes, and between "10" and "20" bytes.
1203       *
1204       * @Then /^following "(?P<link_string>[^"]*)" should download between "(?P<min_bytes>\d+)" and "(?P<max_bytes>\d+)" bytes$/
1205       * @throws ExpectationException
1206       * @param string $link the text of the link.
1207       * @param number $minexpectedsize the minimum expected file size in bytes.
1208       * @param number $maxexpectedsize the maximum expected file size in bytes.
1209       */
1210      public function following_should_download_between_and_bytes($link, $minexpectedsize, $maxexpectedsize) {
1211          // If the minimum is greater than the maximum then swap the values.
1212          if ((int)$minexpectedsize > (int)$maxexpectedsize) {
1213              list($minexpectedsize, $maxexpectedsize) = array($maxexpectedsize, $minexpectedsize);
1214          }
1215  
1216          $result = $this->download_file_from_link($link);
1217          $actualsize = (int)strlen($result);
1218          if ($actualsize < $minexpectedsize || $actualsize > $maxexpectedsize) {
1219              throw new ExpectationException('Downloaded data was ' . $actualsize .
1220                      ' bytes, expecting between ' . $minexpectedsize . ' and ' .
1221                      $maxexpectedsize, $this->getSession());
1222          }
1223      }
1224  }


Generated: Fri Nov 28 20:29:05 2014 Cross-referenced by PHPXref 0.7.1