Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403]

   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   * Behat grade related steps definitions.
  19   *
  20   * @package    core_grades
  21   * @category   test
  22   * @copyright  2014 Mark Nelson <markn@moodle.com>
  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__ . '/../../../lib/behat/behat_base.php');
  29  
  30  use Behat\Gherkin\Node\TableNode as TableNode;
  31  
  32  class behat_grade extends behat_base {
  33  
  34      /**
  35       * Enters a grade via the gradebook for a specific grade item and user when viewing the 'Grader report' with editing mode turned on.
  36       *
  37       * @Given /^I give the grade "(?P<grade_number>(?:[^"]|\\")*)" to the user "(?P<username_string>(?:[^"]|\\")*)" for the grade item "(?P<grade_activity_string>(?:[^"]|\\")*)"$/
  38       * @param int $grade
  39       * @param string $userfullname the user's fullname as returned by fullname()
  40       * @param string $itemname
  41       */
  42      public function i_give_the_grade($grade, $userfullname, $itemname) {
  43          $gradelabel = $userfullname . ' ' . $itemname;
  44          $fieldstr = get_string('useractivitygrade', 'gradereport_grader', $gradelabel);
  45  
  46          $this->execute('behat_forms::i_set_the_field_to', array($this->escape($fieldstr), $grade));
  47      }
  48  
  49      /**
  50       * Enters a quick feedback via the gradebook for a specific grade item and user when viewing
  51       * the 'Grader report' with editing mode turned on.
  52       *
  53       * @Given /^I give the feedback "(?P<grade_number>(?:[^"]|\\")*)" to the user "(?P<username_string>(?:[^"]|\\")*)" for the grade item "(?P<grade_activity_string>(?:[^"]|\\")*)"$/
  54       * @param string $feedback
  55       * @param string $userfullname the user's fullname as returned by fullname()
  56       * @param string $itemname
  57       */
  58      public function i_give_the_feedback($feedback, $userfullname, $itemname) {
  59          $gradelabel = $userfullname . ' ' . $itemname;
  60          $fieldstr = get_string('useractivityfeedback', 'gradereport_grader', $gradelabel);
  61  
  62          $this->execute('behat_forms::i_set_the_field_to', array($this->escape($fieldstr), $this->escape($feedback)));
  63      }
  64  
  65      /**
  66       * Changes the settings of a grade item or category or the course.
  67       *
  68       * Teacher must be either on the grade setup page or on the Grader report page with editing mode turned on.
  69       *
  70       * @Given /^I set the following settings for grade item "(?P<grade_item_string>(?:[^"]|\\")*)":$/
  71       * @param string $gradeitem
  72       * @param TableNode $data
  73       */
  74      public function i_set_the_following_settings_for_grade_item($gradeitem, TableNode $data) {
  75  
  76          $gradeitem = behat_context_helper::escape($gradeitem);
  77  
  78          if ($this->running_javascript()) {
  79              $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]";
  80              if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
  81                  $this->execute("behat_action_menu::i_open_the_action_menu_in",
  82                          array("//tr[contains(.,$gradeitem)]",
  83                                  "xpath_element"));
  84              }
  85          }
  86  
  87          $savechanges = get_string('savechanges', 'grades');
  88          $edit = behat_context_helper::escape(get_string('edit') . '  ');
  89          $linkxpath = "//a[./*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') " .
  90                  "and starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
  91  
  92          $this->execute("behat_general::i_click_on", array($this->escape($linkxpath), "xpath_element"));
  93          $this->execute("behat_forms::i_set_the_following_fields_to_these_values", $data);
  94          $this->execute('behat_forms::press_button', $this->escape($savechanges));
  95      }
  96  
  97      /**
  98       * Hids a grade item or category.
  99       *
 100       * Teacher must be on the grade setup page.
 101       *
 102       * @Given /^I hide the grade item "(?P<grade_item_string>(?:[^"]|\\")*)"$/
 103       * @param string $gradeitem
 104       */
 105      public function i_hide_the_grade_item($gradeitem) {
 106  
 107          $gradeitem = behat_context_helper::escape($gradeitem);
 108  
 109          if ($this->running_javascript()) {
 110              $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]//a[contains(@class,'toggle-display')]";
 111              if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
 112                  $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
 113              }
 114          }
 115  
 116          $this->execute("behat_general::i_click_on_in_the", array(get_string('hide'), 'link',
 117              "//tr[descendant::*[text() = " . $this->escape($gradeitem) . "]]", 'xpath_element'));
 118      }
 119  
 120      /**
 121       * Duplicates a grade item or category.
 122       *
 123       * Teacher must be on the grade setup page.
 124       *
 125       * @Given /^I duplicate the grade item "(?P<grade_item_string>(?:[^"]|\\")*)"$/
 126       * @param string $gradeitem
 127       */
 128      public function i_duplicate_the_grade_item($gradeitem) {
 129  
 130          $gradeitem = behat_context_helper::escape($gradeitem);
 131  
 132          if ($this->running_javascript()) {
 133              $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]//a[contains(@class,'toggle-display')]";
 134              if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
 135                  $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
 136              }
 137          }
 138  
 139          $this->execute("behat_general::i_click_on_in_the", array(get_string('duplicate'), 'link',
 140              "//tr[descendant::*[text() = " . $this->escape($gradeitem) . "]]", 'xpath_element'));
 141      }
 142  
 143      /**
 144       * Sets a calculated manual grade item. Needs a table with item name - idnumber relation.
 145       * The step requires you to be in the 'Gradebook setup' page.
 146       *
 147       * @Given /^I set "(?P<calculation_string>(?:[^"]|\\")*)" calculation for grade item "(?P<grade_item_string>(?:[^"]|\\")*)" with idnumbers:$/
 148       * @param string $calculation The calculation.
 149       * @param string $gradeitem The grade item name.
 150       * @param TableNode $TableNode The grade item name - idnumbers relation.
 151       */
 152      public function i_set_calculation_for_grade_item_with_idnumbers($calculation, $gradeitem, TableNode $data) {
 153  
 154          $gradeitem = behat_context_helper::escape($gradeitem);
 155  
 156          if ($this->running_javascript()) {
 157              $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]";
 158              if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
 159                  $this->execute("behat_action_menu::i_open_the_action_menu_in",
 160                          array("//tr[contains(.,$gradeitem)]",
 161                                  "xpath_element"));
 162              }
 163          }
 164  
 165          // Going to edit calculation.
 166          $savechanges = get_string('savechanges', 'grades');
 167          $edit = behat_context_helper::escape(get_string('editcalculation', 'grades'));
 168          $linkxpath = "//a[./*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') " .
 169                  "and starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
 170          $this->execute("behat_general::i_click_on", array($this->escape($linkxpath), "xpath_element"));
 171  
 172          // Mapping names to idnumbers.
 173          $datahash = $data->getRowsHash();
 174          foreach ($datahash as $gradeitem => $idnumber) {
 175              // This xpath looks for course, categories and items with the provided name.
 176              // Grrr, we can't equal in categoryitem and courseitem because there is a line jump...
 177              $inputxpath = "//input[@class='idnumber'][" .
 178                      "parent::li[@class='item'][text()='" . $gradeitem . "']" .
 179                      " or " .
 180                      "parent::li[@class='categoryitem' or @class='courseitem']" .
 181                      "/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
 182                      "]";
 183              $this->execute('behat_forms::i_set_the_field_with_xpath_to', array($inputxpath, $idnumber));
 184          }
 185  
 186          $this->execute('behat_forms::press_button', get_string('addidnumbers', 'grades'));
 187          $this->execute('behat_forms::i_set_the_field_to', array(get_string('calculation', 'grades'), $calculation));
 188          $this->execute('behat_forms::press_button', $savechanges);
 189  
 190      }
 191  
 192      /**
 193       * Sets a calculated manual grade category total. Needs a table with item name - idnumber relation.
 194       * The step requires you to be in the 'Gradebook setup' page.
 195       *
 196       * @Given /^I set "(?P<calculation_string>(?:[^"]|\\")*)" calculation for grade category "(?P<grade_item_string>(?:[^"]|\\")*)" with idnumbers:$/
 197       * @param string $calculation The calculation.
 198       * @param string $gradeitem The grade item name.
 199       * @param TableNode $data The grade item name - idnumbers relation.
 200       */
 201      public function i_set_calculation_for_grade_category_with_idnumbers($calculation, $gradeitem, TableNode $data) {
 202  
 203          $gradecategorytotal = behat_context_helper::escape($gradeitem . ' total');
 204          $gradeitem = behat_context_helper::escape($gradeitem);
 205  
 206          if ($this->running_javascript()) {
 207              $xpath = "//tr[contains(.,$gradecategorytotal)]//*[contains(@class,'moodle-actionmenu')]";
 208              if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
 209                  $xpath = "//tr[contains(.,$gradecategorytotal)]";
 210                  $this->execute("behat_action_menu::i_open_the_action_menu_in", array($xpath, "xpath_element"));
 211              }
 212          }
 213  
 214          // Going to edit calculation.
 215          $savechanges = get_string('savechanges', 'grades');
 216          $edit = behat_context_helper::escape(get_string('editcalculation', 'grades'));
 217          $linkxpath = "//a[./*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') " .
 218                  "and starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
 219          $this->execute("behat_general::i_click_on", array($this->escape($linkxpath), "xpath_element"));
 220  
 221          // Mapping names to idnumbers.
 222          $datahash = $data->getRowsHash();
 223          foreach ($datahash as $gradeitem => $idnumber) {
 224              // This xpath looks for course, categories and items with the provided name.
 225              // Grrr, we can't equal in categoryitem and courseitem because there is a line jump...
 226              $inputxpath = "//input[@class='idnumber'][" .
 227                      "parent::li[@class='item'][text()='" . $gradeitem . "']" .
 228                      " | " .
 229                      "parent::li[@class='categoryitem' or @class='courseitem']" .
 230                      "/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
 231                      "]";
 232              $this->execute('behat_forms::i_set_the_field_with_xpath_to', array($inputxpath, $idnumber));
 233          }
 234  
 235          $this->execute('behat_forms::press_button', get_string('addidnumbers', 'grades'));
 236  
 237          $this->execute('behat_forms::i_set_the_field_to', array(get_string('calculation', 'grades'), $calculation));
 238          $this->execute('behat_forms::press_button', $savechanges);
 239      }
 240  
 241      /**
 242       * Resets the weights for the grade category
 243       *
 244       * Teacher must be on the grade setup page.
 245       *
 246       * @Given /^I reset weights for grade category "(?P<grade_item_string>(?:[^"]|\\")*)"$/
 247       * @param $gradeitem
 248       */
 249      public function i_reset_weights_for_grade_category($gradeitem) {
 250  
 251          $steps = array();
 252  
 253          if ($this->running_javascript()) {
 254              $gradeitemliteral = behat_context_helper::escape($gradeitem);
 255              $xpath = "//tr[contains(.,$gradeitemliteral)]//*[contains(@class,'moodle-actionmenu')]";
 256              if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
 257                  $xpath = "//tr[contains(.,$gradeitemliteral)]";
 258                  $this->execute("behat_action_menu::i_open_the_action_menu_in", array($xpath, "xpath_element"));
 259              }
 260          }
 261  
 262          $linktext = get_string('resetweights', 'grades', (object)array('itemname' => $gradeitem));
 263          $this->execute("behat_general::i_click_on", array($this->escape($linktext), "link"));
 264      }
 265  
 266      /**
 267       * Step allowing to test before-the-fix behaviour of the gradebook
 268       *
 269       * @Given /^gradebook calculations for the course "(?P<coursename_string>(?:[^"]|\\")*)" are frozen at version "(?P<version_string>(?:[^"]|\\")*)"$/
 270       * @param string $coursename
 271       * @param string $version
 272       */
 273      public function gradebook_calculations_for_the_course_are_frozen_at_version($coursename, $version) {
 274          global $DB;
 275          $courseid = $DB->get_field('course', 'id', array('shortname' => $coursename), MUST_EXIST);
 276          set_config('gradebook_calculations_freeze_' . $courseid, $version);
 277      }
 278  
 279      /**
 280       * Select the tab in the gradebook. We must be on one of the gradebook pages already.
 281       *
 282       * @param string $gradepath examples: "View > User report", "Letters > View", "Scales"
 283       */
 284      protected function select_in_gradebook_tabs($gradepath) {
 285          $gradepath = preg_split('/\s*>\s*/', trim($gradepath));
 286          if (count($gradepath) > 2) {
 287              throw new coding_exception('Grade path is too long (must have no more than two items separated with ">")');
 288          }
 289  
 290          $xpath = '//div[contains(@class,\'grade-navigation\')]';
 291  
 292          // If the first row of the grade-navigation tabs does not have $gradepath[0] as active tab, click on it.
 293          $link = '\'' . $this->escape($gradepath[0]) . '\'';
 294          $xpathrow1 = $xpath . '//ul[1]//*[contains(@class,\'active\') and contains(normalize-space(.), ' . $link . ')]';
 295          if (!$this->getSession()->getPage()->findAll('xpath', $xpathrow1)) {
 296              $this->find('xpath', $xpath . '//ul[1]/li/a[text()=' . $link . ']')->click();
 297              $this->wait_for_pending_js();
 298          }
 299  
 300          if (isset($gradepath[1])) {
 301              // If the second row of the grade-navigation tabs does not have $gradepath[1] as active tab, click on it.
 302              $link = '\'' . $this->escape($gradepath[1]) . '\'';
 303              $xpathrow2 = $xpath . '//ul[2]//*[contains(@class,\'active\') and contains(normalize-space(.), ' . $link . ')]';
 304              if (!$this->getSession()->getPage()->findAll('xpath', $xpathrow2)) {
 305                  $this->find('xpath', $xpath . '//ul[2]/li/a[text()=' . $link . ']')->click();
 306                  $this->wait_for_pending_js();
 307              }
 308          }
 309      }
 310  
 311      /**
 312       * Navigates to the course gradebook and selects a specified item from the grade navigation tabs.
 313       *
 314       * Examples:
 315       * - I navigate to "Setup > Gradebook setup" in the course gradebook
 316       * - I navigate to "Scales" in the course gradebook
 317       * - I navigate to "Letters > View" in the course gradebook
 318       * - I navigate to "View > User report" in the course gradebook // for teachers
 319       * - I navigate to "User report" in the course gradebook // for students
 320       *
 321       * @Given /^I navigate to "(?P<gradepath_string>(?:[^"]|\\")*)" in the course gradebook$/
 322       * @param string $gradepath
 323       */
 324      public function i_navigate_to_in_the_course_gradebook($gradepath) {
 325          // If we are not on one of the gradebook pages already, follow "Grades" link in the navigation drawer.
 326          $xpath = '//div[contains(@class,\'grade-navigation\')]';
 327          if (!$this->getSession()->getPage()->findAll('xpath', $xpath)) {
 328              $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', get_string('grades'));
 329          }
 330  
 331          $this->select_in_gradebook_tabs($gradepath);
 332      }
 333  }