Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   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   * Steps definitions for marking guides.
  19   *
  20   * @package   gradingform_guide
  21   * @category  test
  22   * @copyright 2015 Jun Pataleta
  23   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  require_once (__DIR__ . '/../../../../../../lib/behat/behat_base.php');
  27  
  28  use Behat\Gherkin\Node\TableNode as TableNode,
  29      Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
  30      Behat\Mink\Exception\ExpectationException as ExpectationException;
  31  
  32  /**
  33   * Steps definitions to help with marking guides.
  34   *
  35   * @package   gradingform_guide
  36   * @category  test
  37   * @copyright 2015 Jun Pataleta
  38   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  39   */
  40  class behat_gradingform_guide extends behat_base {
  41  
  42      /**
  43       * Defines the marking guide with the provided data, following marking guide's definition grid cells.
  44       *
  45       * This method fills the marking guide of the marking guide definition
  46       * form; the provided TableNode should contain one row for
  47       * each criterion and each cell of the row should contain:
  48       * # Criterion name, a.k.a. shortname
  49       * # Description for students
  50       * # Description for markers
  51       * # Max score
  52       *
  53       * Works with both JS and non-JS.
  54       *
  55       * @When /^I define the following marking guide:$/
  56       * @throws ExpectationException
  57       * @param TableNode $guide
  58       */
  59      public function i_define_the_following_marking_guide(TableNode $guide) {
  60          $steptableinfo = '| Criterion name | Description for students | Description for markers | Maximum score |';
  61  
  62          if ($criteria = $guide->getHash()) {
  63              $addcriterionbutton = $this->find_button(get_string('addcriterion', 'gradingform_guide'));
  64  
  65              foreach ($criteria as $index => $criterion) {
  66                  // Make sure the criterion array has 4 elements.
  67                  if (count($criterion) != 4) {
  68                      throw new ExpectationException(
  69                          'The criterion definition should contain name, description for students and markers, and maximum points. ' .
  70                          'Please follow this format: ' . $steptableinfo,
  71                          $this->getSession()
  72                      );
  73                  }
  74  
  75                  // On load, there's already a criterion template ready.
  76                  $shortnamevisible = false;
  77                  if ($index > 0) {
  78                      // So if the index is greater than 0, we click the Add new criterion button to add a new criterion.
  79                      $addcriterionbutton->click();
  80                      $shortnamevisible = true;
  81                  }
  82  
  83                  $criterionroot = 'guide[criteria][NEWID' . ($index + 1) . ']';
  84  
  85                  // Set the field value for the Criterion name.
  86                  $this->set_guide_field_value($criterionroot . '[shortname]', $criterion['Criterion name'], $shortnamevisible);
  87  
  88                  // Set the field value for the Description for students field.
  89                  $this->set_guide_field_value($criterionroot . '[description]', $criterion['Description for students']);
  90  
  91                  // Set the field value for the Description for markers field.
  92                  $this->set_guide_field_value($criterionroot . '[descriptionmarkers]', $criterion['Description for markers']);
  93  
  94                  // Set the field value for the Max score field.
  95                  $this->set_guide_field_value($criterionroot . '[maxscore]', $criterion['Maximum score']);
  96              }
  97          }
  98      }
  99  
 100      /**
 101       * Defines the marking guide with the provided data, following marking guide's definition grid cells.
 102       *
 103       * This method fills the table of frequently used comments of the marking guide definition form.
 104       * The provided TableNode should contain one row for each frequently used comment.
 105       * Each row contains:
 106       * # Comment
 107       *
 108       * Works with both JS and non-JS.
 109       *
 110       * @When /^I define the following frequently used comments:$/
 111       * @throws ExpectationException
 112       * @param TableNode $commentstable
 113       */
 114      public function i_define_the_following_frequently_used_comments(TableNode $commentstable) {
 115          $steptableinfo = '| Comment |';
 116  
 117          if ($comments = $commentstable->getRows()) {
 118              $addcommentbutton = $this->find_button(get_string('addcomment', 'gradingform_guide'));
 119  
 120              foreach ($comments as $index => $comment) {
 121                  // Make sure the comment array has only 1 element.
 122                  if (count($comment) != 1) {
 123                      throw new ExpectationException(
 124                          'The comment cannot be empty. Please follow this format: ' . $steptableinfo,
 125                          $this->getSession()
 126                      );
 127                  }
 128  
 129                  // On load, there's already a comment template ready.
 130                  $commentfieldvisible = false;
 131                  if ($index > 0) {
 132                      // So if the index is greater than 0, we click the Add frequently used comment button to add a new criterion.
 133                      $addcommentbutton->click();
 134                      $commentfieldvisible = true;
 135                  }
 136  
 137                  $commentroot = 'guide[comments][NEWID' . ($index + 1) . ']';
 138  
 139                  // Set the field value for the frequently used comment.
 140                  $this->set_guide_field_value($commentroot . '[description]', $comment[0], $commentfieldvisible);
 141              }
 142          }
 143      }
 144  
 145      /**
 146       * Performs grading of the student by filling out the marking guide.
 147       * Set one line per criterion and for each criterion set "| Criterion name | Points | Remark |".
 148       *
 149       * @When /^I grade by filling the marking guide with:$/
 150       *
 151       * @throws ExpectationException
 152       * @param TableNode $guide
 153       * @return void
 154       */
 155      public function i_grade_by_filling_the_marking_guide_with(TableNode $guide) {
 156  
 157          $criteria = $guide->getRowsHash();
 158  
 159          $stepusage = '"I grade by filling the rubric with:" step needs you to provide a table where each row is a criterion' .
 160              ' and each criterion has 3 different values: | Criterion name | Number of points | Remark text |';
 161  
 162          // First element -> name, second -> points, third -> Remark.
 163          foreach ($criteria as $name => $criterion) {
 164  
 165              // We only expect the points and the remark, as the criterion name is $name.
 166              if (count($criterion) !== 2) {
 167                  throw new ExpectationException($stepusage, $this->getSession());
 168              }
 169  
 170              // Numeric value here.
 171              $points = $criterion[0];
 172              if (!is_numeric($points)) {
 173                  throw new ExpectationException($stepusage, $this->getSession());
 174              }
 175  
 176              $criterionid = 0;
 177              if ($criterionnamediv = $this->find('xpath', "//div[@class='criterionshortname'][text()='$name']")) {
 178                  $criteriondivname = $criterionnamediv->getAttribute('name');
 179                  // Criterion's name is of the format "advancedgrading[criteria][ID][shortname]".
 180                  // So just explode the string with "][" as delimiter to extract the criterion ID.
 181                  if ($nameparts = explode('][', $criteriondivname)) {
 182                      $criterionid = $nameparts[1];
 183                  }
 184              }
 185  
 186              if ($criterionid) {
 187                  $criterionroot = 'advancedgrading[criteria]' . '[' . $criterionid . ']';
 188  
 189                  $this->execute('behat_forms::i_set_the_field_to', array($criterionroot . '[score]', $points));
 190  
 191                  $this->execute('behat_forms::i_set_the_field_to', array($criterionroot . '[remark]', $criterion[1]));
 192              }
 193          }
 194      }
 195  
 196      /**
 197       * Makes a hidden marking guide field visible (if necessary) and sets a value on it.
 198       *
 199       * @param string $name The name of the field
 200       * @param string $value The value to set
 201       * @param bool $visible
 202       * @return void
 203       */
 204      protected function set_guide_field_value($name, $value, $visible = false) {
 205          // Fields are hidden by default.
 206          if ($this->running_javascript() && $visible === false) {
 207              $xpath = "//*[@name='$name']/following-sibling::*[contains(concat(' ', normalize-space(@class), ' '), ' plainvalue ')]";
 208              $textnode = $this->find('xpath', $xpath);
 209              $textnode->click();
 210          }
 211  
 212          // Set the value now.
 213          $field = $this->find_field($name);
 214          $field->setValue($value);
 215      }
 216  }