See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 310] [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 * Choice module library functions tests 19 * 20 * @package mod_choice 21 * @category test 22 * @copyright 2015 Juan Leyva <juan@moodle.com> 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 * @since Moodle 3.0 25 */ 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 global $CFG; 30 31 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 32 require_once($CFG->dirroot . '/mod/choice/lib.php'); 33 34 /** 35 * Choice module library functions tests 36 * 37 * @package mod_choice 38 * @category test 39 * @copyright 2015 Juan Leyva <juan@moodle.com> 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 * @since Moodle 3.0 42 */ 43 class mod_choice_lib_testcase extends externallib_advanced_testcase { 44 45 /** 46 * Test choice_view 47 * @return void 48 */ 49 public function test_choice_view() { 50 global $CFG; 51 52 $this->resetAfterTest(); 53 54 $this->setAdminUser(); 55 // Setup test data. 56 $course = $this->getDataGenerator()->create_course(); 57 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id)); 58 $context = context_module::instance($choice->cmid); 59 $cm = get_coursemodule_from_instance('choice', $choice->id); 60 61 // Trigger and capture the event. 62 $sink = $this->redirectEvents(); 63 64 choice_view($choice, $course, $cm, $context); 65 66 $events = $sink->get_events(); 67 $this->assertCount(1, $events); 68 $event = array_shift($events); 69 70 // Checking that the event contains the expected values. 71 $this->assertInstanceOf('\mod_choice\event\course_module_viewed', $event); 72 $this->assertEquals($context, $event->get_context()); 73 $url = new \moodle_url('/mod/choice/view.php', array('id' => $cm->id)); 74 $this->assertEquals($url, $event->get_url()); 75 $this->assertEventContextNotUsed($event); 76 $this->assertNotEmpty($event->get_name()); 77 } 78 79 /** 80 * Test choice_can_view_results 81 * @return void 82 */ 83 public function test_choice_can_view_results() { 84 global $DB, $USER; 85 86 $this->resetAfterTest(); 87 88 $this->setAdminUser(); 89 // Setup test data. 90 $course = $this->getDataGenerator()->create_course(); 91 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id)); 92 $context = context_module::instance($choice->cmid); 93 $cm = get_coursemodule_from_instance('choice', $choice->id); 94 95 // Default values are false, user cannot view results. 96 $canview = choice_can_view_results($choice); 97 $this->assertFalse($canview); 98 99 // Show results forced. 100 $choice->showresults = CHOICE_SHOWRESULTS_ALWAYS; 101 $DB->update_record('choice', $choice); 102 $canview = choice_can_view_results($choice); 103 $this->assertTrue($canview); 104 105 // Add a time restriction (choice not open yet). 106 $choice->timeopen = time() + YEARSECS; 107 $DB->update_record('choice', $choice); 108 $canview = choice_can_view_results($choice); 109 $this->assertFalse($canview); 110 111 // Show results after closing. 112 $choice->timeopen = 0; 113 $choice->showresults = CHOICE_SHOWRESULTS_AFTER_CLOSE; 114 $DB->update_record('choice', $choice); 115 $canview = choice_can_view_results($choice); 116 $this->assertFalse($canview); 117 118 $choice->timeclose = time() - HOURSECS; 119 $DB->update_record('choice', $choice); 120 $canview = choice_can_view_results($choice); 121 $this->assertTrue($canview); 122 123 // Show results after answering. 124 $choice->timeclose = 0; 125 $choice->showresults = CHOICE_SHOWRESULTS_AFTER_ANSWER; 126 $DB->update_record('choice', $choice); 127 $canview = choice_can_view_results($choice); 128 $this->assertFalse($canview); 129 130 // Get the first option. 131 $choicewithoptions = choice_get_choice($choice->id); 132 $optionids = array_keys($choicewithoptions->option); 133 134 choice_user_submit_response($optionids[0], $choice, $USER->id, $course, $cm); 135 136 $canview = choice_can_view_results($choice); 137 $this->assertTrue($canview); 138 139 } 140 141 /** 142 * @expectedException moodle_exception 143 */ 144 public function test_choice_user_submit_response_validation() { 145 global $USER; 146 147 $this->resetAfterTest(); 148 149 $this->setAdminUser(); 150 // Setup test data. 151 $course = $this->getDataGenerator()->create_course(); 152 $choice1 = $this->getDataGenerator()->create_module('choice', array('course' => $course->id)); 153 $choice2 = $this->getDataGenerator()->create_module('choice', array('course' => $course->id)); 154 $cm = get_coursemodule_from_instance('choice', $choice1->id); 155 156 $choicewithoptions1 = choice_get_choice($choice1->id); 157 $choicewithoptions2 = choice_get_choice($choice2->id); 158 $optionids1 = array_keys($choicewithoptions1->option); 159 $optionids2 = array_keys($choicewithoptions2->option); 160 161 // Make sure we cannot submit options from a different choice instance. 162 choice_user_submit_response($optionids2[0], $choice1, $USER->id, $course, $cm); 163 } 164 165 /** 166 * Test choice_get_user_response 167 * @return void 168 */ 169 public function test_choice_get_user_response() { 170 $this->resetAfterTest(); 171 172 $this->setAdminUser(); 173 // Setup test data. 174 $course = $this->getDataGenerator()->create_course(); 175 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 176 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id)); 177 $cm = get_coursemodule_from_instance('choice', $choice->id); 178 179 $choicewithoptions = choice_get_choice($choice->id); 180 $optionids = array_keys($choicewithoptions->option); 181 182 choice_user_submit_response($optionids[0], $choice, $student->id, $course, $cm); 183 $responses = choice_get_user_response($choice, $student->id); 184 $this->assertCount(1, $responses); 185 $response = array_shift($responses); 186 $this->assertEquals($optionids[0], $response->optionid); 187 188 // Multiple responses. 189 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 'allowmultiple' => 1)); 190 $cm = get_coursemodule_from_instance('choice', $choice->id); 191 192 $choicewithoptions = choice_get_choice($choice->id); 193 $optionids = array_keys($choicewithoptions->option); 194 195 // Submit a response with the options reversed. 196 $selections = $optionids; 197 rsort($selections); 198 choice_user_submit_response($selections, $choice, $student->id, $course, $cm); 199 $responses = choice_get_user_response($choice, $student->id); 200 $this->assertCount(count($optionids), $responses); 201 foreach ($responses as $resp) { 202 $this->assertEquals(array_shift($optionids), $resp->optionid); 203 } 204 } 205 206 /** 207 * Test choice_get_my_response 208 * @return void 209 */ 210 public function test_choice_get_my_response() { 211 global $USER; 212 213 $this->resetAfterTest(); 214 215 $this->setAdminUser(); 216 // Setup test data. 217 $course = $this->getDataGenerator()->create_course(); 218 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id)); 219 $cm = get_coursemodule_from_instance('choice', $choice->id); 220 221 $choicewithoptions = choice_get_choice($choice->id); 222 $optionids = array_keys($choicewithoptions->option); 223 224 choice_user_submit_response($optionids[0], $choice, $USER->id, $course, $cm); 225 $responses = choice_get_my_response($choice); 226 $this->assertCount(1, $responses); 227 $response = array_shift($responses); 228 $this->assertEquals($optionids[0], $response->optionid); 229 230 // Multiple responses. 231 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 'allowmultiple' => 1)); 232 $cm = get_coursemodule_from_instance('choice', $choice->id); 233 234 $choicewithoptions = choice_get_choice($choice->id); 235 $optionids = array_keys($choicewithoptions->option); 236 237 // Submit a response with the options reversed. 238 $selections = $optionids; 239 rsort($selections); 240 choice_user_submit_response($selections, $choice, $USER->id, $course, $cm); 241 $responses = choice_get_my_response($choice); 242 $this->assertCount(count($optionids), $responses); 243 foreach ($responses as $resp) { 244 $this->assertEquals(array_shift($optionids), $resp->optionid); 245 } 246 } 247 248 /** 249 * Test choice_get_availability_status 250 * @return void 251 */ 252 public function test_choice_get_availability_status() { 253 global $USER; 254 255 $this->resetAfterTest(); 256 257 $this->setAdminUser(); 258 // Setup test data. 259 $course = $this->getDataGenerator()->create_course(); 260 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id)); 261 262 // No time restrictions and updates allowed. 263 list($status, $warnings) = choice_get_availability_status($choice, false); 264 $this->assertEquals(true, $status); 265 $this->assertCount(0, $warnings); 266 267 // No updates allowed, but haven't answered yet. 268 $choice->allowupdate = false; 269 list($status, $warnings) = choice_get_availability_status($choice, false); 270 $this->assertEquals(true, $status); 271 $this->assertCount(0, $warnings); 272 273 // No updates allowed and have answered. 274 $cm = get_coursemodule_from_instance('choice', $choice->id); 275 $choicewithoptions = choice_get_choice($choice->id); 276 $optionids = array_keys($choicewithoptions->option); 277 choice_user_submit_response($optionids[0], $choice, $USER->id, $course, $cm); 278 list($status, $warnings) = choice_get_availability_status($choice, false); 279 $this->assertEquals(false, $status); 280 $this->assertCount(1, $warnings); 281 $this->assertEquals('choicesaved', array_keys($warnings)[0]); 282 283 $choice->allowupdate = true; 284 285 // With time restrictions, still open. 286 $choice->timeopen = time() - DAYSECS; 287 $choice->timeclose = time() + DAYSECS; 288 list($status, $warnings) = choice_get_availability_status($choice, false); 289 $this->assertEquals(true, $status); 290 $this->assertCount(0, $warnings); 291 292 // Choice not open yet. 293 $choice->timeopen = time() + DAYSECS; 294 $choice->timeclose = $choice->timeopen + DAYSECS; 295 list($status, $warnings) = choice_get_availability_status($choice, false); 296 $this->assertEquals(false, $status); 297 $this->assertCount(1, $warnings); 298 $this->assertEquals('notopenyet', array_keys($warnings)[0]); 299 300 // Choice closed. 301 $choice->timeopen = time() - DAYSECS; 302 $choice->timeclose = time() - 1; 303 list($status, $warnings) = choice_get_availability_status($choice, false); 304 $this->assertEquals(false, $status); 305 $this->assertCount(1, $warnings); 306 $this->assertEquals('expired', array_keys($warnings)[0]); 307 } 308 309 /* 310 * The choice's event should not be shown to a user when the user cannot view the choice activity at all. 311 */ 312 public function test_choice_core_calendar_provide_event_action_in_hidden_section() { 313 global $CFG; 314 315 $this->resetAfterTest(); 316 317 $this->setAdminUser(); 318 319 // Create a course. 320 $course = $this->getDataGenerator()->create_course(); 321 322 // Create a student. 323 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 324 325 // Create a choice. 326 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 327 'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS)); 328 329 // Create a calendar event. 330 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN); 331 332 // Set sections 0 as hidden. 333 set_section_visible($course->id, 0, 0); 334 335 // Now, log out. 336 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 337 $this->setUser(); 338 339 // Create an action factory. 340 $factory = new \core_calendar\action_factory(); 341 342 // Decorate action event for the student. 343 $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id); 344 345 // Confirm the event is not shown at all. 346 $this->assertNull($actionevent); 347 } 348 349 /* 350 * The choice's event should not be shown to a user who does not have permission to view the choice. 351 */ 352 public function test_choice_core_calendar_provide_event_action_for_non_user() { 353 global $CFG; 354 355 $this->resetAfterTest(); 356 357 $this->setAdminUser(); 358 359 // Create a course. 360 $course = $this->getDataGenerator()->create_course(); 361 362 // Create a choice. 363 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 364 'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS)); 365 366 // Create a calendar event. 367 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN); 368 369 // Now, log out. 370 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 371 $this->setUser(); 372 373 // Create an action factory. 374 $factory = new \core_calendar\action_factory(); 375 376 // Decorate action event. 377 $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory); 378 379 // Confirm the event is not shown at all. 380 $this->assertNull($actionevent); 381 } 382 383 public function test_choice_core_calendar_provide_event_action_open() { 384 $this->resetAfterTest(); 385 386 $this->setAdminUser(); 387 388 // Create a course. 389 $course = $this->getDataGenerator()->create_course(); 390 391 // Create a choice. 392 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 393 'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS)); 394 395 // Create a calendar event. 396 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN); 397 398 // Create an action factory. 399 $factory = new \core_calendar\action_factory(); 400 401 // Decorate action event. 402 $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory); 403 404 // Confirm the event was decorated. 405 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 406 $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name()); 407 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 408 $this->assertEquals(1, $actionevent->get_item_count()); 409 $this->assertTrue($actionevent->is_actionable()); 410 } 411 412 public function test_choice_core_calendar_provide_event_action_open_for_user() { 413 $this->resetAfterTest(); 414 415 $this->setAdminUser(); 416 417 // Create a course. 418 $course = $this->getDataGenerator()->create_course(); 419 420 // Create a student. 421 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 422 423 // Create a choice. 424 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 425 'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS)); 426 427 // Create a calendar event. 428 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN); 429 430 // Create an action factory. 431 $factory = new \core_calendar\action_factory(); 432 433 // Decorate action event for the student. 434 $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id); 435 436 // Confirm the event was decorated. 437 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 438 $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name()); 439 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 440 $this->assertEquals(1, $actionevent->get_item_count()); 441 $this->assertTrue($actionevent->is_actionable()); 442 } 443 444 /** 445 * An event should not have an action if the user has already submitted a response 446 * to the choice activity. 447 */ 448 public function test_choice_core_calendar_provide_event_action_already_submitted() { 449 $this->resetAfterTest(); 450 451 $this->setAdminUser(); 452 453 // Create a course. 454 $course = $this->getDataGenerator()->create_course(); 455 456 // Create a student. 457 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 458 459 // Create a choice. 460 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 461 'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS)); 462 $cm = get_coursemodule_from_instance('choice', $choice->id); 463 464 $choicewithoptions = choice_get_choice($choice->id); 465 $optionids = array_keys($choicewithoptions->option); 466 467 choice_user_submit_response($optionids[0], $choice, $student->id, $course, $cm); 468 469 // Create a calendar event. 470 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN); 471 472 // Create an action factory. 473 $factory = new \core_calendar\action_factory(); 474 475 $this->setUser($student); 476 477 // Decorate action event. 478 $action = mod_choice_core_calendar_provide_event_action($event, $factory); 479 480 // Confirm no action was returned if the user has already submitted the 481 // choice activity. 482 $this->assertNull($action); 483 } 484 485 /** 486 * An event should not have an action if the user has already submitted a response 487 * to the choice activity. 488 */ 489 public function test_choice_core_calendar_provide_event_action_already_submitted_for_user() { 490 $this->resetAfterTest(); 491 492 $this->setAdminUser(); 493 494 // Create a course. 495 $course = $this->getDataGenerator()->create_course(); 496 497 // Create a student. 498 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 499 500 // Create a choice. 501 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 502 'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS)); 503 $cm = get_coursemodule_from_instance('choice', $choice->id); 504 505 $choicewithoptions = choice_get_choice($choice->id); 506 $optionids = array_keys($choicewithoptions->option); 507 508 choice_user_submit_response($optionids[0], $choice, $student->id, $course, $cm); 509 510 // Create a calendar event. 511 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN); 512 513 // Create an action factory. 514 $factory = new \core_calendar\action_factory(); 515 516 // Decorate action event for the student. 517 $action = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id); 518 519 // Confirm no action was returned if the user has already submitted the 520 // choice activity. 521 $this->assertNull($action); 522 } 523 524 public function test_choice_core_calendar_provide_event_action_closed() { 525 $this->resetAfterTest(); 526 527 $this->setAdminUser(); 528 529 // Create a course. 530 $course = $this->getDataGenerator()->create_course(); 531 532 $timeclose = time() - DAYSECS; 533 // Create a choice. 534 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 535 'timeclose' => $timeclose)); 536 537 // Create a calendar event. 538 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN, $timeclose - 1); 539 540 // Create an action factory. 541 $factory = new \core_calendar\action_factory(); 542 543 // Decorate action event. 544 $action = mod_choice_core_calendar_provide_event_action($event, $factory); 545 546 // Confirm not action was provided for a closed activity. 547 $this->assertNull($action); 548 } 549 550 public function test_choice_core_calendar_provide_event_action_closed_for_user() { 551 $this->resetAfterTest(); 552 553 $this->setAdminUser(); 554 555 // Create a course. 556 $course = $this->getDataGenerator()->create_course(); 557 558 // Create a student. 559 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 560 561 $timeclose = time() - DAYSECS; 562 // Create a choice. 563 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 564 'timeclose' => $timeclose)); 565 566 // Create a calendar event. 567 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN, $timeclose - 1); 568 569 // Create an action factory. 570 $factory = new \core_calendar\action_factory(); 571 572 // Decorate action event for the student. 573 $action = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id); 574 575 // Confirm not action was provided for a closed activity. 576 $this->assertNull($action); 577 } 578 579 public function test_choice_core_calendar_provide_event_action_open_in_future() { 580 $this->resetAfterTest(); 581 582 $this->setAdminUser(); 583 584 // Create a course. 585 $course = $this->getDataGenerator()->create_course(); 586 587 $timeopen = time() + DAYSECS; 588 $timeclose = $timeopen + DAYSECS; 589 590 // Create a choice. 591 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 592 'timeopen' => $timeopen, 'timeclose' => $timeclose)); 593 594 // Create a calendar event. 595 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN, $timeopen); 596 597 // Create an action factory. 598 $factory = new \core_calendar\action_factory(); 599 600 // Decorate action event. 601 $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory); 602 603 // Confirm the event was decorated. 604 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 605 $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name()); 606 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 607 $this->assertEquals(1, $actionevent->get_item_count()); 608 $this->assertFalse($actionevent->is_actionable()); 609 } 610 611 public function test_choice_core_calendar_provide_event_action_open_in_future_for_user() { 612 global $CFG; 613 614 $this->resetAfterTest(); 615 616 $this->setAdminUser(); 617 618 // Create a course. 619 $course = $this->getDataGenerator()->create_course(); 620 621 // Create a student. 622 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 623 624 $timeopen = time() + DAYSECS; 625 $timeclose = $timeopen + DAYSECS; 626 627 // Create a choice. 628 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id, 629 'timeopen' => $timeopen, 'timeclose' => $timeclose)); 630 631 // Create a calendar event. 632 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN, $timeopen); 633 634 // Now, log out. 635 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 636 $this->setUser(); 637 638 // Create an action factory. 639 $factory = new \core_calendar\action_factory(); 640 641 // Decorate action event for the student. 642 $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id); 643 644 // Confirm the event was decorated. 645 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 646 $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name()); 647 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 648 $this->assertEquals(1, $actionevent->get_item_count()); 649 $this->assertFalse($actionevent->is_actionable()); 650 } 651 652 public function test_choice_core_calendar_provide_event_action_no_time_specified() { 653 $this->resetAfterTest(); 654 655 $this->setAdminUser(); 656 657 // Create a course. 658 $course = $this->getDataGenerator()->create_course(); 659 660 // Create a choice. 661 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id)); 662 663 // Create a calendar event. 664 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN); 665 666 // Create an action factory. 667 $factory = new \core_calendar\action_factory(); 668 669 // Decorate action event. 670 $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory); 671 672 // Confirm the event was decorated. 673 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 674 $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name()); 675 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 676 $this->assertEquals(1, $actionevent->get_item_count()); 677 $this->assertTrue($actionevent->is_actionable()); 678 } 679 680 public function test_choice_core_calendar_provide_event_action_no_time_specified_for_user() { 681 global $CFG; 682 683 $this->resetAfterTest(); 684 685 $this->setAdminUser(); 686 687 // Create a course. 688 $course = $this->getDataGenerator()->create_course(); 689 690 // Create a student. 691 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 692 693 // Create a choice. 694 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id)); 695 696 // Create a calendar event. 697 $event = $this->create_action_event($course->id, $choice->id, CHOICE_EVENT_TYPE_OPEN); 698 699 // Now, log out. 700 $CFG->forcelogin = true; // We don't want to be logged in as guest, as guest users might still have some capabilities. 701 $this->setUser(); 702 703 // Create an action factory. 704 $factory = new \core_calendar\action_factory(); 705 706 // Decorate action event for the student. 707 $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id); 708 709 // Confirm the event was decorated. 710 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 711 $this->assertEquals(get_string('viewchoices', 'choice'), $actionevent->get_name()); 712 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 713 $this->assertEquals(1, $actionevent->get_item_count()); 714 $this->assertTrue($actionevent->is_actionable()); 715 } 716 717 public function test_choice_core_calendar_provide_event_action_already_completed() { 718 $this->resetAfterTest(); 719 set_config('enablecompletion', 1); 720 $this->setAdminUser(); 721 722 // Create the activity. 723 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 724 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id), 725 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 726 727 // Get some additional data. 728 $cm = get_coursemodule_from_instance('choice', $choice->id); 729 730 // Create a calendar event. 731 $event = $this->create_action_event($course->id, $choice->id, 732 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 733 734 // Mark the activity as completed. 735 $completion = new completion_info($course); 736 $completion->set_module_viewed($cm); 737 738 // Create an action factory. 739 $factory = new \core_calendar\action_factory(); 740 741 // Decorate action event. 742 $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory); 743 744 // Ensure result was null. 745 $this->assertNull($actionevent); 746 } 747 748 public function test_choice_core_calendar_provide_event_action_already_completed_for_user() { 749 $this->resetAfterTest(); 750 set_config('enablecompletion', 1); 751 $this->setAdminUser(); 752 753 // Create the activity. 754 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 755 $choice = $this->getDataGenerator()->create_module('choice', array('course' => $course->id), 756 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 757 758 // Enrol a student in the course. 759 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 760 761 // Get some additional data. 762 $cm = get_coursemodule_from_instance('choice', $choice->id); 763 764 // Create a calendar event. 765 $event = $this->create_action_event($course->id, $choice->id, 766 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 767 768 // Mark the activity as completed for the student. 769 $completion = new completion_info($course); 770 $completion->set_module_viewed($cm, $student->id); 771 772 // Create an action factory. 773 $factory = new \core_calendar\action_factory(); 774 775 // Decorate action event for the student. 776 $actionevent = mod_choice_core_calendar_provide_event_action($event, $factory, $student->id); 777 778 // Ensure result was null. 779 $this->assertNull($actionevent); 780 } 781 782 /** 783 * Creates an action event. 784 * 785 * @param int $courseid 786 * @param int $instanceid The choice id. 787 * @param string $eventtype The event type. eg. CHOICE_EVENT_TYPE_OPEN. 788 * @param int|null $timestart The start timestamp for the event 789 * @return bool|calendar_event 790 */ 791 private function create_action_event($courseid, $instanceid, $eventtype, $timestart = null) { 792 $event = new stdClass(); 793 $event->name = 'Calendar event'; 794 $event->modulename = 'choice'; 795 $event->courseid = $courseid; 796 $event->instance = $instanceid; 797 $event->type = CALENDAR_EVENT_TYPE_ACTION; 798 $event->eventtype = $eventtype; 799 800 if ($timestart) { 801 $event->timestart = $timestart; 802 } else { 803 $event->timestart = time(); 804 } 805 806 return calendar_event::create($event); 807 } 808 809 /** 810 * Test the callback responsible for returning the completion rule descriptions. 811 * This function should work given either an instance of the module (cm_info), such as when checking the active rules, 812 * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type. 813 */ 814 public function test_mod_choice_completion_get_active_rule_descriptions() { 815 $this->resetAfterTest(); 816 $this->setAdminUser(); 817 818 // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't. 819 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 1]); 820 $choice1 = $this->getDataGenerator()->create_module('choice', [ 821 'course' => $course->id, 822 'completion' => 2, 823 'completionsubmit' => 1 824 ]); 825 $choice2 = $this->getDataGenerator()->create_module('choice', [ 826 'course' => $course->id, 827 'completion' => 2, 828 'completionsubmit' => 0 829 ]); 830 $cm1 = cm_info::create(get_coursemodule_from_instance('choice', $choice1->id)); 831 $cm2 = cm_info::create(get_coursemodule_from_instance('choice', $choice2->id)); 832 833 // Data for the stdClass input type. 834 // This type of input would occur when checking the default completion rules for an activity type, where we don't have 835 // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info. 836 $moddefaults = new stdClass(); 837 $moddefaults->customdata = ['customcompletionrules' => ['completionsubmit' => 1]]; 838 $moddefaults->completion = 2; 839 840 $activeruledescriptions = [get_string('completionsubmit', 'choice')]; 841 $this->assertEquals(mod_choice_get_completion_active_rule_descriptions($cm1), $activeruledescriptions); 842 $this->assertEquals(mod_choice_get_completion_active_rule_descriptions($cm2), []); 843 $this->assertEquals(mod_choice_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions); 844 $this->assertEquals(mod_choice_get_completion_active_rule_descriptions(new stdClass()), []); 845 } 846 847 /** 848 * An unkown event type should not change the choice instance. 849 */ 850 public function test_mod_choice_core_calendar_event_timestart_updated_unknown_event() { 851 global $CFG, $DB; 852 require_once($CFG->dirroot . "/calendar/lib.php"); 853 854 $this->resetAfterTest(true); 855 $this->setAdminUser(); 856 $generator = $this->getDataGenerator(); 857 $course = $generator->create_course(); 858 $choicegenerator = $generator->get_plugin_generator('mod_choice'); 859 $timeopen = time(); 860 $timeclose = $timeopen + DAYSECS; 861 $choice = $choicegenerator->create_instance(['course' => $course->id]); 862 $choice->timeopen = $timeopen; 863 $choice->timeclose = $timeclose; 864 $DB->update_record('choice', $choice); 865 866 // Create a valid event. 867 $event = new \calendar_event([ 868 'name' => 'Test event', 869 'description' => '', 870 'format' => 1, 871 'courseid' => $course->id, 872 'groupid' => 0, 873 'userid' => 2, 874 'modulename' => 'choice', 875 'instance' => $choice->id, 876 'eventtype' => CHOICE_EVENT_TYPE_OPEN . "SOMETHING ELSE", 877 'timestart' => 1, 878 'timeduration' => 86400, 879 'visible' => 1 880 ]); 881 882 mod_choice_core_calendar_event_timestart_updated($event, $choice); 883 884 $choice = $DB->get_record('choice', ['id' => $choice->id]); 885 $this->assertEquals($timeopen, $choice->timeopen); 886 $this->assertEquals($timeclose, $choice->timeclose); 887 } 888 889 /** 890 * A CHOICE_EVENT_TYPE_OPEN event should update the timeopen property of 891 * the choice activity. 892 */ 893 public function test_mod_choice_core_calendar_event_timestart_updated_open_event() { 894 global $CFG, $DB; 895 require_once($CFG->dirroot . "/calendar/lib.php"); 896 897 $this->resetAfterTest(true); 898 $this->setAdminUser(); 899 $generator = $this->getDataGenerator(); 900 $course = $generator->create_course(); 901 $choicegenerator = $generator->get_plugin_generator('mod_choice'); 902 $timeopen = time(); 903 $timeclose = $timeopen + DAYSECS; 904 $timemodified = 1; 905 $newtimeopen = $timeopen - DAYSECS; 906 $choice = $choicegenerator->create_instance(['course' => $course->id]); 907 $choice->timeopen = $timeopen; 908 $choice->timeclose = $timeclose; 909 $choice->timemodified = $timemodified; 910 $DB->update_record('choice', $choice); 911 912 // Create a valid event. 913 $event = new \calendar_event([ 914 'name' => 'Test event', 915 'description' => '', 916 'format' => 1, 917 'courseid' => $course->id, 918 'groupid' => 0, 919 'userid' => 2, 920 'modulename' => 'choice', 921 'instance' => $choice->id, 922 'eventtype' => CHOICE_EVENT_TYPE_OPEN, 923 'timestart' => $newtimeopen, 924 'timeduration' => 86400, 925 'visible' => 1 926 ]); 927 928 // Trigger and capture the event when adding a contact. 929 $sink = $this->redirectEvents(); 930 931 mod_choice_core_calendar_event_timestart_updated($event, $choice); 932 933 $triggeredevents = $sink->get_events(); 934 $moduleupdatedevents = array_filter($triggeredevents, function($e) { 935 return is_a($e, 'core\event\course_module_updated'); 936 }); 937 938 $choice = $DB->get_record('choice', ['id' => $choice->id]); 939 // Ensure the timeopen property matches the event timestart. 940 $this->assertEquals($newtimeopen, $choice->timeopen); 941 // Ensure the timeclose isn't changed. 942 $this->assertEquals($timeclose, $choice->timeclose); 943 // Ensure the timemodified property has been changed. 944 $this->assertNotEquals($timemodified, $choice->timemodified); 945 // Confirm that a module updated event is fired when the module 946 // is changed. 947 $this->assertNotEmpty($moduleupdatedevents); 948 } 949 950 /** 951 * A CHOICE_EVENT_TYPE_CLOSE event should update the timeclose property of 952 * the choice activity. 953 */ 954 public function test_mod_choice_core_calendar_event_timestart_updated_close_event() { 955 global $CFG, $DB; 956 require_once($CFG->dirroot . "/calendar/lib.php"); 957 958 $this->resetAfterTest(true); 959 $this->setAdminUser(); 960 $generator = $this->getDataGenerator(); 961 $course = $generator->create_course(); 962 $choicegenerator = $generator->get_plugin_generator('mod_choice'); 963 $timeopen = time(); 964 $timeclose = $timeopen + DAYSECS; 965 $timemodified = 1; 966 $newtimeclose = $timeclose + DAYSECS; 967 $choice = $choicegenerator->create_instance(['course' => $course->id]); 968 $choice->timeopen = $timeopen; 969 $choice->timeclose = $timeclose; 970 $choice->timemodified = $timemodified; 971 $DB->update_record('choice', $choice); 972 973 // Create a valid event. 974 $event = new \calendar_event([ 975 'name' => 'Test event', 976 'description' => '', 977 'format' => 1, 978 'courseid' => $course->id, 979 'groupid' => 0, 980 'userid' => 2, 981 'modulename' => 'choice', 982 'instance' => $choice->id, 983 'eventtype' => CHOICE_EVENT_TYPE_CLOSE, 984 'timestart' => $newtimeclose, 985 'timeduration' => 86400, 986 'visible' => 1 987 ]); 988 989 // Trigger and capture the event when adding a contact. 990 $sink = $this->redirectEvents(); 991 992 mod_choice_core_calendar_event_timestart_updated($event, $choice); 993 994 $triggeredevents = $sink->get_events(); 995 $moduleupdatedevents = array_filter($triggeredevents, function($e) { 996 return is_a($e, 'core\event\course_module_updated'); 997 }); 998 999 $choice = $DB->get_record('choice', ['id' => $choice->id]); 1000 // Ensure the timeclose property matches the event timestart. 1001 $this->assertEquals($newtimeclose, $choice->timeclose); 1002 // Ensure the timeopen isn't changed. 1003 $this->assertEquals($timeopen, $choice->timeopen); 1004 // Ensure the timemodified property has been changed. 1005 $this->assertNotEquals($timemodified, $choice->timemodified); 1006 // Confirm that a module updated event is fired when the module 1007 // is changed. 1008 $this->assertNotEmpty($moduleupdatedevents); 1009 } 1010 1011 /** 1012 * An unkown event type should not have any limits 1013 */ 1014 public function test_mod_choice_core_calendar_get_valid_event_timestart_range_unknown_event() { 1015 global $CFG, $DB; 1016 require_once($CFG->dirroot . "/calendar/lib.php"); 1017 1018 $this->resetAfterTest(true); 1019 $this->setAdminUser(); 1020 $generator = $this->getDataGenerator(); 1021 $course = $generator->create_course(); 1022 $timeopen = time(); 1023 $timeclose = $timeopen + DAYSECS; 1024 $choice = new \stdClass(); 1025 $choice->timeopen = $timeopen; 1026 $choice->timeclose = $timeclose; 1027 1028 // Create a valid event. 1029 $event = new \calendar_event([ 1030 'name' => 'Test event', 1031 'description' => '', 1032 'format' => 1, 1033 'courseid' => $course->id, 1034 'groupid' => 0, 1035 'userid' => 2, 1036 'modulename' => 'choice', 1037 'instance' => 1, 1038 'eventtype' => CHOICE_EVENT_TYPE_OPEN . "SOMETHING ELSE", 1039 'timestart' => 1, 1040 'timeduration' => 86400, 1041 'visible' => 1 1042 ]); 1043 1044 list ($min, $max) = mod_choice_core_calendar_get_valid_event_timestart_range($event, $choice); 1045 $this->assertNull($min); 1046 $this->assertNull($max); 1047 } 1048 1049 /** 1050 * The open event should be limited by the choice's timeclose property, if it's set. 1051 */ 1052 public function test_mod_choice_core_calendar_get_valid_event_timestart_range_open_event() { 1053 global $CFG, $DB; 1054 require_once($CFG->dirroot . "/calendar/lib.php"); 1055 1056 $this->resetAfterTest(true); 1057 $this->setAdminUser(); 1058 $generator = $this->getDataGenerator(); 1059 $course = $generator->create_course(); 1060 $timeopen = time(); 1061 $timeclose = $timeopen + DAYSECS; 1062 $choice = new \stdClass(); 1063 $choice->timeopen = $timeopen; 1064 $choice->timeclose = $timeclose; 1065 1066 // Create a valid event. 1067 $event = new \calendar_event([ 1068 'name' => 'Test event', 1069 'description' => '', 1070 'format' => 1, 1071 'courseid' => $course->id, 1072 'groupid' => 0, 1073 'userid' => 2, 1074 'modulename' => 'choice', 1075 'instance' => 1, 1076 'eventtype' => CHOICE_EVENT_TYPE_OPEN, 1077 'timestart' => 1, 1078 'timeduration' => 86400, 1079 'visible' => 1 1080 ]); 1081 1082 // The max limit should be bounded by the timeclose value. 1083 list ($min, $max) = mod_choice_core_calendar_get_valid_event_timestart_range($event, $choice); 1084 1085 $this->assertNull($min); 1086 $this->assertEquals($timeclose, $max[0]); 1087 1088 // No timeclose value should result in no upper limit. 1089 $choice->timeclose = 0; 1090 list ($min, $max) = mod_choice_core_calendar_get_valid_event_timestart_range($event, $choice); 1091 1092 $this->assertNull($min); 1093 $this->assertNull($max); 1094 } 1095 1096 /** 1097 * The close event should be limited by the choice's timeopen property, if it's set. 1098 */ 1099 public function test_mod_choice_core_calendar_get_valid_event_timestart_range_close_event() { 1100 global $CFG, $DB; 1101 require_once($CFG->dirroot . "/calendar/lib.php"); 1102 1103 $this->resetAfterTest(true); 1104 $this->setAdminUser(); 1105 $generator = $this->getDataGenerator(); 1106 $course = $generator->create_course(); 1107 $timeopen = time(); 1108 $timeclose = $timeopen + DAYSECS; 1109 $choice = new \stdClass(); 1110 $choice->timeopen = $timeopen; 1111 $choice->timeclose = $timeclose; 1112 1113 // Create a valid event. 1114 $event = new \calendar_event([ 1115 'name' => 'Test event', 1116 'description' => '', 1117 'format' => 1, 1118 'courseid' => $course->id, 1119 'groupid' => 0, 1120 'userid' => 2, 1121 'modulename' => 'choice', 1122 'instance' => 1, 1123 'eventtype' => CHOICE_EVENT_TYPE_CLOSE, 1124 'timestart' => 1, 1125 'timeduration' => 86400, 1126 'visible' => 1 1127 ]); 1128 1129 // The max limit should be bounded by the timeclose value. 1130 list ($min, $max) = mod_choice_core_calendar_get_valid_event_timestart_range($event, $choice); 1131 1132 $this->assertEquals($timeopen, $min[0]); 1133 $this->assertNull($max); 1134 1135 // No timeclose value should result in no upper limit. 1136 $choice->timeopen = 0; 1137 list ($min, $max) = mod_choice_core_calendar_get_valid_event_timestart_range($event, $choice); 1138 1139 $this->assertNull($min); 1140 $this->assertNull($max); 1141 } 1142 1143 /** 1144 * Test choice_user_submit_response for a choice with specific options. 1145 * Options: 1146 * allowmultiple: false 1147 * limitanswers: false 1148 */ 1149 public function test_choice_user_submit_response_no_multiple_no_limits() { 1150 global $DB; 1151 $this->resetAfterTest(true); 1152 1153 $generator = $this->getDataGenerator(); 1154 $course = $generator->create_course(); 1155 $user = $generator->create_user(); 1156 $user2 = $generator->create_user(); 1157 1158 // User must be enrolled in the course for choice limits to be honoured properly. 1159 $role = $DB->get_record('role', ['shortname' => 'student']); 1160 $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id); 1161 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id); 1162 1163 // Create choice, with updates allowed and a two options both limited to 1 response each. 1164 $choice = $generator->get_plugin_generator('mod_choice')->create_instance([ 1165 'course' => $course->id, 1166 'allowupdate' => false, 1167 'limitanswers' => false, 1168 'allowmultiple' => false, 1169 'option' => ['red', 'green'], 1170 ]); 1171 $cm = get_coursemodule_from_instance('choice', $choice->id); 1172 1173 // Get the choice, with options and limits included. 1174 $choicewithoptions = choice_get_choice($choice->id); 1175 $optionids = array_keys($choicewithoptions->option); 1176 1177 // Now, save an response which includes the first option. 1178 $this->assertNull(choice_user_submit_response($optionids[0], $choicewithoptions, $user->id, $course, $cm)); 1179 1180 // Confirm that saving again without changing the selected option will not throw a 'choice full' exception. 1181 $this->assertNull(choice_user_submit_response($optionids[1], $choicewithoptions, $user->id, $course, $cm)); 1182 1183 // Confirm that saving a response for student 2 including the first option is allowed. 1184 $this->assertNull(choice_user_submit_response($optionids[0], $choicewithoptions, $user2->id, $course, $cm)); 1185 1186 // Confirm that trying to save multiple options results in an exception. 1187 $this->expectException('moodle_exception'); 1188 choice_user_submit_response([$optionids[1], $optionids[1]], $choicewithoptions, $user->id, $course, $cm); 1189 } 1190 1191 /** 1192 * Test choice_user_submit_response for a choice with specific options. 1193 * Options: 1194 * allowmultiple: true 1195 * limitanswers: false 1196 */ 1197 public function test_choice_user_submit_response_multiples_no_limits() { 1198 global $DB; 1199 $this->resetAfterTest(true); 1200 1201 $generator = $this->getDataGenerator(); 1202 $course = $generator->create_course(); 1203 $user = $generator->create_user(); 1204 $user2 = $generator->create_user(); 1205 1206 // User must be enrolled in the course for choice limits to be honoured properly. 1207 $role = $DB->get_record('role', ['shortname' => 'student']); 1208 $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id); 1209 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id); 1210 1211 // Create choice, with updates allowed and a two options both limited to 1 response each. 1212 $choice = $generator->get_plugin_generator('mod_choice')->create_instance([ 1213 'course' => $course->id, 1214 'allowupdate' => false, 1215 'allowmultiple' => true, 1216 'limitanswers' => false, 1217 'option' => ['red', 'green'], 1218 ]); 1219 $cm = get_coursemodule_from_instance('choice', $choice->id); 1220 1221 // Get the choice, with options and limits included. 1222 $choicewithoptions = choice_get_choice($choice->id); 1223 $optionids = array_keys($choicewithoptions->option); 1224 1225 // Save a response which includes the first option only. 1226 $this->assertNull(choice_user_submit_response([$optionids[0]], $choicewithoptions, $user->id, $course, $cm)); 1227 1228 // Confirm that adding an option to the response is allowed. 1229 $this->assertNull(choice_user_submit_response([$optionids[0], $optionids[1]], $choicewithoptions, $user->id, $course, $cm)); 1230 1231 // Confirm that saving a response for student 2 including the first option is allowed. 1232 $this->assertNull(choice_user_submit_response($optionids[0], $choicewithoptions, $user2->id, $course, $cm)); 1233 1234 // Confirm that removing an option from the response is allowed. 1235 $this->assertNull(choice_user_submit_response([$optionids[0]], $choicewithoptions, $user->id, $course, $cm)); 1236 1237 // Confirm that removing all options from the response is not allowed via this method. 1238 $this->expectException('moodle_exception'); 1239 choice_user_submit_response([], $choicewithoptions, $user->id, $course, $cm); 1240 } 1241 1242 /** 1243 * Test choice_user_submit_response for a choice with specific options. 1244 * Options: 1245 * allowmultiple: false 1246 * limitanswers: true 1247 */ 1248 public function test_choice_user_submit_response_no_multiples_limits() { 1249 global $DB; 1250 $this->resetAfterTest(true); 1251 1252 $generator = $this->getDataGenerator(); 1253 $course = $generator->create_course(); 1254 $user = $generator->create_user(); 1255 $user2 = $generator->create_user(); 1256 1257 // User must be enrolled in the course for choice limits to be honoured properly. 1258 $role = $DB->get_record('role', ['shortname' => 'student']); 1259 $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id); 1260 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id); 1261 1262 // Create choice, with updates allowed and a two options both limited to 1 response each. 1263 $choice = $generator->get_plugin_generator('mod_choice')->create_instance([ 1264 'course' => $course->id, 1265 'allowupdate' => false, 1266 'allowmultiple' => false, 1267 'limitanswers' => true, 1268 'option' => ['red', 'green'], 1269 'limit' => [1, 1] 1270 ]); 1271 $cm = get_coursemodule_from_instance('choice', $choice->id); 1272 1273 // Get the choice, with options and limits included. 1274 $choicewithoptions = choice_get_choice($choice->id); 1275 $optionids = array_keys($choicewithoptions->option); 1276 1277 // Save a response which includes the first option only. 1278 $this->assertNull(choice_user_submit_response($optionids[0], $choicewithoptions, $user->id, $course, $cm)); 1279 1280 // Confirm that changing the option in the response is allowed. 1281 $this->assertNull(choice_user_submit_response($optionids[1], $choicewithoptions, $user->id, $course, $cm)); 1282 1283 // Confirm that limits are respected by trying to save the same option as another user. 1284 $this->expectException('moodle_exception'); 1285 choice_user_submit_response($optionids[1], $choicewithoptions, $user2->id, $course, $cm); 1286 } 1287 1288 /** 1289 * Test choice_user_submit_response for a choice with specific options. 1290 * Options: 1291 * allowmultiple: true 1292 * limitanswers: true 1293 */ 1294 public function test_choice_user_submit_response_multiples_limits() { 1295 global $DB; 1296 $this->resetAfterTest(true); 1297 1298 $generator = $this->getDataGenerator(); 1299 $course = $generator->create_course(); 1300 $user = $generator->create_user(); 1301 $user2 = $generator->create_user(); 1302 1303 // User must be enrolled in the course for choice limits to be honoured properly. 1304 $role = $DB->get_record('role', ['shortname' => 'student']); 1305 $this->getDataGenerator()->enrol_user($user->id, $course->id, $role->id); 1306 $this->getDataGenerator()->enrol_user($user2->id, $course->id, $role->id); 1307 1308 // Create choice, with updates allowed and a two options both limited to 1 response each. 1309 $choice = $generator->get_plugin_generator('mod_choice')->create_instance([ 1310 'course' => $course->id, 1311 'allowupdate' => false, 1312 'allowmultiple' => true, 1313 'limitanswers' => true, 1314 'option' => ['red', 'green'], 1315 'limit' => [1, 1] 1316 ]); 1317 $cm = get_coursemodule_from_instance('choice', $choice->id); 1318 1319 // Get the choice, with options and limits included. 1320 $choicewithoptions = choice_get_choice($choice->id); 1321 $optionids = array_keys($choicewithoptions->option); 1322 1323 // Now, save a response which includes the first option only. 1324 $this->assertNull(choice_user_submit_response([$optionids[0]], $choicewithoptions, $user->id, $course, $cm)); 1325 1326 // Confirm that changing the option in the response is allowed. 1327 $this->assertNull(choice_user_submit_response([$optionids[1]], $choicewithoptions, $user->id, $course, $cm)); 1328 1329 // Confirm that adding an option to the response is allowed. 1330 $this->assertNull(choice_user_submit_response([$optionids[0], $optionids[1]], $choicewithoptions, $user->id, $course, $cm)); 1331 1332 // Confirm that limits are respected by trying to save the same option as another user. 1333 $this->expectException('moodle_exception'); 1334 choice_user_submit_response($optionids[1], $choicewithoptions, $user2->id, $course, $cm); 1335 } 1336 1337 /** 1338 * A user who does not have capabilities to add events to the calendar should be able to create an choice. 1339 */ 1340 public function test_creation_with_no_calendar_capabilities() { 1341 $this->resetAfterTest(); 1342 $course = self::getDataGenerator()->create_course(); 1343 $context = context_course::instance($course->id); 1344 $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher'); 1345 $roleid = self::getDataGenerator()->create_role(); 1346 self::getDataGenerator()->role_assign($roleid, $user->id, $context->id); 1347 assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true); 1348 $generator = self::getDataGenerator()->get_plugin_generator('mod_choice'); 1349 // Create an instance as a user without the calendar capabilities. 1350 $this->setUser($user); 1351 $time = time(); 1352 $params = array( 1353 'course' => $course->id, 1354 'timeopen' => $time + 200, 1355 'timeclose' => $time + 500, 1356 ); 1357 $generator->create_instance($params); 1358 } 1359 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body