Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

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

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

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