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