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