See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401] [Versions 401 and 402] [Versions 401 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 * SCORM module library functions tests 19 * 20 * @package mod_scorm 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_scorm; 27 28 use mod_scorm_get_completion_active_rule_descriptions; 29 30 defined('MOODLE_INTERNAL') || die(); 31 32 global $CFG; 33 34 require_once($CFG->dirroot . '/webservice/tests/helpers.php'); 35 require_once($CFG->dirroot . '/mod/scorm/lib.php'); 36 37 /** 38 * SCORM module library functions tests 39 * 40 * @package mod_scorm 41 * @category test 42 * @copyright 2015 Juan Leyva <juan@moodle.com> 43 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 44 * @since Moodle 3.0 45 */ 46 class lib_test extends \advanced_testcase { 47 48 /** 49 * Set up for every test 50 */ 51 public function setUp(): void { 52 global $DB; 53 $this->resetAfterTest(); 54 $this->setAdminUser(); 55 56 // Setup test data. 57 $this->course = $this->getDataGenerator()->create_course(); 58 $this->scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $this->course->id)); 59 $this->context = \context_module::instance($this->scorm->cmid); 60 $this->cm = get_coursemodule_from_instance('scorm', $this->scorm->id); 61 62 // Create users. 63 $this->student = self::getDataGenerator()->create_user(); 64 $this->teacher = self::getDataGenerator()->create_user(); 65 66 // Users enrolments. 67 $this->studentrole = $DB->get_record('role', array('shortname' => 'student')); 68 $this->teacherrole = $DB->get_record('role', array('shortname' => 'editingteacher')); 69 $this->getDataGenerator()->enrol_user($this->student->id, $this->course->id, $this->studentrole->id, 'manual'); 70 $this->getDataGenerator()->enrol_user($this->teacher->id, $this->course->id, $this->teacherrole->id, 'manual'); 71 } 72 73 /** Test scorm_check_mode 74 * 75 * @return void 76 */ 77 public function test_scorm_check_mode() { 78 global $CFG; 79 80 $newattempt = 'on'; 81 $attempt = 1; 82 $mode = 'normal'; 83 scorm_check_mode($this->scorm, $newattempt, $attempt, $this->student->id, $mode); 84 $this->assertEquals('off', $newattempt); 85 86 $scoes = scorm_get_scoes($this->scorm->id); 87 $sco = array_pop($scoes); 88 scorm_insert_track($this->student->id, $this->scorm->id, $sco->id, 1, 'cmi.core.lesson_status', 'completed'); 89 $newattempt = 'on'; 90 scorm_check_mode($this->scorm, $newattempt, $attempt, $this->student->id, $mode); 91 $this->assertEquals('on', $newattempt); 92 93 // Now do the same with a SCORM 2004 package. 94 $record = new \stdClass(); 95 $record->course = $this->course->id; 96 $record->packagefilepath = $CFG->dirroot.'/mod/scorm/tests/packages/RuntimeBasicCalls_SCORM20043rdEdition.zip'; 97 $scorm13 = $this->getDataGenerator()->create_module('scorm', $record); 98 $newattempt = 'on'; 99 $attempt = 1; 100 $mode = 'normal'; 101 scorm_check_mode($scorm13, $newattempt, $attempt, $this->student->id, $mode); 102 $this->assertEquals('off', $newattempt); 103 104 $scoes = scorm_get_scoes($scorm13->id); 105 $sco = array_pop($scoes); 106 scorm_insert_track($this->student->id, $scorm13->id, $sco->id, 1, 'cmi.completion_status', 'completed'); 107 108 $newattempt = 'on'; 109 $attempt = 1; 110 $mode = 'normal'; 111 scorm_check_mode($scorm13, $newattempt, $attempt, $this->student->id, $mode); 112 $this->assertEquals('on', $newattempt); 113 } 114 115 /** 116 * Test scorm_view 117 * @return void 118 */ 119 public function test_scorm_view() { 120 global $CFG; 121 122 // Trigger and capture the event. 123 $sink = $this->redirectEvents(); 124 125 scorm_view($this->scorm, $this->course, $this->cm, $this->context); 126 127 $events = $sink->get_events(); 128 $this->assertCount(1, $events); 129 $event = array_shift($events); 130 131 // Checking that the event contains the expected values. 132 $this->assertInstanceOf('\mod_scorm\event\course_module_viewed', $event); 133 $this->assertEquals($this->context, $event->get_context()); 134 $url = new \moodle_url('/mod/scorm/view.php', array('id' => $this->cm->id)); 135 $this->assertEquals($url, $event->get_url()); 136 $this->assertEventContextNotUsed($event); 137 $this->assertNotEmpty($event->get_name()); 138 } 139 140 /** 141 * Test scorm_get_availability_status and scorm_require_available 142 * @return void 143 */ 144 public function test_scorm_check_and_require_available() { 145 global $DB; 146 147 $this->setAdminUser(); 148 149 // User override case. 150 $this->scorm->timeopen = time() + DAYSECS; 151 $this->scorm->timeclose = time() - DAYSECS; 152 list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context); 153 $this->assertEquals(true, $status); 154 $this->assertCount(0, $warnings); 155 156 // Now check with a student. 157 list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context, $this->student->id); 158 $this->assertEquals(false, $status); 159 $this->assertCount(2, $warnings); 160 $this->assertArrayHasKey('notopenyet', $warnings); 161 $this->assertArrayHasKey('expired', $warnings); 162 $this->assertEquals(userdate($this->scorm->timeopen), $warnings['notopenyet']); 163 $this->assertEquals(userdate($this->scorm->timeclose), $warnings['expired']); 164 165 // Reset the scorm's times. 166 $this->scorm->timeopen = $this->scorm->timeclose = 0; 167 168 // Set to the student user. 169 self::setUser($this->student); 170 171 // Usual case. 172 list($status, $warnings) = scorm_get_availability_status($this->scorm, false); 173 $this->assertEquals(true, $status); 174 $this->assertCount(0, $warnings); 175 176 // SCORM not open. 177 $this->scorm->timeopen = time() + DAYSECS; 178 list($status, $warnings) = scorm_get_availability_status($this->scorm, false); 179 $this->assertEquals(false, $status); 180 $this->assertCount(1, $warnings); 181 182 // SCORM closed. 183 $this->scorm->timeopen = 0; 184 $this->scorm->timeclose = time() - DAYSECS; 185 list($status, $warnings) = scorm_get_availability_status($this->scorm, false); 186 $this->assertEquals(false, $status); 187 $this->assertCount(1, $warnings); 188 189 // SCORM not open and closed. 190 $this->scorm->timeopen = time() + DAYSECS; 191 list($status, $warnings) = scorm_get_availability_status($this->scorm, false); 192 $this->assertEquals(false, $status); 193 $this->assertCount(2, $warnings); 194 195 // Now additional checkings with different parameters values. 196 list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context); 197 $this->assertEquals(false, $status); 198 $this->assertCount(2, $warnings); 199 200 // SCORM not open. 201 $this->scorm->timeopen = time() + DAYSECS; 202 $this->scorm->timeclose = 0; 203 list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context); 204 $this->assertEquals(false, $status); 205 $this->assertCount(1, $warnings); 206 207 // SCORM closed. 208 $this->scorm->timeopen = 0; 209 $this->scorm->timeclose = time() - DAYSECS; 210 list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context); 211 $this->assertEquals(false, $status); 212 $this->assertCount(1, $warnings); 213 214 // SCORM not open and closed. 215 $this->scorm->timeopen = time() + DAYSECS; 216 list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context); 217 $this->assertEquals(false, $status); 218 $this->assertCount(2, $warnings); 219 220 // As teacher now. 221 self::setUser($this->teacher); 222 223 // SCORM not open and closed. 224 $this->scorm->timeopen = time() + DAYSECS; 225 list($status, $warnings) = scorm_get_availability_status($this->scorm, false); 226 $this->assertEquals(false, $status); 227 $this->assertCount(2, $warnings); 228 229 // Now, we use the special capability. 230 // SCORM not open and closed. 231 $this->scorm->timeopen = time() + DAYSECS; 232 list($status, $warnings) = scorm_get_availability_status($this->scorm, true, $this->context); 233 $this->assertEquals(true, $status); 234 $this->assertCount(0, $warnings); 235 236 // Check exceptions does not broke anything. 237 scorm_require_available($this->scorm, true, $this->context); 238 // Now, expect exceptions. 239 $this->expectException('moodle_exception'); 240 $this->expectExceptionMessage(get_string("notopenyet", "scorm", userdate($this->scorm->timeopen))); 241 242 // Now as student other condition. 243 self::setUser($this->student); 244 $this->scorm->timeopen = 0; 245 $this->scorm->timeclose = time() - DAYSECS; 246 247 $this->expectException('moodle_exception'); 248 $this->expectExceptionMessage(get_string("expired", "scorm", userdate($this->scorm->timeclose))); 249 scorm_require_available($this->scorm, false); 250 } 251 252 /** 253 * Test scorm_get_last_completed_attempt 254 * 255 * @return void 256 */ 257 public function test_scorm_get_last_completed_attempt() { 258 $this->assertEquals(1, scorm_get_last_completed_attempt($this->scorm->id, $this->student->id)); 259 } 260 261 public function test_scorm_core_calendar_provide_event_action_open() { 262 $this->resetAfterTest(); 263 264 $this->setAdminUser(); 265 266 // Create a course. 267 $course = $this->getDataGenerator()->create_course(); 268 269 // Create a scorm activity. 270 $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id, 271 'timeopen' => time() - DAYSECS, 'timeclose' => time() + DAYSECS)); 272 273 // Create a calendar event. 274 $event = $this->create_action_event($course->id, $scorm->id, SCORM_EVENT_TYPE_OPEN); 275 276 // Only students see scorm events. 277 $this->setUser($this->student); 278 279 // Create an action factory. 280 $factory = new \core_calendar\action_factory(); 281 282 // Decorate action event. 283 $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory); 284 285 // Confirm the event was decorated. 286 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 287 $this->assertEquals(get_string('enter', 'scorm'), $actionevent->get_name()); 288 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 289 $this->assertEquals(1, $actionevent->get_item_count()); 290 $this->assertTrue($actionevent->is_actionable()); 291 } 292 293 public function test_scorm_core_calendar_provide_event_action_closed() { 294 $this->resetAfterTest(); 295 296 $this->setAdminUser(); 297 298 // Create a course. 299 $course = $this->getDataGenerator()->create_course(); 300 301 // Create a scorm activity. 302 $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id, 303 'timeclose' => time() - DAYSECS)); 304 305 // Create a calendar event. 306 $event = $this->create_action_event($course->id, $scorm->id, SCORM_EVENT_TYPE_OPEN); 307 308 // Create an action factory. 309 $factory = new \core_calendar\action_factory(); 310 311 // Decorate action event. 312 $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory); 313 314 // No event on the dashboard if module is closed. 315 $this->assertNull($actionevent); 316 } 317 318 public function test_scorm_core_calendar_provide_event_action_open_in_future() { 319 $this->resetAfterTest(); 320 321 $this->setAdminUser(); 322 323 // Create a course. 324 $course = $this->getDataGenerator()->create_course(); 325 326 // Create a scorm activity. 327 $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id, 328 'timeopen' => time() + DAYSECS)); 329 330 // Create a calendar event. 331 $event = $this->create_action_event($course->id, $scorm->id, SCORM_EVENT_TYPE_OPEN); 332 333 // Only students see scorm events. 334 $this->setUser($this->student); 335 336 // Create an action factory. 337 $factory = new \core_calendar\action_factory(); 338 339 // Decorate action event. 340 $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory); 341 342 // Confirm the event was decorated. 343 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 344 $this->assertEquals(get_string('enter', 'scorm'), $actionevent->get_name()); 345 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 346 $this->assertEquals(1, $actionevent->get_item_count()); 347 $this->assertFalse($actionevent->is_actionable()); 348 } 349 350 public function test_scorm_core_calendar_provide_event_action_with_different_user_as_admin() { 351 $this->resetAfterTest(); 352 353 $this->setAdminUser(); 354 355 // Create a course. 356 $course = $this->getDataGenerator()->create_course(); 357 358 // Create a scorm activity. 359 $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id, 360 'timeopen' => time() + DAYSECS)); 361 362 // Create a calendar event. 363 $event = $this->create_action_event($course->id, $scorm->id, SCORM_EVENT_TYPE_OPEN); 364 365 // Create an action factory. 366 $factory = new \core_calendar\action_factory(); 367 368 // Decorate action event override with a passed in user. 369 $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory, $this->student->id); 370 $actionevent2 = mod_scorm_core_calendar_provide_event_action($event, $factory); 371 372 // Only students see scorm events. 373 $this->assertNull($actionevent2); 374 375 // Confirm the event was decorated. 376 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 377 $this->assertEquals(get_string('enter', 'scorm'), $actionevent->get_name()); 378 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 379 $this->assertEquals(1, $actionevent->get_item_count()); 380 $this->assertFalse($actionevent->is_actionable()); 381 } 382 383 public function test_scorm_core_calendar_provide_event_action_no_time_specified() { 384 $this->resetAfterTest(); 385 386 $this->setAdminUser(); 387 388 // Create a course. 389 $course = $this->getDataGenerator()->create_course(); 390 391 // Create a scorm activity. 392 $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id)); 393 394 // Create a calendar event. 395 $event = $this->create_action_event($course->id, $scorm->id, SCORM_EVENT_TYPE_OPEN); 396 397 // Only students see scorm events. 398 $this->setUser($this->student); 399 400 // Create an action factory. 401 $factory = new \core_calendar\action_factory(); 402 403 // Decorate action event. 404 $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory); 405 406 // Confirm the event was decorated. 407 $this->assertInstanceOf('\core_calendar\local\event\value_objects\action', $actionevent); 408 $this->assertEquals(get_string('enter', 'scorm'), $actionevent->get_name()); 409 $this->assertInstanceOf('moodle_url', $actionevent->get_url()); 410 $this->assertEquals(1, $actionevent->get_item_count()); 411 $this->assertTrue($actionevent->is_actionable()); 412 } 413 414 public function test_scorm_core_calendar_provide_event_action_already_completed() { 415 $this->resetAfterTest(); 416 set_config('enablecompletion', 1); 417 $this->setAdminUser(); 418 419 // Create the activity. 420 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 421 $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id), 422 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 423 424 // Get some additional data. 425 $cm = get_coursemodule_from_instance('scorm', $scorm->id); 426 427 // Create a calendar event. 428 $event = $this->create_action_event($course->id, $scorm->id, 429 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 430 431 // Mark the activity as completed. 432 $completion = new \completion_info($course); 433 $completion->set_module_viewed($cm); 434 435 // Create an action factory. 436 $factory = new \core_calendar\action_factory(); 437 438 // Decorate action event. 439 $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory); 440 441 // Ensure result was null. 442 $this->assertNull($actionevent); 443 } 444 445 public function test_scorm_core_calendar_provide_event_action_already_completed_for_user() { 446 $this->resetAfterTest(); 447 set_config('enablecompletion', 1); 448 $this->setAdminUser(); 449 450 // Create the activity. 451 $course = $this->getDataGenerator()->create_course(array('enablecompletion' => 1)); 452 $scorm = $this->getDataGenerator()->create_module('scorm', array('course' => $course->id), 453 array('completion' => 2, 'completionview' => 1, 'completionexpected' => time() + DAYSECS)); 454 455 // Enrol a student in the course. 456 $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); 457 458 // Get some additional data. 459 $cm = get_coursemodule_from_instance('scorm', $scorm->id); 460 461 // Create a calendar event. 462 $event = $this->create_action_event($course->id, $scorm->id, 463 \core_completion\api::COMPLETION_EVENT_TYPE_DATE_COMPLETION_EXPECTED); 464 465 // Mark the activity as completed for the student. 466 $completion = new \completion_info($course); 467 $completion->set_module_viewed($cm, $student->id); 468 469 // Create an action factory. 470 $factory = new \core_calendar\action_factory(); 471 472 // Decorate action event for the student. 473 $actionevent = mod_scorm_core_calendar_provide_event_action($event, $factory, $student->id); 474 475 // Ensure result was null. 476 $this->assertNull($actionevent); 477 } 478 479 /** 480 * Creates an action event. 481 * 482 * @param int $courseid 483 * @param int $instanceid The data id. 484 * @param string $eventtype The event type. eg. DATA_EVENT_TYPE_OPEN. 485 * @param int|null $timestart The start timestamp for the event 486 * @return bool|calendar_event 487 */ 488 private function create_action_event($courseid, $instanceid, $eventtype, $timestart = null) { 489 $event = new \stdClass(); 490 $event->name = 'Calendar event'; 491 $event->modulename = 'scorm'; 492 $event->courseid = $courseid; 493 $event->instance = $instanceid; 494 $event->type = CALENDAR_EVENT_TYPE_ACTION; 495 $event->eventtype = $eventtype; 496 $event->eventtype = $eventtype; 497 498 if ($timestart) { 499 $event->timestart = $timestart; 500 } else { 501 $event->timestart = time(); 502 } 503 504 return \calendar_event::create($event); 505 } 506 507 /** 508 * Test the callback responsible for returning the completion rule descriptions. 509 * This function should work given either an instance of the module (cm_info), such as when checking the active rules, 510 * or if passed a stdClass of similar structure, such as when checking the the default completion settings for a mod type. 511 */ 512 public function test_mod_scorm_completion_get_active_rule_descriptions() { 513 $this->resetAfterTest(); 514 $this->setAdminUser(); 515 516 // Two activities, both with automatic completion. One has the 'completionsubmit' rule, one doesn't. 517 $course = $this->getDataGenerator()->create_course(['enablecompletion' => 2]); 518 $scorm1 = $this->getDataGenerator()->create_module('scorm', [ 519 'course' => $course->id, 520 'completion' => 2, 521 'completionstatusrequired' => 6, 522 'completionscorerequired' => 5, 523 'completionstatusallscos' => 1 524 ]); 525 $scorm2 = $this->getDataGenerator()->create_module('scorm', [ 526 'course' => $course->id, 527 'completion' => 2, 528 'completionstatusrequired' => null, 529 'completionscorerequired' => null, 530 'completionstatusallscos' => null 531 ]); 532 $cm1 = \cm_info::create(get_coursemodule_from_instance('scorm', $scorm1->id)); 533 $cm2 = \cm_info::create(get_coursemodule_from_instance('scorm', $scorm2->id)); 534 535 // Data for the stdClass input type. 536 // This type of input would occur when checking the default completion rules for an activity type, where we don't have 537 // any access to cm_info, rather the input is a stdClass containing completion and customdata attributes, just like cm_info. 538 $moddefaults = new \stdClass(); 539 $moddefaults->customdata = ['customcompletionrules' => [ 540 'completionstatusrequired' => 6, 541 'completionscorerequired' => 5, 542 'completionstatusallscos' => 1 543 ]]; 544 $moddefaults->completion = 2; 545 546 // Determine the selected statuses using a bitwise operation. 547 $cvalues = array(); 548 foreach (scorm_status_options(true) as $key => $value) { 549 if (($scorm1->completionstatusrequired & $key) == $key) { 550 $cvalues[] = $value; 551 } 552 } 553 $statusstring = implode(', ', $cvalues); 554 555 $activeruledescriptions = [ 556 get_string('completionstatusrequireddesc', 'scorm', $statusstring), 557 get_string('completionscorerequireddesc', 'scorm', $scorm1->completionscorerequired), 558 get_string('completionstatusallscos', 'scorm'), 559 ]; 560 $this->assertEquals(mod_scorm_get_completion_active_rule_descriptions($cm1), $activeruledescriptions); 561 $this->assertEquals(mod_scorm_get_completion_active_rule_descriptions($cm2), []); 562 $this->assertEquals(mod_scorm_get_completion_active_rule_descriptions($moddefaults), $activeruledescriptions); 563 $this->assertEquals(mod_scorm_get_completion_active_rule_descriptions(new \stdClass()), []); 564 } 565 566 /** 567 * An unkown event type should not change the scorm instance. 568 */ 569 public function test_mod_scorm_core_calendar_event_timestart_updated_unknown_event() { 570 global $CFG, $DB; 571 require_once($CFG->dirroot . "/calendar/lib.php"); 572 573 $this->resetAfterTest(true); 574 $this->setAdminUser(); 575 $generator = $this->getDataGenerator(); 576 $course = $generator->create_course(); 577 $scormgenerator = $generator->get_plugin_generator('mod_scorm'); 578 $timeopen = time(); 579 $timeclose = $timeopen + DAYSECS; 580 $scorm = $scormgenerator->create_instance(['course' => $course->id]); 581 $scorm->timeopen = $timeopen; 582 $scorm->timeclose = $timeclose; 583 $DB->update_record('scorm', $scorm); 584 585 // Create a valid event. 586 $event = new \calendar_event([ 587 'name' => 'Test event', 588 'description' => '', 589 'format' => 1, 590 'courseid' => $course->id, 591 'groupid' => 0, 592 'userid' => 2, 593 'modulename' => 'scorm', 594 'instance' => $scorm->id, 595 'eventtype' => SCORM_EVENT_TYPE_OPEN . "SOMETHING ELSE", 596 'timestart' => 1, 597 'timeduration' => 86400, 598 'visible' => 1 599 ]); 600 601 mod_scorm_core_calendar_event_timestart_updated($event, $scorm); 602 603 $scorm = $DB->get_record('scorm', ['id' => $scorm->id]); 604 $this->assertEquals($timeopen, $scorm->timeopen); 605 $this->assertEquals($timeclose, $scorm->timeclose); 606 } 607 608 /** 609 * A SCORM_EVENT_TYPE_OPEN event should update the timeopen property of 610 * the scorm activity. 611 */ 612 public function test_mod_scorm_core_calendar_event_timestart_updated_open_event() { 613 global $CFG, $DB; 614 require_once($CFG->dirroot . "/calendar/lib.php"); 615 616 $this->resetAfterTest(true); 617 $this->setAdminUser(); 618 $generator = $this->getDataGenerator(); 619 $course = $generator->create_course(); 620 $scormgenerator = $generator->get_plugin_generator('mod_scorm'); 621 $timeopen = time(); 622 $timeclose = $timeopen + DAYSECS; 623 $timemodified = 1; 624 $newtimeopen = $timeopen - DAYSECS; 625 $scorm = $scormgenerator->create_instance(['course' => $course->id]); 626 $scorm->timeopen = $timeopen; 627 $scorm->timeclose = $timeclose; 628 $scorm->timemodified = $timemodified; 629 $DB->update_record('scorm', $scorm); 630 631 // Create a valid event. 632 $event = new \calendar_event([ 633 'name' => 'Test event', 634 'description' => '', 635 'format' => 1, 636 'courseid' => $course->id, 637 'groupid' => 0, 638 'userid' => 2, 639 'modulename' => 'scorm', 640 'instance' => $scorm->id, 641 'eventtype' => SCORM_EVENT_TYPE_OPEN, 642 'timestart' => $newtimeopen, 643 'timeduration' => 86400, 644 'visible' => 1 645 ]); 646 647 // Trigger and capture the event when adding a contact. 648 $sink = $this->redirectEvents(); 649 650 mod_scorm_core_calendar_event_timestart_updated($event, $scorm); 651 652 $triggeredevents = $sink->get_events(); 653 $moduleupdatedevents = array_filter($triggeredevents, function($e) { 654 return is_a($e, 'core\event\course_module_updated'); 655 }); 656 657 $scorm = $DB->get_record('scorm', ['id' => $scorm->id]); 658 // Ensure the timeopen property matches the event timestart. 659 $this->assertEquals($newtimeopen, $scorm->timeopen); 660 // Ensure the timeclose isn't changed. 661 $this->assertEquals($timeclose, $scorm->timeclose); 662 // Ensure the timemodified property has been changed. 663 $this->assertNotEquals($timemodified, $scorm->timemodified); 664 // Confirm that a module updated event is fired when the module 665 // is changed. 666 $this->assertNotEmpty($moduleupdatedevents); 667 } 668 669 /** 670 * A SCORM_EVENT_TYPE_CLOSE event should update the timeclose property of 671 * the scorm activity. 672 */ 673 public function test_mod_scorm_core_calendar_event_timestart_updated_close_event() { 674 global $CFG, $DB; 675 require_once($CFG->dirroot . "/calendar/lib.php"); 676 677 $this->resetAfterTest(true); 678 $this->setAdminUser(); 679 $generator = $this->getDataGenerator(); 680 $course = $generator->create_course(); 681 $scormgenerator = $generator->get_plugin_generator('mod_scorm'); 682 $timeopen = time(); 683 $timeclose = $timeopen + DAYSECS; 684 $timemodified = 1; 685 $newtimeclose = $timeclose + DAYSECS; 686 $scorm = $scormgenerator->create_instance(['course' => $course->id]); 687 $scorm->timeopen = $timeopen; 688 $scorm->timeclose = $timeclose; 689 $scorm->timemodified = $timemodified; 690 $DB->update_record('scorm', $scorm); 691 692 // Create a valid event. 693 $event = new \calendar_event([ 694 'name' => 'Test event', 695 'description' => '', 696 'format' => 1, 697 'courseid' => $course->id, 698 'groupid' => 0, 699 'userid' => 2, 700 'modulename' => 'scorm', 701 'instance' => $scorm->id, 702 'eventtype' => SCORM_EVENT_TYPE_CLOSE, 703 'timestart' => $newtimeclose, 704 'timeduration' => 86400, 705 'visible' => 1 706 ]); 707 708 // Trigger and capture the event when adding a contact. 709 $sink = $this->redirectEvents(); 710 711 mod_scorm_core_calendar_event_timestart_updated($event, $scorm); 712 713 $triggeredevents = $sink->get_events(); 714 $moduleupdatedevents = array_filter($triggeredevents, function($e) { 715 return is_a($e, 'core\event\course_module_updated'); 716 }); 717 718 $scorm = $DB->get_record('scorm', ['id' => $scorm->id]); 719 // Ensure the timeclose property matches the event timestart. 720 $this->assertEquals($newtimeclose, $scorm->timeclose); 721 // Ensure the timeopen isn't changed. 722 $this->assertEquals($timeopen, $scorm->timeopen); 723 // Ensure the timemodified property has been changed. 724 $this->assertNotEquals($timemodified, $scorm->timemodified); 725 // Confirm that a module updated event is fired when the module 726 // is changed. 727 $this->assertNotEmpty($moduleupdatedevents); 728 } 729 730 /** 731 * An unkown event type should not have any limits 732 */ 733 public function test_mod_scorm_core_calendar_get_valid_event_timestart_range_unknown_event() { 734 global $CFG, $DB; 735 require_once($CFG->dirroot . "/calendar/lib.php"); 736 737 $this->resetAfterTest(true); 738 $this->setAdminUser(); 739 $generator = $this->getDataGenerator(); 740 $course = $generator->create_course(); 741 $timeopen = time(); 742 $timeclose = $timeopen + DAYSECS; 743 $scorm = new \stdClass(); 744 $scorm->timeopen = $timeopen; 745 $scorm->timeclose = $timeclose; 746 747 // Create a valid event. 748 $event = new \calendar_event([ 749 'name' => 'Test event', 750 'description' => '', 751 'format' => 1, 752 'courseid' => $course->id, 753 'groupid' => 0, 754 'userid' => 2, 755 'modulename' => 'scorm', 756 'instance' => 1, 757 'eventtype' => SCORM_EVENT_TYPE_OPEN . "SOMETHING ELSE", 758 'timestart' => 1, 759 'timeduration' => 86400, 760 'visible' => 1 761 ]); 762 763 list ($min, $max) = mod_scorm_core_calendar_get_valid_event_timestart_range($event, $scorm); 764 $this->assertNull($min); 765 $this->assertNull($max); 766 } 767 768 /** 769 * The open event should be limited by the scorm's timeclose property, if it's set. 770 */ 771 public function test_mod_scorm_core_calendar_get_valid_event_timestart_range_open_event() { 772 global $CFG, $DB; 773 require_once($CFG->dirroot . "/calendar/lib.php"); 774 775 $this->resetAfterTest(true); 776 $this->setAdminUser(); 777 $generator = $this->getDataGenerator(); 778 $course = $generator->create_course(); 779 $timeopen = time(); 780 $timeclose = $timeopen + DAYSECS; 781 $scorm = new \stdClass(); 782 $scorm->timeopen = $timeopen; 783 $scorm->timeclose = $timeclose; 784 785 // Create a valid event. 786 $event = new \calendar_event([ 787 'name' => 'Test event', 788 'description' => '', 789 'format' => 1, 790 'courseid' => $course->id, 791 'groupid' => 0, 792 'userid' => 2, 793 'modulename' => 'scorm', 794 'instance' => 1, 795 'eventtype' => SCORM_EVENT_TYPE_OPEN, 796 'timestart' => 1, 797 'timeduration' => 86400, 798 'visible' => 1 799 ]); 800 801 // The max limit should be bounded by the timeclose value. 802 list ($min, $max) = mod_scorm_core_calendar_get_valid_event_timestart_range($event, $scorm); 803 804 $this->assertNull($min); 805 $this->assertEquals($timeclose, $max[0]); 806 807 // No timeclose value should result in no upper limit. 808 $scorm->timeclose = 0; 809 list ($min, $max) = mod_scorm_core_calendar_get_valid_event_timestart_range($event, $scorm); 810 811 $this->assertNull($min); 812 $this->assertNull($max); 813 } 814 815 /** 816 * The close event should be limited by the scorm's timeopen property, if it's set. 817 */ 818 public function test_mod_scorm_core_calendar_get_valid_event_timestart_range_close_event() { 819 global $CFG, $DB; 820 require_once($CFG->dirroot . "/calendar/lib.php"); 821 822 $this->resetAfterTest(true); 823 $this->setAdminUser(); 824 $generator = $this->getDataGenerator(); 825 $course = $generator->create_course(); 826 $timeopen = time(); 827 $timeclose = $timeopen + DAYSECS; 828 $scorm = new \stdClass(); 829 $scorm->timeopen = $timeopen; 830 $scorm->timeclose = $timeclose; 831 832 // Create a valid event. 833 $event = new \calendar_event([ 834 'name' => 'Test event', 835 'description' => '', 836 'format' => 1, 837 'courseid' => $course->id, 838 'groupid' => 0, 839 'userid' => 2, 840 'modulename' => 'scorm', 841 'instance' => 1, 842 'eventtype' => SCORM_EVENT_TYPE_CLOSE, 843 'timestart' => 1, 844 'timeduration' => 86400, 845 'visible' => 1 846 ]); 847 848 // The max limit should be bounded by the timeclose value. 849 list ($min, $max) = mod_scorm_core_calendar_get_valid_event_timestart_range($event, $scorm); 850 851 $this->assertEquals($timeopen, $min[0]); 852 $this->assertNull($max); 853 854 // No timeclose value should result in no upper limit. 855 $scorm->timeopen = 0; 856 list ($min, $max) = mod_scorm_core_calendar_get_valid_event_timestart_range($event, $scorm); 857 858 $this->assertNull($min); 859 $this->assertNull($max); 860 } 861 862 /** 863 * A user who does not have capabilities to add events to the calendar should be able to create a SCORM. 864 */ 865 public function test_creation_with_no_calendar_capabilities() { 866 $this->resetAfterTest(); 867 $course = self::getDataGenerator()->create_course(); 868 $context = \context_course::instance($course->id); 869 $user = self::getDataGenerator()->create_and_enrol($course, 'editingteacher'); 870 $roleid = self::getDataGenerator()->create_role(); 871 self::getDataGenerator()->role_assign($roleid, $user->id, $context->id); 872 assign_capability('moodle/calendar:manageentries', CAP_PROHIBIT, $roleid, $context, true); 873 $generator = self::getDataGenerator()->get_plugin_generator('mod_scorm'); 874 // Create an instance as a user without the calendar capabilities. 875 $this->setUser($user); 876 $time = time(); 877 $params = array( 878 'course' => $course->id, 879 'timeopen' => $time + 200, 880 'timeclose' => $time + 2000, 881 ); 882 $generator->create_instance($params); 883 } 884 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body