Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   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 qtype_ddmarker;
  18  
  19  use question_display_options;
  20  use question_hint_ddmarker;
  21  use question_state;
  22  
  23  defined('MOODLE_INTERNAL') || die();
  24  global $CFG;
  25  
  26  require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
  27  require_once($CFG->dirroot . '/question/type/ddmarker/tests/helper.php');
  28  
  29  
  30  /**
  31   * Unit tests for the drag-and-drop markers question type.
  32   *
  33   * @package   qtype_ddmarker
  34   * @copyright 2012 The Open University
  35   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  36   */
  37  class walkthrough_test extends \qbehaviour_walkthrough_test_base {
  38  
  39      /**
  40       * Get an expectation that the output contains a marker.
  41       * @param unknown $choice which choice.
  42       * @param unknown $infinite whether there are infinitely many of that choice.
  43       * @return \question_contains_tag_with_attributes the expectation.
  44       */
  45      protected function get_contains_draggable_marker_home_expectation($choice, $infinite) {
  46          $class = 'marker choice'.$choice;
  47          if ($infinite) {
  48              $class .= ' infinite';
  49          }
  50  
  51          $expectedattrs = array();
  52          $expectedattrs['class'] = $class;
  53  
  54          return new \question_contains_tag_with_attributes('span', $expectedattrs);
  55      }
  56  
  57      /**
  58       * (non-PHPdoc)
  59       * @see qbehaviour_walkthrough_test_base::get_contains_hidden_expectation()
  60       */
  61      protected function get_contains_hidden_expectation($choiceno, $value = null) {
  62          $name = $this->quba->get_field_prefix($this->slot) .'c'. $choiceno;
  63          $expectedattributes = array('type' => 'hidden', 'name' => s($name));
  64          $expectedattributes['class'] = "choices choice{$choiceno}";
  65          if (!is_null($value)) {
  66              $expectedattributes['value'] = s($value);
  67          }
  68          return new \question_contains_tag_with_attributes('input', $expectedattributes);
  69      }
  70  
  71      public function test_interactive_behaviour() {
  72  
  73          // Create a drag-and-drop question.
  74          $dd = \test_question_maker::make_question('ddmarker');
  75          $dd->hints = array(
  76              new question_hint_ddmarker(13, 'This is the first hint.',
  77                                                              FORMAT_HTML, false, false, false),
  78              new question_hint_ddmarker(14, 'This is the second hint.',
  79                                                              FORMAT_HTML, true, true, false),
  80          );
  81          $dd->shufflechoices = false;
  82          $this->start_attempt_at_question($dd, 'interactive', 12);
  83  
  84          // Check the initial state.
  85          $this->check_current_state(question_state::$todo);
  86          $this->check_current_mark(null);
  87  
  88          $this->check_current_output(
  89                  $this->get_contains_draggable_marker_home_expectation(1, false),
  90                  $this->get_contains_draggable_marker_home_expectation(2, false),
  91                  $this->get_contains_draggable_marker_home_expectation(3, false),
  92                  $this->get_contains_hidden_expectation(1),
  93                  $this->get_contains_hidden_expectation(2),
  94                  $this->get_contains_hidden_expectation(3),
  95                  $this->get_contains_submit_button_expectation(true),
  96                  $this->get_does_not_contain_feedback_expectation(),
  97                  $this->get_tries_remaining_expectation(3),
  98                  $this->get_no_hint_visible_expectation());
  99  
 100          $completelywrong = array('c1' => '0,250', 'c2' => '100,250', 'c3' => '150,250');
 101          // Save the wrong answer.
 102          $this->process_submission($completelywrong);
 103          // Verify.
 104          $this->check_current_state(question_state::$todo);
 105          $this->check_current_mark(null);
 106  
 107          $this->check_current_output(
 108                  $this->get_contains_draggable_marker_home_expectation(1, false),
 109                  $this->get_contains_draggable_marker_home_expectation(2, false),
 110                  $this->get_contains_draggable_marker_home_expectation(3, false),
 111                  $this->get_contains_hidden_expectation(1, '0,250'),
 112                  $this->get_contains_hidden_expectation(2, '100,250'),
 113                  $this->get_contains_hidden_expectation(3, '150,250'),
 114                  $this->get_contains_submit_button_expectation(true),
 115                  $this->get_does_not_contain_feedback_expectation(),
 116                  $this->get_tries_remaining_expectation(3),
 117                  $this->get_no_hint_visible_expectation());
 118          // Submit the wrong answer.
 119          $this->process_submission($completelywrong + array('-submit' => 1));
 120  
 121          // Verify.
 122          $this->check_current_state(question_state::$todo);
 123          $this->check_current_mark(null);
 124          $this->check_current_output(
 125                  $this->get_contains_draggable_marker_home_expectation(1, false),
 126                  $this->get_contains_draggable_marker_home_expectation(2, false),
 127                  $this->get_contains_draggable_marker_home_expectation(3, false),
 128                  $this->get_contains_hidden_expectation(1, '0,250'),
 129                  $this->get_contains_hidden_expectation(2, '100,250'),
 130                  $this->get_contains_hidden_expectation(3, '150,250'),
 131                  $this->get_contains_try_again_button_expectation(true),
 132                  $this->get_contains_hint_expectation('This is the first hint'));
 133  
 134          // Do try again.
 135          $this->process_submission(array('-tryagain' => 1));
 136  
 137          // Verify.
 138          $this->check_current_state(question_state::$todo);
 139          $this->check_current_mark(null);
 140  
 141          $this->check_current_output(
 142                  $this->get_contains_draggable_marker_home_expectation(1, false),
 143                  $this->get_contains_draggable_marker_home_expectation(2, false),
 144                  $this->get_contains_draggable_marker_home_expectation(3, false),
 145                  $this->get_contains_hidden_expectation(1, '0,250'),
 146                  $this->get_contains_hidden_expectation(2, '100,250'),
 147                  $this->get_contains_hidden_expectation(3, '150,250'),
 148                  $this->get_contains_submit_button_expectation(true),
 149                  $this->get_does_not_contain_correctness_expectation(),
 150                  $this->get_does_not_contain_feedback_expectation(),
 151                  $this->get_tries_remaining_expectation(2),
 152                  $this->get_no_hint_visible_expectation());
 153  
 154          // Submit the right answer.
 155          $this->process_submission(
 156                      array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150', '-submit' => 1));
 157  
 158          // Verify.
 159          $this->check_current_state(question_state::$gradedright);
 160          $this->check_current_mark(8);
 161          $this->check_current_output(
 162                  $this->get_contains_draggable_marker_home_expectation(1, false),
 163                  $this->get_contains_draggable_marker_home_expectation(2, false),
 164                  $this->get_contains_draggable_marker_home_expectation(3, false),
 165                  $this->get_contains_hidden_expectation(1, '50,50'),
 166                  $this->get_contains_hidden_expectation(2, '150,50'),
 167                  $this->get_contains_hidden_expectation(3, '100,150'),
 168                  $this->get_does_not_contain_submit_button_expectation(),
 169                  $this->get_contains_correct_expectation(),
 170                  $this->get_no_hint_visible_expectation());
 171  
 172          // Check regrading does not mess anything up.
 173          $this->quba->regrade_all_questions();
 174  
 175          // Verify.
 176          $this->check_current_state(question_state::$gradedright);
 177          $this->check_current_mark(8);
 178      }
 179  
 180      public function test_deferred_feedback() {
 181  
 182          // Create a drag-and-drop question.
 183          $dd = \test_question_maker::make_question('ddmarker');
 184          $dd->shufflechoices = false;
 185          $this->start_attempt_at_question($dd, 'deferredfeedback', 12);
 186  
 187          // Check the initial state.
 188          $this->check_current_state(question_state::$todo);
 189          $this->check_current_mark(null);
 190  
 191          $this->check_current_output(
 192                  $this->get_contains_draggable_marker_home_expectation(1, false),
 193                  $this->get_contains_draggable_marker_home_expectation(2, false),
 194                  $this->get_contains_draggable_marker_home_expectation(3, false),
 195                  $this->get_contains_hidden_expectation(1),
 196                  $this->get_contains_hidden_expectation(2),
 197                  $this->get_contains_hidden_expectation(3),
 198                  $this->get_does_not_contain_feedback_expectation());
 199  
 200          // Save a partial answer.
 201          $this->process_submission(array('c1' => '150,50', 'c2' => '50,50'));
 202          // Verify.
 203          $this->check_current_state(question_state::$complete);
 204          $this->check_current_mark(null);
 205  
 206          $this->check_current_output(
 207                  $this->get_contains_draggable_marker_home_expectation(1, false),
 208                  $this->get_contains_draggable_marker_home_expectation(2, false),
 209                  $this->get_contains_draggable_marker_home_expectation(3, false),
 210                  $this->get_contains_hidden_expectation(1, '150,50'),
 211                  $this->get_contains_hidden_expectation(2, '50,50'),
 212                  $this->get_contains_hidden_expectation(3, ''),
 213                  $this->get_does_not_contain_correctness_expectation(),
 214                  $this->get_does_not_contain_feedback_expectation());
 215          // Save the right answer.
 216          $this->process_submission(
 217                          array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150'));
 218  
 219          // Verify.
 220          $this->check_current_state(question_state::$complete);
 221          $this->check_current_mark(null);
 222          $this->check_current_output(
 223                  $this->get_contains_draggable_marker_home_expectation(1, false),
 224                  $this->get_contains_draggable_marker_home_expectation(2, false),
 225                  $this->get_contains_draggable_marker_home_expectation(3, false),
 226                  $this->get_contains_hidden_expectation(1, '50,50'),
 227                  $this->get_contains_hidden_expectation(2, '150,50'),
 228                  $this->get_contains_hidden_expectation(3, '100,150'),
 229                  $this->get_does_not_contain_correctness_expectation(),
 230                  $this->get_does_not_contain_feedback_expectation());
 231  
 232          // Finish the attempt.
 233          $this->quba->finish_all_questions();
 234  
 235          // Verify.
 236          $this->check_current_state(question_state::$gradedright);
 237          $this->check_current_mark(12);
 238  
 239          $this->check_current_output(
 240                  $this->get_contains_draggable_marker_home_expectation(1, false),
 241                  $this->get_contains_draggable_marker_home_expectation(2, false),
 242                  $this->get_contains_draggable_marker_home_expectation(3, false),
 243                  $this->get_contains_hidden_expectation(1, '50,50'),
 244                  $this->get_contains_hidden_expectation(2, '150,50'),
 245                  $this->get_contains_hidden_expectation(3, '100,150'),
 246                  $this->get_contains_correct_expectation());
 247  
 248          // Change the right answer a bit.
 249          $dd->rightchoices[2] = 1;
 250  
 251          // Check regrading does not mess anything up.
 252          $this->quba->regrade_all_questions();
 253  
 254          // Verify.
 255          $this->check_current_state(question_state::$gradedpartial);
 256          $this->check_current_mark(8);
 257      }
 258  
 259      public function test_deferred_feedback_unanswered() {
 260  
 261          // Create a drag-and-drop question.
 262          $dd = \test_question_maker::make_question('ddmarker');
 263          $dd->shufflechoices = false;
 264          $this->start_attempt_at_question($dd, 'deferredfeedback', 12);
 265  
 266          // Check the initial state.
 267          $this->check_current_state(question_state::$todo);
 268          $this->check_current_mark(null);
 269          $this->check_current_output(
 270                  $this->get_contains_draggable_marker_home_expectation(1, false),
 271                  $this->get_contains_draggable_marker_home_expectation(2, false),
 272                  $this->get_contains_draggable_marker_home_expectation(3, false),
 273                  $this->get_contains_hidden_expectation(1),
 274                  $this->get_contains_hidden_expectation(2),
 275                  $this->get_contains_hidden_expectation(3),
 276                  $this->get_does_not_contain_correctness_expectation(),
 277                  $this->get_does_not_contain_feedback_expectation());
 278          $this->check_step_count(1);
 279  
 280          // Save a blank response.
 281          $this->process_submission(array('c1' => '', 'c2' => '', 'c3' => ''));
 282  
 283          // Verify.
 284          $this->check_current_state(question_state::$todo);
 285          $this->check_current_mark(null);
 286          $this->check_current_output(
 287                  $this->get_contains_draggable_marker_home_expectation(1, false),
 288                  $this->get_contains_draggable_marker_home_expectation(2, false),
 289                  $this->get_contains_draggable_marker_home_expectation(3, false),
 290                  $this->get_contains_hidden_expectation(1, ''),
 291                  $this->get_contains_hidden_expectation(2, ''),
 292                  $this->get_contains_hidden_expectation(3, ''),
 293                  $this->get_does_not_contain_correctness_expectation(),
 294                  $this->get_does_not_contain_feedback_expectation());
 295          $this->check_step_count(1);
 296  
 297          // Finish the attempt.
 298          $this->quba->finish_all_questions();
 299  
 300          // Verify.
 301          $this->check_current_state(question_state::$gaveup);
 302          $this->check_current_mark(null);
 303          $this->check_current_output(
 304                  $this->get_contains_draggable_marker_home_expectation(1, false),
 305                  $this->get_contains_draggable_marker_home_expectation(2, false),
 306                  $this->get_contains_draggable_marker_home_expectation(3, false));
 307      }
 308  
 309      public function test_deferred_feedback_partial_answer() {
 310  
 311          // Create a drag-and-drop question.
 312          $dd = \test_question_maker::make_question('ddmarker');
 313          $dd->shufflechoices = false;
 314          $this->start_attempt_at_question($dd, 'deferredfeedback', 3);
 315  
 316          // Check the initial state.
 317          $this->check_current_state(question_state::$todo);
 318          $this->check_current_mark(null);
 319          $this->check_current_output(
 320                  $this->get_contains_draggable_marker_home_expectation(1, false),
 321                  $this->get_contains_draggable_marker_home_expectation(2, false),
 322                  $this->get_contains_draggable_marker_home_expectation(3, false),
 323                  $this->get_contains_hidden_expectation(1),
 324                  $this->get_contains_hidden_expectation(2),
 325                  $this->get_contains_hidden_expectation(3),
 326                  $this->get_does_not_contain_correctness_expectation(),
 327                  $this->get_does_not_contain_feedback_expectation());
 328  
 329          $this->process_submission(array('c1' => '50,50', 'c2' => '150,50', 'c3' => ''));
 330  
 331          // Verify.
 332          $this->check_current_state(question_state::$complete);
 333          $this->check_current_mark(null);
 334          $this->check_current_output(
 335                  $this->get_contains_draggable_marker_home_expectation(1, false),
 336                  $this->get_contains_draggable_marker_home_expectation(2, false),
 337                  $this->get_contains_draggable_marker_home_expectation(3, false),
 338                  $this->get_contains_hidden_expectation(1, '50,50'),
 339                  $this->get_contains_hidden_expectation(2, '150,50'),
 340                  $this->get_contains_hidden_expectation(3, ''),
 341                  $this->get_does_not_contain_correctness_expectation(),
 342                  $this->get_does_not_contain_feedback_expectation());
 343  
 344          // Finish the attempt.
 345          $this->quba->finish_all_questions();
 346  
 347          // Verify.
 348          $this->check_current_state(question_state::$gradedpartial);
 349          $this->check_current_mark(2);
 350          $this->check_current_output(
 351                  $this->get_contains_draggable_marker_home_expectation(1, false),
 352                  $this->get_contains_draggable_marker_home_expectation(2, false),
 353                  $this->get_contains_draggable_marker_home_expectation(3, false),
 354                  $this->get_contains_partcorrect_expectation());
 355      }
 356  
 357      public function test_interactive_grading() {
 358  
 359          // Create a drag-and-drop question.
 360          $dd = \test_question_maker::make_question('ddmarker');
 361          $dd->hints = array(
 362              new question_hint_ddmarker(1, 'This is the first hint.',
 363                      FORMAT_MOODLE, true, true, false),
 364              new question_hint_ddmarker(2, 'This is the second hint.',
 365                      FORMAT_MOODLE, true, true, false),
 366          );
 367          $dd->shufflechoices = false;
 368          $this->start_attempt_at_question($dd, 'interactive', 12);
 369  
 370          // Check the initial state.
 371          $this->check_current_state(question_state::$todo);
 372          $this->check_current_mark(null);
 373          $this->assertEquals('interactivecountback',
 374                  $this->quba->get_question_attempt($this->slot)->get_behaviour_name());
 375          $this->check_current_output(
 376                  $this->get_contains_draggable_marker_home_expectation(1, false),
 377                  $this->get_contains_draggable_marker_home_expectation(2, false),
 378                  $this->get_contains_draggable_marker_home_expectation(3, false),
 379                  $this->get_contains_hidden_expectation(1),
 380                  $this->get_contains_hidden_expectation(2),
 381                  $this->get_contains_hidden_expectation(3),
 382                  $this->get_contains_submit_button_expectation(true),
 383                  $this->get_does_not_contain_feedback_expectation(),
 384                  $this->get_tries_remaining_expectation(3),
 385                  $this->get_does_not_contain_num_parts_correct(),
 386                  $this->get_no_hint_visible_expectation());
 387  
 388          // Submit an response with the first two parts right.
 389          $this->process_submission(
 390                      array('c1' => '50,50', 'c2' => '150,50', 'c3' => '150,50', '-submit' => 1));
 391  
 392          // Verify.
 393          $this->check_current_state(question_state::$todo);
 394          $this->check_current_mark(null);
 395          $this->check_current_output(
 396                  $this->get_contains_draggable_marker_home_expectation(1, false),
 397                  $this->get_contains_draggable_marker_home_expectation(2, false),
 398                  $this->get_contains_draggable_marker_home_expectation(3, false),
 399                  $this->get_does_not_contain_submit_button_expectation(),
 400                  $this->get_contains_try_again_button_expectation(true),
 401                  $this->get_does_not_contain_correctness_expectation(),
 402                  $this->get_contains_hint_expectation('This is the first hint'),
 403                  $this->get_contains_num_parts_correct(2),
 404                  $this->get_contains_standard_partiallycorrect_combined_feedback_expectation(),
 405                  $this->get_contains_hidden_expectation(1, '50,50'),
 406                  $this->get_contains_hidden_expectation(2, '150,50'),
 407                  $this->get_contains_hidden_expectation(3, '150,50'));
 408  
 409          // Check that extract responses will return the reset data.
 410          $prefix = $this->quba->get_field_prefix($this->slot);
 411          $this->assertEquals(array('c1' => '50,50', 'c2' => '150,50'),
 412                  $this->quba->extract_responses($this->slot,
 413                  array($prefix . 'c1' => '50,50', $prefix . 'c2' => '150,50', '-tryagain' => 1)));
 414  
 415          // Do try again.
 416          // keys c3 is an extra hidden fields to clear data.
 417          $this->process_submission(
 418                          array('c1' => '50,50', 'c2' => '150,50', 'c3' => '', '-tryagain' => 1));
 419  
 420          // Verify.
 421          $this->check_current_state(question_state::$todo);
 422          $this->check_current_mark(null);
 423          $this->check_current_output(
 424                  $this->get_contains_draggable_marker_home_expectation(1, false),
 425                  $this->get_contains_draggable_marker_home_expectation(2, false),
 426                  $this->get_contains_draggable_marker_home_expectation(3, false),
 427                  $this->get_contains_hidden_expectation(1, '50,50'),
 428                  $this->get_contains_hidden_expectation(2, '150,50'),
 429                  $this->get_contains_hidden_expectation(3, ''),
 430                  $this->get_contains_submit_button_expectation(true),
 431                  $this->get_does_not_contain_try_again_button_expectation(),
 432                  $this->get_does_not_contain_correctness_expectation(),
 433                  $this->get_does_not_contain_feedback_expectation(),
 434                  $this->get_tries_remaining_expectation(2),
 435                  $this->get_no_hint_visible_expectation());
 436  
 437          // Submit an response with the first and last parts right.
 438          $this->process_submission(
 439                      array('c1' => '50,50', 'c2' => '150,150', 'c3' => '100,150', '-submit' => 1));
 440  
 441          // Verify.
 442          $this->check_current_state(question_state::$todo);
 443          $this->check_current_mark(null);
 444          $this->check_current_output(
 445                  $this->get_contains_draggable_marker_home_expectation(1, false),
 446                  $this->get_contains_draggable_marker_home_expectation(2, false),
 447                  $this->get_contains_draggable_marker_home_expectation(3, false),
 448                  $this->get_does_not_contain_submit_button_expectation(),
 449                  $this->get_contains_try_again_button_expectation(true),
 450                  $this->get_does_not_contain_correctness_expectation(),
 451                  $this->get_contains_hint_expectation('This is the second hint'),
 452                  $this->get_contains_num_parts_correct(2),
 453                  $this->get_contains_standard_partiallycorrect_combined_feedback_expectation(),
 454                  $this->get_contains_hidden_expectation(1, '50,50'),
 455                  $this->get_contains_hidden_expectation(2, '150,150'),
 456                  $this->get_contains_hidden_expectation(3, '100,150'));
 457  
 458          // Do try again.
 459          $this->process_submission(
 460                          array('c1' => '50,50', 'c2' => '', 'c3' => '', '-tryagain' => 1));
 461  
 462          // Verify.
 463          $this->check_current_state(question_state::$todo);
 464          $this->check_current_mark(null);
 465          $this->check_current_output(
 466                  $this->get_contains_draggable_marker_home_expectation(1, false),
 467                  $this->get_contains_draggable_marker_home_expectation(2, false),
 468                  $this->get_contains_draggable_marker_home_expectation(3, false),
 469                  $this->get_contains_hidden_expectation(1, '50,50'),
 470                  $this->get_contains_hidden_expectation(2, ''),
 471                  $this->get_contains_hidden_expectation(3, ''),
 472                  $this->get_contains_submit_button_expectation(true),
 473                  $this->get_does_not_contain_try_again_button_expectation(),
 474                  $this->get_does_not_contain_correctness_expectation(),
 475                  $this->get_does_not_contain_feedback_expectation(),
 476                  $this->get_tries_remaining_expectation(1),
 477                  $this->get_no_hint_visible_expectation());
 478  
 479          // Submit the right answer.
 480          $this->process_submission(
 481                      array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150', '-submit' => 1));
 482  
 483          // Verify.
 484          $this->check_current_state(question_state::$gradedright);
 485          $this->check_current_mark(8);
 486          $this->check_current_output(
 487                  $this->get_contains_draggable_marker_home_expectation(1, false),
 488                  $this->get_contains_draggable_marker_home_expectation(2, false),
 489                  $this->get_contains_draggable_marker_home_expectation(3, false),
 490                  $this->get_contains_hidden_expectation(1, '50,50'),
 491                  $this->get_contains_hidden_expectation(2, '150,50'),
 492                  $this->get_contains_hidden_expectation(3, '100,150'),
 493                  $this->get_does_not_contain_submit_button_expectation(),
 494                  $this->get_does_not_contain_try_again_button_expectation(),
 495                  $this->get_contains_correct_expectation(),
 496                  $this->get_no_hint_visible_expectation(),
 497                  $this->get_does_not_contain_num_parts_correct(),
 498                  $this->get_contains_standard_correct_combined_feedback_expectation());
 499      }
 500  
 501      public function test_interactive_correct_no_submit() {
 502  
 503          // Create a drag-and-drop question.
 504          $dd = \test_question_maker::make_question('ddmarker');
 505          $dd->hints = array(
 506              new question_hint_ddmarker(23, 'This is the first hint.',
 507                      FORMAT_MOODLE, false, false, false),
 508              new question_hint_ddmarker(24, 'This is the second hint.',
 509                      FORMAT_MOODLE, true, true, false),
 510          );
 511          $dd->shufflechoices = false;
 512          $this->start_attempt_at_question($dd, 'interactive', 3);
 513  
 514          // Check the initial state.
 515          $this->check_current_state(question_state::$todo);
 516          $this->check_current_mark(null);
 517          $this->check_current_output(
 518                  $this->get_contains_draggable_marker_home_expectation(1, false),
 519                  $this->get_contains_draggable_marker_home_expectation(2, false),
 520                  $this->get_contains_draggable_marker_home_expectation(3, false),
 521                  $this->get_contains_hidden_expectation(1),
 522                  $this->get_contains_hidden_expectation(2),
 523                  $this->get_contains_hidden_expectation(3),
 524                  $this->get_contains_submit_button_expectation(true),
 525                  $this->get_does_not_contain_feedback_expectation(),
 526                  $this->get_tries_remaining_expectation(3),
 527                  $this->get_no_hint_visible_expectation());
 528  
 529          // Save the right answer.
 530          $this->process_submission(array('c1' => '50,50', 'c2' => '150,50', 'c3' => '100,150'));
 531  
 532          // Finish the attempt without clicking check.
 533          $this->quba->finish_all_questions();
 534  
 535          // Verify.
 536          $this->check_current_state(question_state::$gradedright);
 537          $this->check_current_mark(3);
 538          $this->check_current_output(
 539                  $this->get_contains_draggable_marker_home_expectation(1, false),
 540                  $this->get_contains_draggable_marker_home_expectation(2, false),
 541                  $this->get_contains_draggable_marker_home_expectation(3, false),
 542                  $this->get_does_not_contain_submit_button_expectation(),
 543                  $this->get_contains_correct_expectation(),
 544                  $this->get_no_hint_visible_expectation());
 545  
 546          // Check regrading does not mess anything up.
 547          $this->quba->regrade_all_questions();
 548  
 549          // Verify.
 550          $this->check_current_state(question_state::$gradedright);
 551          $this->check_current_mark(3);
 552      }
 553  
 554      public function test_interactive_partial_no_submit() {
 555  
 556          // Create a drag-and-drop question.
 557          $dd = \test_question_maker::make_question('ddmarker');
 558          $dd->hints = array(
 559              new question_hint_ddmarker(23, 'This is the first hint.',
 560                      FORMAT_MOODLE, false, false, false),
 561              new question_hint_ddmarker(24, 'This is the second hint.',
 562                      FORMAT_MOODLE, true, true, false),
 563          );
 564          $dd->shufflechoices = false;
 565          $this->start_attempt_at_question($dd, 'interactive', 3);
 566  
 567          // Check the initial state.
 568          $this->check_current_state(question_state::$todo);
 569          $this->check_current_mark(null);
 570  
 571          $this->check_current_output(
 572                  $this->get_contains_draggable_marker_home_expectation(1, false),
 573                  $this->get_contains_draggable_marker_home_expectation(2, false),
 574                  $this->get_contains_draggable_marker_home_expectation(3, false),
 575                  $this->get_contains_hidden_expectation(1),
 576                  $this->get_contains_hidden_expectation(2),
 577                  $this->get_contains_hidden_expectation(3),
 578                  $this->get_contains_submit_button_expectation(true),
 579                  $this->get_does_not_contain_feedback_expectation(),
 580                  $this->get_tries_remaining_expectation(3),
 581                  $this->get_no_hint_visible_expectation());
 582  
 583          // Save the a partially right answer.
 584          $this->process_submission(array('c1' => '50,50', 'c2' => '50,50', 'c3' => '100,150'));
 585  
 586          // Finish the attempt without clicking check.
 587          $this->quba->finish_all_questions();
 588  
 589          // Verify.
 590          $this->check_current_state(question_state::$gradedpartial);
 591          $this->check_current_mark(2);
 592  
 593          $this->check_current_output(
 594                  $this->get_contains_draggable_marker_home_expectation(1, false),
 595                  $this->get_contains_draggable_marker_home_expectation(2, false),
 596                  $this->get_contains_draggable_marker_home_expectation(3, false),
 597                  $this->get_does_not_contain_submit_button_expectation(),
 598                  $this->get_contains_partcorrect_expectation(),
 599                  $this->get_no_hint_visible_expectation());
 600  
 601          // Check regrading does not mess anything up.
 602          $this->quba->regrade_all_questions();
 603  
 604          // Verify.
 605          $this->check_current_state(question_state::$gradedpartial);
 606          $this->check_current_mark(2);
 607      }
 608  
 609      public function test_interactive_no_right_clears() {
 610  
 611          // Create a drag-and-drop question.
 612          $dd = \test_question_maker::make_question('ddmarker');
 613          $dd->hints = array(
 614              new question_hint_ddmarker(23, 'This is the first hint.',
 615                                                                  FORMAT_MOODLE, false, true, false),
 616              new question_hint_ddmarker(24, 'This is the second hint.',
 617                                                                  FORMAT_MOODLE, true, true, false),
 618          );
 619          $dd->shufflechoices = false;
 620          $this->start_attempt_at_question($dd, 'interactive', 3);
 621  
 622          // Check the initial state.
 623          $this->check_current_state(question_state::$todo);
 624          $this->check_current_mark(null);
 625  
 626          $this->check_current_output(
 627                  $this->get_contains_marked_out_of_summary(),
 628                  $this->get_contains_draggable_marker_home_expectation(1, false),
 629                  $this->get_contains_draggable_marker_home_expectation(2, false),
 630                  $this->get_contains_draggable_marker_home_expectation(3, false),
 631                  $this->get_contains_hidden_expectation(1),
 632                  $this->get_contains_hidden_expectation(2),
 633                  $this->get_contains_hidden_expectation(3),
 634                  $this->get_contains_submit_button_expectation(true),
 635                  $this->get_does_not_contain_feedback_expectation(),
 636                  $this->get_tries_remaining_expectation(3),
 637                  $this->get_no_hint_visible_expectation());
 638  
 639          // Save the a completely wrong answer.
 640          $this->process_submission(
 641                      array('c1' => '100,150', 'c2' => '100,150', 'c3' => '50,50', '-submit' => 1));
 642  
 643          // Verify.
 644          $this->check_current_state(question_state::$todo);
 645          $this->check_current_mark(null);
 646          $this->check_current_output(
 647                  $this->get_contains_marked_out_of_summary(),
 648                  $this->get_contains_draggable_marker_home_expectation(1, false),
 649                  $this->get_contains_draggable_marker_home_expectation(2, false),
 650                  $this->get_contains_draggable_marker_home_expectation(3, false),
 651                  $this->get_does_not_contain_submit_button_expectation(),
 652                  $this->get_contains_hint_expectation('This is the first hint'));
 653  
 654          // Do try again.
 655          $this->process_submission(
 656                          array('c1' => '', 'c2' => '', 'c3' => '', '-tryagain' => 1));
 657  
 658          // Check that all the wrong answers have been cleared.
 659          $this->check_current_state(question_state::$todo);
 660          $this->check_current_mark(null);
 661          $this->check_current_output(
 662                  $this->get_contains_marked_out_of_summary(),
 663                  $this->get_contains_draggable_marker_home_expectation(1, false),
 664                  $this->get_contains_draggable_marker_home_expectation(2, false),
 665                  $this->get_contains_draggable_marker_home_expectation(3, false),
 666                  $this->get_contains_hidden_expectation(1, ''),
 667                  $this->get_contains_hidden_expectation(2, ''),
 668                  $this->get_contains_hidden_expectation(3, ''),
 669                  $this->get_contains_submit_button_expectation(true),
 670                  $this->get_does_not_contain_feedback_expectation(),
 671                  $this->get_tries_remaining_expectation(2),
 672                  $this->get_no_hint_visible_expectation());
 673      }
 674  
 675      public function test_display_of_right_answer_when_shuffled() {
 676  
 677          // Create a drag-and-drop question.
 678          $dd = \test_question_maker::make_question('ddmarker');
 679          $this->start_attempt_at_question($dd, 'deferredfeedback', 3);
 680  
 681          // Check the initial state.
 682          $this->check_current_state(question_state::$todo);
 683          $this->check_current_mark(null);
 684  
 685          $this->check_current_output(
 686                  $this->get_contains_hidden_expectation(1),
 687                  $this->get_contains_hidden_expectation(2),
 688                  $this->get_contains_hidden_expectation(3),
 689                  $this->get_does_not_contain_feedback_expectation());
 690  
 691          // Save a partial answer.
 692          $this->process_submission($dd->get_correct_response());
 693  
 694          // Verify.
 695          $this->check_current_state(question_state::$complete);
 696          $this->check_current_mark(null);
 697          $rightanswer = array($dd->get_right_choice_for(1) => '50,50',
 698                                  $dd->get_right_choice_for(2) => '150,50',
 699                                  $dd->get_right_choice_for(3) => '100,150');
 700          $this->check_current_output(
 701              $this->get_contains_hidden_expectation(1, $rightanswer[1]),
 702              $this->get_contains_hidden_expectation(2, $rightanswer[2]),
 703              $this->get_contains_hidden_expectation(3, $rightanswer[3]),
 704              $this->get_does_not_contain_correctness_expectation(),
 705              $this->get_does_not_contain_feedback_expectation());
 706  
 707          // Finish the attempt.
 708          $this->quba->finish_all_questions();
 709  
 710          // Verify.
 711          $this->displayoptions->rightanswer = question_display_options::VISIBLE;
 712          $this->assertEquals('{Drop zone 1 -> quick}, '.
 713                              '{Drop zone 2 -> fox}, '.
 714                              '{Drop zone 3 -> lazy}',
 715                              $dd->get_right_answer_summary());
 716          $this->check_current_state(question_state::$gradedright);
 717          $this->check_current_mark(3);
 718      }
 719  }