[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/tests/behat/ -> behat_navigation.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   * Navigation 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  use Behat\Mink\Exception\DriverException as DriverException;
  32  
  33  /**
  34   * Steps definitions to navigate through the navigation tree nodes.
  35   *
  36   * @package    core
  37   * @category   test
  38   * @copyright  2012 David Monllaó
  39   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  40   */
  41  class behat_navigation extends behat_base {
  42  
  43      /**
  44       * Helper function to get a navigation nodes text element given its text from within the navigation block.
  45       *
  46       * This function finds the node with the given text from within the navigation block.
  47       * It checks to make sure the node is visible, and then returns it.
  48       *
  49       * @param string $text
  50       * @param bool $branch Set this true if you're only interested in the node if its a branch.
  51       * @param null|bool $collapsed Set this to true or false if you want the node to either be collapsed or not.
  52       *    If its left as null then we don't worry about it.
  53       * @param null|string|Exception|false $exception The exception to throw if the node is not found.
  54       * @return \Behat\Mink\Element\NodeElement
  55       */
  56      protected function get_node_text_node($text, $branch = false, $collapsed = null, $exception = null) {
  57          if ($exception === null) {
  58              $exception = new ExpectationException('The "' . $text . '" node could not be found', $this->getSession());
  59          } else if (is_string($exception)) {
  60              $exception = new ExpectationException($exception, $this->getSession());
  61          }
  62  
  63          $nodetextliteral = behat_context_helper::escape($text);
  64          $hasblocktree = "[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]";
  65          $hasbranch = "[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]";
  66          $hascollapsed = "p[@aria-expanded='false']";
  67          $notcollapsed = "p[@aria-expanded='true']";
  68          $match = "[normalize-space(.)={$nodetextliteral}]";
  69  
  70          // Avoid problems with quotes.
  71          $isbranch = ($branch) ? $hasbranch : '';
  72          if ($collapsed === true) {
  73              $iscollapsed = $hascollapsed;
  74          } else if ($collapsed === false) {
  75              $iscollapsed = $notcollapsed;
  76          } else {
  77              $iscollapsed = 'p';
  78          }
  79  
  80          // First check root nodes, it can be a span or link.
  81          $xpath  = "//ul{$hasblocktree}/li/{$hascollapsed}{$isbranch}/span{$match}|";
  82          $xpath  .= "//ul{$hasblocktree}/li/{$hascollapsed}{$isbranch}/a{$match}|";
  83  
  84          // Next search for the node containing the text within a link.
  85          $xpath .= "//ul{$hasblocktree}//ul/li/{$iscollapsed}{$isbranch}/a{$match}|";
  86  
  87          // Finally search for the node containing the text within a span.
  88          $xpath .= "//ul{$hasblocktree}//ul/li/{$iscollapsed}{$isbranch}/span{$match}";
  89  
  90          $node = $this->find('xpath', $xpath, $exception);
  91          $this->ensure_node_is_visible($node);
  92          return $node;
  93      }
  94  
  95      /**
  96       * Returns true if the navigation node with the given text is expandable.
  97       *
  98       * @Given /^navigation node "([^"]*)" should be expandable$/
  99       *
 100       * @throws ExpectationException
 101       * @param string $nodetext
 102       * @return bool
 103       */
 104      public function navigation_node_should_be_expandable($nodetext) {
 105          if (!$this->running_javascript()) {
 106              // Nodes are only expandable when JavaScript is enabled.
 107              return false;
 108          }
 109  
 110          $node = $this->get_node_text_node($nodetext, true);
 111          $node = $node->getParent();
 112          if ($node->hasClass('emptybranch')) {
 113              throw new ExpectationException('The "' . $nodetext . '" node is not expandable', $this->getSession());
 114          }
 115  
 116          return true;
 117      }
 118  
 119      /**
 120       * Returns true if the navigation node with the given text is not expandable.
 121       *
 122       * @Given /^navigation node "([^"]*)" should not be expandable$/
 123       *
 124       * @throws ExpectationException
 125       * @param string $nodetext
 126       * @return bool
 127       */
 128      public function navigation_node_should_not_be_expandable($nodetext) {
 129          if (!$this->running_javascript()) {
 130              // Nodes are only expandable when JavaScript is enabled.
 131              return false;
 132          }
 133  
 134          $node = $this->get_node_text_node($nodetext);
 135          $node = $node->getParent();
 136  
 137          if ($node->hasClass('emptybranch') || $node->hasClass('tree_item')) {
 138              return true;
 139          }
 140          throw new ExpectationException('The "' . $nodetext . '" node is expandable', $this->getSession());
 141      }
 142  
 143      /**
 144       * Click on an entry in the user menu.
 145       * @Given /^I follow "(?P<nodetext_string>(?:[^"]|\\")*)" in the user menu$/
 146       *
 147       * @param string $nodetext
 148       */
 149      public function i_follow_in_the_user_menu($nodetext) {
 150  
 151          if ($this->running_javascript()) {
 152              // The user menu must be expanded when JS is enabled.
 153              $xpath = "//div[@class='usermenu']//a[contains(concat(' ', @class, ' '), ' toggle-display ')]";
 154              $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
 155          }
 156  
 157          // Now select the link.
 158          // The CSS path is always present, with or without JS.
 159          $csspath = ".usermenu [data-rel='menu-content']";
 160  
 161          $this->execute('behat_general::i_click_on_in_the',
 162              array($nodetext, "link", $csspath, "css_element")
 163          );
 164      }
 165  
 166      /**
 167       * Expands the selected node of the navigation tree that matches the text.
 168       * @Given /^I expand "(?P<nodetext_string>(?:[^"]|\\")*)" node$/
 169       *
 170       * @throws ExpectationException
 171       * @param string $nodetext
 172       * @return bool|void
 173       */
 174      public function i_expand_node($nodetext) {
 175  
 176          // This step is useless with Javascript disabled as Moodle auto expands
 177          // all of tree's nodes; adding this because of scenarios that shares the
 178          // same steps with and without Javascript enabled.
 179          if (!$this->running_javascript()) {
 180              if ($nodetext === get_string('administrationsite')) {
 181                  // Administration menu is not loaded by default any more. Click the link to expand.
 182                  $this->execute('behat_general::i_click_on_in_the',
 183                      array($nodetext, "link", get_string('administration'), "block")
 184                  );
 185                  return true;
 186              }
 187              return true;
 188          }
 189  
 190          $node = $this->get_node_text_node($nodetext, true, true, 'The "' . $nodetext . '" node can not be expanded');
 191          // Check if the node is a link AND a branch.
 192          if (strtolower($node->getTagName()) === 'a') {
 193              // We just want to expand the node, we don't want to follow it.
 194              $node = $node->getParent();
 195          }
 196          $node->click();
 197      }
 198  
 199      /**
 200       * Collapses the selected node of the navigation tree that matches the text.
 201       *
 202       * @Given /^I collapse "(?P<nodetext_string>(?:[^"]|\\")*)" node$/
 203       * @throws ExpectationException
 204       * @param string $nodetext
 205       * @return bool|void
 206       */
 207      public function i_collapse_node($nodetext) {
 208  
 209          // No collapsible nodes with non-JS browsers.
 210          if (!$this->running_javascript()) {
 211              return true;
 212          }
 213  
 214          $node = $this->get_node_text_node($nodetext, true, false, 'The "' . $nodetext . '" node can not be collapsed');
 215          // Check if the node is a link AND a branch.
 216          if (strtolower($node->getTagName()) === 'a') {
 217              // We just want to expand the node, we don't want to follow it.
 218              $node = $node->getParent();
 219          }
 220          $node->click();
 221      }
 222  
 223      /**
 224       * Click link in navigation tree that matches the text in parentnode/s (seperated using greater-than character if more than one)
 225       *
 226       * @Given /^I navigate to "(?P<nodetext_string>(?:[^"]|\\")*)" node in "(?P<parentnodes_string>(?:[^"]|\\")*)"$/
 227       *
 228       * @throws ExpectationException
 229       * @param string $nodetext navigation node to click.
 230       * @param string $parentnodes comma seperated list of parent nodes.
 231       * @return void
 232       */
 233      public function i_navigate_to_node_in($nodetext, $parentnodes) {
 234  
 235          // Site admin is different and needs special treatment.
 236          $siteadminstr = get_string('administrationsite');
 237  
 238          // Create array of all parentnodes.
 239          $parentnodes = array_map('trim', explode('>', $parentnodes));
 240          $countparentnode = count($parentnodes);
 241  
 242          // If JS is disabled and Site administration is not expanded we
 243          // should follow it, so all the lower-level nodes are available.
 244          if (!$this->running_javascript()) {
 245              if ($parentnodes[0] === $siteadminstr) {
 246                  // We don't know if there if Site admin is already expanded so
 247                  // don't wait, it is non-JS and we already waited for the DOM.
 248                  $siteadminlink = $this->getSession()->getPage()->find('named_exact', array('link', "'" . $siteadminstr . "'"));
 249                  if ($siteadminlink) {
 250                      $siteadminlink->click();
 251                  }
 252              }
 253          }
 254  
 255          // Get top level node.
 256          $node = $this->get_top_navigation_node($parentnodes[0]);
 257  
 258          // Expand all nodes.
 259          for ($i = 0; $i < $countparentnode; $i++) {
 260              if ($i > 0) {
 261                  // Sub nodes within top level node.
 262                  $node = $this->get_navigation_node($parentnodes[$i], $node);
 263              }
 264  
 265              // The p node contains the aria jazz.
 266              $pnodexpath = "/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]";
 267              $pnode = $node->find('xpath', $pnodexpath);
 268  
 269              // Keep expanding all sub-parents if js enabled.
 270              if ($pnode && $this->running_javascript() && $pnode->hasAttribute('aria-expanded') &&
 271                  ($pnode->getAttribute('aria-expanded') == "false")) {
 272  
 273                  $this->ensure_node_is_visible($pnode);
 274  
 275                  // If node is a link then some driver click in the middle of the node, which click on link and
 276                  // page gets redirected. To ensure expansion works in all cases, check if the node to expand is a
 277                  // link and if yes then click on link and wait for it to navigate to next page with node expanded.
 278                  $nodetoexpandliteral = behat_context_helper::escape($parentnodes[$i]);
 279                  $nodetoexpandxpathlink = $pnodexpath . "/a[normalize-space(.)=" . $nodetoexpandliteral . "]";
 280  
 281                  if ($nodetoexpandlink = $node->find('xpath', $nodetoexpandxpathlink)) {
 282                      $behatgeneralcontext = behat_context_helper::get('behat_general');
 283                      $nodetoexpandlink->click();
 284                      $behatgeneralcontext->wait_until_the_page_is_ready();
 285                  } else {
 286                      $pnode->click();
 287                  }
 288  
 289                  // Wait for node to load, if not loaded before.
 290                  if ($pnode->hasAttribute('data-loaded') && $pnode->getAttribute('data-loaded') == "false") {
 291                      $jscondition = '(document.evaluate("' . $pnode->getXpath() . '", document, null, '.
 292                          'XPathResult.ANY_TYPE, null).iterateNext().getAttribute(\'data-loaded\') == "true")';
 293  
 294                      $this->getSession()->wait(self::EXTENDED_TIMEOUT * 1000, $jscondition);
 295                  }
 296              }
 297          }
 298  
 299          // Finally, click on requested node under navigation.
 300          $nodetextliteral = behat_context_helper::escape($nodetext);
 301          $xpath = "/ul/li/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]" .
 302                  "/a[normalize-space(.)=" . $nodetextliteral . "]";
 303          $nodetoclick = $node->find('xpath', $xpath);
 304  
 305          // Throw exception if no node found.
 306          if (!$nodetoclick) {
 307              throw new ExpectationException('Navigation node "' . $nodetext . '" not found under "' .
 308                  implode($parentnodes, ' > ') . '"', $this->getSession());
 309          }
 310  
 311          $nodetoclick->click();
 312      }
 313  
 314      /**
 315       * Helper function to get top navigation node in tree.
 316       *
 317       * @throws ExpectationException if note not found.
 318       * @param string $nodetext name of top navigation node in tree.
 319       * @return NodeElement
 320       */
 321      protected function get_top_navigation_node($nodetext) {
 322  
 323          // Avoid problems with quotes.
 324          $nodetextliteral = behat_context_helper::escape($nodetext);
 325          $exception = new ExpectationException('Top navigation node "' . $nodetext . ' not found in "', $this->getSession());
 326  
 327          // First find in navigation block.
 328          $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]" .
 329              "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
 330              "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
 331              "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
 332              "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
 333              "/span[normalize-space(.)=" . $nodetextliteral ."]]" .
 334              "|" .
 335              "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" .
 336              "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
 337              "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
 338              "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
 339              "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
 340              "/span[normalize-space(.)=" . $nodetextliteral ."]]" .
 341              "|" .
 342              "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" .
 343              "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
 344              "/li[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
 345              "/span[normalize-space(.)=" . $nodetextliteral ."]]" .
 346              "|" .
 347              "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" .
 348              "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
 349              "/li[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
 350              "/a[normalize-space(.)=" . $nodetextliteral ."]]";
 351  
 352          $node = $this->find('xpath', $xpath, $exception);
 353  
 354          return $node;
 355      }
 356  
 357      /**
 358       * Helper function to get sub-navigation node.
 359       *
 360       * @throws ExpectationException if note not found.
 361       * @param string $nodetext node to find.
 362       * @param NodeElement $parentnode parent navigation node.
 363       * @return NodeElement.
 364       */
 365      protected function get_navigation_node($nodetext, $parentnode = null) {
 366  
 367          // Avoid problems with quotes.
 368          $nodetextliteral = behat_context_helper::escape($nodetext);
 369  
 370          $xpath = "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
 371              "[child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
 372              "/child::span[normalize-space(.)=" . $nodetextliteral ."]]";
 373          $node = $parentnode->find('xpath', $xpath);
 374          if (!$node) {
 375              $xpath = "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
 376                  "[child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
 377                  "/child::a[normalize-space(.)=" . $nodetextliteral ."]]";
 378              $node = $parentnode->find('xpath', $xpath);
 379          }
 380  
 381          if (!$node) {
 382              throw new ExpectationException('Sub-navigation node "' . $nodetext . '" not found under "' .
 383                  $parentnode->getText() . '"', $this->getSession());
 384          }
 385          return $node;
 386      }
 387  
 388      /**
 389       * Step to open the navigation bar if it is needed.
 390       *
 391       * The top log in and log out links are hidden when middle or small
 392       * size windows (or devices) are used. This step returns a step definition
 393       * clicking to expand the navbar if it is hidden.
 394       *
 395       * @Given /^I expand navigation bar$/
 396       */
 397      public function get_expand_navbar_step() {
 398  
 399          // Checking if we need to click the navbar button to show the navigation menu, it
 400          // is hidden by default when using clean theme and a medium or small screen size.
 401  
 402          // The DOM and the JS should be all ready and loaded. Running without spinning
 403          // as this is a widely used step and we can not spend time here trying to see
 404          // a DOM node that is not always there (at the moment clean is not even the
 405          // default theme...).
 406          $navbuttonjs = "return (
 407              Y.one('.btn-navbar') &&
 408              Y.one('.btn-navbar').getComputedStyle('display') !== 'none'
 409          )";
 410  
 411          // Adding an extra click we need to show the 'Log in' link.
 412          if (!$this->getSession()->getDriver()->evaluateScript($navbuttonjs)) {
 413              return false;
 414          }
 415  
 416          $this->execute('behat_general::i_click_on', array(".btn-navbar", "css_element"));
 417      }
 418  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1