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