Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

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

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * Unit tests for the drag-and-drop onto image question type.
  19   *
  20   * @package   qtype_ddimageortext
  21   * @copyright 2010 The Open University
  22   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  23   */
  24  
  25  
  26  defined('MOODLE_INTERNAL') || die();
  27  
  28  global $CFG;
  29  require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
  30  require_once($CFG->dirroot . '/question/type/ddimageortext/tests/helper.php');
  31  
  32  
  33  /**
  34   * Unit tests for the drag-and-drop onto image question type.
  35   *
  36   * @copyright 2010 The Open University
  37   * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  38   */
  39  class qtype_ddimageortext_walkthrough_test extends qbehaviour_walkthrough_test_base {
  40  
  41      /**
  42       * Get an expectation that the output contains an item ready to drag.
  43       * @param int $dragitemno the item number.
  44       * @param int $choice which choice this is.
  45       * @param int $group which drag group it belongs to.
  46       * @return question_contains_tag_with_attributes the required expectation.
  47       */
  48      protected function get_contains_drag_image_home_expectation($dragitemno, $choice, $group) {
  49          $class = 'group' . $group . ' draghome choice' . $choice;
  50  
  51          $expectedattrs = array();
  52          $expectedattrs['class'] = $class;
  53  
  54          return new question_contains_tag_with_attributes('div', $expectedattrs);
  55      }
  56  
  57      public function test_interactive_behaviour() {
  58  
  59          // Create a drag-and-drop question.
  60          $dd = test_question_maker::make_question('ddimageortext');
  61          $dd->hints = array(
  62              new question_hint_with_parts(13, 'This is the first hint.', FORMAT_HTML, false, false),
  63              new question_hint_with_parts(14, 'This is the second hint.', FORMAT_HTML, true, true),
  64          );
  65          $dd->shufflechoices = false;
  66          $this->start_attempt_at_question($dd, 'interactive', 12);
  67  
  68          // Check the initial state.
  69          $this->check_current_state(question_state::$todo);
  70          $this->check_current_mark(null);
  71  
  72          $this->check_current_output(
  73              $this->get_contains_drag_image_home_expectation(1, 1, 1),
  74              $this->get_contains_drag_image_home_expectation(2, 2, 1),
  75              $this->get_contains_drag_image_home_expectation(3, 1, 2),
  76              $this->get_contains_drag_image_home_expectation(4, 2, 2),
  77              $this->get_contains_hidden_expectation(
  78                  $this->quba->get_field_prefix($this->slot) . 'p1'),
  79              $this->get_contains_hidden_expectation(
  80                  $this->quba->get_field_prefix($this->slot) . 'p2'),
  81              $this->get_contains_hidden_expectation(
  82                  $this->quba->get_field_prefix($this->slot) . 'p3'),
  83              $this->get_contains_hidden_expectation(
  84                  $this->quba->get_field_prefix($this->slot) . 'p4'),
  85              $this->get_contains_submit_button_expectation(true),
  86              $this->get_does_not_contain_feedback_expectation(),
  87              $this->get_tries_remaining_expectation(3),
  88              $this->get_no_hint_visible_expectation());
  89  
  90          // Save the wrong answer.
  91          $this->process_submission(array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1'));
  92          // Verify.
  93          $this->check_current_state(question_state::$todo);
  94          $this->check_current_mark(null);
  95  
  96          $this->check_current_output(
  97              $this->get_contains_drag_image_home_expectation(1, 1, 1),
  98              $this->get_contains_drag_image_home_expectation(2, 2, 1),
  99              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 100              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 101              $this->get_contains_hidden_expectation(
 102                  $this->quba->get_field_prefix($this->slot) . 'p1', 2),
 103              $this->get_contains_hidden_expectation(
 104                  $this->quba->get_field_prefix($this->slot) . 'p2', 1),
 105              $this->get_contains_hidden_expectation(
 106                  $this->quba->get_field_prefix($this->slot) . 'p3', 2),
 107              $this->get_contains_hidden_expectation(
 108                  $this->quba->get_field_prefix($this->slot) . 'p4', 1),
 109              $this->get_contains_submit_button_expectation(true),
 110              $this->get_does_not_contain_feedback_expectation(),
 111              $this->get_tries_remaining_expectation(3),
 112              $this->get_no_hint_visible_expectation());
 113          // Submit the wrong answer.
 114          $this->process_submission(
 115              array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1', '-submit' => 1));
 116  
 117          // Verify.
 118          $this->check_current_state(question_state::$todo);
 119          $this->check_current_mark(null);
 120          $this->check_current_output(
 121              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 122              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 123              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 124              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 125              $this->get_contains_hidden_expectation(
 126                  $this->quba->get_field_prefix($this->slot) . 'p1', 2),
 127              $this->get_contains_hidden_expectation(
 128                  $this->quba->get_field_prefix($this->slot) . 'p2', 1),
 129              $this->get_contains_hidden_expectation(
 130                  $this->quba->get_field_prefix($this->slot) . 'p3', 2),
 131              $this->get_contains_hidden_expectation(
 132                  $this->quba->get_field_prefix($this->slot) . 'p4', 1),
 133              $this->get_contains_try_again_button_expectation(true),
 134              $this->get_contains_hint_expectation('This is the first hint'));
 135  
 136          // Do try again.
 137          $this->process_submission(array('-tryagain' => 1));
 138  
 139          // Verify.
 140          $this->check_current_state(question_state::$todo);
 141          $this->check_current_mark(null);
 142  
 143          $this->check_current_output(
 144              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 145              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 146              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 147              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 148              $this->get_contains_hidden_expectation(
 149                  $this->quba->get_field_prefix($this->slot) . 'p1', '2'),
 150              $this->get_contains_hidden_expectation(
 151                  $this->quba->get_field_prefix($this->slot) . 'p2', '1'),
 152              $this->get_contains_hidden_expectation(
 153                  $this->quba->get_field_prefix($this->slot) . 'p3', '2'),
 154              $this->get_contains_hidden_expectation(
 155                  $this->quba->get_field_prefix($this->slot) . 'p4', '1'),
 156              $this->get_contains_submit_button_expectation(true),
 157              $this->get_does_not_contain_correctness_expectation(),
 158              $this->get_does_not_contain_feedback_expectation(),
 159              $this->get_tries_remaining_expectation(2),
 160              $this->get_no_hint_visible_expectation());
 161  
 162          // Submit the right answer.
 163          $this->process_submission(
 164              array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2', '-submit' => 1));
 165  
 166          // Verify.
 167          $this->check_current_state(question_state::$gradedright);
 168          $this->check_current_mark(8);
 169          $this->check_current_output(
 170              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 171              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 172              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 173              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 174              $this->get_contains_hidden_expectation(
 175                  $this->quba->get_field_prefix($this->slot) . 'p1', '1'),
 176              $this->get_contains_hidden_expectation(
 177                  $this->quba->get_field_prefix($this->slot) . 'p2', '2'),
 178              $this->get_contains_hidden_expectation(
 179                  $this->quba->get_field_prefix($this->slot) . 'p3', '1'),
 180              $this->get_contains_hidden_expectation(
 181                  $this->quba->get_field_prefix($this->slot) . 'p4', '2'),
 182              $this->get_does_not_contain_submit_button_expectation(),
 183              $this->get_contains_correct_expectation(),
 184              $this->get_no_hint_visible_expectation());
 185  
 186          // Check regrading does not mess anything up.
 187          $this->quba->regrade_all_questions();
 188  
 189          // Verify.
 190          $this->check_current_state(question_state::$gradedright);
 191          $this->check_current_mark(8);
 192      }
 193  
 194      public function test_deferred_feedback() {
 195  
 196          // Create a drag-and-drop question.
 197          $dd = test_question_maker::make_question('ddimageortext');
 198          $dd->shufflechoices = false;
 199          $this->start_attempt_at_question($dd, 'deferredfeedback', 12);
 200  
 201          // Check the initial state.
 202          $this->check_current_state(question_state::$todo);
 203          $this->check_current_mark(null);
 204  
 205          $this->check_current_output(
 206              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 207              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 208              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 209              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 210              $this->get_contains_hidden_expectation(
 211                  $this->quba->get_field_prefix($this->slot) . 'p1'),
 212              $this->get_contains_hidden_expectation(
 213                  $this->quba->get_field_prefix($this->slot) . 'p2'),
 214              $this->get_contains_hidden_expectation(
 215                  $this->quba->get_field_prefix($this->slot) . 'p3'),
 216              $this->get_contains_hidden_expectation(
 217                  $this->quba->get_field_prefix($this->slot) . 'p4'),
 218              $this->get_does_not_contain_feedback_expectation());
 219  
 220          // Save a partial answer.
 221          $this->process_submission(array('p1' => '2', 'p2' => '1'));
 222          // Verify.
 223          $this->check_current_state(question_state::$invalid);
 224          $this->check_current_mark(null);
 225  
 226          $this->check_current_output(
 227              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 228              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 229              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 230              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 231              $this->get_contains_hidden_expectation(
 232                  $this->quba->get_field_prefix($this->slot) . 'p1', 2),
 233              $this->get_contains_hidden_expectation(
 234                  $this->quba->get_field_prefix($this->slot) . 'p2', 1),
 235              $this->get_contains_hidden_expectation(
 236                  $this->quba->get_field_prefix($this->slot) . 'p3', ''),
 237              $this->get_contains_hidden_expectation(
 238                  $this->quba->get_field_prefix($this->slot) . 'p4', ''),
 239              $this->get_does_not_contain_correctness_expectation(),
 240              $this->get_does_not_contain_feedback_expectation());
 241          // Save the right answer.
 242          $this->process_submission(
 243              array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'));
 244  
 245          // Verify.
 246          $this->check_current_state(question_state::$complete);
 247          $this->check_current_mark(null);
 248          $this->check_current_output(
 249              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 250              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 251              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 252              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 253              $this->get_contains_hidden_expectation(
 254                  $this->quba->get_field_prefix($this->slot) . 'p1', 1),
 255              $this->get_contains_hidden_expectation(
 256                  $this->quba->get_field_prefix($this->slot) . 'p2', 2),
 257              $this->get_contains_hidden_expectation(
 258                  $this->quba->get_field_prefix($this->slot) . 'p3', 1),
 259              $this->get_contains_hidden_expectation(
 260                  $this->quba->get_field_prefix($this->slot) . 'p4', 2),
 261              $this->get_does_not_contain_correctness_expectation(),
 262              $this->get_does_not_contain_feedback_expectation());
 263  
 264          // Finish the attempt.
 265          $this->quba->finish_all_questions();
 266  
 267          // Verify.
 268          $this->check_current_state(question_state::$gradedright);
 269          $this->check_current_mark(12);
 270  
 271          $this->check_current_output(
 272              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 273              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 274              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 275              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 276              $this->get_contains_hidden_expectation(
 277                  $this->quba->get_field_prefix($this->slot) . 'p1', 1),
 278              $this->get_contains_hidden_expectation(
 279                  $this->quba->get_field_prefix($this->slot) . 'p2', 2),
 280              $this->get_contains_hidden_expectation(
 281                  $this->quba->get_field_prefix($this->slot) . 'p3', 1),
 282              $this->get_contains_hidden_expectation(
 283                  $this->quba->get_field_prefix($this->slot) . 'p4', 2),
 284              $this->get_contains_correct_expectation());
 285  
 286          // Change the right answer a bit.
 287          $dd->rightchoices[2] = 1;
 288  
 289          // Check regrading does not mess anything up.
 290          $this->quba->regrade_all_questions();
 291  
 292          // Verify.
 293          $this->check_current_state(question_state::$gradedpartial);
 294          $this->check_current_mark(9);
 295      }
 296  
 297      public function test_deferred_feedback_unanswered() {
 298  
 299          // Create a drag-and-drop question.
 300          $dd = test_question_maker::make_question('ddimageortext');
 301          $dd->shufflechoices = false;
 302          $this->start_attempt_at_question($dd, 'deferredfeedback', 12);
 303  
 304          // Check the initial state.
 305          $this->check_current_state(question_state::$todo);
 306          $this->check_current_mark(null);
 307          $this->check_current_output(
 308              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 309              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 310              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 311              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 312              $this->get_contains_hidden_expectation(
 313                  $this->quba->get_field_prefix($this->slot) . 'p1'),
 314              $this->get_contains_hidden_expectation(
 315                  $this->quba->get_field_prefix($this->slot) . 'p2'),
 316              $this->get_contains_hidden_expectation(
 317                  $this->quba->get_field_prefix($this->slot) . 'p3'),
 318              $this->get_contains_hidden_expectation(
 319                  $this->quba->get_field_prefix($this->slot) . 'p4'),
 320              $this->get_does_not_contain_correctness_expectation(),
 321              $this->get_does_not_contain_feedback_expectation());
 322          $this->check_step_count(1);
 323  
 324          // Save a blank response.
 325          $this->process_submission(array('p1' => '', 'p2' => '', 'p3' => '', 'p4' => ''));
 326  
 327          // Verify.
 328          $this->check_current_state(question_state::$todo);
 329          $this->check_current_mark(null);
 330          $this->check_current_output(
 331              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 332              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 333              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 334              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 335              $this->get_contains_hidden_expectation(
 336                  $this->quba->get_field_prefix($this->slot) . 'p1', ''),
 337              $this->get_contains_hidden_expectation(
 338                  $this->quba->get_field_prefix($this->slot) . 'p2', ''),
 339              $this->get_contains_hidden_expectation(
 340                  $this->quba->get_field_prefix($this->slot) . 'p3', ''),
 341              $this->get_contains_hidden_expectation(
 342                  $this->quba->get_field_prefix($this->slot) . 'p4', ''),
 343              $this->get_does_not_contain_correctness_expectation(),
 344              $this->get_does_not_contain_feedback_expectation());
 345          $this->check_step_count(1);
 346  
 347          // Finish the attempt.
 348          $this->quba->finish_all_questions();
 349  
 350          // Verify.
 351          $this->check_current_state(question_state::$gaveup);
 352          $this->check_current_mark(null);
 353          $this->check_current_output(
 354              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 355              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 356              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 357              $this->get_contains_drag_image_home_expectation(4, 2, 2));
 358      }
 359  
 360      public function test_deferred_feedback_partial_answer() {
 361  
 362          // Create a drag-and-drop question.
 363          $dd = test_question_maker::make_question('ddimageortext');
 364          $dd->shufflechoices = false;
 365          $this->start_attempt_at_question($dd, 'deferredfeedback', 3);
 366  
 367          // Check the initial state.
 368          $this->check_current_state(question_state::$todo);
 369          $this->check_current_mark(null);
 370          $this->check_current_output(
 371              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 372              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 373              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 374              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 375              $this->get_contains_hidden_expectation(
 376                  $this->quba->get_field_prefix($this->slot) . 'p1'),
 377              $this->get_contains_hidden_expectation(
 378                  $this->quba->get_field_prefix($this->slot) . 'p2'),
 379              $this->get_contains_hidden_expectation(
 380                  $this->quba->get_field_prefix($this->slot) . 'p3'),
 381              $this->get_contains_hidden_expectation(
 382                  $this->quba->get_field_prefix($this->slot) . 'p4'),
 383              $this->get_does_not_contain_correctness_expectation(),
 384              $this->get_does_not_contain_feedback_expectation());
 385  
 386          // Save a partial response.
 387          $this->process_submission(array('p1' => '1', 'p2' => '2', 'p3' => '', 'p4' => ''));
 388  
 389          // Verify.
 390          $this->check_current_state(question_state::$invalid);
 391          $this->check_current_mark(null);
 392          $this->check_current_output(
 393              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 394              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 395              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 396              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 397              $this->get_contains_hidden_expectation(
 398                  $this->quba->get_field_prefix($this->slot) . 'p1', 1),
 399              $this->get_contains_hidden_expectation(
 400                  $this->quba->get_field_prefix($this->slot) . 'p2', 2),
 401              $this->get_contains_hidden_expectation(
 402                  $this->quba->get_field_prefix($this->slot) . 'p3', 0),
 403              $this->get_contains_hidden_expectation(
 404                  $this->quba->get_field_prefix($this->slot) . 'p4', 0),
 405              $this->get_does_not_contain_correctness_expectation(),
 406              $this->get_does_not_contain_feedback_expectation());
 407  
 408          // Finish the attempt.
 409          $this->quba->finish_all_questions();
 410  
 411          // Verify.
 412          $this->check_current_state(question_state::$gradedpartial);
 413          $this->check_current_mark(1.5);
 414          $this->check_current_output(
 415              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 416              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 417              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 418              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 419              $this->get_contains_partcorrect_expectation());
 420      }
 421  
 422      public function test_interactive_grading() {
 423  
 424          // Create a drag-and-drop question.
 425          $dd = test_question_maker::make_question('ddimageortext');
 426          $dd->hints = array(
 427              new question_hint_with_parts(1, 'This is the first hint.',
 428                  FORMAT_MOODLE, true, true),
 429              new question_hint_with_parts(2, 'This is the second hint.',
 430                  FORMAT_MOODLE, true, true),
 431          );
 432          $dd->shufflechoices = false;
 433          $this->start_attempt_at_question($dd, 'interactive', 12);
 434  
 435          // Check the initial state.
 436          $this->check_current_state(question_state::$todo);
 437          $this->check_current_mark(null);
 438          $this->assertEquals('interactivecountback',
 439              $this->quba->get_question_attempt($this->slot)->get_behaviour_name());
 440          $this->check_current_output(
 441              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 442              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 443              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 444              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 445              $this->get_contains_hidden_expectation(
 446                  $this->quba->get_field_prefix($this->slot) . 'p1'),
 447              $this->get_contains_hidden_expectation(
 448                  $this->quba->get_field_prefix($this->slot) . 'p2'),
 449              $this->get_contains_hidden_expectation(
 450                  $this->quba->get_field_prefix($this->slot) . 'p3'),
 451              $this->get_contains_hidden_expectation(
 452                  $this->quba->get_field_prefix($this->slot) . 'p4'),
 453              $this->get_contains_submit_button_expectation(true),
 454              $this->get_does_not_contain_feedback_expectation(),
 455              $this->get_tries_remaining_expectation(3),
 456              $this->get_does_not_contain_num_parts_correct(),
 457              $this->get_no_hint_visible_expectation());
 458  
 459          // Submit an response with the first two parts right.
 460          $this->process_submission(
 461              array('p1' => '1', 'p2' => '2', 'p3' => '2', 'p4' => '1', '-submit' => 1));
 462  
 463          // Verify.
 464          $this->check_current_state(question_state::$todo);
 465          $this->check_current_mark(null);
 466          $this->check_current_output(
 467              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 468              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 469              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 470              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 471              $this->get_does_not_contain_submit_button_expectation(),
 472              $this->get_contains_try_again_button_expectation(true),
 473              $this->get_does_not_contain_correctness_expectation(),
 474              $this->get_contains_hint_expectation('This is the first hint'),
 475              $this->get_contains_num_parts_correct(2),
 476              $this->get_contains_standard_partiallycorrect_combined_feedback_expectation(),
 477              $this->get_contains_hidden_expectation(
 478                  $this->quba->get_field_prefix($this->slot) . 'p1', 1),
 479              $this->get_contains_hidden_expectation(
 480                  $this->quba->get_field_prefix($this->slot) . 'p2', 2),
 481              $this->get_contains_hidden_expectation(
 482                  $this->quba->get_field_prefix($this->slot) . 'p3', 2),
 483              $this->get_contains_hidden_expectation(
 484                  $this->quba->get_field_prefix($this->slot) . 'p4', 1));
 485  
 486          // Check that extract responses will return the reset data.
 487          $prefix = $this->quba->get_field_prefix($this->slot);
 488          $this->assertEquals(array('p1' => '1', 'p2' => '2'),
 489              $this->quba->extract_responses($this->slot,
 490                  array($prefix . 'p1' => '1', $prefix . 'p2' => '2', '-tryagain' => 1)));
 491  
 492          // Do try again.
 493          // keys p3 and p4 are extra hidden fields to clear data.
 494          $this->process_submission(
 495              array('p1' => '1', 'p2' => '2', 'p3' => '', 'p4' => '', '-tryagain' => 1));
 496  
 497          // Verify.
 498          $this->check_current_state(question_state::$todo);
 499          $this->check_current_mark(null);
 500          $this->check_current_output(
 501              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 502              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 503              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 504              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 505              $this->get_contains_hidden_expectation(
 506                  $this->quba->get_field_prefix($this->slot) . 'p1', 1),
 507              $this->get_contains_hidden_expectation(
 508                  $this->quba->get_field_prefix($this->slot) . 'p2', 2),
 509              $this->get_contains_hidden_expectation(
 510                  $this->quba->get_field_prefix($this->slot) . 'p3', 0),
 511              $this->get_contains_hidden_expectation(
 512                  $this->quba->get_field_prefix($this->slot) . 'p4', 0),
 513              $this->get_contains_submit_button_expectation(true),
 514              $this->get_does_not_contain_try_again_button_expectation(),
 515              $this->get_does_not_contain_correctness_expectation(),
 516              $this->get_does_not_contain_feedback_expectation(),
 517              $this->get_tries_remaining_expectation(2),
 518              $this->get_no_hint_visible_expectation());
 519  
 520          // Submit an response with the first and last parts right.
 521          $this->process_submission(
 522              array('p1' => '1', 'p2' => '1', 'p3' => '2', 'p4' => '2', '-submit' => 1));
 523  
 524          // Verify.
 525          $this->check_current_state(question_state::$todo);
 526          $this->check_current_mark(null);
 527          $this->check_current_output(
 528              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 529              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 530              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 531              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 532              $this->get_does_not_contain_submit_button_expectation(),
 533              $this->get_contains_try_again_button_expectation(true),
 534              $this->get_does_not_contain_correctness_expectation(),
 535              $this->get_contains_hint_expectation('This is the second hint'),
 536              $this->get_contains_num_parts_correct(2),
 537              $this->get_contains_standard_partiallycorrect_combined_feedback_expectation(),
 538              $this->get_contains_hidden_expectation(
 539                  $this->quba->get_field_prefix($this->slot) . 'p1', 1),
 540              $this->get_contains_hidden_expectation(
 541                  $this->quba->get_field_prefix($this->slot) . 'p2', 1),
 542              $this->get_contains_hidden_expectation(
 543                  $this->quba->get_field_prefix($this->slot) . 'p3', 2),
 544              $this->get_contains_hidden_expectation(
 545                  $this->quba->get_field_prefix($this->slot) . 'p4', 2));
 546  
 547          // Do try again.
 548          $this->process_submission(
 549              array('p1' => '1', 'p2' => '', 'p3' => '', 'p4' => '2', '-tryagain' => 1));
 550  
 551          // Verify.
 552          $this->check_current_state(question_state::$todo);
 553          $this->check_current_mark(null);
 554          $this->check_current_output(
 555              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 556              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 557              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 558              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 559              $this->get_contains_hidden_expectation(
 560                  $this->quba->get_field_prefix($this->slot) . 'p1', 1),
 561              $this->get_contains_hidden_expectation(
 562                  $this->quba->get_field_prefix($this->slot) . 'p2', 0),
 563              $this->get_contains_hidden_expectation(
 564                  $this->quba->get_field_prefix($this->slot) . 'p3', 0),
 565              $this->get_contains_hidden_expectation(
 566                  $this->quba->get_field_prefix($this->slot) . 'p4', 2),
 567              $this->get_contains_submit_button_expectation(true),
 568              $this->get_does_not_contain_try_again_button_expectation(),
 569              $this->get_does_not_contain_correctness_expectation(),
 570              $this->get_does_not_contain_feedback_expectation(),
 571              $this->get_tries_remaining_expectation(1),
 572              $this->get_no_hint_visible_expectation());
 573  
 574          // Submit the right answer.
 575          $this->process_submission(
 576              array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2', '-submit' => 1));
 577  
 578          // Verify.
 579          $this->check_current_state(question_state::$gradedright);
 580          $this->check_current_mark(7);
 581          $this->check_current_output(
 582              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 583              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 584              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 585              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 586              $this->get_contains_hidden_expectation(
 587                  $this->quba->get_field_prefix($this->slot) . 'p1', 1),
 588              $this->get_contains_hidden_expectation(
 589                  $this->quba->get_field_prefix($this->slot) . 'p2', 2),
 590              $this->get_contains_hidden_expectation(
 591                  $this->quba->get_field_prefix($this->slot) . 'p3', 1),
 592              $this->get_contains_hidden_expectation(
 593                  $this->quba->get_field_prefix($this->slot) . 'p4', 2),
 594              $this->get_does_not_contain_submit_button_expectation(),
 595              $this->get_does_not_contain_try_again_button_expectation(),
 596              $this->get_contains_correct_expectation(),
 597              $this->get_no_hint_visible_expectation(),
 598              $this->get_does_not_contain_num_parts_correct(),
 599              $this->get_contains_standard_correct_combined_feedback_expectation());
 600      }
 601  
 602      public function test_interactive_correct_no_submit() {
 603  
 604          // Create a drag-and-drop question.
 605          $dd = test_question_maker::make_question('ddimageortext');
 606          $dd->hints = array(
 607              new question_hint_with_parts(23, 'This is the first hint.',
 608                  FORMAT_MOODLE, false, false),
 609              new question_hint_with_parts(24, 'This is the second hint.',
 610                  FORMAT_MOODLE, true, true),
 611          );
 612          $dd->shufflechoices = false;
 613          $this->start_attempt_at_question($dd, 'interactive', 3);
 614  
 615          // Check the initial state.
 616          $this->check_current_state(question_state::$todo);
 617          $this->check_current_mark(null);
 618          $this->check_current_output(
 619              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 620              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 621              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 622              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 623              $this->get_contains_hidden_expectation(
 624                  $this->quba->get_field_prefix($this->slot) . 'p1'),
 625              $this->get_contains_hidden_expectation(
 626                  $this->quba->get_field_prefix($this->slot) . 'p2'),
 627              $this->get_contains_hidden_expectation(
 628                  $this->quba->get_field_prefix($this->slot) . 'p3'),
 629              $this->get_contains_hidden_expectation(
 630                  $this->quba->get_field_prefix($this->slot) . 'p4'),
 631              $this->get_contains_submit_button_expectation(true),
 632              $this->get_does_not_contain_feedback_expectation(),
 633              $this->get_tries_remaining_expectation(3),
 634              $this->get_no_hint_visible_expectation());
 635  
 636          // Save the right answer.
 637          $this->process_submission(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'));
 638  
 639          // Finish the attempt without clicking check.
 640          $this->quba->finish_all_questions();
 641  
 642          // Verify.
 643          $this->check_current_state(question_state::$gradedright);
 644          $this->check_current_mark(3);
 645          $this->check_current_output(
 646              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 647              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 648              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 649              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 650              $this->get_does_not_contain_submit_button_expectation(),
 651              $this->get_contains_correct_expectation(),
 652              $this->get_no_hint_visible_expectation());
 653  
 654          // Check regrading does not mess anything up.
 655          $this->quba->regrade_all_questions();
 656  
 657          // Verify.
 658          $this->check_current_state(question_state::$gradedright);
 659          $this->check_current_mark(3);
 660      }
 661  
 662      public function test_interactive_partial_no_submit() {
 663  
 664          // Create a drag-and-drop question.
 665          $dd = test_question_maker::make_question('ddimageortext');
 666          $dd->hints = array(
 667              new question_hint_with_parts(23, 'This is the first hint.',
 668                  FORMAT_MOODLE, false, false),
 669              new question_hint_with_parts(24, 'This is the second hint.',
 670                  FORMAT_MOODLE, true, true),
 671          );
 672          $dd->shufflechoices = false;
 673          $this->start_attempt_at_question($dd, 'interactive', 4);
 674  
 675          // Check the initial state.
 676          $this->check_current_state(question_state::$todo);
 677          $this->check_current_mark(null);
 678  
 679          $this->check_current_output(
 680              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 681              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 682              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 683              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 684              $this->get_contains_hidden_expectation(
 685                  $this->quba->get_field_prefix($this->slot) . 'p1'),
 686              $this->get_contains_hidden_expectation(
 687                  $this->quba->get_field_prefix($this->slot) . 'p2'),
 688              $this->get_contains_hidden_expectation(
 689                  $this->quba->get_field_prefix($this->slot) . 'p3'),
 690              $this->get_contains_hidden_expectation(
 691                  $this->quba->get_field_prefix($this->slot) . 'p4'),
 692              $this->get_contains_submit_button_expectation(true),
 693              $this->get_does_not_contain_feedback_expectation(),
 694              $this->get_tries_remaining_expectation(3),
 695              $this->get_no_hint_visible_expectation());
 696  
 697          // Save the a partially right answer.
 698          $this->process_submission(array('p1' => '1', 'p2' => '1', 'p3' => '2', 'p4' => '1'));
 699  
 700          // Finish the attempt without clicking check.
 701          $this->quba->finish_all_questions();
 702  
 703          // Verify.
 704          $this->check_current_state(question_state::$gradedpartial);
 705          $this->check_current_mark(1);
 706  
 707          $this->check_current_output(
 708              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 709              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 710              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 711              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 712              $this->get_does_not_contain_submit_button_expectation(),
 713              $this->get_contains_partcorrect_expectation(),
 714              $this->get_no_hint_visible_expectation());
 715  
 716          // Check regrading does not mess anything up.
 717          $this->quba->regrade_all_questions();
 718  
 719          // Verify.
 720          $this->check_current_state(question_state::$gradedpartial);
 721          $this->check_current_mark(1);
 722      }
 723  
 724      public function test_interactive_no_right_clears() {
 725  
 726          // Create a drag-and-drop question.
 727          $dd = test_question_maker::make_question('ddimageortext');
 728          $dd->hints = array(
 729              new question_hint_with_parts(23, 'This is the first hint.', FORMAT_MOODLE, false, true),
 730              new question_hint_with_parts(24, 'This is the second hint.', FORMAT_MOODLE, true, true),
 731          );
 732          $dd->shufflechoices = false;
 733          $this->start_attempt_at_question($dd, 'interactive', 3);
 734  
 735          // Check the initial state.
 736          $this->check_current_state(question_state::$todo);
 737          $this->check_current_mark(null);
 738  
 739          $this->check_current_output(
 740              $this->get_contains_marked_out_of_summary(),
 741              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 742              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 743              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 744              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 745              $this->get_contains_hidden_expectation(
 746                  $this->quba->get_field_prefix($this->slot) . 'p1'),
 747              $this->get_contains_hidden_expectation(
 748                  $this->quba->get_field_prefix($this->slot) . 'p2'),
 749              $this->get_contains_hidden_expectation(
 750                  $this->quba->get_field_prefix($this->slot) . 'p3'),
 751              $this->get_contains_hidden_expectation(
 752                  $this->quba->get_field_prefix($this->slot) . 'p4'),
 753              $this->get_contains_submit_button_expectation(true),
 754              $this->get_does_not_contain_feedback_expectation(),
 755              $this->get_tries_remaining_expectation(3),
 756              $this->get_no_hint_visible_expectation());
 757  
 758          // Save the a completely wrong answer.
 759          $this->process_submission(
 760              array('p1' => '2', 'p2' => '1', 'p3' => '2', 'p4' => '1', '-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_drag_image_home_expectation(1, 1, 1),
 768              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 769              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 770              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 771              $this->get_does_not_contain_submit_button_expectation(),
 772              $this->get_contains_hint_expectation('This is the first hint'));
 773  
 774          // Do try again.
 775          $this->process_submission(
 776              array('p1' => '', 'p2' => '', 'p3' => '', 'p4' => '', '-tryagain' => 1));
 777  
 778          // Check that all the wrong answers have been cleared.
 779          $this->check_current_state(question_state::$todo);
 780          $this->check_current_mark(null);
 781          $this->check_current_output(
 782              $this->get_contains_marked_out_of_summary(),
 783              $this->get_contains_drag_image_home_expectation(1, 1, 1),
 784              $this->get_contains_drag_image_home_expectation(2, 2, 1),
 785              $this->get_contains_drag_image_home_expectation(3, 1, 2),
 786              $this->get_contains_drag_image_home_expectation(4, 2, 2),
 787              $this->get_contains_hidden_expectation(
 788                  $this->quba->get_field_prefix($this->slot) . 'p1', 0),
 789              $this->get_contains_hidden_expectation(
 790                  $this->quba->get_field_prefix($this->slot) . 'p2', 0),
 791              $this->get_contains_hidden_expectation(
 792                  $this->quba->get_field_prefix($this->slot) . 'p3', 0),
 793              $this->get_contains_hidden_expectation(
 794                  $this->quba->get_field_prefix($this->slot) . 'p4', 0),
 795              $this->get_contains_submit_button_expectation(true),
 796              $this->get_does_not_contain_feedback_expectation(),
 797              $this->get_tries_remaining_expectation(2),
 798              $this->get_no_hint_visible_expectation());
 799      }
 800  
 801      public function test_display_of_right_answer_when_shuffled() {
 802  
 803          // Create a drag-and-drop question.
 804          $dd = test_question_maker::make_question('ddimageortext');
 805          $this->start_attempt_at_question($dd, 'deferredfeedback', 3);
 806  
 807          // Check the initial state.
 808          $this->check_current_state(question_state::$todo);
 809          $this->check_current_mark(null);
 810  
 811          $this->check_current_output(
 812              $this->get_contains_hidden_expectation(
 813                  $this->quba->get_field_prefix($this->slot) . 'p1'),
 814              $this->get_contains_hidden_expectation(
 815                  $this->quba->get_field_prefix($this->slot) . 'p2'),
 816              $this->get_contains_hidden_expectation(
 817                  $this->quba->get_field_prefix($this->slot) . 'p3'),
 818              $this->get_contains_hidden_expectation(
 819                  $this->quba->get_field_prefix($this->slot) . 'p4'),
 820              $this->get_does_not_contain_feedback_expectation());
 821  
 822          // Save a partial answer.
 823          $this->process_submission($dd->get_correct_response());
 824  
 825          // Verify.
 826          $this->check_current_state(question_state::$complete);
 827          $this->check_current_mark(null);
 828          $this->check_current_output(
 829              $this->get_contains_hidden_expectation(
 830                  $this->quba->get_field_prefix($this->slot) . 'p1',
 831                  $dd->get_right_choice_for(1)),
 832              $this->get_contains_hidden_expectation(
 833                  $this->quba->get_field_prefix($this->slot) . 'p2',
 834                  $dd->get_right_choice_for(2)),
 835              $this->get_contains_hidden_expectation(
 836                  $this->quba->get_field_prefix($this->slot) . 'p3',
 837                  $dd->get_right_choice_for(3)),
 838              $this->get_contains_hidden_expectation(
 839                  $this->quba->get_field_prefix($this->slot) . 'p4',
 840                  $dd->get_right_choice_for(4)),
 841              $this->get_does_not_contain_correctness_expectation(),
 842              $this->get_does_not_contain_feedback_expectation());
 843  
 844          // Finish the attempt.
 845          $this->quba->finish_all_questions();
 846  
 847          // Verify.
 848          $this->displayoptions->rightanswer = question_display_options::VISIBLE;
 849          $this->assertEquals('Drop zone 1 -> {1. quick} '.
 850                  'Drop zone 2 -> {2. fox} '.
 851                  'Drop zone 3 -> {3. lazy} '.
 852                  'Drop zone 4 -> {4. dog}',
 853              $dd->get_right_answer_summary());
 854          $this->check_current_state(question_state::$gradedright);
 855          $this->check_current_mark(3);
 856      }
 857  
 858      public function test_mixed_lang_rendering() {
 859  
 860          // Create a mixe drag-and-drop question.
 861          $dd = test_question_maker::make_question('ddimageortext', 'mixedlang');
 862          $dd->shufflechoices = false;
 863          $this->start_attempt_at_question($dd, 'interactive', 1);
 864  
 865          // Check the initial state.
 866          $this->check_current_state(question_state::$todo);
 867          $this->check_current_mark(null);
 868          $this->check_current_output(
 869                  new question_pattern_expectation('~<div class="group1 draghome choice1"><span lang="fr">la</span></div>~'),
 870                  new question_pattern_expectation('~<div class="group1 draghome choice2"><span lang="fr">ma</span></div>~')
 871          );
 872      }
 873  }