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