Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]
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 * This file contains tests that walks a question through the interactive 19 * behaviour. 20 * 21 * @package qbehaviour_interactive 22 * @copyright 2009 The Open University 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 global $CFG; 30 require_once (__DIR__ . '/../../../engine/lib.php'); 31 require_once (__DIR__ . '/../../../engine/tests/helpers.php'); 32 33 34 /** 35 * Unit tests for the interactive behaviour. 36 * 37 * @copyright 2009 The Open University 38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 */ 40 class qbehaviour_interactive_walkthrough_test extends qbehaviour_walkthrough_test_base { 41 42 public function test_interactive_feedback_multichoice_right() { 43 44 // Create a multichoice single question. 45 $mc = test_question_maker::make_a_multichoice_single_question(); 46 $mc->hints = array( 47 new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, false, false), 48 new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), 49 ); 50 $this->start_attempt_at_question($mc, 'interactive'); 51 52 $rightindex = $this->get_mc_right_answer_index($mc); 53 $wrongindex = ($rightindex + 1) % 3; 54 55 // Check the initial state. 56 $this->check_current_state(question_state::$todo); 57 $this->check_current_mark(null); 58 $this->check_current_output( 59 $this->get_contains_marked_out_of_summary(), 60 $this->get_contains_question_text_expectation($mc), 61 $this->get_contains_mc_radio_expectation(0, true, false), 62 $this->get_contains_mc_radio_expectation(1, true, false), 63 $this->get_contains_mc_radio_expectation(2, true, false), 64 $this->get_contains_submit_button_expectation(true), 65 $this->get_does_not_contain_feedback_expectation(), 66 $this->get_tries_remaining_expectation(3), 67 $this->get_no_hint_visible_expectation()); 68 69 // Save the wrong answer. 70 $this->process_submission(array('answer' => $wrongindex)); 71 72 // Verify. 73 $this->check_current_state(question_state::$todo); 74 $this->check_current_mark(null); 75 $this->check_current_output( 76 $this->get_contains_marked_out_of_summary(), 77 $this->get_contains_mc_radio_expectation($wrongindex, true, true), 78 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false), 79 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false), 80 $this->get_contains_submit_button_expectation(true), 81 $this->get_does_not_contain_correctness_expectation(), 82 $this->get_does_not_contain_feedback_expectation(), 83 $this->get_tries_remaining_expectation(3), 84 $this->get_no_hint_visible_expectation()); 85 86 // Submit the wrong answer. 87 $this->process_submission(array('answer' => $wrongindex, '-submit' => 1)); 88 89 // Verify. 90 $this->check_current_state(question_state::$todo); 91 $this->check_current_mark(null); 92 $this->check_current_output( 93 $this->get_contains_marked_out_of_summary(), 94 $this->get_contains_mc_radio_expectation($wrongindex, false, true), 95 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false), 96 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false), 97 $this->get_does_not_contain_submit_button_expectation(), 98 $this->get_contains_try_again_button_expectation(true), 99 $this->get_does_not_contain_correctness_expectation(), 100 new question_pattern_expectation('/Tries remaining: 2/'), 101 $this->get_contains_hint_expectation('This is the first hint')); 102 103 // Check that, if we review in this state, the try again button is disabled. 104 $displayoptions = new question_display_options(); 105 $displayoptions->readonly = true; 106 $html = $this->quba->render_question($this->slot, $displayoptions); 107 $this->assert($this->get_contains_try_again_button_expectation(false), $html); 108 109 // Do try again. 110 $this->process_submission(array('-tryagain' => 1)); 111 112 // Verify. 113 $this->check_current_state(question_state::$todo); 114 $this->check_current_mark(null); 115 $this->check_current_output( 116 $this->get_contains_marked_out_of_summary(), 117 $this->get_contains_mc_radio_expectation($wrongindex, true, true), 118 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false), 119 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false), 120 $this->get_contains_submit_button_expectation(true), 121 $this->get_does_not_contain_correctness_expectation(), 122 $this->get_does_not_contain_feedback_expectation(), 123 $this->get_tries_remaining_expectation(2), 124 $this->get_no_hint_visible_expectation()); 125 126 // Submit the right answer. 127 $this->process_submission(array('answer' => $rightindex, '-submit' => 1)); 128 129 // Verify. 130 $this->check_current_state(question_state::$gradedright); 131 $this->check_current_mark(0.6666667); 132 $this->check_current_output( 133 $this->get_contains_mark_summary(0.6666667), 134 $this->get_contains_mc_radio_expectation($rightindex, false, true), 135 $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false), 136 $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false), 137 $this->get_does_not_contain_submit_button_expectation(), 138 $this->get_contains_correct_expectation(), 139 $this->get_no_hint_visible_expectation()); 140 141 // Finish the attempt - should not need to add a new state. 142 $numsteps = $this->get_step_count(); 143 $this->quba->finish_all_questions(); 144 145 // Verify. 146 $this->assertEquals($numsteps, $this->get_step_count()); 147 $this->check_current_state(question_state::$gradedright); 148 $this->check_current_mark(0.6666667); 149 $this->check_current_output( 150 $this->get_contains_mark_summary(0.6666667), 151 $this->get_contains_mc_radio_expectation($rightindex, false, true), 152 $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false), 153 $this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, false, false), 154 $this->get_contains_correct_expectation(), 155 $this->get_no_hint_visible_expectation()); 156 157 // Process a manual comment. 158 $this->manual_grade('Not good enough!', 0.5, FORMAT_HTML); 159 160 // Verify. 161 $this->check_current_state(question_state::$mangrpartial); 162 $this->check_current_mark(0.5); 163 $this->check_current_output( 164 $this->get_contains_mark_summary(0.5), 165 $this->get_contains_partcorrect_expectation(), 166 new question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/')); 167 168 // Check regrading does not mess anything up. 169 $this->quba->regrade_all_questions(); 170 171 // Verify. 172 $this->check_current_state(question_state::$mangrpartial); 173 $this->check_current_mark(0.5); 174 $this->check_current_output( 175 $this->get_contains_mark_summary(0.5), 176 $this->get_contains_partcorrect_expectation()); 177 178 $autogradedstep = $this->get_step($this->get_step_count() - 2); 179 $this->assertEqualsWithDelta($autogradedstep->get_fraction(), 0.6666667, 0.0000001); 180 } 181 182 public function test_interactive_finish_when_try_again_showing() { 183 184 // Create a multichoice single question. 185 $mc = test_question_maker::make_a_multichoice_single_question(); 186 $mc->showstandardinstruction = true; 187 $mc->hints = array( 188 new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, false, false), 189 ); 190 $this->start_attempt_at_question($mc, 'interactive'); 191 192 $rightindex = $this->get_mc_right_answer_index($mc); 193 $wrongindex = ($rightindex + 1) % 3; 194 195 // Check the initial state. 196 $this->check_current_state(question_state::$todo); 197 $this->check_current_mark(null); 198 $this->check_current_output( 199 $this->get_contains_marked_out_of_summary(), 200 $this->get_contains_question_text_expectation($mc), 201 $this->get_contains_mc_radio_expectation(0, true, false), 202 $this->get_contains_mc_radio_expectation(1, true, false), 203 $this->get_contains_mc_radio_expectation(2, true, false), 204 $this->get_contains_submit_button_expectation(true), 205 $this->get_does_not_contain_feedback_expectation(), 206 $this->get_tries_remaining_expectation(2), 207 $this->get_no_hint_visible_expectation(), 208 new question_pattern_expectation('/' . 209 preg_quote(get_string('selectone', 'qtype_multichoice'), '/') . '/')); 210 211 // Submit the wrong answer. 212 $this->process_submission(array('answer' => $wrongindex, '-submit' => 1)); 213 214 // Verify. 215 $this->check_current_state(question_state::$todo); 216 $this->check_current_mark(null); 217 $this->check_current_output( 218 $this->get_contains_marked_out_of_summary(), 219 $this->get_contains_mc_radio_expectation($wrongindex, false, true), 220 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false), 221 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false), 222 $this->get_does_not_contain_submit_button_expectation(), 223 $this->get_contains_try_again_button_expectation(true), 224 $this->get_does_not_contain_correctness_expectation(), 225 new question_pattern_expectation('/Tries remaining: 1/'), 226 $this->get_contains_hint_expectation('This is the first hint')); 227 228 // Finish the attempt. 229 $this->quba->finish_all_questions(); 230 231 // Verify. 232 $this->check_current_state(question_state::$gradedwrong); 233 $this->check_current_mark(0); 234 $this->check_current_output( 235 $this->get_contains_mark_summary(0), 236 $this->get_contains_mc_radio_expectation($wrongindex, false, true), 237 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false), 238 $this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, false, false), 239 $this->get_contains_incorrect_expectation(), 240 $this->get_no_hint_visible_expectation()); 241 } 242 243 public function test_interactive_shortanswer_try_to_submit_blank() { 244 245 // Create a short answer question. 246 $sa = test_question_maker::make_question('shortanswer'); 247 $sa->hints = array( 248 new question_hint(0, 'This is the first hint.', FORMAT_HTML), 249 new question_hint(0, 'This is the second hint.', FORMAT_HTML), 250 ); 251 $this->start_attempt_at_question($sa, 'interactive'); 252 253 // Check the initial state. 254 $this->check_current_state(question_state::$todo); 255 $this->check_current_mark(null); 256 $this->check_current_output( 257 $this->get_contains_marked_out_of_summary(), 258 $this->get_contains_submit_button_expectation(true), 259 $this->get_does_not_contain_feedback_expectation(), 260 $this->get_does_not_contain_validation_error_expectation(), 261 $this->get_does_not_contain_try_again_button_expectation(), 262 $this->get_no_hint_visible_expectation()); 263 264 // Submit blank. 265 $this->process_submission(array('-submit' => 1, 'answer' => '')); 266 267 // Verify. 268 $this->check_current_state(question_state::$invalid); 269 $this->check_current_mark(null); 270 $this->check_current_output( 271 $this->get_contains_marked_out_of_summary(), 272 $this->get_contains_submit_button_expectation(true), 273 $this->get_does_not_contain_feedback_expectation(), 274 $this->get_contains_validation_error_expectation(), 275 $this->get_does_not_contain_try_again_button_expectation(), 276 $this->get_no_hint_visible_expectation()); 277 278 // Now get it wrong. 279 $this->process_submission(array('-submit' => 1, 'answer' => 'newt')); 280 281 // Verify. 282 $this->check_current_state(question_state::$todo); 283 $this->check_current_mark(null); 284 $this->check_current_output( 285 $this->get_contains_marked_out_of_summary(), 286 $this->get_does_not_contain_submit_button_expectation(), 287 $this->get_does_not_contain_validation_error_expectation(), 288 $this->get_contains_try_again_button_expectation(true), 289 new question_pattern_expectation('/Tries remaining: 2/'), 290 $this->get_contains_hint_expectation('This is the first hint')); 291 $this->assertEquals('newt', 292 $this->quba->get_response_summary($this->slot)); 293 294 // Try again. 295 $this->process_submission(array('-tryagain' => 1)); 296 297 // Verify. 298 $this->check_current_state(question_state::$todo); 299 $this->check_current_mark(null); 300 $this->check_current_output( 301 $this->get_contains_marked_out_of_summary(), 302 $this->get_contains_submit_button_expectation(true), 303 $this->get_does_not_contain_feedback_expectation(), 304 $this->get_does_not_contain_validation_error_expectation(), 305 $this->get_does_not_contain_try_again_button_expectation(), 306 $this->get_no_hint_visible_expectation()); 307 308 // Now submit blank again. 309 $this->process_submission(array('-submit' => 1, 'answer' => '')); 310 311 // Verify. 312 $this->check_current_state(question_state::$invalid); 313 $this->check_current_mark(null); 314 $this->check_current_output( 315 $this->get_contains_marked_out_of_summary(), 316 $this->get_contains_submit_button_expectation(true), 317 $this->get_does_not_contain_feedback_expectation(), 318 $this->get_contains_validation_error_expectation(), 319 $this->get_does_not_contain_try_again_button_expectation(), 320 $this->get_no_hint_visible_expectation()); 321 322 // Now get it right. 323 $this->process_submission(array('-submit' => 1, 'answer' => 'frog')); 324 325 // Verify. 326 $this->check_current_state(question_state::$gradedright); 327 $this->check_current_mark(0.6666667); 328 $this->check_current_output( 329 $this->get_contains_mark_summary(0.6666667), 330 $this->get_does_not_contain_submit_button_expectation(), 331 $this->get_contains_correct_expectation(), 332 $this->get_does_not_contain_validation_error_expectation(), 333 $this->get_no_hint_visible_expectation()); 334 $this->assertEquals('frog', 335 $this->quba->get_response_summary($this->slot)); 336 } 337 338 public function test_interactive_feedback_multichoice_multiple_reset() { 339 340 // Create a multichoice multiple question. 341 $mc = test_question_maker::make_a_multichoice_multi_question(); 342 $mc->showstandardinstruction = true; 343 $mc->hints = array( 344 new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true), 345 new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), 346 ); 347 $this->start_attempt_at_question($mc, 'interactive', 2); 348 349 $right = array_keys($mc->get_correct_response()); 350 $wrong = array_diff(array('choice0', 'choice1', 'choice2', 'choice3'), $right); 351 $wrong = array_values(array_diff( 352 array('choice0', 'choice1', 'choice2', 'choice3'), $right)); 353 354 // Check the initial state. 355 $this->check_current_state(question_state::$todo); 356 $this->check_current_mark(null); 357 $this->check_current_output( 358 $this->get_contains_marked_out_of_summary(), 359 $this->get_contains_question_text_expectation($mc), 360 $this->get_contains_mc_checkbox_expectation('choice0', true, false), 361 $this->get_contains_mc_checkbox_expectation('choice1', true, false), 362 $this->get_contains_mc_checkbox_expectation('choice2', true, false), 363 $this->get_contains_mc_checkbox_expectation('choice3', true, false), 364 $this->get_contains_submit_button_expectation(true), 365 $this->get_does_not_contain_feedback_expectation(), 366 $this->get_does_not_contain_num_parts_correct(), 367 $this->get_tries_remaining_expectation(3), 368 $this->get_no_hint_visible_expectation(), 369 new question_pattern_expectation('/' . 370 preg_quote(get_string('selectmulti', 'qtype_multichoice'), '/') . '/')); 371 372 // Submit an answer with one right, and one wrong. 373 $this->process_submission(array($right[0] => 1, $wrong[0] => 1, '-submit' => 1)); 374 375 // Verify. 376 $this->check_current_state(question_state::$todo); 377 $this->check_current_mark(null); 378 $this->check_current_output( 379 $this->get_contains_marked_out_of_summary(), 380 $this->get_contains_mc_checkbox_expectation($right[0], false, true), 381 $this->get_contains_mc_checkbox_expectation($right[1], false, false), 382 $this->get_contains_mc_checkbox_expectation($wrong[0], false, true), 383 $this->get_contains_mc_checkbox_expectation($wrong[1], false, false), 384 $this->get_does_not_contain_submit_button_expectation(), 385 $this->get_contains_try_again_button_expectation(true), 386 $this->get_does_not_contain_correctness_expectation(), 387 new question_pattern_expectation('/Tries remaining: 2/'), 388 $this->get_contains_hint_expectation('This is the first hint'), 389 $this->get_contains_num_parts_correct(1), 390 $this->get_contains_standard_incorrect_combined_feedback_expectation(), 391 $this->get_contains_hidden_expectation( 392 $this->quba->get_field_prefix($this->slot) . $right[0], '1'), 393 $this->get_does_not_contain_hidden_expectation( 394 $this->quba->get_field_prefix($this->slot) . $right[1]), 395 $this->get_contains_hidden_expectation( 396 $this->quba->get_field_prefix($this->slot) . $wrong[0], '0'), 397 $this->get_does_not_contain_hidden_expectation( 398 $this->quba->get_field_prefix($this->slot) . $wrong[1])); 399 400 // Do try again. 401 $this->process_submission(array($right[0] => 1, '-tryagain' => 1)); 402 403 // Verify. 404 $this->check_current_state(question_state::$todo); 405 $this->check_current_mark(null); 406 $this->check_current_output( 407 $this->get_contains_marked_out_of_summary(), 408 $this->get_contains_mc_checkbox_expectation($right[0], true, true), 409 $this->get_contains_mc_checkbox_expectation($right[1], true, false), 410 $this->get_contains_mc_checkbox_expectation($wrong[0], true, false), 411 $this->get_contains_mc_checkbox_expectation($wrong[1], true, false), 412 $this->get_contains_submit_button_expectation(true), 413 $this->get_does_not_contain_correctness_expectation(), 414 $this->get_does_not_contain_feedback_expectation(), 415 $this->get_tries_remaining_expectation(2), 416 $this->get_no_hint_visible_expectation()); 417 } 418 419 public function test_interactive_regrade_changing_num_tries_leaving_open() { 420 // Create a multichoice multiple question. 421 $q = test_question_maker::make_question('shortanswer'); 422 $q->hints = array( 423 new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true), 424 new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), 425 ); 426 $this->start_attempt_at_question($q, 'interactive', 3); 427 428 // Check the initial state. 429 $this->check_current_state(question_state::$todo); 430 $this->check_current_mark(null); 431 $this->check_current_output( 432 $this->get_tries_remaining_expectation(3)); 433 434 // Submit the right answer. 435 $this->process_submission(array('answer' => 'frog', '-submit' => 1)); 436 437 // Verify. 438 $this->check_current_state(question_state::$gradedright); 439 $this->check_current_mark(3); 440 441 // Now change the quiestion so that answer is only partially right, and regrade. 442 $q->answers[13]->fraction = 0.6666667; 443 $q->answers[14]->fraction = 1; 444 445 $this->quba->regrade_all_questions(false); 446 447 // Verify. 448 $this->check_current_state(question_state::$todo); 449 $this->check_current_mark(null); 450 } 451 452 public function test_interactive_regrade_changing_num_tries_finished() { 453 // Create a multichoice multiple question. 454 $q = test_question_maker::make_question('shortanswer'); 455 $q->hints = array( 456 new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true), 457 new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), 458 ); 459 $this->start_attempt_at_question($q, 'interactive', 3); 460 461 // Check the initial state. 462 $this->check_current_state(question_state::$todo); 463 $this->check_current_mark(null); 464 $this->check_current_output( 465 $this->get_tries_remaining_expectation(3)); 466 467 // Submit the right answer. 468 $this->process_submission(array('answer' => 'frog', '-submit' => 1)); 469 470 // Verify. 471 $this->check_current_state(question_state::$gradedright); 472 $this->check_current_mark(3); 473 474 // Now change the quiestion so that answer is only partially right, and regrade. 475 $q->answers[13]->fraction = 0.6666667; 476 $q->answers[14]->fraction = 1; 477 478 $this->quba->regrade_all_questions(true); 479 480 // Verify. 481 $this->check_current_state(question_state::$gradedpartial); 482 // TODO I don't think 1 is the right fraction here. However, it is what 483 // you get attempting a question like this without regrading being involved, 484 // and I am currently interested in testing regrading here. 485 $this->check_current_mark(1); 486 } 487 488 public function test_review_of_interactive_questions_before_finished() { 489 // Create a multichoice multiple question. 490 $q = test_question_maker::make_question('shortanswer'); 491 $q->hints = array( 492 new question_hint_with_parts(0, 'This is the first hint.', FORMAT_HTML, true, true), 493 new question_hint_with_parts(0, 'This is the second hint.', FORMAT_HTML, true, true), 494 ); 495 $this->start_attempt_at_question($q, 'interactive', 3); 496 497 // Check the initial state. 498 $this->check_current_state(question_state::$todo); 499 $this->check_current_mark(null); 500 $this->check_current_output( 501 $this->get_contains_submit_button_expectation(true), 502 $this->get_does_not_contain_feedback_expectation(), 503 $this->get_tries_remaining_expectation(3), 504 $this->get_does_not_contain_try_again_button_expectation()); 505 506 // Now check what the teacher sees when they review the question. 507 $this->displayoptions->readonly = true; 508 $this->check_current_output( 509 $this->get_contains_submit_button_expectation(false), 510 $this->get_does_not_contain_feedback_expectation(), 511 $this->get_tries_remaining_expectation(3), 512 $this->get_does_not_contain_try_again_button_expectation()); 513 $this->displayoptions->readonly = false; 514 515 // Submit a wrong answer. 516 $this->process_submission(array('answer' => 'cat', '-submit' => 1)); 517 518 // Check the Try again button now shows up correctly. 519 $this->check_current_state(question_state::$todo); 520 $this->check_current_mark(null); 521 $this->check_current_output( 522 $this->get_does_not_contain_submit_button_expectation(), 523 $this->get_contains_hint_expectation('This is the first hint.'), 524 $this->get_tries_remaining_expectation(2), 525 $this->get_contains_try_again_button_expectation(true)); 526 527 // And check that a disabled Try again button shows up when the question is reviewed. 528 $this->displayoptions->readonly = true; 529 $this->check_current_output( 530 $this->get_does_not_contain_submit_button_expectation(), 531 $this->get_contains_hint_expectation('This is the first hint.'), 532 $this->get_tries_remaining_expectation(2), 533 $this->get_contains_try_again_button_expectation(false)); 534 } 535 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body