Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

   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  namespace qbehaviour_manualgraded;
  18  
  19  use question_display_options;
  20  use question_state;
  21  
  22  defined('MOODLE_INTERNAL') || die();
  23  
  24  global $CFG;
  25  require_once (__DIR__ . '/../../../engine/lib.php');
  26  require_once (__DIR__ . '/../../../engine/tests/helpers.php');
  27  
  28  
  29  /**
  30   * Unit tests for the manual graded behaviour.
  31   *
  32   * @package    qbehaviour_manualgraded
  33   * @copyright  2009 The Open University
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class walkthrough_test extends \qbehaviour_walkthrough_test_base {
  37      public function test_manual_graded_essay() {
  38          global $PAGE;
  39  
  40          // The current text editor depends on the users profile setting - so it needs a valid user.
  41          $this->setAdminUser();
  42          // Required to init a text editor.
  43          $PAGE->set_url('/');
  44  
  45          // Create an essay question.
  46          $essay = \test_question_maker::make_an_essay_question();
  47          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
  48  
  49          // Check the right model is being used.
  50          $this->assertEquals('manualgraded', $this->quba->get_question_attempt(
  51                  $this->slot)->get_behaviour_name());
  52  
  53          // Check the initial state.
  54          $this->check_current_state(question_state::$todo);
  55          $this->check_current_mark(null);
  56          $this->check_current_output($this->get_contains_question_text_expectation($essay),
  57                  $this->get_does_not_contain_feedback_expectation());
  58  
  59          // Simulate some data submitted by the student.
  60          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
  61  
  62          // Verify.
  63          $this->check_current_state(question_state::$complete);
  64          $this->check_current_mark(null);
  65          $this->check_current_output(
  66                  new \question_contains_tag_with_attribute('textarea', 'name',
  67                  $this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
  68                  $this->get_does_not_contain_feedback_expectation());
  69  
  70          // Process the same data again, check it does not create a new step.
  71          $numsteps = $this->get_step_count();
  72          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
  73          $this->check_step_count($numsteps);
  74  
  75          // Process different data, check it creates a new step.
  76          $this->process_submission(array('answer' => '', 'answerformat' => FORMAT_HTML));
  77          $this->check_step_count($numsteps + 1);
  78          $this->check_current_state(question_state::$todo);
  79  
  80          // Change back, check it creates a new step.
  81          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
  82          $this->check_step_count($numsteps + 2);
  83  
  84          // Finish the attempt.
  85          $this->quba->finish_all_questions();
  86  
  87          // Verify.
  88          $this->check_current_state(question_state::$needsgrading);
  89          $this->check_current_mark(null);
  90          $this->assertEquals('This is my wonderful essay!',
  91                  $this->quba->get_response_summary($this->slot));
  92  
  93          // Process a manual comment.
  94          $this->manual_grade('Not good enough!', 10, FORMAT_HTML);
  95  
  96          // Verify.
  97          $this->check_current_state(question_state::$mangrright);
  98          $this->check_current_mark(10);
  99          $this->check_current_output(
 100                  new \question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/'));
 101  
 102          // Now change the max mark for the question and regrade.
 103          $this->quba->regrade_question($this->slot, true, 1);
 104  
 105          // Verify.
 106          $this->check_current_state(question_state::$mangrright);
 107          $this->check_current_mark(1);
 108      }
 109  
 110      public function test_manual_graded_essay_not_answered() {
 111          global $PAGE;
 112  
 113          // The current text editor depends on the users profile setting - so it needs a valid user.
 114          $this->setAdminUser();
 115          // Required to init a text editor.
 116          $PAGE->set_url('/');
 117  
 118          // Create an essay question.
 119          $essay = \test_question_maker::make_an_essay_question();
 120          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
 121  
 122          // Check the right model is being used.
 123          $this->assertEquals('manualgraded', $this->quba->get_question_attempt(
 124                  $this->slot)->get_behaviour_name());
 125  
 126          // Check the initial state.
 127          $this->check_current_state(question_state::$todo);
 128          $this->check_current_mark(null);
 129          $this->check_current_output($this->get_contains_question_text_expectation($essay),
 130                  $this->get_does_not_contain_feedback_expectation());
 131  
 132          // Finish the attempt.
 133          $this->quba->finish_all_questions();
 134  
 135          // Verify.
 136          $this->check_current_state(question_state::$gaveup);
 137          $this->check_current_mark(null);
 138          $this->assertEquals('',
 139                  $this->quba->get_response_summary($this->slot));
 140  
 141          // Process a manual comment.
 142          $this->manual_grade('Not good enough!', 1, FORMAT_HTML);
 143  
 144          // Verify.
 145          $this->check_current_state(question_state::$mangrpartial);
 146          $this->check_current_mark(1);
 147          $this->check_current_output(
 148                  new \question_pattern_expectation('/' . preg_quote('Not good enough!') . '/'));
 149  
 150          // Now change the max mark for the question and regrade.
 151          $this->quba->regrade_question($this->slot, true, 1);
 152  
 153          // Verify.
 154          $this->check_current_state(question_state::$mangrpartial);
 155          $this->check_current_mark(0.1);
 156      }
 157  
 158      public function test_manual_graded_truefalse() {
 159  
 160          // Create a true-false question with correct answer true.
 161          $tf = \test_question_maker::make_question('truefalse', 'true');
 162          $this->start_attempt_at_question($tf, 'manualgraded', 2);
 163  
 164          // Check the initial state.
 165          $this->check_current_state(question_state::$todo);
 166          $this->check_current_mark(null);
 167          $this->check_current_output(
 168                  $this->get_contains_question_text_expectation($tf),
 169                  $this->get_does_not_contain_feedback_expectation());
 170  
 171          // Process a true answer and check the expected result.
 172          $this->process_submission(array('answer' => 1));
 173  
 174          $this->check_current_state(question_state::$complete);
 175          $this->check_current_mark(null);
 176          $this->check_current_output(
 177                  $this->get_contains_tf_true_radio_expectation(true, true),
 178                  $this->get_does_not_contain_correctness_expectation(),
 179                  $this->get_does_not_contain_feedback_expectation());
 180  
 181          // Finish the attempt.
 182          $this->quba->finish_all_questions();
 183  
 184          // Verify.
 185          $this->check_current_state(question_state::$needsgrading);
 186          $this->check_current_mark(null);
 187          $this->check_current_output(
 188                  $this->get_does_not_contain_correctness_expectation(),
 189                  $this->get_does_not_contain_specific_feedback_expectation());
 190  
 191          // Process a manual comment.
 192          $this->manual_grade('Not good enough!', 1, FORMAT_HTML);
 193  
 194          $this->check_current_state(question_state::$mangrpartial);
 195          $this->check_current_mark(1);
 196          $this->check_current_output(
 197              $this->get_does_not_contain_correctness_expectation(),
 198              $this->get_does_not_contain_specific_feedback_expectation(),
 199              new \question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/'));
 200      }
 201  
 202      public function test_manual_grade_ungraded_question() {
 203          global $PAGE;
 204  
 205          // The current text editor depends on the users profile setting - so it needs a valid user.
 206          $this->setAdminUser();
 207          // Required to init a text editor.
 208          $PAGE->set_url('/');
 209  
 210          // Create an essay question.
 211          $essay = \test_question_maker::make_an_essay_question();
 212          $this->start_attempt_at_question($essay, 'deferredfeedback', 0);
 213  
 214          // Check the right model is being used.
 215          $this->assertEquals('manualgraded', $this->quba->get_question_attempt(
 216                  $this->slot)->get_behaviour_name());
 217  
 218          // Check the initial state.
 219          $this->check_current_state(question_state::$todo);
 220          $this->check_current_mark(null);
 221          $this->check_current_output($this->get_contains_question_text_expectation($essay),
 222                  $this->get_does_not_contain_feedback_expectation());
 223  
 224          // Simulate some data submitted by the student.
 225          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
 226  
 227          // Verify.
 228          $this->check_current_state(question_state::$complete);
 229          $this->check_current_mark(null);
 230          $this->check_current_output(
 231                  new \question_contains_tag_with_attribute('textarea', 'name',
 232                  $this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
 233                  $this->get_does_not_contain_feedback_expectation());
 234  
 235          // Finish the attempt.
 236          $this->quba->finish_all_questions();
 237  
 238          // Verify.
 239          $this->check_current_state(question_state::$needsgrading);
 240          $this->check_current_mark(null);
 241          $this->assertEquals('This is my wonderful essay!',
 242                  $this->quba->get_response_summary($this->slot));
 243  
 244          // Process a manual comment. Note: null mark is the whole point here.
 245          $this->manual_grade('Not good enough!', null, FORMAT_HTML);
 246  
 247          // Verify.
 248          // I am pretty sure this next assertion is incorrect. We should change
 249          // the question state to indicate that this quetion has now been commented
 250          // on. However, that is tricky, because what if, after that, the mam mark
 251          // for the qusetions is changed. So, for now, this assertion verifies
 252          // the current behaviour.
 253          $this->check_current_state(question_state::$needsgrading);
 254          $this->check_current_mark(null);
 255          $this->check_current_output(
 256                  new \question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/'));
 257      }
 258  
 259      public function test_manual_graded_ignore_repeat_sumbission() {
 260          // Create an essay question.
 261          $essay = \test_question_maker::make_an_essay_question();
 262          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
 263  
 264          // Check the right model is being used.
 265          $this->assertEquals('manualgraded', $this->quba->get_question_attempt(
 266                  $this->slot)->get_behaviour_name());
 267  
 268          // Check the initial state.
 269          $this->check_current_state(question_state::$todo);
 270          $this->check_current_mark(null);
 271  
 272          // Simulate some data submitted by the student.
 273          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
 274  
 275          // Verify.
 276          $this->check_current_state(question_state::$complete);
 277          $this->check_current_mark(null);
 278  
 279          // Finish the attempt.
 280          $this->quba->finish_all_questions();
 281  
 282          // Verify.
 283          $this->check_current_state(question_state::$needsgrading);
 284          $this->check_current_mark(null);
 285          $this->assertEquals('This is my wonderful essay!',
 286                  $this->quba->get_response_summary($this->slot));
 287  
 288          // Process a blank manual comment. Ensure it does not change the state.
 289          $numsteps = $this->get_step_count();
 290          $this->manual_grade('', '', FORMAT_HTML);
 291          $this->check_step_count($numsteps);
 292          $this->check_current_state(question_state::$needsgrading);
 293          $this->check_current_mark(null);
 294  
 295          // Process a comment, but with the mark blank. Should be recorded, but
 296          // not change the mark.
 297          $this->manual_grade('I am not sure what grade to award.', '', FORMAT_HTML);
 298          $this->check_step_count($numsteps + 1);
 299          $this->check_current_state(question_state::$needsgrading);
 300          $this->check_current_mark(null);
 301          $this->check_current_output(
 302                  new \question_pattern_expectation('/' .
 303                          preg_quote('I am not sure what grade to award.', '/') . '/'));
 304  
 305          // Now grade it.
 306          $this->manual_grade('Pretty good!', '9.00000', FORMAT_HTML);
 307          $this->check_step_count($numsteps + 2);
 308          $this->check_current_state(question_state::$mangrpartial);
 309          $this->check_current_mark(9);
 310          $this->check_current_output(
 311                  new \question_pattern_expectation('/' . preg_quote('Pretty good!', '/') . '/'));
 312  
 313          // Process the same data again, and make sure it does not add a step.
 314          $this->manual_grade('Pretty good!', '9.00000', FORMAT_HTML);
 315          $this->check_step_count($numsteps + 2);
 316          $this->check_current_state(question_state::$mangrpartial);
 317          $this->check_current_mark(9);
 318  
 319          // Now set the mark back to blank.
 320          $this->manual_grade('Actually, I am not sure any more.', '', FORMAT_HTML);
 321          $this->check_step_count($numsteps + 3);
 322          $this->check_current_state(question_state::$needsgrading);
 323          $this->check_current_mark(null);
 324          $this->check_current_output(
 325                  new \question_pattern_expectation('/' .
 326                          preg_quote('Actually, I am not sure any more.', '/') . '/'));
 327  
 328          $qa = $this->quba->get_question_attempt($this->slot);
 329          $this->assertEquals('Commented: Actually, I am not sure any more.',
 330                  $qa->summarise_action($qa->get_last_step()));
 331      }
 332  
 333      public function test_manual_graded_ignore_repeat_sumbission_commas() {
 334          // Create an essay question.
 335          $essay = \test_question_maker::make_an_essay_question();
 336          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
 337  
 338          // Check the right model is being used.
 339          $this->assertEquals('manualgraded', $this->quba->get_question_attempt(
 340                  $this->slot)->get_behaviour_name());
 341  
 342          // Check the initial state.
 343          $this->check_current_state(question_state::$todo);
 344          $this->check_current_mark(null);
 345  
 346          // Simulate some data submitted by the student.
 347          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
 348  
 349          // Verify.
 350          $this->check_current_state(question_state::$complete);
 351          $this->check_current_mark(null);
 352  
 353          // Finish the attempt.
 354          $this->quba->finish_all_questions();
 355  
 356          // Verify.
 357          $this->check_current_state(question_state::$needsgrading);
 358          $this->check_current_mark(null);
 359          $this->assertEquals('This is my wonderful essay!',
 360                  $this->quba->get_response_summary($this->slot));
 361  
 362          // Now grade it with a mark with a comma.
 363          $numsteps = $this->get_step_count();
 364          $this->manual_grade('Pretty good!', '9,00000', FORMAT_HTML);
 365          $this->check_step_count($numsteps + 1);
 366          $this->check_current_state(question_state::$mangrpartial);
 367          $this->check_current_mark(9);
 368          $qa = $this->get_question_attempt();
 369          $this->assertEquals('Manually graded 9 with comment: Pretty good!',
 370                  $qa->summarise_action($qa->get_last_step()));
 371          $this->check_current_output(
 372                  new \question_pattern_expectation('/' . preg_quote('Pretty good!', '/') . '/'));
 373  
 374          // Process the same mark with a dot. Verify it does not add a new step.
 375          $this->manual_grade('Pretty good!', '9.00000', FORMAT_HTML);
 376          $this->check_step_count($numsteps + 1);
 377          $this->check_current_state(question_state::$mangrpartial);
 378          $this->check_current_mark(9);
 379      }
 380  
 381      public function test_manual_graded_essay_can_grade_0() {
 382          global $PAGE;
 383  
 384          // The current text editor depends on the users profile setting - so it needs a valid user.
 385          $this->setAdminUser();
 386          // Required to init a text editor.
 387          $PAGE->set_url('/');
 388  
 389          // Create an essay question.
 390          $essay = \test_question_maker::make_an_essay_question();
 391          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
 392  
 393          // Check the right model is being used.
 394          $this->assertEquals('manualgraded', $this->quba->get_question_attempt(
 395                  $this->slot)->get_behaviour_name());
 396  
 397          // Check the initial state.
 398          $this->check_current_state(question_state::$todo);
 399          $this->check_current_mark(null);
 400          $this->check_current_output($this->get_contains_question_text_expectation($essay),
 401                  $this->get_does_not_contain_feedback_expectation());
 402  
 403          // Simulate some data submitted by the student.
 404          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
 405  
 406          // Verify.
 407          $this->check_current_state(question_state::$complete);
 408          $this->check_current_mark(null);
 409          $this->check_current_output(
 410                  new \question_contains_tag_with_attribute('textarea', 'name',
 411                  $this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
 412                  $this->get_does_not_contain_feedback_expectation());
 413  
 414          // Finish the attempt.
 415          $this->quba->finish_all_questions();
 416  
 417          // Verify.
 418          $this->check_current_state(question_state::$needsgrading);
 419          $this->check_current_mark(null);
 420          $this->assertEquals('This is my wonderful essay!',
 421                  $this->quba->get_response_summary($this->slot));
 422  
 423          // Process a blank comment and a grade of 0.
 424          $this->manual_grade('', 0, FORMAT_HTML);
 425  
 426          // Verify.
 427          $this->check_current_state(question_state::$mangrwrong);
 428          $this->check_current_mark(0);
 429      }
 430  
 431      public function test_manual_graded_change_comment_format() {
 432          global $PAGE;
 433  
 434          // The current text editor depends on the users profile setting - so it needs a valid user.
 435          $this->setAdminUser();
 436          // Required to init a text editor.
 437          $PAGE->set_url('/');
 438  
 439          // Create an essay question.
 440          $essay = \test_question_maker::make_an_essay_question();
 441          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
 442  
 443          // Simulate some data submitted by the student.
 444          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
 445  
 446          // Finish the attempt.
 447          $this->quba->finish_all_questions();
 448  
 449          // Process an example comment and a grade of 0.
 450          $this->manual_grade('example', 0, FORMAT_HTML);
 451          // Verify the format is FORMAT_HTML.
 452          $this->check_comment('example', FORMAT_HTML);
 453  
 454          // Process the same grade and comment with different format.
 455          $this->manual_grade('example', 0, FORMAT_MARKDOWN);
 456          // Verify the format is FORMAT_MARKDOWN.
 457          $this->check_comment('example', FORMAT_MARKDOWN);
 458      }
 459  
 460      public function test_manual_graded_respects_display_options() {
 461          // This test is for MDL-43874. Manual comments were not respecting the
 462          // Display options for feedback.
 463          global $PAGE;
 464  
 465          // The current text editor depends on the users profile setting - so it needs a valid user.
 466          $this->setAdminUser();
 467          // Required to init a text editor.
 468          $PAGE->set_url('/');
 469  
 470          // Create an essay question.
 471          $essay = \test_question_maker::make_an_essay_question();
 472          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
 473  
 474          // Check the right model is being used.
 475          $this->assertEquals('manualgraded', $this->quba->get_question_attempt(
 476                  $this->slot)->get_behaviour_name());
 477  
 478          // Check the initial state.
 479          $this->check_current_state(question_state::$todo);
 480          $this->check_current_mark(null);
 481          $this->check_current_output($this->get_contains_question_text_expectation($essay),
 482                  $this->get_does_not_contain_feedback_expectation());
 483  
 484          // Simulate some data submitted by the student.
 485          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
 486  
 487          // Verify.
 488          $this->check_current_state(question_state::$complete);
 489          $this->check_current_mark(null);
 490          $this->check_current_output(
 491                  new \question_contains_tag_with_attribute('textarea', 'name',
 492                  $this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
 493                  $this->get_does_not_contain_feedback_expectation());
 494  
 495          // Finish the attempt.
 496          $this->quba->finish_all_questions();
 497  
 498          // Verify.
 499          $this->check_current_state(question_state::$needsgrading);
 500          $this->check_current_mark(null);
 501          $this->assertEquals('This is my wonderful essay!',
 502                  $this->quba->get_response_summary($this->slot));
 503  
 504          // Process a comment and a grade.
 505          $this->manual_grade('This should only appear if the displya options allow it', 5, FORMAT_HTML);
 506  
 507          // Verify.
 508          $this->check_current_state(question_state::$mangrpartial);
 509          $this->check_current_mark(5);
 510  
 511          $this->displayoptions->manualcomment = question_display_options::HIDDEN;
 512          $this->check_output_does_not_contain('This should only appear if the displya options allow it');
 513          $this->displayoptions->manualcomment = question_display_options::VISIBLE;
 514          $this->check_output_contains('This should only appear if the displya options allow it');
 515      }
 516  
 517      public function test_manual_graded_invalid_value_throws_exception() {
 518          global $PAGE;
 519  
 520          // The current text editor depends on the users profile setting - so it needs a valid user.
 521          $this->setAdminUser();
 522          // Required to init a text editor.
 523          $PAGE->set_url('/');
 524  
 525          // Create an essay question.
 526          $essay = \test_question_maker::make_an_essay_question();
 527          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
 528  
 529          // Check the right model is being used.
 530          $this->assertEquals('manualgraded', $this->quba->get_question_attempt(
 531                  $this->slot)->get_behaviour_name());
 532  
 533          // Check the initial state.
 534          $this->check_current_state(question_state::$todo);
 535          $this->check_current_mark(null);
 536          $this->check_current_output($this->get_contains_question_text_expectation($essay),
 537                  $this->get_does_not_contain_feedback_expectation());
 538  
 539          // Simulate some data submitted by the student.
 540          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
 541  
 542          // Verify.
 543          $this->check_current_state(question_state::$complete);
 544          $this->check_current_mark(null);
 545          $this->check_current_output(
 546                  new \question_contains_tag_with_attribute('textarea', 'name',
 547                  $this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
 548                  $this->get_does_not_contain_feedback_expectation());
 549  
 550          // Finish the attempt.
 551          $this->quba->finish_all_questions();
 552  
 553          // Verify.
 554          $this->check_current_state(question_state::$needsgrading);
 555          $this->check_current_mark(null);
 556          $this->assertEquals('This is my wonderful essay!',
 557                  $this->quba->get_response_summary($this->slot));
 558  
 559          // Try to process a an invalid grade.
 560          $this->expectException('coding_exception');
 561          $this->manual_grade('Comment', 'frog', FORMAT_HTML);
 562      }
 563  
 564      public function test_manual_graded_out_of_range_throws_exception() {
 565          global $PAGE;
 566  
 567          // The current text editor depends on the users profile setting - so it needs a valid user.
 568          $this->setAdminUser();
 569          // Required to init a text editor.
 570          $PAGE->set_url('/');
 571  
 572          // Create an essay question.
 573          $essay = \test_question_maker::make_an_essay_question();
 574          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
 575  
 576          // Check the right model is being used.
 577          $this->assertEquals('manualgraded', $this->quba->get_question_attempt(
 578                  $this->slot)->get_behaviour_name());
 579  
 580          // Check the initial state.
 581          $this->check_current_state(question_state::$todo);
 582          $this->check_current_mark(null);
 583          $this->check_current_output($this->get_contains_question_text_expectation($essay),
 584                  $this->get_does_not_contain_feedback_expectation());
 585  
 586          // Simulate some data submitted by the student.
 587          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
 588  
 589          // Verify.
 590          $this->check_current_state(question_state::$complete);
 591          $this->check_current_mark(null);
 592          $this->check_current_output(
 593                  new \question_contains_tag_with_attribute('textarea', 'name',
 594                  $this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
 595                  $this->get_does_not_contain_feedback_expectation());
 596  
 597          // Finish the attempt.
 598          $this->quba->finish_all_questions();
 599  
 600          // Verify.
 601          $this->check_current_state(question_state::$needsgrading);
 602          $this->check_current_mark(null);
 603          $this->assertEquals('This is my wonderful essay!',
 604                  $this->quba->get_response_summary($this->slot));
 605  
 606          // Try to process a an invalid grade.
 607          $this->expectException('coding_exception');
 608          $this->manual_grade('Comment', '10.1', FORMAT_HTML);
 609      }
 610  
 611      public function test_manual_graded_displays_proper_comment_format () {
 612  
 613          global $PAGE;
 614  
 615          // The current text editor depends on the users profile setting - so it needs a valid user.
 616          $this->setAdminUser();
 617          // Required to init a text editor.
 618          $PAGE->set_url('/');
 619  
 620          // Create an essay question.
 621          $essay = \test_question_maker::make_an_essay_question();
 622          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
 623  
 624          // Check the right model is being used.
 625          $this->assertEquals('manualgraded', $this->quba->get_question_attempt(
 626                  $this->slot)->get_behaviour_name());
 627  
 628          // Simulate some data submitted by the student.
 629          $this->process_submission(
 630                  array(
 631                      'answer' => "A submission!",
 632                      'answerformat' => FORMAT_PLAIN
 633                  )
 634          );
 635  
 636          // Finish the attempt.
 637          $this->quba->finish_all_questions();
 638  
 639          // Write a manual comment in markdown.
 640          $this->manual_grade("*one\n*two\n*three\n", 10, FORMAT_MARKDOWN);
 641  
 642          // Check that feedback contains the original markdown format.
 643          $preg = '/<textarea [^>]+name="[^"]+-comment"[^>]+>\*one\n\*two\n\*three\n/';
 644          $this->displayoptions->manualcomment = question_display_options::EDITABLE;
 645          $this->check_current_output(
 646              new \question_pattern_expectation($preg)
 647          );
 648      }
 649  
 650      public function test_manual_grading_reshows_exactly_the_mark_input() {
 651          global $PAGE;
 652  
 653          // The current text editor depends on the users profile setting - so it needs a valid user.
 654          $this->setAdminUser();
 655          // Required to init a text editor.
 656          $PAGE->set_url('/');
 657  
 658          // Create an essay question graded out of 15 and attempt it.
 659          $essay = \test_question_maker::make_an_essay_question();
 660          $this->start_attempt_at_question($essay, 'deferredfeedback', 15);
 661          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
 662          $this->quba->finish_all_questions();
 663  
 664          // Verify.
 665          $this->check_current_state(question_state::$needsgrading);
 666          $this->check_current_mark(null);
 667          $this->assertEquals('This is my wonderful essay!',
 668                  $this->quba->get_response_summary($this->slot));
 669  
 670          // Try to process a grade where the score will be stored rounded.
 671          $this->manual_grade('Comment', '5.0', FORMAT_HTML);
 672  
 673          // Verify.
 674          $this->check_current_state(question_state::$mangrpartial);
 675          $this->check_current_mark(5);
 676          $this->displayoptions->manualcomment = question_display_options::EDITABLE;
 677          $this->render();
 678          $this->check_output_contains_text_input('-mark', '5.0');
 679  
 680          // Rescale what the question is worth, and verify the display.
 681          $this->get_question_attempt()->set_max_mark(1);
 682          $this->render();
 683          $this->check_output_contains_text_input('-mark', '0.3333333');
 684      }
 685  
 686      public function test_manual_grading_history_display() {
 687          global $PAGE;
 688  
 689          // The current text editor depends on the users profile setting - so it needs a valid user.
 690          $this->setAdminUser();
 691          // Required to init a text editor.
 692          $PAGE->set_url('/');
 693  
 694          // Create an essay question graded out of 15 and attempt it.
 695          $essay = \test_question_maker::make_an_essay_question();
 696          $this->start_attempt_at_question($essay, 'deferredfeedback', 10);
 697          $this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
 698          $this->quba->finish_all_questions();
 699  
 700          // Verify.
 701          $this->check_current_state(question_state::$needsgrading);
 702  
 703          // Process an initial grade and comment.
 704          $this->manual_grade('First comment', '5.0', FORMAT_HTML);
 705  
 706          // Process a second grade and comment.
 707          $this->manual_grade('Second comment', '7.0', FORMAT_HTML);
 708  
 709          // Verify.
 710          $this->check_current_state(question_state::$mangrpartial);
 711          $this->check_current_mark(7);
 712          $this->displayoptions->history = question_display_options::VISIBLE;
 713          $this->render();
 714          $this->check_output_contains('Manually graded 5 with comment: First comment');
 715          $this->check_output_contains('Manually graded 7 with comment: Second comment');
 716      }
 717  }