Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 310 and 402] [Versions 311 and 402] [Versions 39 and 402] [Versions 400 and 402] [Versions 401 and 402]

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